Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-05-15 18:12:13 +00:00
parent 7151b07ad4
commit 9d935a8ee1
128 changed files with 1462 additions and 751 deletions

View File

@ -592,7 +592,7 @@ lib/gitlab/checks/**
/doc/administration/credentials_inventory.md @idurham
/doc/administration/custom_html_header_tags.md @eread
/doc/administration/custom_project_templates.md @brendan777
/doc/administration/dedicated/ @emily.sahlani
/doc/administration/dedicated/ @lyspin
/doc/administration/dedicated/hosted_runners.md @rsarangadharan
/doc/administration/diff_limits.md @brendan777
/doc/administration/docs_self_host.md @axil
@ -649,8 +649,7 @@ lib/gitlab/checks/**
/doc/administration/operations/gitlab_sshd.md @brendan777
/doc/administration/operations/moving_repositories.md @eread
/doc/administration/package_information/ @axil
/doc/administration/packages/ @lyspin
/doc/administration/packages/_index.md @z_painter
/doc/administration/packages/ @z_painter
/doc/administration/pages/ @msedlakjakubowski
/doc/administration/polling.md @axil
/doc/administration/raketasks/ @axil
@ -738,12 +737,12 @@ lib/gitlab/checks/**
/doc/api/cluster_discovery.md @z_painter
/doc/api/code_suggestions.md @jglassman1
/doc/api/commits.md @brendan777
/doc/api/container_registry.md @lyspin
/doc/api/container_repository_protection_rules.md @lyspin
/doc/api/container_registry.md @z_painter
/doc/api/container_repository_protection_rules.md @z_painter
/doc/api/custom_attributes.md @msedlakjakubowski
/doc/api/dependencies.md @rdickenson
/doc/api/dependency_list_export.md @rlehmann1
/doc/api/dependency_proxy.md @lyspin
/doc/api/dependency_proxy.md @z_painter
/doc/api/deploy_keys.md @z_painter
/doc/api/deploy_tokens.md @z_painter
/doc/api/deployments.md @z_painter
@ -933,7 +932,7 @@ lib/gitlab/checks/**
/doc/ci/examples/ @lyspin
/doc/ci/examples/deployment/ @z_painter
/doc/ci/examples/semantic-release.md @z_painter
/doc/ci/gitlab_google_cloud_integration/ @lyspin
/doc/ci/gitlab_google_cloud_integration/ @z_painter
/doc/ci/inputs/ @marcel.amirault
/doc/ci/interactive_web_terminal/ @rsarangadharan
/doc/ci/jobs/ @marcel.amirault
@ -1046,7 +1045,7 @@ lib/gitlab/checks/**
/doc/solutions/integrations/servicenow.md @ashrafkhamis
/doc/subscriptions/ @lciutacu
/doc/subscriptions/gitlab_com/ @lyspin
/doc/subscriptions/gitlab_dedicated/ @emily.sahlani
/doc/subscriptions/gitlab_dedicated/ @lyspin
/doc/topics/ @msedlakjakubowski
/doc/topics/autodevops/ @z_painter
/doc/topics/git/ @brendan777
@ -1160,9 +1159,6 @@ lib/gitlab/checks/**
/doc/user/operations_dashboard/ @z_painter
/doc/user/organization/ @phillipwells
/doc/user/packages/ @z_painter
/doc/user/packages/container_registry/ @lyspin
/doc/user/packages/dependency_proxy/ @lyspin
/doc/user/packages/harbor_container_registry/ @lyspin
/doc/user/permissions.md @idurham
/doc/user/profile/_index.md @idurham
/doc/user/profile/account/ @idurham
@ -1196,8 +1192,8 @@ lib/gitlab/checks/**
/doc/user/project/integrations/confluence.md @msedlakjakubowski
/doc/user/project/integrations/git_guardian.md @brendan777
/doc/user/project/integrations/github.md @lyspin
/doc/user/project/integrations/google_artifact_management.md @lyspin
/doc/user/project/integrations/harbor.md @lyspin
/doc/user/project/integrations/google_artifact_management.md @z_painter
/doc/user/project/integrations/harbor.md @z_painter
/doc/user/project/integrations/matrix.md @sselhorn
/doc/user/project/issue_board.md @msedlakjakubowski
/doc/user/project/issues/ @msedlakjakubowski

View File

@ -47,6 +47,7 @@ After your merge request has been approved according to our [approval guidelines
- [ ] **IMPORTANT**: When this issue is ready for release (Default branch MR and backports are approved and ready to be merged), apply the ~"security-target" label.
- The `gitlab-release-tools-bot` evaluates and links issues with the label to the active [Security Tracking Issue]. If the bot finds the issue is not ready to be included in the patch release, it will leave a comment on the issue explaining what needs to be done.
- This issue will only be included in a patch release if it is successfully linked to the [Security Tracking Issue].
- Refer to the ["Release Information" dashboard](https://dashboards.gitlab.net/d/delivery-release_info/delivery3a-release-information?orgId=1) for information about the next patch release, including the targeted versions, expected release date, and current status.
## Documentation and final details

View File

@ -0,0 +1,160 @@
---
# Cop supports --autocorrect.
InternalAffairs/CopDescriptionWithExample:
Details: grace period
Exclude:
- 'rubocop/cop/active_model_errors_direct_manipulation.rb'
- 'rubocop/cop/active_record_association_reload.rb'
- 'rubocop/cop/api/base.rb'
- 'rubocop/cop/api/class_level_allow_access_with_scope.rb'
- 'rubocop/cop/api/ensure_string_detail.rb'
- 'rubocop/cop/api/grape_array_missing_coerce.rb'
- 'rubocop/cop/avoid_becomes.rb'
- 'rubocop/cop/avoid_keyword_arguments_in_sidekiq_workers.rb'
- 'rubocop/cop/background_migration/dictionary_file.rb'
- 'rubocop/cop/background_migration/feature_category.rb'
- 'rubocop/cop/code_reuse/finder.rb'
- 'rubocop/cop/code_reuse/presenter.rb'
- 'rubocop/cop/code_reuse/serializer.rb'
- 'rubocop/cop/code_reuse/service_class.rb'
- 'rubocop/cop/code_reuse/worker.rb'
- 'rubocop/cop/database/avoid_inheritance_column.rb'
- 'rubocop/cop/database/disable_referential_integrity.rb'
- 'rubocop/cop/database/establish_connection.rb'
- 'rubocop/cop/default_scope.rb'
- 'rubocop/cop/destroy_all.rb'
- 'rubocop/cop/experiments_test_coverage.rb'
- 'rubocop/cop/feature_flag_usage.rb'
- 'rubocop/cop/file_decompression.rb'
- 'rubocop/cop/filename_length.rb'
- 'rubocop/cop/gitlab/ai/order_constants.rb'
- 'rubocop/cop/gitlab/avoid_current_organization.rb'
- 'rubocop/cop/gitlab/avoid_feature_category_not_owned.rb'
- 'rubocop/cop/gitlab/avoid_gitlab_instance_checks.rb'
- 'rubocop/cop/gitlab/avoid_uploaded_file_from_params.rb'
- 'rubocop/cop/gitlab/bounded_contexts.rb'
- 'rubocop/cop/gitlab/bulk_insert.rb'
- 'rubocop/cop/gitlab/change_timezone.rb'
- 'rubocop/cop/gitlab/const_get_inherit_false.rb'
- 'rubocop/cop/gitlab/delegate_predicate_methods.rb'
- 'rubocop/cop/gitlab/documentation_links/hardcoded_url.rb'
- 'rubocop/cop/gitlab/ee_only_class.rb'
- 'rubocop/cop/gitlab/except.rb'
- 'rubocop/cop/gitlab/feature_available_usage.rb'
- 'rubocop/cop/gitlab/feature_flag_without_actor.rb'
- 'rubocop/cop/gitlab/finder_with_find_by.rb'
- 'rubocop/cop/gitlab/hard_delete_calls.rb'
- 'rubocop/cop/gitlab/http_v2.rb'
- 'rubocop/cop/gitlab/httparty.rb'
- 'rubocop/cop/gitlab/intersect.rb'
- 'rubocop/cop/gitlab/json.rb'
- 'rubocop/cop/gitlab/license_available_usage.rb'
- 'rubocop/cop/gitlab/mark_used_feature_flags.rb'
- 'rubocop/cop/gitlab/module_with_instance_variables.rb'
- 'rubocop/cop/gitlab/policy_rule_boolean.rb'
- 'rubocop/cop/gitlab/predicate_memoization.rb'
- 'rubocop/cop/gitlab/rails_logger.rb'
- 'rubocop/cop/gitlab/rspec/avoid_setup.rb'
- 'rubocop/cop/gitlab/service_response.rb'
- 'rubocop/cop/gitlab/strong_memoize_attr.rb'
- 'rubocop/cop/gitlab/union.rb'
- 'rubocop/cop/gitlab/without_reactive_cache.rb'
- 'rubocop/cop/graphql/authorize_types.rb'
- 'rubocop/cop/graphql/descriptions.rb'
- 'rubocop/cop/graphql/enum_names.rb'
- 'rubocop/cop/graphql/enum_values.rb'
- 'rubocop/cop/graphql/graphql_name_position.rb'
- 'rubocop/cop/graphql/id_type.rb'
- 'rubocop/cop/graphql/json_type.rb'
- 'rubocop/cop/graphql/old_types.rb'
- 'rubocop/cop/graphql/resolver_type.rb'
- 'rubocop/cop/group_public_or_visible_to_user.rb'
- 'rubocop/cop/include_sidekiq_worker.rb'
- 'rubocop/cop/inject_enterprise_edition_module.rb'
- 'rubocop/cop/migration/add_concurrent_foreign_key.rb'
- 'rubocop/cop/migration/add_concurrent_index.rb'
- 'rubocop/cop/migration/add_index.rb'
- 'rubocop/cop/migration/add_limit_to_text_columns.rb'
- 'rubocop/cop/migration/add_reference.rb'
- 'rubocop/cop/migration/add_timestamps.rb'
- 'rubocop/cop/migration/async_post_migrate_only.rb'
- 'rubocop/cop/migration/avoid_finalize_background_migration.rb'
- 'rubocop/cop/migration/background_migration_missing_active_concern.rb'
- 'rubocop/cop/migration/background_migration_record.rb'
- 'rubocop/cop/migration/background_migrations.rb'
- 'rubocop/cop/migration/batch_migrations_post_only.rb'
- 'rubocop/cop/migration/batched_migration_base_class.rb'
- 'rubocop/cop/migration/change_column_null_on_high_traffic_table.rb'
- 'rubocop/cop/migration/complex_indexes_require_name.rb'
- 'rubocop/cop/migration/create_table_with_foreign_keys.rb'
- 'rubocop/cop/migration/datetime.rb'
- 'rubocop/cop/migration/drop_table.rb'
- 'rubocop/cop/migration/migration_record.rb'
- 'rubocop/cop/migration/migration_with_milestone.rb'
- 'rubocop/cop/migration/prevent_adding_columns.rb'
- 'rubocop/cop/migration/prevent_feature_flags_usage.rb'
- 'rubocop/cop/migration/prevent_global_enable_lock_retries_with_disable_ddl_transaction.rb'
- 'rubocop/cop/migration/prevent_index_creation.rb'
- 'rubocop/cop/migration/prevent_strings.rb'
- 'rubocop/cop/migration/refer_to_index_by_name.rb'
- 'rubocop/cop/migration/remove_column.rb'
- 'rubocop/cop/migration/remove_concurrent_index.rb'
- 'rubocop/cop/migration/remove_index.rb'
- 'rubocop/cop/migration/safer_boolean_column.rb'
- 'rubocop/cop/migration/schedule_async.rb'
- 'rubocop/cop/migration/schema_addition_methods_no_post.rb'
- 'rubocop/cop/migration/sidekiq_queue_migrate.rb'
- 'rubocop/cop/migration/timestamps.rb'
- 'rubocop/cop/migration/unfinished_dependencies.rb'
- 'rubocop/cop/migration/update_column_in_batches.rb'
- 'rubocop/cop/migration/versioned_migration_class.rb'
- 'rubocop/cop/migration/with_lock_retries_disallowed_method.rb'
- 'rubocop/cop/migration/with_lock_retries_with_change.rb'
- 'rubocop/cop/performance/active_record_subtransaction_methods.rb'
- 'rubocop/cop/performance/active_record_subtransactions.rb'
- 'rubocop/cop/performance/ar_count_each.rb'
- 'rubocop/cop/performance/ar_exists_and_present_blank.rb'
- 'rubocop/cop/performance/readlines_each.rb'
- 'rubocop/cop/project_path_helper.rb'
- 'rubocop/cop/put_group_routes_under_scope.rb'
- 'rubocop/cop/put_project_routes_under_scope.rb'
- 'rubocop/cop/qa/ambiguous_page_object_name.rb'
- 'rubocop/cop/qa/element_with_pattern.rb'
- 'rubocop/cop/qa/fabricate_usage.rb'
- 'rubocop/cop/qa/feature_flags.rb'
- 'rubocop/cop/qa/selector_usage.rb'
- 'rubocop/cop/redis_queue_usage.rb'
- 'rubocop/cop/rspec/any_instance_of.rb'
- 'rubocop/cop/rspec/avoid_conditional_statements.rb'
- 'rubocop/cop/rspec/avoid_test_prof.rb'
- 'rubocop/cop/rspec/be_success_matcher.rb'
- 'rubocop/cop/rspec/before_all.rb'
- 'rubocop/cop/rspec/duplicate_spec_location.rb'
- 'rubocop/cop/rspec/env_assignment.rb'
- 'rubocop/cop/rspec/expect_gitlab_tracking.rb'
- 'rubocop/cop/rspec/factories_in_migration_specs.rb'
- 'rubocop/cop/rspec/factory_bot/avoid_create.rb'
- 'rubocop/cop/rspec/factory_bot/inline_association.rb'
- 'rubocop/cop/rspec/factory_bot/strategy_in_callback.rb'
- 'rubocop/cop/rspec/httparty_basic_auth.rb'
- 'rubocop/cop/rspec/modify_sidekiq_middleware.rb'
- 'rubocop/cop/rspec/top_level_describe_path.rb'
- 'rubocop/cop/rspec/web_mock_enable.rb'
- 'rubocop/cop/safe_params.rb'
- 'rubocop/cop/scalability/bulk_perform_with_context.rb'
- 'rubocop/cop/scalability/cron_worker_context.rb'
- 'rubocop/cop/scalability/file_uploads.rb'
- 'rubocop/cop/scalability/idempotent_worker.rb'
- 'rubocop/cop/scalability/random_cron_schedule.rb'
- 'rubocop/cop/sidekiq/enforce_database_health_signal_deferral.rb'
- 'rubocop/cop/sidekiq_api_usage.rb'
- 'rubocop/cop/sidekiq_load_balancing/worker_data_consistency.rb'
- 'rubocop/cop/sidekiq_options_queue.rb'
- 'rubocop/cop/sidekiq_redis_call.rb'
- 'rubocop/cop/static_translation_definition.rb'
- 'rubocop/cop/style/regexp_literal_mixed_preserve.rb'
- 'rubocop/cop/usage_data/distinct_count_by_large_foreign_key.rb'
- 'rubocop/cop/usage_data/histogram_with_large_table.rb'
- 'rubocop/cop/usage_data/instrumentation_superclass.rb'
- 'rubocop/cop/usage_data/large_table.rb'
- 'rubocop/cop/user_admin.rb'

View File

@ -1 +1 @@
bad4daa394053a0f74090abdda47f9035f4e3f9a
a6759e58c21ed1c1238cc9a67abfdb0de9f87bd8

View File

@ -645,7 +645,7 @@
{"name":"rubocop-rspec","version":"3.0.5","platform":"ruby","checksum":"c6a8e29fb1b00d227c32df159e92f5ebb9e0ff734e52955fb13aff5c74977e0f"},
{"name":"rubocop-rspec_rails","version":"2.30.0","platform":"ruby","checksum":"888112e83f9d7ef7ad2397e9d69a0b9614a4bae24f072c399804a180f80c4c46"},
{"name":"ruby-fogbugz","version":"0.3.0","platform":"ruby","checksum":"5e04cde474648f498a71cf1e1a7ab42c66b953862fbe224f793ec0a7a1d5f657"},
{"name":"ruby-lsp","version":"0.23.15","platform":"ruby","checksum":"5e3dd3e775ba477854e577dc4aa5f0d3d59f32d90f8622787f01080d4e84e09f"},
{"name":"ruby-lsp","version":"0.23.17","platform":"ruby","checksum":"d2b570a18cf76c24d75439f1a69e40c224ec4c523aba842cc434da4f7cb20e56"},
{"name":"ruby-lsp-rails","version":"0.3.31","platform":"ruby","checksum":"670aed466e54b5632e4907b8dedb91d8b144917c42513e013d656af175bf8c76"},
{"name":"ruby-lsp-rspec","version":"0.1.22","platform":"ruby","checksum":"e982edf5cd6ec1530c3f5fa7e423624ad00532ebeff7fc94e02c7516a9b759c0"},
{"name":"ruby-magic","version":"0.6.0","platform":"ruby","checksum":"7b2138877b7d23aff812c95564eba6473b74b815ef85beb0eb792e729a2b6101"},

View File

@ -1739,7 +1739,7 @@ GEM
ruby-fogbugz (0.3.0)
crack (~> 0.4)
multipart-post (~> 2.0)
ruby-lsp (0.23.15)
ruby-lsp (0.23.17)
language_server-protocol (~> 3.17.0)
prism (>= 1.2, < 2.0)
rbs (>= 3, < 4)

View File

@ -645,7 +645,7 @@
{"name":"rubocop-rspec","version":"3.0.5","platform":"ruby","checksum":"c6a8e29fb1b00d227c32df159e92f5ebb9e0ff734e52955fb13aff5c74977e0f"},
{"name":"rubocop-rspec_rails","version":"2.30.0","platform":"ruby","checksum":"888112e83f9d7ef7ad2397e9d69a0b9614a4bae24f072c399804a180f80c4c46"},
{"name":"ruby-fogbugz","version":"0.3.0","platform":"ruby","checksum":"5e04cde474648f498a71cf1e1a7ab42c66b953862fbe224f793ec0a7a1d5f657"},
{"name":"ruby-lsp","version":"0.23.15","platform":"ruby","checksum":"5e3dd3e775ba477854e577dc4aa5f0d3d59f32d90f8622787f01080d4e84e09f"},
{"name":"ruby-lsp","version":"0.23.17","platform":"ruby","checksum":"d2b570a18cf76c24d75439f1a69e40c224ec4c523aba842cc434da4f7cb20e56"},
{"name":"ruby-lsp-rails","version":"0.3.31","platform":"ruby","checksum":"670aed466e54b5632e4907b8dedb91d8b144917c42513e013d656af175bf8c76"},
{"name":"ruby-lsp-rspec","version":"0.1.22","platform":"ruby","checksum":"e982edf5cd6ec1530c3f5fa7e423624ad00532ebeff7fc94e02c7516a9b759c0"},
{"name":"ruby-magic","version":"0.6.0","platform":"ruby","checksum":"7b2138877b7d23aff812c95564eba6473b74b815ef85beb0eb792e729a2b6101"},

View File

@ -1739,7 +1739,7 @@ GEM
ruby-fogbugz (0.3.0)
crack (~> 0.4)
multipart-post (~> 2.0)
ruby-lsp (0.23.15)
ruby-lsp (0.23.17)
language_server-protocol (~> 3.17.0)
prism (>= 1.2, < 2.0)
rbs (>= 3, < 4)

View File

@ -116,12 +116,14 @@ export default {
:board="board"
:is-swimlanes-on="isSwimlanesOn"
:filters="filters"
class="gl-min-w-0"
@setFilters="$emit('setFilters', $event)"
/>
<epic-board-filtered-search
v-else
:board="board"
:filters="filters"
class="gl-min-w-0"
@setFilters="$emit('setFilters', $event)"
/>
</div>

View File

@ -3,18 +3,21 @@ import { nextTick } from 'vue';
import { GlForm, GlButton, GlFormGroup } from '@gitlab/ui';
import { VARIANT_DANGER, VARIANT_INFO, createAlert } from '~/alert';
import axios from '~/lib/utils/axios_utils';
import { convertObjectPropsToSnakeCase } from '~/lib/utils/common_utils';
import SetStatusForm from '~/set_status_modal/set_status_form.vue';
import SettingsSection from '~/vue_shared/components/settings/settings_section.vue';
import TimezoneDropdown from '~/vue_shared/components/timezone_dropdown/timezone_dropdown.vue';
import { isUserBusy, computedClearStatusAfterValue } from '~/set_status_modal/utils';
import { AVAILABILITY_STATUS } from '~/set_status_modal/constants';
import { i18n, statusI18n, timezoneI18n } from '../constants';
import { i18n, statusI18n, timezoneI18n, mainI18n } from '../constants';
import UserAvatar from './user_avatar.vue';
import UserMainSettings from './user_main_settings.vue';
export default {
components: {
UserAvatar,
UserMainSettings,
GlForm,
GlFormGroup,
GlButton,
@ -30,7 +33,13 @@ export default {
'currentClearStatusAfter',
'timezones',
'userTimezone',
'userMainSettings',
],
provide() {
return {
i18n: this.$options.i18n,
};
},
props: {
profilePath: {
type: String,
@ -52,6 +61,7 @@ export default {
clearStatusAfter: null,
},
timezone: this.userTimezone,
userMainSetting: this.userMainSettings,
};
},
computed: {
@ -88,6 +98,11 @@ export default {
formData.append('user[avatar]', this.avatarBlob, 'avatar.png');
}
const mainSettingForm = convertObjectPropsToSnakeCase(this.userMainSetting);
Object.entries(mainSettingForm).forEach(([key, value]) => {
formData.append(`user[${key}]`, value);
});
formData.append('user[timezone]', this.timezone);
try {
@ -138,11 +153,15 @@ export default {
onTimezoneInput(selectedTimezone) {
this.timezone = selectedTimezone.identifier || '';
},
onMainSettingChange(updatedUserSettings) {
this.userMainSetting = updatedUserSettings;
},
},
i18n: {
...i18n,
...statusI18n,
...timezoneI18n,
...mainI18n,
},
};
</script>
@ -175,13 +194,20 @@ export default {
:description="$options.i18n.setTimezoneDescription"
class="js-search-settings-section"
>
<gl-form-group :label="__('Timezone')" class="gl-md-form-input-lg">
<gl-form-group :label="$options.i18n.timezone" class="gl-md-form-input-lg">
<timezone-dropdown :value="timezone" :timezone-data="timezones" @input="onTimezoneInput" />
</gl-form-group>
</settings-section>
<!-- TODO: to implement profile editing form fields -->
<!-- It will be implemented in the upcoming MRs -->
<!-- Related issue: https://gitlab.com/gitlab-org/gitlab/-/issues/389918 -->
<settings-section
:heading="$options.i18n.mainTitle"
:description="$options.i18n.mainDescription"
class="js-search-settings-section"
>
<user-main-settings :user-settings="userMainSetting" @change="onMainSettingChange" />
</settings-section>
<div class="js-hide-when-nothing-matches-search settings-sticky-footer gl-flex gl-gap-3">
<gl-button
variant="confirm"

View File

@ -0,0 +1,173 @@
<script>
import { GlFormGroup, GlFormInput, GlFormTextarea, GlFormCheckbox, GlLink } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import { s__ } from '~/locale';
import { sanitize } from '~/lib/dompurify';
export default {
components: {
GlLink,
GlFormGroup,
GlFormInput,
GlFormTextarea,
GlFormCheckbox,
},
i18n: {
fullName: s__('Profiles|Full name'),
fullNameDescription: s__('Profiles|Enter your name, so people you know can recognize you.'),
fullNameSafeDescription: sanitize(s__('Profiles|No "&lt;" or "&gt;" characters, please.'), {
ALLOWED_TAGS: [],
}),
fullNameRequired: s__(
'Profiles|Using emoji in names seems fun, but please try to set a status message instead',
),
userId: s__('Profiles|User ID'),
pronouns: s__('Profiles|Pronouns'),
pronounsDescription: s__(
'Profiles|Enter your pronouns to let people know how to refer to you.',
),
pronunciation: s__('Profiles|Pronunciation'),
pronunciationDescription: s__(
'Profiles|Enter how your name is pronounced to help people address you correctly.',
),
websiteUrl: s__('Profiles|Website URL'),
websiteUrlPlaceholder: s__('Profiles|https://website.com'),
location: s__('Profiles|Location'),
locationPlaceholder: s__('Profiles|City, country'),
jobTitle: s__('Profiles|Job title'),
organization: s__('Profiles|Organization'),
organizationDescription: s__('Profiles|Who you represent or work for.'),
bio: s__('Profiles|Bio'),
bioDescription: s__('Profiles|Tell us about yourself in fewer than 250 characters.'),
privateProfile: s__('Profiles|Private profile'),
privateProfileLabel: s__(
"Profiles|Don't display activity-related personal information on your profile.",
),
privateProfileLink: s__('Profiles|what information is hidden?'),
privateContributions: s__('Profiles|Private contributions'),
privateContributionsLabel: s__('Profiles|Include private contributions on your profile'),
privateContributionsDescription: s__(
'Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information.',
),
achievements: s__('Profiles|Achievements'),
achievementsLabel: s__('Profiles|Display achievements on your profile'),
},
props: {
userSettings: {
type: Object,
required: true,
},
},
computed: {
form: {
get() {
return this.userSettings;
},
set(value) {
this.$emit('change', value);
},
},
nameState() {
return this.form.name.trim().length > 0;
},
fullNameDescription() {
return this.nameState
? `${this.$options.i18n.fullNameDescription} ${this.$options.i18n.fullNameSafeDescription}`
: '';
},
},
privatePageLink: helpPagePath('/user/profile/_index.md', {
anchor: 'make-your-user-profile-page-private',
}),
};
</script>
<template>
<div>
<gl-form-group
:label="$options.i18n.fullName"
:description="fullNameDescription"
:state="nameState"
:invalid-feedback="$options.i18n.fullNameRequired"
data-testid="full-name-group"
>
<gl-form-input v-model="form.name" width="lg" required :state="nameState" />
</gl-form-group>
<gl-form-group :label="$options.i18n.userId" data-testid="user-id-group">
<gl-form-input v-model="form.id" width="lg" readonly />
</gl-form-group>
<gl-form-group
:label="$options.i18n.pronouns"
:description="$options.i18n.pronounsDescription"
data-testid="pronouns-group"
>
<gl-form-input v-model="form.pronouns" width="lg" />
</gl-form-group>
<gl-form-group
:label="$options.i18n.pronunciation"
:description="$options.i18n.pronunciationDescription"
data-testid="pronunciation-group"
>
<gl-form-input v-model="form.pronunciation" width="lg" />
</gl-form-group>
<gl-form-group :label="$options.i18n.websiteUrl" data-testid="website-url-group">
<gl-form-input
v-model="form.websiteUrl"
width="lg"
:placeholder="$options.i18n.websiteUrlPlaceholder"
/>
</gl-form-group>
<gl-form-group :label="$options.i18n.location" data-testid="location-group">
<gl-form-input
v-model="form.location"
width="lg"
:placeholder="$options.i18n.locationPlaceholder"
/>
</gl-form-group>
<gl-form-group :label="$options.i18n.jobTitle" data-testid="job-title-group">
<gl-form-input v-model="form.jobTitle" width="lg" />
</gl-form-group>
<gl-form-group :label="$options.i18n.organization" data-testid="organization-group">
<gl-form-input v-model="form.organization" width="lg" />
<template #description>{{ $options.i18n.organizationDescription }}</template>
</gl-form-group>
<gl-form-group
:label="$options.i18n.bio"
:description="$options.i18n.bioDescription"
data-testid="bio-group"
class="gl-mb-6 gl-max-w-80"
>
<gl-form-textarea v-model="form.bio" rows="4" maxlength="250" />
</gl-form-group>
<div class="gl-border-t gl-pt-6">
<gl-form-group :label="$options.i18n.privateProfile" data-testid="private-profile-group">
<gl-form-checkbox v-model="form.privateProfile" data-testid="private-profile-checkbox">
{{ $options.i18n.privateProfileLabel }}
<gl-link :href="$options.privatePageLink" data-testid="private-profile-link">{{
$options.i18n.privateProfileLink
}}</gl-link>
</gl-form-checkbox>
</gl-form-group>
<gl-form-group
:label="$options.i18n.privateContributions"
data-testid="private-contributions-group"
>
<gl-form-checkbox v-model="form.includePrivateContributions">
{{ $options.i18n.privateContributionsLabel }}
<template #help>
{{ $options.i18n.privateContributionsDescription }}
</template>
</gl-form-checkbox>
</gl-form-group>
<gl-form-group
:label="$options.i18n.achievements"
class="gl-mb-0"
data-testid="achievements-group"
>
<gl-form-checkbox v-model="form.achievementsEnabled">
{{ $options.i18n.achievementsLabel }}
</gl-form-checkbox>
</gl-form-group>
</div>
</div>
</template>

View File

@ -32,6 +32,12 @@ export const statusI18n = {
export const timezoneI18n = {
setTimezoneTitle: s__('Profiles|Time settings'),
setTimezoneDescription: s__('Profiles|Set your local time zone.'),
timezone: s__('Profiles|Timezone'),
};
export const mainI18n = {
mainTitle: s__('Profiles|Main settings'),
mainDescription: s__('Profiles|This information will appear on your profile.'),
};
export const i18n = {

View File

@ -3,7 +3,7 @@ import { parseBoolean } from '~/lib/utils/common_utils';
import ProfileEditApp from './components/profile_edit_app.vue';
export const initProfileEdit = () => {
const mountEl = document.querySelector('.js-user-profile');
const mountEl = document.querySelector('.js-user-profile-edit');
if (!mountEl) return false;
@ -17,6 +17,18 @@ export const initProfileEdit = () => {
currentClearStatusAfter,
timezones,
userTimezone,
userId,
name,
pronouns,
pronunciation,
websiteUrl,
location,
jobTitle,
organization,
bio,
privateProfile,
includePrivateContributions,
achievementsEnabled,
...provides
} = mountEl.dataset;
@ -25,6 +37,17 @@ export const initProfileEdit = () => {
name: 'ProfileEditRoot',
provide: {
...provides,
name,
pronouns,
pronunciation,
websiteUrl,
location,
jobTitle,
organization,
bio,
privateProfile: parseBoolean(privateProfile),
includePrivateContributions: parseBoolean(includePrivateContributions),
achievementsEnabled: parseBoolean(achievementsEnabled),
currentEmoji,
currentMessage,
currentAvailability,
@ -35,6 +58,20 @@ export const initProfileEdit = () => {
gravatarLink: JSON.parse(provides.gravatarLink),
timezones: JSON.parse(timezones),
userTimezone,
userMainSettings: {
id: provides.id,
name,
pronouns,
pronunciation,
websiteUrl,
location,
jobTitle,
organization,
bio,
privateProfile,
includePrivateContributions,
achievementsEnabled,
},
},
render(createElement) {
return createElement(ProfileEditApp, {

View File

@ -178,9 +178,10 @@ export default {
this.currentStep = 1;
}
},
onSelectNamespace({ id, fullPath }) {
onSelectNamespace({ id, fullPath, visibility }) {
this.namespace.id = id;
this.namespace.fullPath = fullPath;
this.namespace.visibility = visibility;
this.showValidation = false;
},
},

View File

@ -170,7 +170,6 @@ export default {
fullPath: namespace.fullPath,
isPersonal: namespace.fullPath === this.userNamespaceFullPath,
});
this.setNamespace(namespace);
},
handleSelectTemplate(id, fullPath) {

View File

@ -1,8 +1,21 @@
<script>
import { GlFormGroup, GlFormInput, GlFormSelect, GlSprintf, GlLink } from '@gitlab/ui';
import { GlFormGroup, GlFormInput, GlFormSelect, GlSprintf, GlLink, GlIcon } from '@gitlab/ui';
import { kebabCase } from 'lodash';
import { s__ } from '~/locale';
import { helpPagePath } from '~/helpers/help_page_helper';
import validation, { initForm } from '~/vue_shared/directives/validation';
import SingleChoiceSelector from '~/vue_shared/components/single_choice_selector.vue';
import SingleChoiceSelectorItem from '~/vue_shared/components/single_choice_selector_item.vue';
import {
VISIBILITY_LEVEL_LABELS,
VISIBILITY_LEVEL_PRIVATE_STRING,
VISIBILITY_LEVEL_INTERNAL_STRING,
VISIBILITY_LEVEL_PUBLIC_STRING,
VISIBILITY_TYPE_ICON,
PROJECT_VISIBILITY_LEVEL_DESCRIPTIONS,
VISIBILITY_LEVELS_INTEGER_TO_STRING,
VISIBILITY_LEVELS_STRING_TO_INTEGER,
} from '~/visibility_level/constants';
import { K8S_OPTION, DEPLOYMENT_TARGET_SELECTIONS } from '../form_constants';
import NewProjectDestinationSelect from './project_destination_select.vue';
@ -13,11 +26,22 @@ export default {
GlFormSelect,
GlSprintf,
GlLink,
GlIcon,
NewProjectDestinationSelect,
SingleChoiceSelector,
SingleChoiceSelectorItem,
},
directives: {
validation: validation(),
},
inject: {
restrictedVisibilityLevels: {
default: [],
},
defaultProjectVisibility: {
default: VISIBILITY_LEVEL_PRIVATE_STRING,
},
},
props: {
namespace: {
type: Object,
@ -35,12 +59,19 @@ export default {
form,
selectedNamespace: this.namespace,
selectedTarget: null,
visibilityLevels: [],
};
},
computed: {
isK8sOptionSelected() {
return this.selectedTarget === K8S_OPTION.value;
},
defaultVisibilityLevel() {
return VISIBILITY_LEVELS_INTEGER_TO_STRING[this.defaultProjectVisibility];
},
},
mounted() {
this.setVisibilityLevelsOptions();
},
methods: {
updateSlug() {
@ -48,10 +79,43 @@ export default {
},
onSelectNamespace(newNamespace) {
this.$emit('onSelectNamespace', newNamespace);
this.setVisibilityLevelsOptions();
},
setVisibilityLevelsOptions() {
this.visibilityLevels = [
this.visibilityLevelInfo(VISIBILITY_LEVEL_PRIVATE_STRING),
this.visibilityLevelInfo(VISIBILITY_LEVEL_INTERNAL_STRING),
this.visibilityLevelInfo(VISIBILITY_LEVEL_PUBLIC_STRING),
];
},
visibilityLevelInfo(visibilityLevelString) {
const visibilityLevelInteger = VISIBILITY_LEVELS_STRING_TO_INTEGER[visibilityLevelString];
let disableMessage = '';
if (this.restrictedVisibilityLevels.includes(visibilityLevelInteger)) {
disableMessage = s__(
'VisibilityLevel|This visibility level has been restricted by your administrator.',
);
} else if (
visibilityLevelInteger > VISIBILITY_LEVELS_STRING_TO_INTEGER[this.namespace.visibility]
) {
disableMessage = s__(
'VisibilityLevel|This visibility level is not allowed because the parent group has a more restrictive visibility level.',
);
}
return {
title: VISIBILITY_LEVEL_LABELS[visibilityLevelString],
description: PROJECT_VISIBILITY_LEVEL_DESCRIPTIONS[visibilityLevelString],
icon: VISIBILITY_TYPE_ICON[visibilityLevelString],
value: visibilityLevelString,
disabled: disableMessage !== '',
disabledMessage: disableMessage,
id: 1,
};
},
},
helpPageK8s: helpPagePath('user/clusters/agent/_index'),
K8S_OPTION,
DEPLOYMENT_TARGET_SELECTIONS,
};
</script>
@ -168,6 +232,30 @@ export default {
</template>
</gl-form-group>
<!-- Visibility Level should be added in: https://gitlab.com/gitlab-org/gitlab/-/issues/514700 -->
<gl-form-group :label="s__('ProjectsNew|Visibility Level')">
<single-choice-selector :checked="defaultVisibilityLevel" name="project[visibility_level]">
<single-choice-selector-item
v-for="{
title,
description,
icon,
value,
disabled,
disabledMessage,
id,
} in visibilityLevels"
:id="id"
:key="value"
:value="value"
:description="description"
:disabled-message="disabledMessage"
:disabled="disabled"
:data-testid="`${value}-visibility-level`"
>
<gl-icon :name="icon" />
{{ title }}
</single-choice-selector-item>
</single-choice-selector>
</gl-form-group>
</div>
</template>

View File

@ -30,6 +30,8 @@ export function initNewProjectForm() {
canSelectNamespace,
canCreateProject,
userProjectLimit,
restrictedVisibilityLevels,
defaultProjectVisibility,
importHistoryPath,
importGitlabEnabled,
importGitlabImportPath,
@ -70,6 +72,8 @@ export function initNewProjectForm() {
canSelectNamespace: parseBoolean(canSelectNamespace),
canCreateProject: parseBoolean(canCreateProject),
userProjectLimit: parseInt(userProjectLimit, 10),
restrictedVisibilityLevels: JSON.parse(restrictedVisibilityLevels),
defaultProjectVisibility,
importHistoryPath,
importGitlabEnabled: parseBoolean(importGitlabEnabled),
importGitlabImportPath,

View File

@ -2,11 +2,13 @@
import { GlKeysetPagination } from '@gitlab/ui';
import PageSizeSelector from '~/vue_shared/components/page_size_selector.vue';
import { DEFAULT_PAGE_SIZE } from '~/todos/constants';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
export const CURSOR_CHANGED_EVENT = 'cursor-changed';
export default {
components: {
LocalStorageSync,
GlKeysetPagination,
PageSizeSelector,
},
@ -95,13 +97,15 @@ export default {
</script>
<template>
<div class="gl-relative gl-mt-6 gl-flex gl-justify-between md:gl-justify-center">
<gl-keyset-pagination v-bind="$props" @prev="prevPage" @next="nextPage" />
<local-storage-sync storage-key="todos-page-size" :value="pageSize" @input="handlePageSizeChange">
<div class="gl-relative gl-mt-6 gl-flex gl-justify-between md:gl-justify-center">
<gl-keyset-pagination v-bind="$props" @prev="prevPage" @next="nextPage" />
<page-size-selector
:value="pageSize"
class="gl-right-0 md:gl-absolute"
@input="handlePageSizeChange"
/>
</div>
<page-size-selector
:value="pageSize"
class="gl-right-0 md:gl-absolute"
@input="handlePageSizeChange"
/>
</div>
</local-storage-sync>
</template>

View File

@ -57,6 +57,18 @@ export const ORGANIZATION_VISIBILITY_TYPE = {
),
};
export const PROJECT_VISIBILITY_LEVEL_DESCRIPTIONS = {
[VISIBILITY_LEVEL_PUBLIC_STRING]: s__(
'VisibilityLevel|Project access must be granted explicitly to each user. If this project is part of a group, access is granted to members of the group.',
),
[VISIBILITY_LEVEL_INTERNAL_STRING]: s__(
'VisibilityLevel|The project can be accessed by any logged in user except external users.',
),
[VISIBILITY_LEVEL_PRIVATE_STRING]: s__(
'VisibilityLevel|The project can be accessed without any authentication.',
),
};
export const GROUP_VISIBILITY_LEVEL_DESCRIPTIONS = {
[VISIBILITY_LEVEL_PUBLIC_STRING]: s__(
'VisibilityLevel|The group and any public projects can be viewed without any authentication.',

View File

@ -193,8 +193,7 @@ class GroupsController < Groups::ApplicationController
message: format(
_("'%{group_name}' has been scheduled for deletion and will be deleted on %{date}."),
group_name: group.name,
# FIXME: Replace `group.marked_for_deletion_on` with `group` after https://gitlab.com/gitlab-org/gitlab/-/work_items/527085
date: helpers.permanent_deletion_date_formatted(group.marked_for_deletion_on)
date: helpers.permanent_deletion_date_formatted(group)
)
}
end

View File

@ -38,7 +38,7 @@ module Organizations
result = ::Groups::MarkForDeletionService.new(group, current_user).execute
if result[:status] == :success
removal_time = helpers.permanent_deletion_date_formatted(Date.current)
removal_time = helpers.permanent_deletion_date_formatted
message = _("'%{group_name}' has been scheduled for removal on %{removal_time}.")
render json: { message: format(message, group_name: group.name, removal_time: removal_time) }

View File

@ -207,8 +207,7 @@ class ProjectsController < Projects::ApplicationController
flash[:toast] = format(
_("Deleting project '%{project_name}'. All data will be removed on %{date}."),
project_name: @project.full_name,
# FIXME: Replace `project.marked_for_deletion_at` with `project` after https://gitlab.com/gitlab-org/gitlab/-/work_items/527085
date: helpers.permanent_deletion_date_formatted(@project.marked_for_deletion_at)
date: helpers.permanent_deletion_date_formatted(@project)
)
redirect_to dashboard_projects_path, status: :found
end

View File

@ -478,7 +478,7 @@ module Types
def permanent_deletion_date
return unless group.adjourned_deletion?
permanent_deletion_date_formatted(Date.current)
permanent_deletion_date_formatted
end
private

View File

@ -1050,7 +1050,7 @@ module Types
def permanent_deletion_date
return unless project.adjourned_deletion_configured?
permanent_deletion_date_formatted(Date.current)
permanent_deletion_date_formatted
end
private

View File

@ -90,18 +90,38 @@ module GroupsHelper
}
end
def delete_delayed_group_message(group)
safe_format(
_("This action will place this group, including its subgroups and projects, in a pending deletion state " \
"for %{deletion_adjourned_period} days, and delete it permanently on %{date}."),
deletion_adjourned_period: group.deletion_adjourned_period,
date: tag.strong(permanent_deletion_date_formatted)
)
end
def delete_immediately_group_scheduled_for_deletion_message(group)
safe_format(
_('This group is scheduled for deletion on %{date}. ' \
'This action will permanently delete this group, ' \
'including its subgroups and projects, %{strongOpen}immediately%{strongClose}. ' \
'This action cannot be undone.'),
date: tag.strong(permanent_deletion_date_formatted(group)),
strongOpen: '<strong>'.html_safe,
strongClose: '</strong>'.html_safe
)
end
def remove_group_message(group, permanently_remove)
return permanently_delete_group_message(group) if permanently_remove
return permanently_delete_group_message(group) unless group.adjourned_deletion?
return permanently_delete_group_message(group) if group.marked_for_deletion?
date = permanent_deletion_date_formatted(Date.current)
message = _("The contents of this group, its subgroups and projects will be permanently deleted after " \
"%{deletion_adjourned_period} days on %{date}. After this point, your data cannot be recovered.")
ERB::Util.html_escape(message) % {
date: tag.strong(date), deletion_adjourned_period: group.deletion_adjourned_period
}
safe_format(
_("The contents of this group, its subgroups and projects will be permanently deleted after " \
"%{deletion_adjourned_period} days on %{date}. After this point, your data cannot be recovered."),
deletion_adjourned_period: group.deletion_adjourned_period,
date: tag.strong(permanent_deletion_date_formatted)
)
end
def permanently_delete_group_message(group)

View File

@ -114,7 +114,7 @@ module NamespacesHelper
Rails.application.routes.url_helpers.group_usage_quotas_url(group.root_ancestor, *args)
end
def permanent_deletion_date_formatted(container_or_date, format: '%F')
def permanent_deletion_date_formatted(container_or_date = Date.current, format: '%F')
date =
if container_or_date.respond_to?(:self_deletion_scheduled_deletion_created_on)
container_or_date.self_deletion_scheduled_deletion_created_on

View File

@ -77,6 +77,18 @@ module ProfilesHelper
user_path: user_path(current_user),
timezones: timezone_data_with_unique_identifiers.to_json,
user_timezone: user.timezone,
id: user.id,
name: user.name,
pronouns: user.pronouns,
location: user.location,
pronunciation: user.pronunciation,
website_url: user.website_url,
job_title: user.job_title,
organization: user.organization,
bio: user.bio,
include_private_contributions: user.include_private_contributions?.to_s,
achievements_enabled: user.achievements_enabled.to_s,
private_profile: user.private_profile?.to_s,
**user_status_properties(user)
}
end

View File

@ -117,14 +117,17 @@ module ProjectsHelper
def remove_project_message(project)
if project.delayed_deletion_ready?
_("Deleting a project places it into a read-only state until %{date}, " \
"at which point the project will be permanently deleted. Are you ABSOLUTELY sure?"
) % { date: permanent_deletion_date_formatted(Date.current) }
format(
_("Deleting a project places it into a read-only state until %{date}, " \
"at which point the project will be permanently deleted. Are you ABSOLUTELY sure?"),
date: permanent_deletion_date_formatted(Date.current)
)
else
_(
"You are going to delete %{project_full_name}. Deleted projects " \
"CANNOT be restored! Are you ABSOLUTELY sure?"
) % { project_full_name: project.full_name }
format(
_("You are going to delete %{project_full_name}. Deleted projects " \
"CANNOT be restored! Are you ABSOLUTELY sure?"),
project_full_name: project.full_name
)
end
end
@ -653,7 +656,7 @@ module ProjectsHelper
def project_delete_delayed_button_data(project, button_text = nil)
project_delete_button_shared_data(project, button_text).merge({
restore_help_path: help_page_path('user/project/working_with_projects.md', anchor: 'restore-a-project'),
delayed_deletion_date: permanent_deletion_date_formatted(Date.current),
delayed_deletion_date: permanent_deletion_date_formatted,
form_path: project_path(project)
})
end
@ -754,38 +757,24 @@ module ProjectsHelper
dashboard_projects_landing_paths.include?(request.path) && !current_user.authorized_projects.exists?
end
def scheduled_for_deletion?(project)
project.marked_for_deletion_at.present?
def delete_delayed_project_message(project)
safe_format(
_("This action will place this project, including all its resources, in a pending deletion state " \
"for %{deletion_adjourned_period} days, and delete it permanently on %{date}."),
deletion_adjourned_period: project.deletion_adjourned_period,
date: tag.strong(permanent_deletion_date_formatted)
)
end
def delete_delayed_message(project)
date = permanent_deletion_date_formatted(Date.current)
if project.delayed_deletion_ready?
message = _("This action will place this project, including all its resources, in a pending deletion state " \
"for %{deletion_adjourned_period} days, and delete it permanently on %{date}.")
ERB::Util.html_escape(message) % delete_message_data(project).merge(date: tag.strong(date),
deletion_adjourned_period: project.deletion_adjourned_period)
else
delete_permanently_message
end
end
def delete_immediately_message(project)
return delete_permanently_message unless project.adjourned_deletion?
return delete_delayed_message(project) unless project.marked_for_deletion_on
date = permanent_deletion_date_formatted(project.marked_for_deletion_on)
message = _('This project is scheduled for deletion on %{date}. ' \
'This action will permanently delete this project, ' \
'including all its resources, %{strongOpen}immediately%{strongClose}. This action cannot be undone.')
ERB::Util.html_escape(message) % delete_message_data(project).merge(date: tag.strong(date))
end
def delete_permanently_message
_('This action will permanently delete this project, including all its resources.')
def delete_immediately_project_scheduled_for_deletion_message(project)
safe_format(
_('This project is scheduled for deletion on %{date}. ' \
'This action will permanently delete this project, ' \
'including all its resources, %{strongOpen}immediately%{strongClose}. This action cannot be undone.'),
date: tag.strong(permanent_deletion_date_formatted(project)),
strongOpen: '<strong>'.html_safe,
strongClose: '</strong>'.html_safe
)
end
def project_delete_immediately_button_data(project, button_text = nil)
@ -808,17 +797,6 @@ module ProjectsHelper
private
def delete_message_data(project)
{
project_path_with_namespace: project.path_with_namespace,
project: project.path,
strongOpen: '<strong>'.html_safe,
strongClose: '</strong>'.html_safe,
codeOpen: '<code>'.html_safe,
codeClose: '</code>'.html_safe
}
end
def project_delete_button_shared_data(project, button_text = nil)
merge_requests_count = Projects::AllMergeRequestsCountService.new(project).count
issues_count = Projects::AllIssuesCountService.new(project).count

View File

@ -22,7 +22,7 @@ module Emails
@group = ::Group.find(group_id)
@user = ::User.find(recipient_id)
@deletion_due_in_days = ::Gitlab::CurrentSettings.deletion_adjourned_period.days
@deletion_date = permanent_deletion_date_formatted(@group.marked_for_deletion_on, format: '%B %-d, %Y')
@deletion_date = permanent_deletion_date_formatted(@group, format: '%B %-d, %Y')
email_with_layout(
to: @user.email,

View File

@ -36,7 +36,7 @@ module Emails
@project = ::Project.find(project_id)
@user = ::User.find(recipient_id)
@deletion_due_in_days = ::Gitlab::CurrentSettings.deletion_adjourned_period.days
@deletion_date = permanent_deletion_date_formatted(@project.marked_for_deletion_on, format: '%B %-d, %Y')
@deletion_date = permanent_deletion_date_formatted(@project, format: '%B %-d, %Y')
email_with_layout(
to: @user.email,

View File

@ -62,25 +62,16 @@ module SystemNotes
#
# Returns the created Note object
def change_time_spent
update_activity_counter
time_spent = noteable.time_spent
if time_spent == :reset
body = "removed time spent"
create_note(NoteSummary.new(noteable, project, author, body, action: 'time_tracking'))
else
spent_at = noteable.spent_at&.to_date
parsed_time = Gitlab::TimeTrackingFormatter.output(time_spent.abs)
action = time_spent > 0 ? 'added' : 'subtracted'
text_parts = ["#{action} #{parsed_time} of time spent"]
text_parts << "at #{spent_at}" if spent_at && spent_at != DateTime.current.to_date
body = text_parts.join(' ')
spent_at = noteable.spent_at
time_spent_note(time_spent, spent_at)
end
if noteable.is_a?(Issue)
issue_activity_counter.track_issue_time_spent_changed_action(author: author, project: project)
end
create_note(NoteSummary.new(noteable, project, author, body, action: 'time_tracking'))
end
# Called when a timelog is added to an issuable
@ -96,35 +87,44 @@ module SystemNotes
# Returns the created Note object
def created_timelog(timelog)
time_spent = timelog.time_spent
spent_at = timelog.spent_at&.to_date
parsed_time = Gitlab::TimeTrackingFormatter.output(time_spent.abs)
action = time_spent > 0 ? 'added' : 'subtracted'
spent_at = timelog.spent_at
text_parts = ["#{action} #{parsed_time} of time spent"]
text_parts << "at #{spent_at}" if spent_at && spent_at != DateTime.current.to_date
body = text_parts.join(' ')
if noteable.is_a?(Issue)
issue_activity_counter.track_issue_time_spent_changed_action(author: author, project: project)
end
create_note(NoteSummary.new(noteable, project, author, body, action: 'time_tracking'))
update_activity_counter
time_spent_note(time_spent, spent_at)
end
def remove_timelog(timelog)
time_spent = timelog.time_spent
spent_at = timelog.spent_at&.to_date
spent_at = timelog.spent_at
parsed_time = Gitlab::TimeTrackingFormatter.output(time_spent)
body = "deleted #{parsed_time} of spent time"
body += " from #{spent_at}" if spent_at
body = "deleted #{parsed_time} of spent time from #{formatted_spent_at(spent_at)}"
create_note(NoteSummary.new(noteable, project, author, body, action: 'time_tracking'))
end
private
def update_activity_counter
return unless noteable.is_a?(Issue)
issue_activity_counter.track_issue_time_spent_changed_action(author: author, project: project)
end
def formatted_spent_at(spent_at)
spent_at ||= DateTime.current
timezone = author.timezone || Time.zone.name
spent_at.in_time_zone(timezone)
end
def time_spent_note(time_spent, spent_at)
parsed_time = Gitlab::TimeTrackingFormatter.output(time_spent.abs)
action = time_spent > 0 ? 'added' : 'subtracted'
body = "#{action} #{parsed_time} of time spent at #{formatted_spent_at(spent_at)}"
create_note(NoteSummary.new(noteable, project, author, body, action: 'time_tracking'))
end
def changed_date_body(changed_dates)
%w[start_date due_date].each_with_object([]) do |date_field, word_array|
next unless changed_dates.key?(date_field)
@ -157,14 +157,14 @@ module SystemNotes
def time_estimate_system_note
parsed_time = Gitlab::TimeTrackingFormatter.output(noteable.time_estimate)
previous_estimate = noteable.previous_changes['time_estimate']&.at(0) || 0
parsed_previous_restimate = Gitlab::TimeTrackingFormatter.output(previous_estimate)
parsed_previous_estimate = Gitlab::TimeTrackingFormatter.output(previous_estimate)
if previous_estimate == 0
"added time estimate of #{parsed_time}"
elsif noteable.time_estimate == 0
"removed time estimate of #{parsed_previous_restimate}"
"removed time estimate of #{parsed_previous_estimate}"
else
"changed time estimate to #{parsed_time} from #{parsed_previous_restimate}"
"changed time estimate to #{parsed_time} from #{parsed_previous_estimate}"
end
end
end

View File

@ -1,6 +1,5 @@
- return if group.marked_for_deletion?
- return if group.self_deletion_scheduled?
- remove_form_id = local_assigns.fetch(:remove_form_id, nil)
- date = permanent_deletion_date_formatted(Date.current)
= render Pajamas::CardComponent.new(body_options: { class: 'gl-bg-feedback-danger' }) do |c|
- c.with_header do
@ -9,7 +8,6 @@
- c.with_body do
= form_tag(group, method: :delete, id: remove_form_id) do
%p
= html_escape(_("This action will place this group, including its subgroups and projects, in a pending deletion state for %{deletion_delayed_period} days, and delete it permanently on %{date}.")) % { date: tag.strong(date), deletion_delayed_period: group.deletion_adjourned_period }
%p= delete_delayed_group_message(group)
= render 'groups/settings/remove_button', group: group, remove_form_id: remove_form_id

View File

@ -1,18 +1,16 @@
- return unless group.self_deletion_scheduled?
- remove_form_id = local_assigns.fetch(:remove_form_id, nil)
-# FIXME: Replace `Date.current` with `group` after https://gitlab.com/gitlab-org/gitlab/-/work_items/527085
- date = permanent_deletion_date_formatted(Date.current)
- if group.marked_for_deletion?
= render Pajamas::CardComponent.new(body_options: { class: 'gl-bg-feedback-danger' }) do |c|
- c.with_header do
.gl-flex.gl-grow
%h4.gl-text-base.gl-leading-24.gl-m-0.gl-text-feedback-danger= _('Delete group immediately')
= render Pajamas::CardComponent.new(body_options: { class: 'gl-bg-feedback-danger' }) do |c|
- c.with_header do
.gl-flex.gl-grow
%h4.gl-text-base.gl-leading-24.gl-m-0.gl-text-feedback-danger= _('Delete group immediately')
- c.with_body do
= form_tag(group, method: :delete, id: remove_form_id) do
%p
= html_escape(_("This group is scheduled for deletion on %{date}. This action will permanently delete this group, including its subgroups and projects, %{strong_open}immediately%{strong_close}. This action cannot be undone.")) % { date: tag.strong(date), strong_open: '<strong>'.html_safe, strong_close: '</strong>'.html_safe }
- c.with_body do
= form_tag(group, method: :delete, id: remove_form_id) do
%p= delete_immediately_group_scheduled_for_deletion_message(group)
= hidden_field_tag(:permanently_remove, true)
= hidden_field_tag(:permanently_remove, true)
= render 'groups/settings/remove_button', group: group, remove_form_id: remove_form_id, button_text: _('Delete group immediately')
= render 'groups/settings/remove_button', group: group, remove_form_id: remove_form_id, button_text: _('Delete group immediately')

View File

@ -1,11 +1,9 @@
- if @project.adjourned_deletion?
-# Adjourned deletion feature is configured and available globally or at the namespace level
- if scheduled_for_deletion?(@project)
-# Project has already been marked for delayed deletion, permanently delete this project
= render 'delete_immediately', button_text: _('Delete project immediately')
- if @project.delayed_deletion_ready?
- if @project.self_deletion_scheduled?
%p= delete_immediately_project_scheduled_for_deletion_message(@project)
#js-project-delete-button{ data: project_delete_immediately_button_data(@project, _('Delete project immediately')) }
- else
-# Mark for delayed deletion
= render 'delete_delayed'
- else
-# Adjourned deletion feature is not available, permanently delete the project
= render 'delete_immediately'
%p= _('This action will permanently delete this project, including all its resources.')
#js-project-delete-button{ data: project_delete_immediately_button_data(@project) }

View File

@ -1,2 +1,2 @@
%p= delete_delayed_message(@project)
%p= delete_delayed_project_message(@project)
#js-project-delayed-delete-button{ data: project_delete_delayed_button_data(@project) }

View File

@ -1,4 +0,0 @@
- button_text = local_assigns.fetch(:button_text, nil)
%p= delete_immediately_message(@project)
#js-project-delete-button{ data: project_delete_immediately_button_data(@project, button_text) }

View File

@ -25,6 +25,8 @@
can_select_namespace: current_user.can_select_namespace?.to_s,
can_create_project: current_user.can_create_project?.to_s,
user_project_limit: current_user.projects_limit,
restricted_visibility_levels: restricted_visibility_levels,
default_project_visibility: default_project_visibility,
import_history_path: import_history_index_path,
import_gitlab_enabled: gitlab_project_import_enabled?.to_s,
import_gitlab_import_path: new_import_gitlab_project_path,

View File

@ -2,8 +2,7 @@
- return unless context.scheduled_for_deletion_in_hierarchy_chain?
- context_pending_deletion = context.first_scheduled_for_deletion_in_hierarchy_chain
-# FIXME: Replace `context_pending_deletion.marked_for_deletion_on` with `context_pending_deletion` after https://gitlab.com/gitlab-org/gitlab/-/work_items/527085
- date = permanent_deletion_date_formatted(context_pending_deletion.marked_for_deletion_on)
- date = permanent_deletion_date_formatted(context_pending_deletion)
- context_name = context.is_a?(Group) ? _('group') : _('project')
- group_marked_for_deletion = _("This group and its subgroups and projects are pending deletion, and will be deleted on %{date}.").html_safe % { date: tag.strong(date) }

View File

@ -1,8 +1,7 @@
- return unless context.is_a?(Group) || context.is_a?(Project)
- return unless context.marked_for_deletion?
- return unless context.self_deletion_scheduled?
-# FIXME: Replace `context.marked_for_deletion_on` with `context` after https://gitlab.com/gitlab-org/gitlab/-/work_items/527085
- date = permanent_deletion_date_formatted(context.marked_for_deletion_on)
- date = permanent_deletion_date_formatted(context)
- if context.is_a?(Group)
- context_name = _('group')
@ -18,6 +17,6 @@
- c.with_body do
%p
= (_("This %{context} has been scheduled for deletion on %{strongStart}%{date}%{strongEnd}. To cancel the scheduled deletion, you can restore this %{context}, including all its resources.") % { context: context_name, strongStart: "<strong>", strongEnd: "</strong>", date: date }).html_safe
= safe_format(_("This %{context} has been scheduled for deletion on %{date}. To cancel the scheduled deletion, you can restore this %{context}, including all its resources."), context: context_name, date: tag.strong(date))
= render Pajamas::ButtonComponent.new(variant: :confirm, method: :post, href: restore_path) do
= _('Restore %{context}') % { context: context_name }

View File

@ -1,4 +1,4 @@
- if project.adjourned_deletion_configured? && scheduled_for_deletion?(project)
- if project.delayed_deletion_ready? && project.self_deletion_scheduled?
= render Pajamas::ButtonComponent.new(category: :tertiary,
href: project_restore_path(project),
method: :post,

View File

@ -1,4 +1,4 @@
- return unless project.adjourned_deletion_configured? && scheduled_for_deletion?(project)
- return unless project.delayed_deletion_ready? && project.self_deletion_scheduled?
.gl-flex.gl-items-center.gl-flex-wrap.project-title
%span.small
@ -6,6 +6,5 @@
= _("Marked For Deletion At - %{deletion_time}") % { deletion_time: marked_for_deletion_at }
.gl-flex.gl-items-center.gl-flex-wrap.project-title
%p.small
-# FIXME: Replace `project.marked_for_deletion_at` with `project` after https://gitlab.com/gitlab-org/gitlab/-/work_items/527085
- permanent_deletion_time = permanent_deletion_date_formatted(project.marked_for_deletion_at, format: Date::DATE_FORMATS[:medium])
- permanent_deletion_time = permanent_deletion_date_formatted(project, format: Date::DATE_FORMATS[:medium])
= _("Scheduled Deletion At - %{permanent_deletion_time}") % { permanent_deletion_time: permanent_deletion_time }

View File

@ -5,7 +5,7 @@
- @force_desktop_expanded_sidebar = true
- if Feature.enabled?(:edit_user_profile_vue, current_user)
.js-user-profile{ data: user_profile_data(@user) }
.js-user-profile-edit{ data: user_profile_data(@user) }
- else
= gitlab_ui_form_for @user, url: user_settings_profile_path, method: :put, html: { multipart: true, class: 'edit-user js-edit-user js-quick-submit gl-show-field-errors js-password-prompt-form', remote: true }, authenticity_token: true do |f|
= render ::Layouts::SettingsSectionComponent.new(s_("Profiles|Public avatar")) do |c|

View File

@ -5,7 +5,7 @@ clusterwide: false
column: remember_me_enabled
db_type: boolean
default: 'true'
description: Enable [**Remember me** setting](../administration/settings/account_and_limit_settings.md#turn-remember-me-on-or-off).
description: Enable [**Remember me** setting](../administration/settings/account_and_limit_settings.md#configure-the-remember-me-option).
[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/369133) in GitLab 16.0.
encrypted: false
gitlab_com_different_than_default: false

View File

@ -1,6 +1,8 @@
---
name: your_work_groups_vue
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/502477
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/183596
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/542790
milestone: '17.10'
group: group::organizations
type: wip

View File

@ -98,7 +98,7 @@ When a user is [impersonated](../admin_area.md#user-impersonation), their action
- Audit events include information about the impersonating administrator.
- Extra audit events are recorded for the start and end of the administrator's impersonation session.
![An audit event with an impersonated user.](../img/impersonated_audit_events_v15_7.png)
![An audit event with an impersonated user.](img/impersonated_audit_events_v15_7.png)
## Time zones

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -160,7 +160,7 @@ On the **secondary** site:
objects aren't yet replicated (shown in gray), consider giving the site more
time to complete
![Geo admin dashboard showing the synchronization status of a secondary site](../replication/img/geo_dashboard_v14_0.png)
![Geo admin dashboard showing the synchronization status of a secondary site](img/geo_dashboard_v14_0.png)
If any objects are failing to replicate, this should be investigated before
scheduling the maintenance window. Following a planned failover, anything that

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -86,7 +86,7 @@ On the **secondary** site:
objects aren't replicated (shown in gray), consider giving the site more
time to complete.
![Replication status](../../replication/img/geo_dashboard_v14_0.png)
![Replication status](img/geo_dashboard_v14_0.png)
If any objects are failing to replicate, this should be investigated before
scheduling the maintenance window. After a planned failover, anything that

View File

@ -71,7 +71,7 @@ and there should be no failures (shown in red). If a large proportion of
objects aren't yet replicated (shown in gray), consider giving the site more
time to complete.
![Geo admin dashboard showing the synchronization status of a secondary site.](../../replication/img/geo_dashboard_v14_0.png)
![Geo admin dashboard showing the synchronization status of a secondary site.](img/geo_dashboard_v14_0.png)
If any objects are failing to replicate, this should be investigated before
scheduling the maintenance window. After a planned failover, anything that

View File

@ -409,9 +409,9 @@ When missing files or inconsistencies are present, you can encounter entries in
The same errors are also reflected in the UI under **Admin > Geo > Sites** when reviewing the synchronization status of specific replicables. In this scenario, a specific *upload* is missing:
![The Geo Uploads replicable dashboard displaying all failed errors.](../img/geo_uploads_file_missing_v17_11.png)
![The Geo Uploads replicable dashboard displaying all failed errors.](img/geo_uploads_file_missing_v17_11.png)
![The Geo Uploads replicable dashboard displaying missing file error.](../img/geo_uploads_file_missing_details_v17_11.png)
![The Geo Uploads replicable dashboard displaying missing file error.](img/geo_uploads_file_missing_details_v17_11.png)
#### Clean up inconsistencies

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -322,7 +322,7 @@ secondary site is a read-only copy.
1. Select **Geo > Sites**.
1. Select **Add site**.
![Form to add a new secondary Geo site](../replication/img/adding_a_secondary_v15_8.png)
![Form to add a new secondary Geo site](img/adding_a_secondary_v15_8.png)
1. In **Name**, enter the value for `gitlab_rails['geo_node_name']` in
`/etc/gitlab/gitlab.rb`. The values must match exactly.
@ -397,7 +397,7 @@ The initial replication might take some time.
You can monitor the synchronization process on each Geo site from the primary
site **Geo Sites** dashboard in your browser.
![Geo admin dashboard showing the synchronization status of a secondary site.](../replication/img/geo_dashboard_v14_0.png)
![Geo admin dashboard showing the synchronization status of a secondary site.](img/geo_dashboard_v14_0.png)
## Configure the tracking database

View File

@ -592,7 +592,7 @@ You must manually replicate the secret file across all of your secondary sites,
1. Select **Geo > Sites**.
1. Select **Add site**.
![Form to add a new site with three input fields: Name, External URL, and Internal URL (optional).](../replication/img/adding_a_secondary_v15_8.png)
![Form to add a new site with three input fields: Name, External URL, and Internal URL (optional).](img/adding_a_secondary_v15_8.png)
1. In **Name**, enter the value for `gitlab_rails['geo_node_name']` in
`/etc/gitlab/gitlab.rb`. The values must match exactly.
@ -667,7 +667,7 @@ The initial replication might take some time.
You can monitor the synchronization process on each Geo site from the primary
site **Geo Sites** dashboard in your browser.
![The Geo Sites dashboard displaying the synchronization status.](../replication/img/geo_dashboard_v14_0.png)
![The Geo Sites dashboard displaying the synchronization status.](img/geo_dashboard_v14_0.png)
## Related topics

View File

@ -163,7 +163,7 @@ Configure the GitLab Duo feature and sub-feature to send queries to the configur
For example, for the code generation sub-feature under GitLab Duo Code Suggestions, you can select **Claude-3 on Bedrock deployment (Claude 3)**.
![GitLab Duo Self-Hosted Feature Configuration](../img/gitlab_duo_self_hosted_feature_configuration_v17_11.png)
![GitLab Duo Self-Hosted Feature Configuration](img/gitlab_duo_self_hosted_feature_configuration_v17_11.png)
#### GitLab Duo Chat sub-feature fall back configuration
@ -190,4 +190,4 @@ To disable a GitLab Duo feature or sub-feature:
For example, to specifically disable the `Write Test` and `Refactor Code` features, select **Disabled**:
![Disabling GitLab Duo Feature](../img/gitlab_duo_self_hosted_disable_feature_v17_11.png)
![Disabling GitLab Duo Feature](img/gitlab_duo_self_hosted_disable_feature_v17_11.png)

View File

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -122,7 +122,7 @@ The above blocks are converted to an HTML image tag with source pointing to the
Kroki instance. If the Kroki server is correctly configured, this should
render a nice diagram instead of the block:
![A PlantUML diagram rendered from example code.](../img/kroki_plantuml_diagram_v13_7.png)
![A PlantUML diagram rendered from example code.](img/kroki_plantuml_diagram_v13_7.png)
Kroki supports more than a dozen diagram libraries. Here's a few examples for AsciiDoc:
@ -153,7 +153,7 @@ digraph finite_state_machine {
....
```
![A GraphViz diagram generated from example code.](../img/kroki_graphviz_diagram_v13_7.png)
![A GraphViz diagram generated from example code.](img/kroki_graphviz_diagram_v13_7.png)
**C4 (based on PlantUML)**
@ -179,7 +179,7 @@ Rel(banking_system, mainframe, "Uses")
....
```
![A C4 PlantUML diagram generated from example code.](../img/kroki_c4_diagram_v13_7.png)
![A C4 PlantUML diagram generated from example code.](img/kroki_c4_diagram_v13_7.png)
<!-- vale gitlab_base.Spelling = NO -->
@ -206,4 +206,4 @@ Rel(banking_system, mainframe, "Uses")
....
```
![A Nomnoml diagram generated from example code.](../img/kroki_nomnoml_diagram_v13_7.png)
![A Nomnoml diagram generated from example code.](img/kroki_nomnoml_diagram_v13_7.png)

View File

@ -171,7 +171,7 @@ You can change how long users can remain signed in without activity.
{{< /alert >}}
If [Remember me](#turn-remember-me-on-or-off) is enabled, users' sessions can remain active for an indefinite period of time.
If [Remember me](#configure-the-remember-me-option) is enabled, users' sessions can remain active for an indefinite period of time.
For details, see [cookies used for sign-in](../../user/profile/_index.md#cookies-used-for-sign-in).
@ -195,7 +195,7 @@ By default, sessions expire a set amount of time after the session becomes inact
When the session duration is met, the session ends and the user is signed out even if:
- The user is still actively using the session.
- The user selected [remember me](#turn-remember-me-on-or-off) during sign in.
- The user selected [remember me](#configure-the-remember-me-option) during sign in.
1. On the left sidebar, at the bottom, select **Admin Area**.
1. Select **Settings > General**.
@ -204,7 +204,7 @@ When the session duration is met, the session ends and the user is signed out ev
After a session ends, a window prompts the user to sign in again.
### Turn Remember me on or off
### Configure the Remember me option
{{< history >}}

View File

@ -440,7 +440,7 @@ Prerequisites:
this list by authorization type.
1. Select **Save changes**.
## Disable user invitations
## Prevent invitations to groups and projects
{{< history >}}
@ -448,14 +448,16 @@ Prerequisites:
{{< /history >}}
You can disable the ability for non-administrators to invite users to groups or projects. After
You can remove the ability for non-administrators to invite users to all groups or projects on the instance. After
you configure this setting, only administrators can invite users to groups or projects on the instance.
You can also prevent user invitations for a specific group. For more information, see [prevent user invitations to a group](../../user/group/manage.md#prevent-invitations-to-a-group).
Prerequisites:
- You must be an administrator.
To disable user invitations:
To prevent invitations to an instance:
1. On the left sidebar, at the bottom, select **Admin**.
1. Select **Settings > General**.

View File

@ -39,8 +39,8 @@ Prerequisites:
- You must have the Owner or Maintainer role for the project.
- [Group membership lock]( ../user/group/access_and_permissions.md#prevent-members-from-being-added-to-projects-in-a-group) must be disabled.
- For GitLab Self-Managed instances:
- If [user sign-ups are disabled](../administration/settings/sign_up_restrictions.md#disable-new-sign-ups), an administrator must add the user.
- If [user invitations are disabled](../administration/settings/visibility_and_access_controls.md#disable-user-invitations), an administrator must add the user.
- If [new sign-ups are disabled](../administration/settings/sign_up_restrictions.md#disable-new-sign-ups), an administrator must add the user.
- If [user invitations are not allowed](../administration/settings/visibility_and_access_controls.md#prevent-invitations-to-groups-and-projects), an administrator must add the user.
- If [administrator approval for role promotions is enabled](../administration/settings/sign_up_restrictions.md#turn-on-administrator-approval-for-role-promotions), an administrator must approve the invitation.
```plaintext

View File

@ -688,7 +688,7 @@ to configure other related settings. These requirements are
| `recaptcha_site_key` | string | required by: `recaptcha_enabled` | Site key for reCAPTCHA. |
| `receptive_cluster_agents_enabled` | boolean | no | Enable receptive mode for GitLab Agents for Kubernetes. |
| `receive_max_input_size` | integer | no | Maximum push size (MB). |
| `remember_me_enabled` | boolean | no | Enable [**Remember me** setting](../administration/settings/account_and_limit_settings.md#turn-remember-me-on-or-off). [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/369133) in GitLab 16.0. |
| `remember_me_enabled` | boolean | no | Enable [**Remember me** setting](../administration/settings/account_and_limit_settings.md#configure-the-remember-me-option). [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/369133) in GitLab 16.0. |
| `repository_checks_enabled` | boolean | no | GitLab periodically runs `git fsck` in all project and wiki repositories to look for silent disk corruption issues. |
| `repository_size_limit` | integer | no | Size limit per repository (MB). Premium and Ultimate only. |
| `repository_storages_weighted` | hash of strings to integers | no | Hash of names of taken from `gitlab.yml` to [weights](../administration/repository_storage_paths.md#configure-where-new-repositories-are-stored). New projects are created in one of these stores, chosen by a weighted random selection. |

View File

@ -171,7 +171,7 @@ When you configure related JSON objects and use the template, the pipeline:
1. **Deploys to EC2**: The content is deployed on an [AWS EC2](https://aws.amazon.com/ec2/) instance,
as shown in this diagram:
![Shows the CF-Provision-and-Deploy-EC2 pipeline, including the steps of provisioning infrastructure, pushing artifacts to S3, and deploying to EC2.](../img/cf_ec2_diagram_v13_5.png)
![Shows the CF-Provision-and-Deploy-EC2 pipeline, including the steps of provisioning infrastructure, pushing artifacts to S3, and deploying to EC2.](img/cf_ec2_diagram_v13_5.png)
### Configure the template and JSON

View File

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -62,11 +62,11 @@ The [environment URL](../yaml/_index.md#environmenturl) is displayed in a few
places in GitLab:
- In a merge request as a link:
![Environment URL in merge request](../img/environments_mr_review_app_v11_10.png)
![Environment URL in merge request](img/environments_mr_review_app_v11_10.png)
- In the Environments view as a button:
![Open live environment from environments view](img/environments_open_live_environment_v14_8.png)
- In the Deployments view as a button:
![Environment URL in deployments](../img/deployments_view_v11_10.png)
![Environment URL in deployments](img/deployments_view_v11_10.png)
You can see this information in a merge request if:
@ -75,7 +75,7 @@ You can see this information in a merge request if:
For example:
![Environment URLs in merge request](../img/environments_link_url_mr_v10_1.png)
![Environment URLs in merge request](img/environments_link_url_mr_v10_1.png)
#### Go from source files to public pages

View File

@ -54,7 +54,7 @@ When you use the GitLab Kubernetes integration to deploy to a Kubernetes cluster
you can view cluster and namespace information. On the deployment
job page, it's displayed above the job trace:
![Deployment cluster information with cluster and namespace.](../img/environments_deployment_cluster_v12_8.png)
![Deployment cluster information with cluster and namespace.](img/environments_deployment_cluster_v12_8.png)
## Configure incremental rollouts

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -17,24 +17,25 @@ title: Fine-grained permissions for CI/CD job tokens
{{< details >}}
Tier: Free, Premium, Ultimate
Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
Status: Experiment
- Tier: Free, Premium, Ultimate
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
- Status: Beta
{{< /details >}}
{{< history >}}
- [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/15234) in GitLab 17.10. This feature is an [experiment](../../policy/development_stages_support.md#experiment).
- [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/15234) as an [experiment](../../policy/development_stages_support.md#experiment) in GitLab 17.10.
- [Changed](https://gitlab.com/groups/gitlab-org/-/epics/16199) from experiment to beta in GitLab 18.0.
{{< /history >}}
You can use fine-grained permissions to explicitly allow access to a limited set of API endpoints.
These permissions are applied to the CI/CD job tokens in a specified project.
This feature is an [experiment](../../policy/development_stages_support.md#experiment) and subject to change without notice. This feature is not ready for production use. If you want to use this feature, you should test outside of production first.
This feature is in [beta](../../policy/development_stages_support.md#beta).
## Enable use of fine-grained permissions
## Enable fine-grained permissions
Prerequisites:

View File

@ -65,7 +65,7 @@ Hosted runners for GitLab.com are configured as such:
The following graphic shows the architecture diagram of hosted runners for GitLab.com
![Hosted runners for GitLab.com architecture](../img/gitlab-hosted_runners_architecture_v17_0.png)
![Hosted runners for GitLab.com architecture](img/gitlab-hosted_runners_architecture_v17_0.png)
For more information on how runners are authenticating and executing the job payload, see [Runner Execution Flow](https://docs.gitlab.com/runner#runner-execution-flow).
@ -74,7 +74,7 @@ For more information on how runners are authenticating and executing the job pay
In addition to isolating runners on the network, each ephemeral runner VM only serves a single job and is deleted straight after the job execution.
In the following example, three jobs are executed in a project's pipeline. Each of these jobs runs in a dedicated ephemeral VM.
![Job isolation](../img/build_isolation_v17_9.png)
![Job isolation](img/build_isolation_v17_9.png)
The build job ran on `runner-ns46nmmj-project-43717858`, test job on `f131a6a2runner-new2m-od-project-43717858` and deploy job on `runner-tmand5m-project-43717858`.

View File

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@ -40,7 +40,7 @@ can [use Vault secrets in a CI job](#use-vault-secrets-in-a-ci-job).
The flow for using GitLab with HashiCorp Vault
is summarized by this diagram:
![Flow between GitLab and HashiCorp](../img/gitlab_vault_workflow_v13_4.png "How GitLab authenticates with HashiCorp Vault")
![Flow between GitLab and HashiCorp](img/gitlab_vault_workflow_v13_4.png "How GitLab authenticates with HashiCorp Vault")
1. Configure your vault and secrets.
1. Generate your JWT and provide it to your CI job.

View File

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -229,7 +229,7 @@ After a pipeline runs successfully, you can view code coverage results in:
- Merge request widget: See the coverage percentage and changes compared to the target branch.
![Merge request widget showing code coverage percentage](../img/pipelines_test_coverage_mr_widget_v17_3.png)
![Merge request widget showing code coverage percentage](img/pipelines_test_coverage_mr_widget_v17_3.png)
- Merge request diff: Review which lines are covered by tests. Available with Cobertura and JaCoCo reports.
- Pipeline jobs: Monitor coverage results for individual jobs.

View File

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@ -77,7 +77,7 @@ Customers may have multiple instances of Free tier, subject to some exceptions.
For the Free tier of GitLab.com, [there is a five-user maximum on a top-level namespace with private visibility](../user/free_user_limit.md) per customer or entity.
This five-user maximum is in the aggregate of any Free tier instances. So, for example, if a customer has one Free tier instance with five users,
that customer is prohibited from activating an additional Free tier instance of any user level since the five-user maximum has been met.
that customer is prohibited from activating an additional Free tier instance of any user level because the five-user maximum has been met.
For the Free tier of self-managed, there is no five-user maximum.
@ -147,7 +147,7 @@ The following scenarios reflect questions a customer may ask related to multiple
- Q: I want to buy a license for 50 total users, but want to split these users into two instances. Can I do this?
- A: Yes, provided it is for two GitLab Self-Managed instances, you can apply one Cloud Licensing activation code (or license key) to multiple GitLab Self-Managed instances,
provided that the users on the instances are the same, or are a subset of the total users. In this case, since there are 50 total or unique users, you may split
provided that the users on the instances are the same, or are a subset of the total users. In this case, because there are 50 total or unique users, you may split
those users into two subset instances.
#### Example 2

View File

@ -1,317 +1,13 @@
---
stage: Solutions Architecture
group: Solutions Architecture
info: This page is owned by the Solutions Architecture team.
title: Duo Workflow Use Case for Applying Coding Style
redirect_to: 'duo_workflow/duo_workflow_codestyle.md'
remove_date: '2025-08-15'
---
{{< details >}}
<!-- markdownlint-disable -->
- Tier: Ultimate with GitLab Duo Workflow
- Offering: GitLab.com
- Status: Experiment
This document was moved to [another location](duo_workflow/duo_workflow_codestyle.md).
{{< /details >}}
## Getting Started
### Download the Solution Component
1. Obtain the invitation code from your account team.
1. Download the solution component from [the solution component webstore](https://cloud.gitlab-accelerator-marketplace.com) by using your invitation code.
## Duo Workflow Use Case: Improve Java Application with Style Guide
The document describes GitLab Duo Workflow Solution with prompt and context library. The purpose of the solution is to improve appliction coding based on defined style.
This solution provides a GitLab issue as the prompt and the style guide as the context, designed to automate Java style guidelines to codebases using GitLab Duo Workflow. The prompt and context library enables Duo Workflow to:
1. Access centralized style guide content stored in GitLab repository,
1. Understand domain-specific coding standards, and
1. Apply consistent formatting to Java code while preserving functionality.
For detailed information about GitLab Duo Workflow, review [the document here](../../user/duo_workflow/_index.md).
### Key Benefits
- **Enforces consistent style** across all Java codebases
- **Automates style application** without manual effort
- **Maintains code functionality** while improving readability
- **Integrates with GitLab Workflow** for seamless implementation
- **Reduces code review time** spent addressing style issues
- **Serves as a learning tool** for developers to understand style guidelines
### Sample Result
When properly configured, the prompt will transform your code to match enterprise standards, similar to the transformation shown in this diff:
![Duo Workflow Output](img/duoworkflow-style_output_v17_10.png)
![Style Guide Applied](img/duoworkflow_style_code_transform_v17_10.png)
## Configure the Solutio Prompt and Context Library
### Basic Setup
To run the agentic workflow to review and apply style to your application, you need to set up this use case prompt and context library.
1. **Set up the prompt and contet library** by cloning `Enterprise Code Quality Standards` project
1. **Create a GitLab issue** `Review and Apply Style` with the prompt content from the library file `.gitlab/workflows/java-style-workflow.md`
1. **In the issue** `Review and Apply Style` configure the workflow variables as detailed in the [Configuration section](#configuration-guide)
1. **In your VS code** with the project `Enterprise Code Quality Standards`, start the Duo Workflow with a simple [workflow prompt](#example-duo-workflow-prompt)
1. **Work with the Duo Workflow** by reviewing the proposed plan and automated tasks, if needed add further input to the workflow
1. **Review and commit** the styled code changes to your repository
### Example Duo Workflow Prompt
```yaml
Follow the instructions in issue <issue_reference_id> for the file <path/file_name.java>. Make sure to access any issues or GitLab projects mentioned in the issue to retrieve all necessary information.
```
This simple prompt is powerful because it instructs Duo Workflow to:
1. Read the detailed requirements in a specific issue ID
1. Access the referenced style guide repository
1. Apply the guidelines to the specified file
1. Follow all instructions in the issue
## Configuration Guide
The prompt is defined in the `.gitlab/workflows/java-style-workflow.md` file in the solution package. This file serves as your template for creating GitLab issues that instruct the workflow agent to build out the plan to automate the style guide review on your application and apply the changes.
In the first section of `.gitlab/workflows/java-style-workflow.md`, it defines variables you need to configure for the prompt.
### Variable Definition
The variables are defined directly in the `.gitlab/workflows/java-style-workflow.md` file. This file serves as your template for creating GitLab issues that instruct the AI assistant. You'll modify the variables in this file before creating a new issue with its contents.
#### 1. Style Guide Repository as the Context
The prompt must be configured to point to your organization's style guide repository. In the `java-style-prompt.md` file, replace the following variables:
- `{{GITLAB_INSTANCE}}`: Your GitLab instance URL (e.g., `https://gitlab.example.com`)
- `{{STYLE_GUIDE_PROJECT_ID}}`: The GitLab project ID containing your Java style guide
- `{{STYLE_GUIDE_PROJECT_NAME}}`: The display name for your style guide project
- `{{STYLE_GUIDE_BRANCH}}`: The branch containing the most up-to-date style guide (default: main)
- `{{STYLE_GUIDE_PATH}}`: The path to the style guide document within the repository
Example:
```yaml
GITLAB_INSTANCE=https://gitlab.example.com
STYLE_GUIDE_PROJECT_ID=gl-demo-ultimate-zhenderson/sandbox/enterprise-java-standards
STYLE_GUIDE_PROJECT_NAME=Enterprise Java Standards
STYLE_GUIDE_BRANCH=main
STYLE_GUIDE_PATH=coding-style/java/guidelines/java-coding-standards.md
```
#### 2. Target Repository to Apply Style Improvement
In the same `java-style-prompt.md` file, configure which files to apply the style guide to:
- `{{TARGET_PROJECT_ID}}`: Your Java project's GitLab ID
- `{{TARGET_FILES}}`: Specific files or patterns to target (e.g., "src/main/java/**/*.java")
Example:
```yaml
TARGET_PROJECT_ID=royal-reserve-bank
TARGET_FILES=asset-management-api/src/main/java/com/royal/reserve/bank/asset/management/api/service/AssetManagementService.java
```
### Important Notes About AI-Generated Code
**⚠️ Important Disclaimer**:
GitLab Workflow uses Agentic AI which is non-deterministic, meaning:
- Results may vary between runs even with identical inputs
- The AI assistant's understanding and application of style guidelines may differ slightly each time
- The examples provided in this documentation are illustrative and your actual results may differ
**Best Practices for Working with AI-Generated Code Changes**:
1. **Always review generated code**: Never merge AI-generated changes without thorough human review
1. **Follow proper merge request processes**: Use your standard code review procedures
1. **Run all tests**: Ensure all unit and integration tests pass before merging
1. **Verify style compliance**: Confirm the changes align with your style guide expectations
1. **Incremental application**: Consider applying style changes to smaller sets of files initially
Remember that this tool is meant to assist developers, not replace human judgment in the code review process.
## Step-by-Step Implementation
1. **Create a Style Guide Issue**
- Create a new issue in your project (e.g., Issue #3)
- Include detailed information about the style guidelines to apply
- Reference the external style guide repository if applicable
- Specify requirements like:
```yaml
Task: Code Style Update
Description: Apply the enterprise standard Java style guidelines to the codebase.
Reference Style Guide: Enterprise Java Style Guidelines (https://gitlab.com/gl-demo-ultimate-zhenderson/sandbox/enterprise-java-standards/-/blob/main/coding-style/java/guidelines/java-coding-standards.md)
Constraints:
- Adhere to Enterprise Standard Java Style Guide
- Maintain Functionality
- Implement automated style checks
```
1. **Configure the Prompt**
- Copy the template from `java-style-prompt.md`
- Fill in all configuration variables
- Add any project-specific exceptions or requirements
1. **Execute via GitLab Workflow**
- Submit the configured prompt to Duo Workflow
- Duo Workflow will run through a multi-step process as seen in the sample workflow execution:
- Plan the task with specific tools (`run_read_only_git_command`, `read_file`, `find_files`, `edit_file`)
- Access the referenced issue
- Retrieve the enterprise Java style guide
- Analyze the current code structure
- Apply the style guidelines to the specified file(s)
- Verify the changes maintain functionality
- Provide a detailed report of changes made
1. **Review and Implement**
- Review the suggested changes
- Implement the changes in your codebase
- Run tests to ensure functionality is preserved
- Monitor the task progression through the GitLab Workflow interface
## Sample Workflow Execution
When properly configured, the GitLab Workflow will execute a detailed plan for applying the style guidelines. Here's a sample of what the workflow execution looks like:
### Sample Workflow Plan
The AI assistant first creates an execution plan with specific tools:
1. Access the Java style guidelines by retrieving content from Enterprise Java Standards project using `run_read_only_git_command` on `https://gitlab.com/gl-demo-ultimate-zhenderson/sandbox/enterprise-java-standards.git` for file `coding-style/java/guidelines/java-coding-standards.md`, supported by: `run_read_only_git_command`
1. Read and analyze the current content of `AssetManagementService.java` using the `read_file` tool to understand its structure and identify areas that need style updates, supported by `read_file`
1. Search for any similar files in the project using find_files to ensure consistent style application across related service classes, supported by `find_files`
1. Edit AssetManagementService.java using the edit_file tool to apply the style guidelines, focusing on:
- Package and import organization
- Class and method documentation
- Code indentation and spacing
- Annotation placement
- Method parameter formatting, supported by `edit_file`
1. Verify all Lombok annotations (@Service, @RequiredArgsConstructor, @Slf4j) are properly formatted according to the style guide using grep_files to check annotation patterns across the codebase, supported by `grep_files`
### Sample Analysis Output
The assistant then analyzes the files and requirements:
I've analyzed issue #3 and the `AssetManagementService.java` file. Here's what I found:
1. Issue #3 is about applying enterprise standard Java style guidelines to the Royal Banking codebase.
1. The `AssetManagementService.java` file is located in the `asset-management-api` module.
1. The style guidelines are stored in a separate GitLab project at `gl-demo-ultimate-zhenderson/sandbox/enterprise-java-standards`.
1. The current `AssetManagementService.java` file contains:
1. Package and import statements
1. Service class with Spring annotations
1. One public method for checking asset availability
1. Lombok annotations
1. Javadoc comments
The issue requires:
1. Formatting according to enterprise standard Java style guidelines
1. Implementation of automated style checks
1. Maintaining existing functionality
1. Ensuring all tests pass after style updates
To proceed with the implementation, you'll need to:
1. Access the Java style guidelines from the Enterprise Java Standards project
1. Apply the style guidelines to AssetManagementService.java
1. Verify that the changes don't affect functionality
1. Ensure all tests pass after the updates
### Sample Implementation Result
After execution, the assistant provides the transformed code with explanations of the changes made, including:
1. A summary of style guidelines applied
1. The transformed code with proper formatting
1. Explanations for key style changes
1. Recommendations for automated style enforcement
The changes typically include:
- Standardized import ordering
- Consistent spacing around operators
- Proper Javadoc formatting
- Standardized method parameter alignment
- Consistent brace placement
All while ensuring the code maintains its original functionality.
## Sample Tutorial Use Case: Royal Reserve Bank Java Project
This repository includes a banking tutorial example to demonstrate how the style guide application works in a real-world scenario. The Royal Reserve Bank project follows a microservices architecture with multiple Java services:
- Account API
- Asset Management API
- Transaction API
- Notification API
- API Gateway
- Config Server
- Discovery Server
The sample examples applies enterprise style guidelines to the `AssetManagementService.java` class, demonstrating proper formatting for:
1. Import organization
1. Javadoc standards
1. Method parameter alignment
1. Variable naming conventions
1. Exception handling patterns
## Customizing for Your Organization
To adapt this prompt for your organization's needs:
1. **Style Guide Replacement**
- Point to your organization's style guide repository
- Reference your specific style guide document
1. **Target File Selection**
- Choose specific files or patterns to apply the style guide to
- Prioritize high-visibility code files for initial implementation
1. **Additional Validation**
- Add custom validation requirements
- Specify any exceptions to the standard style rules
1. **Integration with CI/CD**
- Configure the prompt to run as part of your CI/CD pipeline
- Set up automated style checks to ensure ongoing compliance
## Troubleshooting
Common issues and their solutions:
- **Access Denied**: Ensure the AI agent has proper permissions to access both repositories
- **Missing Style Guide**: Verify the style guide path and branch are correct
- **Functionality Changes**: Run all tests after applying style changes to verify functionality
## Contributing
Feel free to enhance this prompt by:
- Adding more style rule explanations
- Creating examples for different Java project types
- Improving the validation workflow
- Adding integration with additional static analysis tools
<!-- This redirect file can be deleted after <2025-08-15>. -->
<!-- Redirects that point to other docs in the same project expire in three months. -->
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
<!-- Before deletion, see: https://docs.gitlab.com/development/documentation/redirects -->

View File

@ -243,7 +243,7 @@ For more information, see
## Auto Review Apps
This is an optional step, since many projects don't have a Kubernetes cluster
This is an optional step because many projects don't have a Kubernetes cluster
available. If the [requirements](requirements.md) are not met, the job is
silently skipped.

View File

@ -11,7 +11,7 @@ Checks for JSON data potentially vulnerable to hijacking. This check looks for a
## Remediation
JSON hijacking allows an attacker to send a GET request via a malicious web site or similar attack vector and utilize a user's stored credentials to retrieve sensitive or protected data to which that user has access. Since a JSON array on its own is valid JavaScript, a malicious GET request to a resource that returns only a JavaScript array can allow the attacker to use a malicious script to read the data in the array from the request. GET requests should never return a JSON array, even if the resource requires authentication to access. Consider using POST instead of a GET for this request or wrapping the array in a JSON object.
JSON hijacking allows an attacker to send a GET request via a malicious web site or similar attack vector and utilize a user's stored credentials to retrieve sensitive or protected data to which that user has access. A JSON array on its own is valid JavaScript, so a malicious GET request to a resource that returns only a JavaScript array can allow the attacker to use a malicious script to read the data in the array from the request. GET requests should never return a JSON array, even if the resource requires authentication to access. Consider using POST instead of a GET for this request or wrapping the array in a JSON object.
## Links

View File

@ -11,7 +11,7 @@ The target application was found to request resources over insecure transport pr
elements which load resources using the `http://` scheme instead of `https://`. It should be noted that most modern browsers
block these requests automatically so there is limited risk.
Some parts of the application may not behave correctly since these files are not being properly loaded.
Some parts of the application may not behave correctly because these files are not being properly loaded.
## Remediation

View File

@ -131,11 +131,17 @@ They require a Premium or Ultimate subscription and one of the available add-ons
| [Vulnerability Resolution](../application_security/vulnerabilities/_index.md#vulnerability-resolution) | {{< icon name="dash-circle" >}} No | {{< icon name="dash-circle" >}} No | {{< icon name="check-circle-filled" >}} Yes |
| [AI Impact Dashboard](../analytics/ai_impact_analytics.md) | {{< icon name="dash-circle" >}} No | {{< icon name="dash-circle" >}} No | {{< icon name="check-circle-filled" >}} Yes |
In addition:
### Features available in GitLab Duo Self-Hosted
- All GitLab Duo Core and Pro features include generally available support for
[GitLab Duo Self-Hosted](../../administration/gitlab_duo_self_hosted/_index.md).
- All GitLab Duo Enterprise-only features include beta support for GitLab Duo Self-Hosted.
Your organization can use [GitLab Duo Self-Hosted](../../administration/gitlab_duo_self_hosted/_index.md)
to self-host the AI gateway and language models if you:
- Have the GitLab Duo Enterprise add-on.
- Are a GitLab Self-Managed customer.
To check which GitLab Duo features are available for use with GitLab Duo Self-Hosted,
and the status of those features, see the
[supported GitLab Duo features for GitLab Duo Self-Hosted](../../administration/gitlab_duo_self_hosted/_index.md#supported-gitlab-duo-features).
### Beta and experimental features

View File

@ -413,8 +413,8 @@ Prerequisites:
- You must have the Owner role for the group.
- For GitLab Self-Managed instances:
- If [New sign-ups are disabled](../../administration/settings/sign_up_restrictions.md#disable-new-sign-ups), an administrator must add the user.
- If [User invitations are disabled](../../administration/settings/visibility_and_access_controls.md#disable-user-invitations), an administrator must add the user.
- If [new sign-ups are disabled](../../administration/settings/sign_up_restrictions.md#disable-new-sign-ups), an administrator must add the user.
- If [user invitations are not allowed](../../administration/settings/visibility_and_access_controls.md#prevent-invitations-to-groups-and-projects), an administrator must add the user.
- If [administrator approval is enabled](../../administration/settings/sign_up_restrictions.md#turn-on-administrator-approval-for-role-promotions), an administrator must approve the invitation.
1. On the left sidebar, select **Search or go to** and find your group.

View File

@ -250,7 +250,7 @@ To disable group mentions:
1. Select **Group mentions are disabled**.
1. Select **Save changes**.
## Disable user invitations to a group
## Prevent invitations to a group
{{< history >}}
@ -258,15 +258,17 @@ To disable group mentions:
{{< /history >}}
You can disable the ability for users to invite new members to sub-groups or projects in a top-level
You can remove the ability for users to invite new members to subgroups or projects in a top-level
group. This also stops group Owners from sending invites. You must disable this setting before you
can invite users again.
On GitLab Self-Managed and GitLab Dedicated instances, you can prevent user invitations for the entire instance. For more information, see [prevent invitations to a groups and projects](../../administration/settings/visibility_and_access_controls.md#prevent-invitations-to-groups-and-projects).
Prerequisites:
- You must have the Owner role for the group.
To disable user invitations:
To prevent invitations to a group:
1. On the left sidebar, select **Search or go to** and find your group.
1. Select **Settings > General**.

View File

@ -61,8 +61,7 @@ the given environment. Hovering above each square you can see the state of a
deploy rolling out. The percentage is the percent of the pods that are updated
to the latest release.
Since deploy boards are tightly coupled with Kubernetes, there is some required
knowledge. In particular, you should be familiar with:
Deploy boards are tightly coupled with Kubernetes, so you should be familiar with:
- [Kubernetes pods](https://kubernetes.io/docs/concepts/workloads/pods/)
- [Kubernetes labels](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/)
@ -71,22 +70,21 @@ knowledge. In particular, you should be familiar with:
## Use cases
Since the deploy board is a visual representation of the Kubernetes pods for a
specific environment, there are a lot of use cases. To name a few:
The deploy board is a visual representation of the Kubernetes pods for a
specific environment, so there are a lot of use cases. To name a few:
- You want to promote what's running in staging, to production. You go to the
environments list, verify that what's running in staging is what you think is
running, then select the [manual job](../../ci/jobs/job_control.md#create-a-job-that-must-be-run-manually) to deploy to production.
- You trigger a deploy, and you have many containers to upgrade so you know
- You want to promote what's running in staging to production. So you go to the
environments list, verify that what's running in staging is what you think it is, then select the [manual job](../../ci/jobs/job_control.md#create-a-job-that-must-be-run-manually) to deploy to production.
- You triggered a deploy, and you have many containers to upgrade, so you know
this takes a while (you've also throttled your deploy to only take down X
containers at a time). But you need to tell someone when it's deployed, so you
go to the environments list, look at the production environment to see what
go to the environments list and look at the production environment to see what
the progress is in real-time as each pod is rolled.
- You get a report that something is weird in production, so you look at the
production environment to see what is running, and if a deploy is ongoing or
stuck or failed.
production environment to see what is running, and if a deploy is ongoing,
stuck, or failed.
- You've got an MR that looks good, but you want to run it on staging because
staging is set up in some way closer to production. You go to the environment
staging is set up in some way closer to production. So you go to the environment
list, find the [Review App](../../ci/review_apps/_index.md) you're interested in, and select the
manual action to deploy it to staging.

View File

@ -19,11 +19,10 @@ control system similar to [SVN](https://subversion.apache.org/).
The following list illustrates the main differences between CVS and Git:
- **Git is distributed.** On the other hand, CVS is centralized using a client-server
architecture. This translates to Git having a more flexible workflow since
- **Git is distributed.** On the other hand, CVS is centralized and uses a client-server
architecture. This translates to Git having a more flexible workflow because
your working area is a copy of the entire repository. This decreases the
overhead when switching branches or merging for example, since you don't have
to communicate with a remote server.
overhead when switching branches or merging, for example, because you don't need to communicate with a remote server.
- **Atomic operations.** In Git all operations are
[atomic](https://en.wikipedia.org/wiki/Atomic_commit), either they succeed as
whole, or they fail without any changes. In CVS, commits (and other operations)

View File

@ -24,7 +24,7 @@ In this document, we focus on the TFVC to Git migration.
The main differences between TFVC and Git are:
- **Git is distributed:** While TFVC is centralized using a client-server architecture,
Git is distributed. This translates to Git having a more flexible workflow since
Git is distributed. This translates to Git having a more flexible workflow because
you work with a copy of the entire repository. This allows you to quickly
switch branches or merge, for example, without needing to communicate with a remote server.
- **Storage:** Changes in a centralized version control system are per file (changeset),

View File

@ -41,7 +41,7 @@ add `#xxx` to the commit message, where `xxx` is the issue number.
git commit -m "this is my commit message. Ref #xxx"
```
Since commit messages cannot usually begin with a `#` character, you may use
Commit messages cannot usually begin with a `#` character, so you may use
the alternative `GL-xxx` notation as well:
```shell

View File

@ -95,8 +95,8 @@ Prerequisites:
- You must have the Owner or Maintainer role.
- [Group membership lock](../../group/access_and_permissions.md#prevent-members-from-being-added-to-projects-in-a-group) must be disabled.
- For GitLab Self-Managed instances:
- If [user sign-ups are disabled](../../../administration/settings/sign_up_restrictions.md#disable-new-sign-ups), an administrator must add the user.
- If [user invitations are disabled](../../../administration/settings/visibility_and_access_controls.md#disable-user-invitations), an administrator must add the user.
- If [new sign-ups are disabled](../../../administration/settings/sign_up_restrictions.md#disable-new-sign-ups), an administrator must add the user.
- If [user invitations are not allowed](../../../administration/settings/visibility_and_access_controls.md#prevent-invitations-to-groups-and-projects), an administrator must add the user.
- If [administrator approval is enabled](../../../administration/settings/sign_up_restrictions.md#turn-on-administrator-approval-for-role-promotions), an administrator must approve the invitation.
To add a user to a project:

View File

@ -44,7 +44,7 @@ Now we have a different picture. [According to Josh Aas](https://letsencrypt.org
<!-- vale gitlab_base.rulename = YES -->
> _We've since come to realize that HTTPS is important for almost all websites. It's important for any website that allows people to sign in with a password, any website that [tracks its users](https://www.washingtonpost.com/news/the-switch/wp/2013/12/10/nsa-uses-google-cookies-to-pinpoint-targets-for-hacking/) in any way, any website that [doesn't want its content altered](https://arstechnica.com/tech-policy/2014/09/why-comcasts-javascript-ad-injections-threaten-security-net-neutrality/), and for any site that offers content people might not want others to know they are consuming. We've also learned that any site not secured by HTTPS [can be used to attack other sites](https://krebsonsecurity.com/2015/04/dont-be-fodder-for-chinas-great-cannon/)._
> _We've come to realize that HTTPS is important for almost all websites. It's important for any website that allows people to sign in with a password, any website that [tracks its users](https://www.washingtonpost.com/news/the-switch/wp/2013/12/10/nsa-uses-google-cookies-to-pinpoint-targets-for-hacking/) in any way, any website that [doesn't want its content altered](https://arstechnica.com/tech-policy/2014/09/why-comcasts-javascript-ad-injections-threaten-security-net-neutrality/), and for any site that offers content people might not want others to know they are consuming. We've also learned that any site not secured by HTTPS [can be used to attack other sites](https://krebsonsecurity.com/2015/04/dont-be-fodder-for-chinas-great-cannon/)._
Therefore, the reason why certificates are so important is that they encrypt
the connection between the **client** (you, your visitors)

Some files were not shown because too many files have changed in this diff Show More