Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-07-12 00:10:18 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-07-12 00:10:18 +0300
commitaba518c582d1dfe00e9aac5d2e54dbdb17fba8a7 (patch)
tree671b341304b11edcba4c67d0d352e4c3a063f86f
parent5842aa35563dacaabf9a80307966fabe366a1321 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.checksum2
-rw-r--r--Gemfile.lock6
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/failure_widget/failed_jobs_list.vue11
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget.vue74
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue5
-rw-r--r--app/controllers/registrations/welcome_controller.rb5
-rw-r--r--app/graphql/types/user_interface.rb5
-rw-r--r--app/models/ai/service_access_token.rb2
-rw-r--r--app/services/service_response.rb4
-rw-r--r--app/views/admin/application_settings/_localization.html.haml2
-rw-r--r--app/views/groups/settings/access_tokens/index.html.haml49
-rw-r--r--app/views/layouts/_head.html.haml27
-rw-r--r--app/views/profiles/personal_access_tokens/index.html.haml8
-rw-r--r--app/views/projects/settings/access_tokens/index.html.haml29
-rw-r--r--app/views/shared/access_tokens/_form.html.haml4
-rw-r--r--config/sidekiq_queues.yml2
-rw-r--r--db/migrate/20230523101514_finalize_user_type_migration.rb15
-rw-r--r--db/migrate/20230710160232_add_expires_at_to_service_access_tokens.rb11
-rw-r--r--db/post_migrate/20230523101514_finalize_user_type_migration.rb22
-rw-r--r--db/post_migrate/20230618020202_finish_user_type_migration.rb22
-rw-r--r--db/schema_migrations/202306180202021
-rw-r--r--db/schema_migrations/202307101602321
-rw-r--r--db/structure.sql3
-rw-r--r--doc/administration/auth/ldap/ldap-troubleshooting.md2
-rw-r--r--doc/administration/integration/terminal.md2
-rw-r--r--doc/administration/license_file.md252
-rw-r--r--doc/administration/operations/index.md2
-rw-r--r--doc/administration/raketasks/maintenance.md13
-rw-r--r--doc/administration/settings/index.md215
-rw-r--r--doc/administration/settings/package_registry_rate_limits.md57
-rw-r--r--doc/administration/settings/project_integration_management.md138
-rw-r--r--doc/administration/snippets/index.md2
-rw-r--r--doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md2
-rw-r--r--doc/administration/wikis/index.md2
-rw-r--r--doc/api/graphql/reference/index.md6
-rw-r--r--doc/install/migrate/compare_sm_to_saas.md4
-rw-r--r--doc/integration/jira/configure.md2
-rw-r--r--doc/subscriptions/self_managed/index.md4
-rw-r--r--doc/user/admin_area/license.md4
-rw-r--r--doc/user/admin_area/license_file.md255
-rw-r--r--doc/user/admin_area/settings/index.md218
-rw-r--r--doc/user/admin_area/settings/package_registry_rate_limits.md60
-rw-r--r--doc/user/admin_area/settings/project_integration_management.md141
-rw-r--r--doc/user/organization/index.md2
-rw-r--r--doc/user/profile/preferences.md2
-rw-r--r--gems/click_house-client/Gemfile.lock6
-rw-r--r--lib/result.rb209
-rw-r--r--locale/gitlab.pot9
-rw-r--r--scripts/allowed_warnings.txt8
-rwxr-xr-xscripts/remote_development/run-smoke-test-suite.sh10
-rw-r--r--spec/features/groups/settings/access_tokens_spec.rb2
-rw-r--r--spec/features/projects/settings/access_tokens_spec.rb2
-rw-r--r--spec/frontend/pipelines/components/pipelines_list/failure_widget/failed_jobs_list_spec.js1
-rw-r--r--spec/frontend/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget_spec.js126
-rw-r--r--spec/graphql/types/user_type_spec.rb1
-rw-r--r--spec/lib/result_spec.rb328
-rw-r--r--spec/migrations/20230523101514_finalize_user_type_migration_spec.rb9
-rw-r--r--spec/models/ai/service_access_token_spec.rb1
-rw-r--r--spec/services/service_response_spec.rb13
-rw-r--r--spec/support/matchers/result_matchers.rb66
-rw-r--r--spec/support/shared_examples/graphql/types/merge_request_interactions_type_shared_examples.rb1
-rw-r--r--spec/support_specs/matchers/result_matchers_spec.rb21
63 files changed, 1584 insertions, 926 deletions
diff --git a/Gemfile b/Gemfile
index 1f00da5900c..8db4bd0980d 100644
--- a/Gemfile
+++ b/Gemfile
@@ -182,7 +182,7 @@ gem 'seed-fu', '~> 2.3.7'
gem 'elasticsearch-model', '~> 7.2'
gem 'elasticsearch-rails', '~> 7.2', require: 'elasticsearch/rails/instrumentation'
gem 'elasticsearch-api', '7.13.3'
-gem 'aws-sdk-core', '~> 3.177.0'
+gem 'aws-sdk-core', '~> 3.178.0'
gem 'aws-sdk-cloudformation', '~> 1'
gem 'aws-sdk-s3', '~> 1.128.0'
gem 'faraday_middleware-aws-sigv4', '~>0.3.0'
diff --git a/Gemfile.checksum b/Gemfile.checksum
index 8516871cc0e..2e517949024 100644
--- a/Gemfile.checksum
+++ b/Gemfile.checksum
@@ -37,7 +37,7 @@
{"name":"aws-eventstream","version":"1.2.0","platform":"ruby","checksum":"ffa53482c92880b001ff2fb06919b9bb82fd847cbb0fa244985d2ebb6dd0d1df"},
{"name":"aws-partitions","version":"1.761.0","platform":"ruby","checksum":"291e444e1edfc92c5521a6dbdd1236ccc3f122b3520163b2be6ec5b6ef350ef2"},
{"name":"aws-sdk-cloudformation","version":"1.41.0","platform":"ruby","checksum":"31e47539719734413671edf9b1a31f8673fbf9688549f50c41affabbcb1c6b26"},
-{"name":"aws-sdk-core","version":"3.177.0","platform":"ruby","checksum":"c884ac2c5c7b53c3ef5219451aa2d3313881afee0fa7d22ef16ad8b848ccd125"},
+{"name":"aws-sdk-core","version":"3.178.0","platform":"ruby","checksum":"192485a032536ff8c8eb037f1204b432129a612f4de13e36c0d2cf0dec8165cb"},
{"name":"aws-sdk-kms","version":"1.64.0","platform":"ruby","checksum":"40de596c95047bfc6e1aacea24f3df6241aa716b6f7ce08ac4c5f7e3120395ad"},
{"name":"aws-sdk-s3","version":"1.128.0","platform":"ruby","checksum":"5b1420d5be9654a9b1b5c8309d75ce72592f3a1e29def15ea07a853b96999d85"},
{"name":"aws-sigv4","version":"1.6.0","platform":"ruby","checksum":"ca9e6a15cd424f1f32b524b9760995331459bc22e67d3daad4fcf0c0084b087d"},
diff --git a/Gemfile.lock b/Gemfile.lock
index 53cb8e5cc56..f9ddaa75f2c 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -254,7 +254,7 @@ GEM
aws-sdk-cloudformation (1.41.0)
aws-sdk-core (~> 3, >= 3.99.0)
aws-sigv4 (~> 1.1)
- aws-sdk-core (3.177.0)
+ aws-sdk-core (3.178.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.5)
@@ -1739,7 +1739,7 @@ DEPENDENCIES
autoprefixer-rails (= 10.2.5.1)
awesome_print
aws-sdk-cloudformation (~> 1)
- aws-sdk-core (~> 3.177.0)
+ aws-sdk-core (~> 3.178.0)
aws-sdk-s3 (~> 1.128.0)
axe-core-rspec
babosa (~> 2.0)
@@ -2038,4 +2038,4 @@ DEPENDENCIES
yajl-ruby (~> 1.4.3)
BUNDLED WITH
- 2.4.14
+ 2.4.16
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/failure_widget/failed_jobs_list.vue b/app/assets/javascripts/pipelines/components/pipelines_list/failure_widget/failed_jobs_list.vue
index de58170b2b1..36687129cdd 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/failure_widget/failed_jobs_list.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/failure_widget/failed_jobs_list.vue
@@ -4,7 +4,7 @@ import { createAlert } from '~/alert';
import { __, s__, sprintf } from '~/locale';
import { getQueryHeaders } from '~/pipelines/components/graph/utils';
import getPipelineFailedJobs from '../../../graphql/queries/get_pipeline_failed_jobs.query.graphql';
-import { sortJobsByStatus } from './utils';
+import { graphqlEtagPipelinePath, sortJobsByStatus } from './utils';
import FailedJobDetails from './failed_job_details.vue';
const POLL_INTERVAL = 10000;
@@ -19,12 +19,8 @@ export default {
GlLoadingIcon,
FailedJobDetails,
},
- inject: ['fullPath'],
+ inject: ['fullPath', 'graphqlPath'],
props: {
- graphqlResourceEtag: {
- required: true,
- type: String,
- },
isPipelineActive: {
required: true,
type: Boolean,
@@ -72,6 +68,9 @@ export default {
},
},
computed: {
+ graphqlResourceEtag() {
+ return graphqlEtagPipelinePath(this.graphqlPath, this.pipelineIid);
+ },
hasFailedJobs() {
return this.failedJobs.length > 0;
},
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget.vue b/app/assets/javascripts/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget.vue
index ba064fc94b6..7d4d2e139f0 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget.vue
@@ -1,13 +1,8 @@
<script>
import { GlButton, GlCollapse, GlIcon, GlLink, GlPopover, GlSprintf } from '@gitlab/ui';
import { __, s__, sprintf } from '~/locale';
-import { etagQueryHeaders } from '~/graphql_shared/utils';
-import getPipelineFailedJobsCount from '../../../graphql/queries/get_pipeline_failed_jobs_count.query.graphql';
-import { graphqlEtagPipelinePath } from './utils';
import FailedJobsList from './failed_jobs_list.vue';
-const POLL_INTERVAL = 10000;
-
export default {
components: {
GlButton,
@@ -18,8 +13,12 @@ export default {
GlSprintf,
FailedJobsList,
},
- inject: ['fullPath', 'graphqlPath'],
+ inject: ['fullPath'],
props: {
+ failedJobsCount: {
+ required: true,
+ type: Number,
+ },
isPipelineActive: {
required: true,
type: Boolean,
@@ -35,82 +34,30 @@ export default {
},
data() {
return {
- failedJobs: [],
- failedJobsCount: 0,
+ currentFailedJobsCount: this.failedJobsCount,
isActive: false,
isExpanded: false,
};
},
- apollo: {
- failedJobsCount: {
- context() {
- return etagQueryHeaders('verify/ci/merge-request/pipelines', this.graphqlResourceEtag);
- },
- query: getPipelineFailedJobsCount,
- variables() {
- return {
- fullPath: this.fullPath,
- pipelineIid: this.pipelineIid,
- };
- },
- update(data) {
- return data?.project?.pipeline?.jobs?.count || 0;
- },
- result({ data }) {
- this.isActive = data?.project?.pipeline?.active || false;
- },
- },
- },
computed: {
bodyClasses() {
return this.isExpanded ? '' : 'gl-display-none';
},
failedJobsCountText() {
- return sprintf(this.$options.i18n.showFailedJobs, { count: this.failedJobsCount });
- },
- graphqlResourceEtag() {
- return graphqlEtagPipelinePath(this.graphqlPath, this.pipelineIid);
- },
- hasFailedJobs() {
- return this.failedJobsCount > 0;
+ return sprintf(this.$options.i18n.showFailedJobs, { count: this.currentFailedJobsCount });
},
iconName() {
return this.isExpanded ? 'chevron-down' : 'chevron-right';
},
},
watch: {
- isPipelineActive(flag) {
- // Turn polling on and off based on REST actions
- // By refetching jobs, we will get the graphql `active`
- // field to update properly and cascade the polling changes
- this.$apollo.queries.failedJobsCount.refetch();
- this.handlePolling(flag);
- },
- isActive(flag) {
- this.handlePolling(flag);
- },
- isExpanded(flag) {
- // When the user toggles the expand state, we check if the pipeline is
- // active, which which case we restart polling for jobs count.
- if (!flag && (this.isActive || this.isPipelineActive)) {
- this.$apollo.queries.failedJobsCount.startPolling(POLL_INTERVAL);
- } else {
- this.$apollo.queries.failedJobsCount.stopPolling();
- }
+ failedJobsCount(val) {
+ this.currentFailedJobsCount = val;
},
},
methods: {
- handlePolling(isActive) {
- // If the pipeline status has changed and the widget is not expanded,
- // We start polling.
- if (!this.isExpanded && isActive) {
- this.$apollo.queries.failedJobsCount.startPolling(POLL_INTERVAL);
- } else {
- this.$apollo.queries.failedJobsCount.stopPolling();
- }
- },
setFailedJobsCount(count) {
- this.failedJobsCount = count;
+ this.currentFailedJobsCount = count;
},
toggleWidget() {
this.isExpanded = !this.isExpanded;
@@ -148,7 +95,6 @@ export default {
>
<failed-jobs-list
v-if="isExpanded"
- :graphql-resource-etag="graphqlResourceEtag"
:is-pipeline-active="isPipelineActive"
:pipeline-iid="pipelineIid"
@failed-jobs-count="setFailedJobsCount"
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue
index 3a51189a91c..dbb0b443235 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue
@@ -146,8 +146,8 @@ export default {
const downstream = pipeline.triggered;
return keepLatestDownstreamPipelines(downstream);
},
- hasFailedJobs(pipeline) {
- return pipeline?.failed_builds?.length > 0 || false;
+ failedJobsCount(pipeline) {
+ return pipeline?.failed_builds?.length || 0;
},
setModalData(data) {
this.pipelineId = data.pipeline.id;
@@ -221,6 +221,7 @@ export default {
<template #row-details="{ item }">
<pipeline-failed-jobs-widget
v-if="showFailedJobsWidget"
+ :failed-jobs-count="failedJobsCount(item)"
:is-pipeline-active="item.active"
:pipeline-iid="item.iid"
:pipeline-path="item.path"
diff --git a/app/controllers/registrations/welcome_controller.rb b/app/controllers/registrations/welcome_controller.rb
index 76aa4afbe80..ec7e7c0b1f0 100644
--- a/app/controllers/registrations/welcome_controller.rb
+++ b/app/controllers/registrations/welcome_controller.rb
@@ -25,8 +25,7 @@ module Registrations
if result.success?
track_event('successfully_submitted_form')
- finish_onboarding_on_welcome_page unless complete_signup_onboarding?
-
+ successful_update_hooks
redirect_to update_success_path
else
render :show
@@ -94,7 +93,7 @@ module Registrations
strong_memoize_attr :user_members
# overridden in EE
- def finish_onboarding_on_welcome_page; end
+ def successful_update_hooks; end
# overridden in EE
def signup_onboarding_path; end
diff --git a/app/graphql/types/user_interface.rb b/app/graphql/types/user_interface.rb
index 5357f2f8e66..0a67cdf749e 100644
--- a/app/graphql/types/user_interface.rb
+++ b/app/graphql/types/user_interface.rb
@@ -197,6 +197,11 @@ module Types
null: true,
description: 'Timestamp of when the user was created.'
+ field :pronouns,
+ type: ::GraphQL::Types::String,
+ null: true,
+ description: 'Pronouns of the user.'
+
definition_methods do
def resolve_type(object, context)
# in the absence of other information, we cannot tell - just default to
diff --git a/app/models/ai/service_access_token.rb b/app/models/ai/service_access_token.rb
index b90ef30a6d9..b22f1a03521 100644
--- a/app/models/ai/service_access_token.rb
+++ b/app/models/ai/service_access_token.rb
@@ -11,7 +11,7 @@ module Ai
encode: false,
encode_iv: false
- validates :token, presence: true
+ validates :token, :expires_at, presence: true
enum category: {
code_suggestions: 1
diff --git a/app/services/service_response.rb b/app/services/service_response.rb
index da4773ab9c7..86efc01bd30 100644
--- a/app/services/service_response.rb
+++ b/app/services/service_response.rb
@@ -56,6 +56,10 @@ class ServiceResponse
reason: reason)
end
+ def deconstruct_keys(keys)
+ to_h.slice(*keys)
+ end
+
def success?
status == :success
end
diff --git a/app/views/admin/application_settings/_localization.html.haml b/app/views/admin/application_settings/_localization.html.haml
index 669c47bafba..19d321ca205 100644
--- a/app/views/admin/application_settings/_localization.html.haml
+++ b/app/views/admin/application_settings/_localization.html.haml
@@ -7,7 +7,7 @@
= f.select :first_day_of_week, first_day_of_week_choices, {}, class: 'form-control'
.form-text.text-muted
= _('Default first day of the week in calendars and date pickers.')
- = link_to _('Learn more.'), help_page_path('user/admin_area/settings/index.md', anchor: 'default-first-day-of-the-week'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to _('Learn more.'), help_page_path('administration/settings/index.md', anchor: 'default-first-day-of-the-week'), target: '_blank', rel: 'noopener noreferrer'
.form-group
= f.label :time_tracking, _('Time tracking'), class: 'label-bold'
diff --git a/app/views/groups/settings/access_tokens/index.html.haml b/app/views/groups/settings/access_tokens/index.html.haml
index 96a492e599e..ac3be429461 100644
--- a/app/views/groups/settings/access_tokens/index.html.haml
+++ b/app/views/groups/settings/access_tokens/index.html.haml
@@ -4,40 +4,39 @@
- type_plural = _('group access tokens')
- @force_desktop_expanded_sidebar = true
-.row.gl-mt-3.js-search-settings-section
- .col-lg-4
- %h4.gl-mt-0
- = page_title
- %p
- - link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/group/settings/group_access_tokens') }
+.settings-section.js-search-settings-section
+ .settings-sticky-header
+ .settings-sticky-header-inner
+ %h4.gl-my-0
+ = page_title
+ %p.gl-text-secondary
+ - help_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/group/settings/group_access_tokens') }
- if current_user.can?(:create_resource_access_tokens, @group)
= _('Generate group access tokens scoped to this group for your applications that need access to the GitLab API.')
- %p
- = html_escape(_('You can also use group access tokens with Git to authenticate over HTTP(S). %{link_start}Learn more.%{link_end}')) % { link_start: link_start, link_end: '</a>'.html_safe }
+ = html_escape(_('You can also use group access tokens with Git to authenticate over HTTP(S). %{link_start}Learn more.%{link_end}')) % { link_start: help_link_start, link_end: '</a>'.html_safe }
- else
- = html_escape(_('Group access token creation is disabled in this group. You can still use and manage existing tokens. %{link_start}Learn more.%{link_end}')) % { link_start: link_start, link_end: '</a>'.html_safe }
- %p
+ = _('Group access token creation is disabled in this group.')
- root_group = @group.root_ancestor
- if current_user.can?(:admin_group, root_group)
- group_settings_link = edit_group_path(root_group)
- link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: group_settings_link }
= html_escape(_('You can enable group access token creation in %{link_start}group settings%{link_end}.')) % { link_start: link_start, link_end: '</a>'.html_safe }
+ = html_escape(_('You can still use and manage existing tokens. %{link_start}Learn more.%{link_end}')) % { link_start: help_link_start, link_end: '</a>'.html_safe }
- .col-lg-8
- #js-new-access-token-app{ data: { access_token_type: type } }
+ #js-new-access-token-app{ data: { access_token_type: type } }
- - if current_user.can?(:create_resource_access_tokens, @group)
- = render 'shared/access_tokens/form',
- ajax: true,
- type: type,
- path: group_settings_access_tokens_path(@group),
- resource: @group,
- token: @resource_access_token,
- scopes: @scopes,
- access_levels: GroupMember.access_level_roles,
- default_access_level: Gitlab::Access::GUEST,
- prefix: :resource_access_token,
- help_path: help_page_path('user/group/settings/group_access_tokens', anchor: 'scopes-for-a-group-access-token')
+ - if current_user.can?(:create_resource_access_tokens, @group)
+ = render 'shared/access_tokens/form',
+ ajax: true,
+ type: type,
+ path: group_settings_access_tokens_path(@group),
+ resource: @group,
+ token: @resource_access_token,
+ scopes: @scopes,
+ access_levels: GroupMember.access_level_roles,
+ default_access_level: Gitlab::Access::GUEST,
+ prefix: :resource_access_token,
+ help_path: help_page_path('user/group/settings/group_access_tokens', anchor: 'scopes-for-a-group-access-token')
- #js-access-token-table-app{ data: { access_token_type: type, access_token_type_plural: type_plural, initial_active_access_tokens: @active_access_tokens.to_json, no_active_tokens_message: _('This group has no active access tokens.'), show_role: true
+ #js-access-token-table-app{ data: { access_token_type: type, access_token_type_plural: type_plural, initial_active_access_tokens: @active_access_tokens.to_json, no_active_tokens_message: _('This group has no active access tokens.'), show_role: true
} }
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index 69a36e6c2e3..53ecad1b474 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -3,12 +3,11 @@
- omit_og = sign_in_with_redirect?
%head{ omit_og ? { } : { prefix: "og: http://ogp.me/ns#" } }
%meta{ charset: "utf-8" }
-
- %title= page_title(site_name)
-
- = render 'layouts/loading_hints'
-
%meta{ 'http-equiv' => 'X-UA-Compatible', content: 'IE=edge' }
+ %meta{ name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1' }
+ %title= page_title(site_name)
+ = Gon::Base.render_data(nonce: content_security_policy_nonce)
+ = yield :project_javascripts
= render 'layouts/startup_js'
= yield :startup_js
@@ -18,8 +17,6 @@
= yield :prefetch_asset_tags
- = favicon_link_tag favicon, id: 'favicon', data: { original_href: favicon }, type: 'image/png'
-
- diffs_colors = user_diffs_colors
= stylesheet_link_tag "themes/#{user_application_theme_css_filename}" if user_application_theme_css_filename
= render 'layouts/diffs_colors_css', diffs_colors if diffs_colors.present? || request.path == profile_preferences_path
@@ -42,8 +39,7 @@
= stylesheet_link_tag 'performance_bar' if performance_bar_enabled?
= render 'layouts/snowplow'
-
- = Gon::Base.render_data(nonce: content_security_policy_nonce)
+ = render 'layouts/loading_hints'
= render_if_exists 'layouts/header/translations'
- if Feature.enabled?(:enable_new_sentry_clientside_integration, current_user) && Gitlab::CurrentSettings.sentry_enabled
@@ -58,8 +54,6 @@
= webpack_controller_bundle_tags
- = yield :project_javascripts
-
- unless omit_og
-# Open Graph - http://ogp.me/
%meta{ property: 'og:type', content: "object" }
@@ -78,16 +72,13 @@
%meta{ property: 'twitter:image', content: page_image }
= page_card_meta_tags
- %meta{ name: "description", content: page_description }
-
- %link{ rel: 'manifest', href: manifest_path(format: :json) }
- %meta{ name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1' }
- %meta{ name: 'theme-color', content: user_theme_primary_color }
-
= csrf_meta_tags
= csp_meta_tag
= action_cable_meta_tag
+ %link{ rel: 'manifest', href: manifest_path(format: :json) }
+ = favicon_link_tag favicon, id: 'favicon', data: { original_href: favicon }, type: 'image/png'
+
-# Apple Safari/iOS home screen icons
= favicon_link_tag 'apple-touch-icon.png', rel: 'apple-touch-icon'
@@ -100,3 +91,5 @@
= render 'layouts/matomo' if extra_config.has_key?('matomo_url') && extra_config.has_key?('matomo_site_id')
-# This is needed by [GitLab JH](https://gitlab.com/gitlab-jh/gitlab/-/issues/184)
= render_if_exists "layouts/frontend_monitor"
+ %meta{ name: "description", content: page_description }
+ %meta{ name: 'theme-color', content: user_theme_primary_color }
diff --git a/app/views/profiles/personal_access_tokens/index.html.haml b/app/views/profiles/personal_access_tokens/index.html.haml
index c6fde261b50..990c866b547 100644
--- a/app/views/profiles/personal_access_tokens/index.html.haml
+++ b/app/views/profiles/personal_access_tokens/index.html.haml
@@ -4,9 +4,11 @@
- type_plural = _('personal access tokens')
- @force_desktop_expanded_sidebar = true
-.gl-pb-6.js-search-settings-section
- %h4.gl-my-0
- = page_title
+.settings-section.js-search-settings-section
+ .settings-sticky-header
+ .settings-sticky-header-inner
+ %h4.gl-my-0
+ = page_title
%p.gl-text-secondary
= s_('AccessTokens|You can generate a personal access token for each application you use that needs access to the GitLab API.')
= s_('AccessTokens|You can also use personal access tokens to authenticate against Git over HTTP.')
diff --git a/app/views/projects/settings/access_tokens/index.html.haml b/app/views/projects/settings/access_tokens/index.html.haml
index df517b5d642..e4af6d59cad 100644
--- a/app/views/projects/settings/access_tokens/index.html.haml
+++ b/app/views/projects/settings/access_tokens/index.html.haml
@@ -4,31 +4,28 @@
- type_plural = _('project access tokens')
- @force_desktop_expanded_sidebar = true
-.row.gl-mt-3.js-search-settings-section
- .col-lg-4
- %h4.gl-mt-0
- = page_title
- %p
- - link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/project/settings/project_access_tokens') }
+.gl-mt-5.js-search-settings-section
+ %h4.gl-my-0
+ = page_title
+ %p.gl-text-secondary
+ - help_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/project/settings/project_access_tokens') }
- if current_user.can?(:create_resource_access_tokens, @project)
= _('Generate project access tokens scoped to this project for your applications that need access to the GitLab API.')
- %p
- = _('You can also use project access tokens with Git to authenticate over HTTP(S). %{link_start}Learn more.%{link_end}').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
+ = _('You can also use project access tokens with Git to authenticate over HTTP(S). %{link_start}Learn more.%{link_end}').html_safe % { link_start: help_link_start, link_end: '</a>'.html_safe }
- else
- = _('Project access token creation is disabled in this group. You can still use and manage existing tokens. %{link_start}Learn more.%{link_end}').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
- %p
+ = _('Project access token creation is disabled in this group.')
- root_group = @project.group.root_ancestor
- if current_user.can?(:admin_group, root_group)
- group_settings_link = edit_group_path(root_group)
- link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: group_settings_link }
= _('You can enable project access token creation in %{link_start}group settings%{link_end}.').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
+ = html_escape(_('You can still use and manage existing tokens. %{link_start}Learn more.%{link_end}')) % { link_start: help_link_start, link_end: '</a>'.html_safe }
- .col-lg-8
- #js-new-access-token-app{ data: { access_token_type: type } }
+ #js-new-access-token-app{ data: { access_token_type: type } }
- - if current_user.can?(:create_resource_access_tokens, @project)
- = render_if_exists 'projects/settings/access_tokens/form',
- type: type
+ - if current_user.can?(:create_resource_access_tokens, @project)
+ = render_if_exists 'projects/settings/access_tokens/form',
+ type: type
- #js-access-token-table-app{ data: { access_token_type: type, access_token_type_plural: type_plural, initial_active_access_tokens: @active_access_tokens.to_json, no_active_tokens_message: _('This project has no active access tokens.'), show_role: true
+ #js-access-token-table-app{ data: { access_token_type: type, access_token_type_plural: type_plural, initial_active_access_tokens: @active_access_tokens.to_json, no_active_tokens_message: _('This project has no active access tokens.'), show_role: true
} }
diff --git a/app/views/shared/access_tokens/_form.html.haml b/app/views/shared/access_tokens/_form.html.haml
index 711bc1c65ca..54af364aca3 100644
--- a/app/views/shared/access_tokens/_form.html.haml
+++ b/app/views/shared/access_tokens/_form.html.haml
@@ -7,7 +7,7 @@
- access_levels = local_assigns.fetch(:access_levels, false)
- default_access_level = local_assigns.fetch(:default_access_level, false)
-%h5.gl-mt-0
+%h5.gl-font-lg.gl-mt-0
= title
= gitlab_ui_form_for token, as: prefix, url: path, method: :post, html: { id: 'js-new-access-token-form', class: 'js-requires-input' }, remote: ajax do |f|
@@ -25,7 +25,7 @@
- if resource
.form-group
= label_tag :access_level, s_("AccessTokens|Select a role"), class: "label-bold"
- .select-wrapper
+ .select-wrapper.gl-form-input-md
= select_tag :"#{prefix}[access_level]", options_for_select(access_levels, default_access_level), class: "form-control select-control", data: { qa_selector: 'access_token_access_level' }
= sprite_icon('chevron-down', css_class: "gl-icon gl-absolute gl-top-3 gl-right-3 gl-text-gray-200")
diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml
index e4c45120ee8..94504635972 100644
--- a/config/sidekiq_queues.yml
+++ b/config/sidekiq_queues.yml
@@ -405,6 +405,8 @@
- 1
- - object_storage
- 1
+- - onboarding_create_iterable_trigger
+ - 1
- - onboarding_issue_created
- 1
- - onboarding_pipeline_created
diff --git a/db/migrate/20230523101514_finalize_user_type_migration.rb b/db/migrate/20230523101514_finalize_user_type_migration.rb
deleted file mode 100644
index f8ae4c7bb2c..00000000000
--- a/db/migrate/20230523101514_finalize_user_type_migration.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-class FinalizeUserTypeMigration < Gitlab::Database::Migration[2.1]
- MIGRATION = 'MigrateHumanUserType'
-
- disable_ddl_transaction!
-
- def up
- finalize_background_migration(MIGRATION)
- end
-
- def down
- # no-op
- end
-end
diff --git a/db/migrate/20230710160232_add_expires_at_to_service_access_tokens.rb b/db/migrate/20230710160232_add_expires_at_to_service_access_tokens.rb
new file mode 100644
index 00000000000..f9e4e014ff4
--- /dev/null
+++ b/db/migrate/20230710160232_add_expires_at_to_service_access_tokens.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class AddExpiresAtToServiceAccessTokens < Gitlab::Database::Migration[2.1]
+ def change
+ # Code using this table has not been implemented yet.
+ # During we run the migration, the table will be empty.
+ # rubocop:disable Rails/NotNullColumn
+ add_column :service_access_tokens, :expires_at, :datetime_with_timezone, null: false
+ # rubocop:enable Rails/NotNullColumn
+ end
+end
diff --git a/db/post_migrate/20230523101514_finalize_user_type_migration.rb b/db/post_migrate/20230523101514_finalize_user_type_migration.rb
new file mode 100644
index 00000000000..c6533bec587
--- /dev/null
+++ b/db/post_migrate/20230523101514_finalize_user_type_migration.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class FinalizeUserTypeMigration < Gitlab::Database::Migration[2.1]
+ MIGRATION = 'MigrateHumanUserType'
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ disable_ddl_transaction!
+
+ def up
+ ensure_batched_background_migration_is_finished(
+ job_class_name: MIGRATION,
+ table_name: :users,
+ column_name: :id,
+ job_arguments: []
+ )
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/post_migrate/20230618020202_finish_user_type_migration.rb b/db/post_migrate/20230618020202_finish_user_type_migration.rb
new file mode 100644
index 00000000000..4a729d5d408
--- /dev/null
+++ b/db/post_migrate/20230618020202_finish_user_type_migration.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class FinishUserTypeMigration < Gitlab::Database::Migration[2.1]
+ MIGRATION = 'MigrateHumanUserType'
+
+ disable_ddl_transaction!
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ def up
+ ensure_batched_background_migration_is_finished(
+ job_class_name: MIGRATION,
+ table_name: :users,
+ column_name: :id,
+ job_arguments: []
+ )
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/schema_migrations/20230618020202 b/db/schema_migrations/20230618020202
new file mode 100644
index 00000000000..f07ea0b2994
--- /dev/null
+++ b/db/schema_migrations/20230618020202
@@ -0,0 +1 @@
+aedea1ec886b1831a94831185aa493e42c777c8c1614f6541580cdaa4f76946e \ No newline at end of file
diff --git a/db/schema_migrations/20230710160232 b/db/schema_migrations/20230710160232
new file mode 100644
index 00000000000..08f2ba7fa32
--- /dev/null
+++ b/db/schema_migrations/20230710160232
@@ -0,0 +1 @@
+1f894fea060c0abee05799f161665214d66c8e8f0509b464b2bf6a35964f457b \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 267ced2ee6a..1dd2d9dd2a3 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -22673,7 +22673,8 @@ CREATE TABLE service_access_tokens (
updated_at timestamp with time zone NOT NULL,
category smallint DEFAULT 0 NOT NULL,
encrypted_token bytea NOT NULL,
- encrypted_token_iv bytea NOT NULL
+ encrypted_token_iv bytea NOT NULL,
+ expires_at timestamp with time zone NOT NULL
);
CREATE SEQUENCE service_access_tokens_id_seq
diff --git a/doc/administration/auth/ldap/ldap-troubleshooting.md b/doc/administration/auth/ldap/ldap-troubleshooting.md
index 219808c1007..bbe0b02c5a7 100644
--- a/doc/administration/auth/ldap/ldap-troubleshooting.md
+++ b/doc/administration/auth/ldap/ldap-troubleshooting.md
@@ -737,7 +737,7 @@ To resolve this error, you must apply a new license to the GitLab instance witho
1. Remove or comment out the GitLab configuration lines for all non-primary LDAP servers.
1. [Reconfigure GitLab](../../restart_gitlab.md#reconfigure-a-linux-package-installation) so that it temporarily uses only one LDAP server.
-1. Enter the [Rails console and add the license key](../../../user/admin_area/license_file.md#add-a-license-through-the-console).
+1. Enter the [Rails console and add the license key](../../../administration/license_file.md#add-a-license-through-the-console).
1. Re-enable the additional LDAP servers in the GitLab configuration and reconfigure GitLab again.
## Users are being removed from group and re-added again
diff --git a/doc/administration/integration/terminal.md b/doc/administration/integration/terminal.md
index e5920520be7..0440c2ce4f3 100644
--- a/doc/administration/integration/terminal.md
+++ b/doc/administration/integration/terminal.md
@@ -114,5 +114,5 @@ lifetime in your GitLab instance:
1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
1. Select **Admin Area**.
-1. Select [**Settings > Web terminal**](../../user/admin_area/settings/index.md#general).
+1. Select [**Settings > Web terminal**](../../administration/settings/index.md#general).
1. Set a `max session time`.
diff --git a/doc/administration/license_file.md b/doc/administration/license_file.md
new file mode 100644
index 00000000000..00359d13253
--- /dev/null
+++ b/doc/administration/license_file.md
@@ -0,0 +1,252 @@
+---
+stage: Fulfillment
+group: Provision
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+<!-- To promote the workflow described in license.md, this page is not included in global left nav. -->
+
+# Activate GitLab EE with a license file or key
+
+If you receive a license file from GitLab (for example, for a trial), you can
+upload it to your instance or add it during installation. The license file is
+a base64-encoded ASCII text file with a `.gitlab-license` extension.
+
+The first time you sign in to your GitLab instance, a note with a
+link to the **Add license** page should be displayed.
+
+Otherwise, to add your license:
+
+1. Sign in to GitLab as an administrator.
+1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
+1. Select **Admin Area**.
+1. Select **Settings > General**.
+1. In the **Add License** area, add a license by either uploading the file or entering the key.
+1. Select the **Terms of Service** checkbox.
+1. Select **Add license**.
+
+NOTE:
+In GitLab 14.7.x to 14.9.x, you can add the license file with the UI.
+In GitLab 14.1.x to 14.7, if you have already activated your subscription with an activation code, you cannot access **Add License** from the Admin Area. You must access **Add License** directly from the URL, `<YourGitLabURL>/admin/license/new`.
+
+## Activate subscription during installation
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/114572) in GitLab 16.0.
+
+To activate your subscription during installation, set the `GITLAB_ACTIVATION_CODE` environment variable with the activation code:
+
+```shell
+export GITLAB_ACTIVATION_CODE=your_activation_code
+```
+
+## Add license file during installation
+
+If you have a license, you can also import it when you install GitLab.
+
+- For self-compiled installations:
+ - Place the `Gitlab.gitlab-license` file in the `config/` directory.
+ - To specify a custom location and filename for the license, set the
+ `GITLAB_LICENSE_FILE` environment variable with the path to the file:
+
+ ```shell
+ export GITLAB_LICENSE_FILE="/path/to/license/file"
+ ```
+
+- For Linux package installations:
+ - Place the `Gitlab.gitlab-license` file in the `/etc/gitlab/` directory.
+ - To specify a custom location and filename for the license, add this entry to `gitlab.rb`:
+
+ ```ruby
+ gitlab_rails['initial_license_file'] = "/path/to/license/file"
+ ```
+
+WARNING:
+These methods only add a license at the time of installation. To renew or upgrade
+a license, add the license in the **Admin Area** in the web user interface.
+
+## Submit license usage data
+
+If you use a license file or key to activate your instance in an offline environment, you must submit your license
+usage data monthly.
+To submit the data, [export your license usage](../subscriptions/self_managed/index.md#export-your-license-usage)
+and send it by email to the renewals service, `renewals-service@customers.gitlab.com`.
+
+If you don't submit your data each month after your subscription start date, an email is sent to the address
+associated with your subscription and a banner displays to remind you to submit your data. The banner displays
+in the **Admin Area** on the **Dashboard** and on the **Subscription** pages. You can only dismiss it until the
+following month after you submit your license usage data.
+
+## What happens when your license expires
+
+Fifteen days before the license expires, a notification banner with the upcoming expiration
+date displays to GitLab administrators.
+
+When your license expires, GitLab locks features, like Git pushes
+and issue creation. Your instance becomes read-only and
+an expiration message displays to all administrators. You have a 14-day grace period
+before this occurs.
+
+To resume functionality, [renew your subscription](../subscriptions/self_managed/index.md#renew-subscription-manually).
+
+If the license has been expired for more than 30 days, you must purchase a [new subscription](../subscriptions/self_managed/index.md) to resume functionality.
+
+To go back to Free features, [delete all expired licenses](#remove-a-license).
+
+## Remove a license
+
+To remove a license from a self-managed instance:
+
+1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
+1. Select **Admin Area**.
+1. Select **Subscription**.
+1. Select **Remove license**.
+
+Repeat these steps to remove all licenses, including those applied in the past.
+
+## View license details and history
+
+To view your license details:
+
+1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
+1. Select **Admin Area**.
+1. Select **Subscription**.
+
+You can add and view more than one license, but only the latest license in
+the current date range is the active license.
+
+When you add a future-dated license, it doesn't take effect until its applicable date.
+You can view all active subscriptions in the **Subscription history** table.
+
+You can also [export](../subscriptions/self_managed/index.md) your license usage information to a CSV file.
+
+NOTE:
+In GitLab 13.6 and earlier, a banner about an expiring license may continue to display
+when you add a new license. This happens when the start date of the new license
+is in the future and the expiring one is still active.
+The banner disappears after the new license becomes active.
+
+## Troubleshooting
+
+### No Subscription area in the Admin Area
+
+You cannot add your license because there is no **Subscription** area.
+This issue might occur if:
+
+- You're running GitLab Community Edition. Before you add your license, you
+ must [upgrade to Enterprise Edition](../update/index.md#community-to-enterprise-edition).
+- You're using GitLab.com. You cannot add a self-managed license to GitLab.com.
+ To use paid features on GitLab.com, [purchase a separate subscription](../subscriptions/gitlab_com/index.md).
+
+### Users exceed license limit upon renewal
+
+GitLab displays a message prompting you to purchase
+additional users. This issue occurs if you add a license that does not have enough
+users to cover the number of users in your instance.
+
+To fix this issue, purchase additional seats to cover those users.
+For more information, read the [licensing FAQ](https://about.gitlab.com/pricing/licensing-faq/).
+
+In GitLab 14.2 and later, for instances that use a license file, the following
+rules apply:
+
+- If the users over license are less than or equal to 10% of the users in the license
+ file, the license is applied and you pay the overage in the next renewal.
+- If the users over license are more than 10% of the users in the license file,
+ you cannot apply the license without purchasing more users.
+
+For example, if you purchase a license for 100 users, you can have 110 users when you add
+your license. However, if you have 111 users, you must purchase more users before you can add
+the license.
+
+### `Start GitLab Ultimate trial` still displays after adding license
+
+To fix this issue, restart [Puma or your entire GitLab instance](../administration/restart_gitlab.md).
+
+### License commands in the rails console
+
+The following commands can be run in the [rails console](../administration/operations/rails_console.md#starting-a-rails-console-session).
+
+WARNING:
+Any command that changes data directly could be damaging if not run correctly, or under the right conditions.
+We highly recommend running them in a test environment with a backup of the instance ready to be restored, just in case.
+
+#### See current license information
+
+```ruby
+# License information (name, company, email address)
+License.current.licensee
+
+# Plan:
+License.current.plan
+
+# Uploaded:
+License.current.created_at
+
+# Started:
+License.current.starts_at
+
+# Expires at:
+License.current.expires_at
+
+# Is this a trial license?
+License.current.trial?
+
+# License ID for lookup on CustomersDot
+License.current.license_id
+
+# License data in Base64-encoded ASCII format
+License.current.data
+
+# Confirm the current billable seat count excluding guest users. This is useful for customers who use an Ultimate subscription tier where Guest seats are not counted.
+User.active.without_bots.excluding_guests.count
+
+```
+
+#### Interaction with licenses that start in the future
+
+```ruby
+# Future license data follows the same format as current license data it just uses a different modifier for the License prefix
+License.future_dated
+```
+
+#### Check if a project feature is available on the instance
+
+Features listed in [`features.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/models/gitlab_subscriptions/features.rb).
+
+```ruby
+License.current.feature_available?(:jira_dev_panel_integration)
+```
+
+#### Check if a project feature is available in a project
+
+Features listed in [`features.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/models/gitlab_subscriptions/features.rb).
+
+```ruby
+p = Project.find_by_full_path('<group>/<project>')
+p.feature_available?(:jira_dev_panel_integration)
+```
+
+#### Add a license through the console
+
+```ruby
+key = "<key>"
+license = License.new(data: key)
+license.save
+License.current # check to make sure it applied
+```
+
+This is needed for example in a known edge-case with
+[expired license and multiple LDAP servers](../administration/auth/ldap/ldap-troubleshooting.md#expired-license-causes-errors-with-multiple-ldap-servers).
+
+#### Remove licenses
+
+To clean up the [License History table](../administration/license_file.md#view-license-details-and-history):
+
+```ruby
+TYPE = :trial?
+# or :expired?
+
+License.select(&TYPE).each(&:destroy!)
+
+# or even License.all.each(&:destroy!)
+```
diff --git a/doc/administration/operations/index.md b/doc/administration/operations/index.md
index 8870c4769ab..be90b0a073f 100644
--- a/doc/administration/operations/index.md
+++ b/doc/administration/operations/index.md
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
Keep your GitLab instance up and running.
- [Housekeeping](../../administration/housekeeping.md)
-- [Activate GitLab EE with license](../../user/admin_area/license_file.md)
+- [Activate GitLab EE with license](../../administration/license_file.md)
- [Fast SSH key lookup](../../administration/operations/fast_ssh_key_lookup.md)
- [File system benchmarking](../../administration/operations/filesystem_benchmarking.md)
- [`gitlab-sshd`](../../administration/operations/gitlab_sshd.md)
diff --git a/doc/administration/raketasks/maintenance.md b/doc/administration/raketasks/maintenance.md
index 50c4b004f9c..d3ea98c13a5 100644
--- a/doc/administration/raketasks/maintenance.md
+++ b/doc/administration/raketasks/maintenance.md
@@ -410,3 +410,16 @@ To re-import the metrics you can run:
```shell
sudo gitlab-rake metrics:setup_common_metrics
```
+
+## Troubleshooting
+
+### Advisory lock connection information
+
+After running the `db:migrate` Rake task, you may see output like the following:
+
+```shell
+main: == [advisory_lock_connection] object_id: 173580, pg_backend_pid: 5532
+main: == [advisory_lock_connection] object_id: 173580, pg_backend_pid: 5532
+```
+
+The messages returned are informational and can be ignored.
diff --git a/doc/administration/settings/index.md b/doc/administration/settings/index.md
new file mode 100644
index 00000000000..8bc15e6b6b7
--- /dev/null
+++ b/doc/administration/settings/index.md
@@ -0,0 +1,215 @@
+---
+stage: Create
+group: Source Code
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+type: index
+---
+
+# Admin Area settings **(FREE SELF)**
+
+As an administrator of a GitLab self-managed instance, you can manage the behavior of your
+deployment.
+
+The **Admin Area** is not accessible on GitLab.com, and settings can only be changed by the
+GitLab.com administrators. For the settings and limits on the GitLab.com instance,
+read [GitLab.com settings](../../user/gitlab_com/index.md).
+
+## Access the Admin Area
+
+To access the **Admin Area**:
+
+1. Sign in to your GitLab instance as an administrator.
+1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
+1. Select **Admin Area**.
+1. Select **Settings**, and the group of settings to view:
+ - [General](#general)
+ - [Geo](#geo)
+ - [CI/CD](#cicd)
+ - [Integrations](#integrations)
+ - [Metrics and profiling](#metrics-and-profiling)
+ - [Network](#network)
+ - [Preferences](#preferences)
+ - [Reporting](#reporting)
+ - [Repository](#repository)
+ - [Templates](#templates)
+
+### General
+
+The **General** settings contain:
+
+- [Visibility and access controls](../../user/admin_area/settings/visibility_and_access_controls.md) - Set default and
+ restrict visibility levels. Configure import sources and Git access protocol.
+- [Account and limit](../../user/admin_area/settings/account_and_limit_settings.md) - Set projects and maximum size limits,
+ session duration, user options, and check feature availability for namespace plan.
+- [Diff limits](../../user/admin_area/diff_limits.md) - Diff content limits.
+- [Sign-up restrictions](../../user/admin_area/settings/sign_up_restrictions.md) - Configure the way a user creates a new account.
+- [Sign in restrictions](../../user/admin_area/settings/sign_in_restrictions.md) - Set requirements for a user to sign in.
+ Enable mandatory two-factor authentication.
+- [Terms of Service and Privacy Policy](../../user/admin_area/settings/terms.md) - Include a Terms of Service agreement
+ and Privacy Policy that all users must accept.
+- [External Authentication](../../user/admin_area/settings/external_authorization.md#configuration) - External Classification Policy Authorization.
+- [Web terminal](../integration/terminal.md#limiting-websocket-connection-time) -
+ Set max session time for web terminal.
+- [FLoC](floc.md) - Enable or disable
+ [Federated Learning of Cohorts (FLoC)](https://en.wikipedia.org/wiki/Federated_Learning_of_Cohorts) tracking.
+- [GitLab for Slack app](../../user/admin_area/settings/slack_app.md) - Enable and configure the GitLab for Slack app.
+
+### CI/CD
+
+The **CI/CD** settings contain:
+
+- [Continuous Integration and Deployment](../../user/admin_area/settings/continuous_integration.md) -
+ Auto DevOps, runners and job artifacts.
+- [Required pipeline configuration](../../user/admin_area/settings/continuous_integration.md#required-pipeline-configuration) -
+ Set an instance-wide auto included [pipeline configuration](../../ci/yaml/index.md).
+ This pipeline configuration is run after the project's own configuration.
+- [Package Registry](../../user/admin_area/settings/continuous_integration.md#package-registry-configuration) -
+ Settings related to the use and experience of using the GitLab Package Registry. Some
+ [risks are involved](../../user/packages/container_registry/reduce_container_registry_storage.md#use-with-external-container-registries)
+ in enabling some of these settings.
+
+## Security and Compliance settings
+
+- [License compliance settings](../../user/admin_area/settings/security_and_compliance.md#choose-package-registry-metadata-to-sync): Enable or disable synchronization of package metadata by a registry type.
+
+### Geo **(PREMIUM SELF)**
+
+The **Geo** setting contains:
+
+- [Geo](../geo/index.md) - Replicate your GitLab instance to other
+ geographical locations. Redirects to **Admin Area > Geo > Settings** are no
+ longer available at **Admin Area > Settings > Geo** in [GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/issues/36896).
+
+### Integrations
+
+The **Integrations** settings contain:
+
+- [Elasticsearch](../../integration/advanced_search/elasticsearch.md#enable-advanced-search) -
+ Elasticsearch integration. Elasticsearch AWS IAM.
+- [Kroki](../integration/kroki.md#enable-kroki-in-gitlab) -
+ Allow rendering of diagrams in AsciiDoc and Markdown documents using [kroki.io](https://kroki.io).
+- [Mailgun](../integration/mailgun.md) - Enable your GitLab instance
+ to receive invite email bounce events from Mailgun, if it is your email provider.
+- [PlantUML](../integration/plantuml.md) - Allow rendering of PlantUML
+ diagrams in documents.
+- [Customer experience improvement and third-party offers](../../user/admin_area/settings/third_party_offers.md) -
+ Control the display of customer experience improvement content and third-party offers.
+- [Snowplow](../../development/internal_analytics/snowplow/index.md) - Configure the Snowplow integration.
+- [Google GKE](../../user/project/clusters/add_gke_clusters.md) - Google GKE integration enables
+ you to provision GKE clusters from GitLab.
+- [Amazon EKS](../../user/project/clusters/add_eks_clusters.md) - Amazon EKS integration enables
+ you to provision EKS clusters from GitLab.
+
+### Metrics and profiling
+
+The **Metrics and profiling** settings contain:
+
+- [Metrics - Prometheus](../monitoring/prometheus/gitlab_metrics.md) -
+ Enable and configure Prometheus metrics.
+- [Metrics - Grafana](../monitoring/performance/grafana_configuration.md#integrate-with-gitlab-ui) -
+ Enable and configure Grafana.
+- [Profiling - Performance bar](../monitoring/performance/performance_bar.md#enable-the-performance-bar-for-non-administrators) -
+ Enable access to the Performance Bar for non-administrator users in a given group.
+- [Usage statistics](../../user/admin_area/settings/usage_statistics.md) - Enable or disable version check and Service Ping.
+
+### Network
+
+The **Network** settings contain:
+
+- Performance optimization - Various settings that affect GitLab performance, including:
+ - [Write to `authorized_keys` file](../operations/fast_ssh_key_lookup.md#set-up-fast-lookup).
+ - [Push event activities limit and bulk push events](../../user/admin_area/settings/push_event_activities_limit.md).
+- [User and IP rate limits](../../user/admin_area/settings/user_and_ip_rate_limits.md) - Configure limits for web and API requests.
+ These rate limits can be overridden:
+ - [Package Registry Rate Limits](../../user/admin_area/settings/package_registry_rate_limits.md) - Configure specific
+ limits for Packages API requests that supersede the user and IP rate limits.
+ - [Git LFS Rate Limits](../../user/admin_area/settings/git_lfs_rate_limits.md) - Configure specific limits for
+ Git LFS requests that supersede the user and IP rate limits.
+ - [Files API Rate Limits](../../user/admin_area/settings/files_api_rate_limits.md) - Configure specific limits for
+ Files API requests that supersede the user and IP rate limits.
+ - [Search rate limits](../instance_limits.md#search-rate-limit) - Configure global search request rate limits for authenticated and unauthenticated users.
+ - [Deprecated API Rate Limits](../../user/admin_area/settings/deprecated_api_rate_limits.md) - Configure specific limits
+ for deprecated API requests that supersede the user and IP rate limits.
+- [Outbound requests](../../security/webhooks.md) - Allow requests to the local network from webhooks and integrations, or deny all outbound requests.
+- [Protected Paths](../../user/admin_area/settings/protected_paths.md) - Configure paths to be protected by Rack Attack.
+- [Incident Management Limits](../../operations/incident_management/index.md) - Limit the
+ number of inbound alerts that can be sent to a project.
+- [Notes creation limit](../../user/admin_area/settings/rate_limit_on_notes_creation.md) - Set a rate limit on the note creation requests.
+- [Get single user limit](../../user/admin_area/settings/rate_limit_on_users_api.md) - Set a rate limit on users API endpoint to get a user by ID.
+- [Projects API rate limits for unauthenticated requests](../../user/admin_area/settings/rate_limit_on_projects_api.md) - Set a rate limit on Projects list API endpoint for unauthenticated requests.
+
+### Preferences
+
+The **Preferences** settings contain:
+
+- [Email](../../user/admin_area/settings/email.md) - Various email settings.
+- [What's new](../whats-new.md) - Configure **What's new** drawer and content.
+- [Help page](help_page.md) - Help page text and support page URL.
+- [Pages](../pages/index.md#custom-domain-verification) -
+ Size and domain settings for static websites.
+- [Polling interval multiplier](../polling.md) -
+ Configure how frequently the GitLab UI polls for updates.
+- [Gitaly timeouts](gitaly_timeouts.md) - Configure Gitaly timeouts.
+- Localization:
+ - [Default first day of the week](../../user/profile/preferences.md).
+ - [Time tracking](../../user/project/time_tracking.md#limit-displayed-units-to-hours).
+- [Sidekiq Job Limits](../../user/admin_area/settings/sidekiq_job_limits.md) - Limit the size of Sidekiq jobs stored in Redis.
+
+### Reporting
+
+The **Reporting** settings contain:
+
+- Spam and Anti-bot protection:
+ - Anti-spam services, such as [reCAPTCHA](../../integration/recaptcha.md),
+ [Akismet](../../integration/akismet.md), or [Spamcheck](../../user/admin_area/reporting/spamcheck.md).
+ - [IP address restrictions](../../user/admin_area/reporting/ip_addr_restrictions.md).
+- [Abuse reports](../../user/admin_area/review_abuse_reports.md) - Set notification email for abuse reports.
+- [Git abuse rate limit](../../user/admin_area/reporting/git_abuse_rate_limit.md) - Configure Git abuse rate limit settings. **(ULTIMATE SELF)**
+
+### Repository
+
+The **Repository** settings contain:
+
+- [Repository's custom initial branch name](../../user/project/repository/branches/default.md#instance-level-custom-initial-branch-name) -
+ Set a custom branch name for new repositories created in your instance.
+- [Repository's initial default branch protection](../../user/project/repository/branches/default.md#instance-level-default-branch-protection) -
+ Configure the branch protections to apply to every repository's default branch.
+- [Repository mirror](../../user/admin_area/settings/visibility_and_access_controls.md#enable-project-mirroring) -
+ Configure repository mirroring.
+- [Repository storage](../repository_storage_types.md) - Configure storage path settings.
+- Repository maintenance:
+ - [Repository checks](../repository_checks.md) - Configure
+ automatic Git checks on repositories.
+ - [Housekeeping](../housekeeping.md). Configure automatic
+ Git housekeeping on repositories.
+ - [Inactive project deletion](../inactive_project_deletion.md). Configure inactive
+ project deletion.
+- [Repository static objects](../static_objects_external_storage.md) -
+ Serve repository static objects (for example, archives and blobs) from an external storage (for example, a CDN).
+
+### Templates **(PREMIUM SELF)**
+
+The **Templates** settings contain:
+
+- [Templates](../../user/admin_area/settings/instance_template_repository.md#configuration) - Set instance-wide template repository.
+- [Custom project templates](../../user/admin_area/custom_project_templates.md) - Select the custom project template source group.
+
+## Default first day of the week
+
+You can change the [Default first day of the week](../../user/profile/preferences.md)
+for the entire GitLab instance:
+
+1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
+1. Select **Admin Area**.
+1. Select **Settings > Preferences**.
+1. Scroll to the **Localization** section, and select your desired first day of the week.
+
+## Default language
+
+You can change the [Default language](../../user/profile/preferences.md)
+for the entire GitLab instance:
+
+1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
+1. Select **Admin Area**.
+1. Select **Settings > Preferences**.
+1. Scroll to the **Localization** section, and select your desired default language.
diff --git a/doc/administration/settings/package_registry_rate_limits.md b/doc/administration/settings/package_registry_rate_limits.md
new file mode 100644
index 00000000000..369b55c8087
--- /dev/null
+++ b/doc/administration/settings/package_registry_rate_limits.md
@@ -0,0 +1,57 @@
+---
+stage: Package
+group: Package Registry
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+type: reference
+---
+
+# Package Registry Rate Limits **(FREE SELF)**
+
+With the [GitLab Package Registry](../../user/packages/package_registry/index.md),
+you can use GitLab as a private or public registry for a variety of common package managers. You can
+publish and share packages, which others can consume as a dependency in downstream projects through
+the [Packages API](../../api/packages.md).
+
+If downstream projects frequently download such dependencies, many requests are made through the
+Packages API. You may therefore reach enforced [user and IP rate limits](../../user/admin_area/settings/user_and_ip_rate_limits.md).
+To address this issue, you can define specific rate limits for the Packages API:
+
+- [Unauthenticated requests (per IP)](#enable-unauthenticated-request-rate-limit-for-packages-api).
+- [Authenticated API requests (per user)](#enable-authenticated-api-request-rate-limit-for-packages-api).
+
+These limits are disabled by default.
+
+When enabled, they supersede the general user and IP rate limits for requests to
+the Packages API. You can therefore keep the general user and IP rate limits, and
+increase the rate limits for the Packages API. Besides this precedence, there is
+no difference in functionality compared to the general user and IP rate limits.
+
+## Enable unauthenticated request rate limit for packages API
+
+To enable the unauthenticated request rate limit:
+
+1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
+1. Select **Admin Area**.
+1. Select **Settings > Network**.
+1. Expand **Package registry rate limits**.
+1. Select **Enable unauthenticated request rate limit**.
+
+ - Optional. Update the **Maximum unauthenticated requests per rate limit period per IP** value.
+ Defaults to `800`.
+ - Optional. Update the **Unauthenticated rate limit period in seconds** value.
+ Defaults to `15`.
+
+## Enable authenticated API request rate limit for packages API
+
+To enable the authenticated API request rate limit:
+
+1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
+1. Select **Admin Area**.
+1. Select **Settings > Network**
+1. Expand **Package registry rate limits**.
+1. Select **Enable authenticated API request rate limit**.
+
+ - Optional. Update the **Maximum authenticated API requests per rate limit period per user** value.
+ Defaults to `1000`.
+ - Optional. Update the **Authenticated API rate limit period in seconds** value.
+ Defaults to `15`.
diff --git a/doc/administration/settings/project_integration_management.md b/doc/administration/settings/project_integration_management.md
new file mode 100644
index 00000000000..1bb4465020c
--- /dev/null
+++ b/doc/administration/settings/project_integration_management.md
@@ -0,0 +1,138 @@
+---
+stage: Manage
+group: Import and Integrate
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+# Project integration management **(FREE)**
+
+Project integrations can be configured and enabled by project administrators. As a GitLab instance
+administrator, you can set default configuration parameters for a given integration that all projects
+can inherit and use, enabling the integration for all projects that are not already using custom
+settings.
+
+You can update these default settings at any time, changing the settings used for all projects that
+are set to use instance-level or group-level defaults. Updating the default settings also enables the integration
+for all projects that didn't have it already enabled.
+
+Only the complete settings for an integration can be inherited. Per-field inheritance is [planned](https://gitlab.com/groups/gitlab-org/-/epics/2137).
+
+## Manage instance-level default settings for a project integration **(FREE SELF)**
+
+> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2137) in GitLab 13.3 for project-level integrations.
+> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2543) in GitLab 13.6 for group-level integrations.
+
+1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
+1. Select **Admin Area**.
+1. Select **Settings > Integrations**.
+1. Select an integration.
+1. Enter configuration details and select **Save changes**.
+
+WARNING:
+This may affect all or most of the groups and projects on your GitLab instance. Review the details
+below.
+
+If this is the first time you are setting up instance-level settings for an integration:
+
+- The integration is enabled for all groups and projects that don't already have this integration configured,
+ if you have the **Enable integration** toggle turned on in the instance-level settings.
+- Groups and projects that already have the integration configured are not affected, but can choose to use the
+ inherited settings at any time.
+
+When you make further changes to the instance defaults:
+
+- They are immediately applied to all groups and projects that have the integration set to use default settings.
+- They are immediately applied to newer groups and projects, created after you last saved defaults for the
+ integration. If your instance-level default setting has the **Enable integration** toggle turned
+ on, the integration is automatically enabled for all such groups and projects.
+- Groups and projects with custom settings selected for the integration are not immediately affected and may
+ choose to use the latest defaults at any time.
+
+Only the complete settings for an integration can be inherited. Per-field inheritance
+is [planned](https://gitlab.com/groups/gitlab-org/-/epics/2137). This would allow
+administrators to update settings inherited by groups and projects without enabling the
+integration on all non-configured groups and projects by default.
+
+### Remove an instance-level default setting
+
+1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
+1. Select **Admin Area**.
+1. Select **Settings > Integrations**.
+1. Select an integration.
+1. Select **Reset** and confirm.
+
+Resetting an instance-level default setting removes the integration from all projects that have the integration set to use default settings.
+
+### View projects that override the default settings
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218252) in GitLab 14.2.
+
+You can view which projects in your instance use custom settings that [override the instance-level default settings](#use-custom-settings-for-a-group-or-project-integration)
+for an integration.
+
+1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
+1. Select **Admin Area**.
+1. Select **Settings > Integrations**.
+1. Select an integration.
+1. Select the **Projects using custom settings** tab.
+
+## Manage group-level default settings for a project integration
+
+> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2543) in GitLab 13.6.
+
+1. Navigate to the group's **Settings > Integrations**.
+1. Select an integration.
+1. Enter configuration details and select **Save changes**.
+
+WARNING:
+This may affect all or most of the subgroups and projects belonging to the group. Review the details below.
+
+If this is the first time you are setting up group-level settings for an integration:
+
+- The integration is enabled for all subgroups and projects belonging to the group that don't already have
+ this integration configured, if you have the **Enable integration** toggle turned on in the group-level
+ settings.
+- Subgroups and projects that already have the integration configured are not affected, but can choose to use
+ the inherited settings at any time.
+
+When you make further changes to the group defaults:
+
+- They are immediately applied to all subgroups and projects belonging to the group that have the integration
+ set to use default settings.
+- They are immediately applied to newer subgroups and projects, even those created after you last saved defaults for the
+ integration. If your group-level default setting has the **Enable integration** toggle turned on,
+ the integration is automatically enabled for all such subgroups and projects.
+
+- Subgroups and projects with custom settings selected for the integration are not immediately affected and
+ may choose to use the latest defaults at any time.
+
+Only the complete settings for an integration can be inherited. Per-field inheritance
+is [planned](https://gitlab.com/groups/gitlab-org/-/epics/2137). This would allow
+administrators to update settings inherited by subgroups and projects without enabling the
+integration on all non-configured groups and projects by default.
+
+### Remove a group-level default setting
+
+1. Navigate to the group's **Settings > Integrations**.
+1. Select an integration.
+1. Select **Reset** and confirm.
+
+Resetting a group-level default setting removes integrations that use default settings and belong to a project or subgroup of the group.
+
+## Use instance-level or group-level default settings for a project integration
+
+> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2543) in GitLab 13.6 for group-level settings.
+
+1. Navigate to **Project > Settings > Integrations**.
+1. Choose the integration you want to enable or update.
+1. From the dropdown list, select **Use default settings**.
+1. Ensure the toggle is set to **Enable integration**.
+1. Select **Save changes**.
+
+## Use custom settings for a group or project integration
+
+1. Navigate to project or group's **Settings > Integrations**.
+1. Choose the integration you want to enable or update.
+1. From the dropdown list, select **Use custom settings**.
+1. Ensure the toggle is set to **Enable integration** and enter all required settings.
+1. Select **Save changes**.
diff --git a/doc/administration/snippets/index.md b/doc/administration/snippets/index.md
index 613d161a64c..9b485140070 100644
--- a/doc/administration/snippets/index.md
+++ b/doc/administration/snippets/index.md
@@ -25,7 +25,7 @@ content changes.
### Snippets size limit configuration
-This setting is not available through the [Admin Area settings](../../user/admin_area/settings/index.md).
+This setting is not available through the [Admin Area settings](../settings/index.md).
To configure this setting, use either the Rails console
or the [Application settings API](../../api/settings.md).
diff --git a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
index fc319fad3e8..f164e8ccbad 100644
--- a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
+++ b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
@@ -50,7 +50,7 @@ This content has been moved to [Troubleshooting CI/CD](../../ci/troubleshooting.
## License
-This content has been moved to [Activate GitLab EE with a license file or key](../../user/admin_area/license_file.md).
+This content has been moved to [Activate GitLab EE with a license file or key](../../administration/license_file.md).
## Registry
diff --git a/doc/administration/wikis/index.md b/doc/administration/wikis/index.md
index 540e50d5c70..2850d546bce 100644
--- a/doc/administration/wikis/index.md
+++ b/doc/administration/wikis/index.md
@@ -26,7 +26,7 @@ is edited again and the content changes.
### Wiki page content size limit configuration
-This setting is not available through the [Admin Area settings](../../user/admin_area/settings/index.md).
+This setting is not available through the [Admin Area settings](../settings/index.md).
To configure this setting, use either the Rails console
or the [Application settings API](../../api/settings.md).
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 761aafe1e65..1a0cf37af44 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -17863,6 +17863,7 @@ A user assigned to a merge request.
| <a id="mergerequestassigneepreferencesgitpodpath"></a>`preferencesGitpodPath` | [`String`](#string) | Web path to the Gitpod section within user preferences. |
| <a id="mergerequestassigneeprofileenablegitpodpath"></a>`profileEnableGitpodPath` | [`String`](#string) | Web path to enable Gitpod for the user. |
| <a id="mergerequestassigneeprojectmemberships"></a>`projectMemberships` | [`ProjectMemberConnection`](#projectmemberconnection) | Project memberships of the user. (see [Connections](#connections)) |
+| <a id="mergerequestassigneepronouns"></a>`pronouns` | [`String`](#string) | Pronouns of the user. |
| <a id="mergerequestassigneepublicemail"></a>`publicEmail` | [`String`](#string) | User's public email. |
| <a id="mergerequestassigneesavedreplies"></a>`savedReplies` | [`SavedReplyConnection`](#savedreplyconnection) | Saved replies authored by the user. Will not return saved replies if `saved_replies` feature flag is disabled. (see [Connections](#connections)) |
| <a id="mergerequestassigneestate"></a>`state` | [`UserState!`](#userstate) | State of the user. |
@@ -18138,6 +18139,7 @@ The author of the merge request.
| <a id="mergerequestauthorpreferencesgitpodpath"></a>`preferencesGitpodPath` | [`String`](#string) | Web path to the Gitpod section within user preferences. |
| <a id="mergerequestauthorprofileenablegitpodpath"></a>`profileEnableGitpodPath` | [`String`](#string) | Web path to enable Gitpod for the user. |
| <a id="mergerequestauthorprojectmemberships"></a>`projectMemberships` | [`ProjectMemberConnection`](#projectmemberconnection) | Project memberships of the user. (see [Connections](#connections)) |
+| <a id="mergerequestauthorpronouns"></a>`pronouns` | [`String`](#string) | Pronouns of the user. |
| <a id="mergerequestauthorpublicemail"></a>`publicEmail` | [`String`](#string) | User's public email. |
| <a id="mergerequestauthorsavedreplies"></a>`savedReplies` | [`SavedReplyConnection`](#savedreplyconnection) | Saved replies authored by the user. Will not return saved replies if `saved_replies` feature flag is disabled. (see [Connections](#connections)) |
| <a id="mergerequestauthorstate"></a>`state` | [`UserState!`](#userstate) | State of the user. |
@@ -18460,6 +18462,7 @@ A user participating in a merge request.
| <a id="mergerequestparticipantpreferencesgitpodpath"></a>`preferencesGitpodPath` | [`String`](#string) | Web path to the Gitpod section within user preferences. |
| <a id="mergerequestparticipantprofileenablegitpodpath"></a>`profileEnableGitpodPath` | [`String`](#string) | Web path to enable Gitpod for the user. |
| <a id="mergerequestparticipantprojectmemberships"></a>`projectMemberships` | [`ProjectMemberConnection`](#projectmemberconnection) | Project memberships of the user. (see [Connections](#connections)) |
+| <a id="mergerequestparticipantpronouns"></a>`pronouns` | [`String`](#string) | Pronouns of the user. |
| <a id="mergerequestparticipantpublicemail"></a>`publicEmail` | [`String`](#string) | User's public email. |
| <a id="mergerequestparticipantsavedreplies"></a>`savedReplies` | [`SavedReplyConnection`](#savedreplyconnection) | Saved replies authored by the user. Will not return saved replies if `saved_replies` feature flag is disabled. (see [Connections](#connections)) |
| <a id="mergerequestparticipantstate"></a>`state` | [`UserState!`](#userstate) | State of the user. |
@@ -18770,6 +18773,7 @@ A user assigned to a merge request as a reviewer.
| <a id="mergerequestreviewerpreferencesgitpodpath"></a>`preferencesGitpodPath` | [`String`](#string) | Web path to the Gitpod section within user preferences. |
| <a id="mergerequestreviewerprofileenablegitpodpath"></a>`profileEnableGitpodPath` | [`String`](#string) | Web path to enable Gitpod for the user. |
| <a id="mergerequestreviewerprojectmemberships"></a>`projectMemberships` | [`ProjectMemberConnection`](#projectmemberconnection) | Project memberships of the user. (see [Connections](#connections)) |
+| <a id="mergerequestreviewerpronouns"></a>`pronouns` | [`String`](#string) | Pronouns of the user. |
| <a id="mergerequestreviewerpublicemail"></a>`publicEmail` | [`String`](#string) | User's public email. |
| <a id="mergerequestreviewersavedreplies"></a>`savedReplies` | [`SavedReplyConnection`](#savedreplyconnection) | Saved replies authored by the user. Will not return saved replies if `saved_replies` feature flag is disabled. (see [Connections](#connections)) |
| <a id="mergerequestreviewerstate"></a>`state` | [`UserState!`](#userstate) | State of the user. |
@@ -23310,6 +23314,7 @@ Core represention of a GitLab user.
| <a id="usercorepreferencesgitpodpath"></a>`preferencesGitpodPath` | [`String`](#string) | Web path to the Gitpod section within user preferences. |
| <a id="usercoreprofileenablegitpodpath"></a>`profileEnableGitpodPath` | [`String`](#string) | Web path to enable Gitpod for the user. |
| <a id="usercoreprojectmemberships"></a>`projectMemberships` | [`ProjectMemberConnection`](#projectmemberconnection) | Project memberships of the user. (see [Connections](#connections)) |
+| <a id="usercorepronouns"></a>`pronouns` | [`String`](#string) | Pronouns of the user. |
| <a id="usercorepublicemail"></a>`publicEmail` | [`String`](#string) | User's public email. |
| <a id="usercoresavedreplies"></a>`savedReplies` | [`SavedReplyConnection`](#savedreplyconnection) | Saved replies authored by the user. Will not return saved replies if `saved_replies` feature flag is disabled. (see [Connections](#connections)) |
| <a id="usercorestate"></a>`state` | [`UserState!`](#userstate) | State of the user. |
@@ -28351,6 +28356,7 @@ Implementations:
| <a id="userpreferencesgitpodpath"></a>`preferencesGitpodPath` | [`String`](#string) | Web path to the Gitpod section within user preferences. |
| <a id="userprofileenablegitpodpath"></a>`profileEnableGitpodPath` | [`String`](#string) | Web path to enable Gitpod for the user. |
| <a id="userprojectmemberships"></a>`projectMemberships` | [`ProjectMemberConnection`](#projectmemberconnection) | Project memberships of the user. (see [Connections](#connections)) |
+| <a id="userpronouns"></a>`pronouns` | [`String`](#string) | Pronouns of the user. |
| <a id="userpublicemail"></a>`publicEmail` | [`String`](#string) | User's public email. |
| <a id="usersavedreplies"></a>`savedReplies` | [`SavedReplyConnection`](#savedreplyconnection) | Saved replies authored by the user. Will not return saved replies if `saved_replies` feature flag is disabled. (see [Connections](#connections)) |
| <a id="userstate"></a>`state` | [`UserState!`](#userstate) | State of the user. |
diff --git a/doc/install/migrate/compare_sm_to_saas.md b/doc/install/migrate/compare_sm_to_saas.md
index a83c4a6865f..4cdc849be2c 100644
--- a/doc/install/migrate/compare_sm_to_saas.md
+++ b/doc/install/migrate/compare_sm_to_saas.md
@@ -17,14 +17,14 @@ In GitLab SaaS, administration tasks are limited compared to a self-managed appl
In a self-managed instance:
-- You have complete access and administrative control over the application, including the [Admin Area](../../user/admin_area/settings/index.md).
+- You have complete access and administrative control over the application, including the [Admin Area](../../administration/settings/index.md).
- You can impersonate, create, add, and remove users.
- You can assign the [`Auditor`](../../administration/auditor_users.md) user type and `External` role.
On GitLab SaaS:
- You have limited administrative control. For example, you cannot impersonate, create, add, or remove users.
-- You cannot access the [Admin Area](../../user/admin_area/settings/index.md).
+- You cannot access the [Admin Area](../../administration/settings/index.md).
- You cannot assign the `Auditor` user type and `External` role.
## Logs
diff --git a/doc/integration/jira/configure.md b/doc/integration/jira/configure.md
index 443c717818b..d7e5272d431 100644
--- a/doc/integration/jira/configure.md
+++ b/doc/integration/jira/configure.md
@@ -22,7 +22,7 @@ Prerequisites:
- Jira personal access token (GitLab 16.0 and later).
You can enable the Jira issue integration by configuring your project settings in GitLab.
-You can configure these settings at the [group level](../../user/admin_area/settings/project_integration_management.md#manage-group-level-default-settings-for-a-project-integration) or at the [instance level](../../user/admin_area/settings/project_integration_management.md#manage-instance-level-default-settings-for-a-project-integration) for self-managed GitLab.
+You can configure these settings at the [group level](../../administration/settings/project_integration_management.md#manage-group-level-default-settings-for-a-project-integration) or at the [instance level](../../administration/settings/project_integration_management.md#manage-instance-level-default-settings-for-a-project-integration) for self-managed GitLab.
To configure your project settings in GitLab:
diff --git a/doc/subscriptions/self_managed/index.md b/doc/subscriptions/self_managed/index.md
index a64358a9c79..01a6bff0877 100644
--- a/doc/subscriptions/self_managed/index.md
+++ b/doc/subscriptions/self_managed/index.md
@@ -345,7 +345,7 @@ A payment receipt is emailed to you, which you can also access in the Customers
If your subscription was activated with an activation code, the additional seats are reflected in
your instance immediately. If you're using a license file, you receive an updated file.
-To add the seats, [add the license file](../../user/admin_area/license_file.md)
+To add the seats, [add the license file](../../administration/license_file.md)
to your instance.
### Renew subscription manually
@@ -451,7 +451,7 @@ before this occurs.
## Activate a license file or key
-If you have a license file or key, you can activate it [in the Admin Area](../../user/admin_area/license_file.md#activate-gitlab-ee-with-a-license-file-or-key).
+If you have a license file or key, you can activate it [in the Admin Area](../../administration/license_file.md#activate-gitlab-ee-with-a-license-file-or-key).
## Contact Support
diff --git a/doc/user/admin_area/license.md b/doc/user/admin_area/license.md
index be6478de2a0..4e003e1f2c8 100644
--- a/doc/user/admin_area/license.md
+++ b/doc/user/admin_area/license.md
@@ -38,12 +38,12 @@ To activate your instance with an activation code:
The subscription is activated.
If you have an offline environment,
-[activate GitLab EE with a license file or key](license_file.md) instead.
+[activate GitLab EE with a license file or key](../../administration/license_file.md) instead.
If you have questions or need assistance activating your instance,
[contact GitLab Support](https://about.gitlab.com/support/#contact-support).
-When [the license expires](license_file.md#what-happens-when-your-license-expires),
+When [the license expires](../../administration/license_file.md#what-happens-when-your-license-expires),
some functionality is locked.
## Verify your GitLab edition
diff --git a/doc/user/admin_area/license_file.md b/doc/user/admin_area/license_file.md
index 12e908b4fe0..4835d656cfa 100644
--- a/doc/user/admin_area/license_file.md
+++ b/doc/user/admin_area/license_file.md
@@ -1,252 +1,11 @@
---
-stage: Fulfillment
-group: Provision
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+redirect_to: '../../administration/license_file.md'
+remove_date: '2023-10-11'
---
-<!-- To promote the workflow described in license.md, this page is not included in global left nav. -->
+This document was moved to [another location](../../administration/license_file.md).
-# Activate GitLab EE with a license file or key
-
-If you receive a license file from GitLab (for example, for a trial), you can
-upload it to your instance or add it during installation. The license file is
-a base64-encoded ASCII text file with a `.gitlab-license` extension.
-
-The first time you sign in to your GitLab instance, a note with a
-link to the **Add license** page should be displayed.
-
-Otherwise, to add your license:
-
-1. Sign in to GitLab as an administrator.
-1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
-1. Select **Admin Area**.
-1. Select **Settings > General**.
-1. In the **Add License** area, add a license by either uploading the file or entering the key.
-1. Select the **Terms of Service** checkbox.
-1. Select **Add license**.
-
-NOTE:
-In GitLab 14.7.x to 14.9.x, you can add the license file with the UI.
-In GitLab 14.1.x to 14.7, if you have already activated your subscription with an activation code, you cannot access **Add License** from the Admin Area. You must access **Add License** directly from the URL, `<YourGitLabURL>/admin/license/new`.
-
-## Activate subscription during installation
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/114572) in GitLab 16.0.
-
-To activate your subscription during installation, set the `GITLAB_ACTIVATION_CODE` environment variable with the activation code:
-
-```shell
-export GITLAB_ACTIVATION_CODE=your_activation_code
-```
-
-## Add license file during installation
-
-If you have a license, you can also import it when you install GitLab.
-
-- For self-compiled installations:
- - Place the `Gitlab.gitlab-license` file in the `config/` directory.
- - To specify a custom location and filename for the license, set the
- `GITLAB_LICENSE_FILE` environment variable with the path to the file:
-
- ```shell
- export GITLAB_LICENSE_FILE="/path/to/license/file"
- ```
-
-- For Linux package installations:
- - Place the `Gitlab.gitlab-license` file in the `/etc/gitlab/` directory.
- - To specify a custom location and filename for the license, add this entry to `gitlab.rb`:
-
- ```ruby
- gitlab_rails['initial_license_file'] = "/path/to/license/file"
- ```
-
-WARNING:
-These methods only add a license at the time of installation. To renew or upgrade
-a license, add the license in the **Admin Area** in the web user interface.
-
-## Submit license usage data
-
-If you use a license file or key to activate your instance in an offline environment, you must submit your license
-usage data monthly.
-To submit the data, [export your license usage](../../subscriptions/self_managed/index.md#export-your-license-usage)
-and send it by email to the renewals service, `renewals-service@customers.gitlab.com`.
-
-If you don't submit your data each month after your subscription start date, an email is sent to the address
-associated with your subscription and a banner displays to remind you to submit your data. The banner displays
-in the **Admin Area** on the **Dashboard** and on the **Subscription** pages. You can only dismiss it until the
-following month after you submit your license usage data.
-
-## What happens when your license expires
-
-Fifteen days before the license expires, a notification banner with the upcoming expiration
-date displays to GitLab administrators.
-
-When your license expires, GitLab locks features, like Git pushes
-and issue creation. Your instance becomes read-only and
-an expiration message displays to all administrators. You have a 14-day grace period
-before this occurs.
-
-To resume functionality, [renew your subscription](../../subscriptions/self_managed/index.md#renew-subscription-manually).
-
-If the license has been expired for more than 30 days, you must purchase a [new subscription](../../subscriptions/self_managed/index.md) to resume functionality.
-
-To go back to Free features, [delete all expired licenses](#remove-a-license).
-
-## Remove a license
-
-To remove a license from a self-managed instance:
-
-1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
-1. Select **Admin Area**.
-1. Select **Subscription**.
-1. Select **Remove license**.
-
-Repeat these steps to remove all licenses, including those applied in the past.
-
-## View license details and history
-
-To view your license details:
-
-1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
-1. Select **Admin Area**.
-1. Select **Subscription**.
-
-You can add and view more than one license, but only the latest license in
-the current date range is the active license.
-
-When you add a future-dated license, it doesn't take effect until its applicable date.
-You can view all active subscriptions in the **Subscription history** table.
-
-You can also [export](../../subscriptions/self_managed/index.md) your license usage information to a CSV file.
-
-NOTE:
-In GitLab 13.6 and earlier, a banner about an expiring license may continue to display
-when you add a new license. This happens when the start date of the new license
-is in the future and the expiring one is still active.
-The banner disappears after the new license becomes active.
-
-## Troubleshooting
-
-### No Subscription area in the Admin Area
-
-You cannot add your license because there is no **Subscription** area.
-This issue might occur if:
-
-- You're running GitLab Community Edition. Before you add your license, you
- must [upgrade to Enterprise Edition](../../update/index.md#community-to-enterprise-edition).
-- You're using GitLab.com. You cannot add a self-managed license to GitLab.com.
- To use paid features on GitLab.com, [purchase a separate subscription](../../subscriptions/gitlab_com/index.md).
-
-### Users exceed license limit upon renewal
-
-GitLab displays a message prompting you to purchase
-additional users. This issue occurs if you add a license that does not have enough
-users to cover the number of users in your instance.
-
-To fix this issue, purchase additional seats to cover those users.
-For more information, read the [licensing FAQ](https://about.gitlab.com/pricing/licensing-faq/).
-
-In GitLab 14.2 and later, for instances that use a license file, the following
-rules apply:
-
-- If the users over license are less than or equal to 10% of the users in the license
- file, the license is applied and you pay the overage in the next renewal.
-- If the users over license are more than 10% of the users in the license file,
- you cannot apply the license without purchasing more users.
-
-For example, if you purchase a license for 100 users, you can have 110 users when you add
-your license. However, if you have 111 users, you must purchase more users before you can add
-the license.
-
-### `Start GitLab Ultimate trial` still displays after adding license
-
-To fix this issue, restart [Puma or your entire GitLab instance](../../administration/restart_gitlab.md).
-
-### License commands in the rails console
-
-The following commands can be run in the [rails console](../../administration/operations/rails_console.md#starting-a-rails-console-session).
-
-WARNING:
-Any command that changes data directly could be damaging if not run correctly, or under the right conditions.
-We highly recommend running them in a test environment with a backup of the instance ready to be restored, just in case.
-
-#### See current license information
-
-```ruby
-# License information (name, company, email address)
-License.current.licensee
-
-# Plan:
-License.current.plan
-
-# Uploaded:
-License.current.created_at
-
-# Started:
-License.current.starts_at
-
-# Expires at:
-License.current.expires_at
-
-# Is this a trial license?
-License.current.trial?
-
-# License ID for lookup on CustomersDot
-License.current.license_id
-
-# License data in Base64-encoded ASCII format
-License.current.data
-
-# Confirm the current billable seat count excluding guest users. This is useful for customers who use an Ultimate subscription tier where Guest seats are not counted.
-User.active.without_bots.excluding_guests.count
-
-```
-
-#### Interaction with licenses that start in the future
-
-```ruby
-# Future license data follows the same format as current license data it just uses a different modifier for the License prefix
-License.future_dated
-```
-
-#### Check if a project feature is available on the instance
-
-Features listed in [`features.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/models/gitlab_subscriptions/features.rb).
-
-```ruby
-License.current.feature_available?(:jira_dev_panel_integration)
-```
-
-#### Check if a project feature is available in a project
-
-Features listed in [`features.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/models/gitlab_subscriptions/features.rb).
-
-```ruby
-p = Project.find_by_full_path('<group>/<project>')
-p.feature_available?(:jira_dev_panel_integration)
-```
-
-#### Add a license through the console
-
-```ruby
-key = "<key>"
-license = License.new(data: key)
-license.save
-License.current # check to make sure it applied
-```
-
-This is needed for example in a known edge-case with
-[expired license and multiple LDAP servers](../../administration/auth/ldap/ldap-troubleshooting.md#expired-license-causes-errors-with-multiple-ldap-servers).
-
-#### Remove licenses
-
-To clean up the [License History table](../../user/admin_area/license_file.md#view-license-details-and-history):
-
-```ruby
-TYPE = :trial?
-# or :expired?
-
-License.select(&TYPE).each(&:destroy!)
-
-# or even License.all.each(&:destroy!)
-```
+<!-- This redirect file can be deleted after <2023-10-11>. -->
+<!-- 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/ee/development/documentation/redirects.html -->
diff --git a/doc/user/admin_area/settings/index.md b/doc/user/admin_area/settings/index.md
index 3b38e1732d0..37112e6897f 100644
--- a/doc/user/admin_area/settings/index.md
+++ b/doc/user/admin_area/settings/index.md
@@ -1,215 +1,11 @@
---
-stage: Create
-group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
-type: index
+redirect_to: '../../../administration/settings/index.md'
+remove_date: '2023-10-10'
---
-# Admin Area settings **(FREE SELF)**
+This document was moved to [another location](../../../administration/settings/index.md).
-As an administrator of a GitLab self-managed instance, you can manage the behavior of your
-deployment.
-
-The **Admin Area** is not accessible on GitLab.com, and settings can only be changed by the
-GitLab.com administrators. For the settings and limits on the GitLab.com instance,
-read [GitLab.com settings](../../gitlab_com/index.md).
-
-## Access the Admin Area
-
-To access the **Admin Area**:
-
-1. Sign in to your GitLab instance as an administrator.
-1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
-1. Select **Admin Area**.
-1. Select **Settings**, and the group of settings to view:
- - [General](#general)
- - [Geo](#geo)
- - [CI/CD](#cicd)
- - [Integrations](#integrations)
- - [Metrics and profiling](#metrics-and-profiling)
- - [Network](#network)
- - [Preferences](#preferences)
- - [Reporting](#reporting)
- - [Repository](#repository)
- - [Templates](#templates)
-
-### General
-
-The **General** settings contain:
-
-- [Visibility and access controls](visibility_and_access_controls.md) - Set default and
- restrict visibility levels. Configure import sources and Git access protocol.
-- [Account and limit](account_and_limit_settings.md) - Set projects and maximum size limits,
- session duration, user options, and check feature availability for namespace plan.
-- [Diff limits](../diff_limits.md) - Diff content limits.
-- [Sign-up restrictions](sign_up_restrictions.md) - Configure the way a user creates a new account.
-- [Sign in restrictions](sign_in_restrictions.md) - Set requirements for a user to sign in.
- Enable mandatory two-factor authentication.
-- [Terms of Service and Privacy Policy](terms.md) - Include a Terms of Service agreement
- and Privacy Policy that all users must accept.
-- [External Authentication](external_authorization.md#configuration) - External Classification Policy Authorization.
-- [Web terminal](../../../administration/integration/terminal.md#limiting-websocket-connection-time) -
- Set max session time for web terminal.
-- [FLoC](floc.md) - Enable or disable
- [Federated Learning of Cohorts (FLoC)](https://en.wikipedia.org/wiki/Federated_Learning_of_Cohorts) tracking.
-- [GitLab for Slack app](slack_app.md) - Enable and configure the GitLab for Slack app.
-
-### CI/CD
-
-The **CI/CD** settings contain:
-
-- [Continuous Integration and Deployment](continuous_integration.md) -
- Auto DevOps, runners and job artifacts.
-- [Required pipeline configuration](continuous_integration.md#required-pipeline-configuration) -
- Set an instance-wide auto included [pipeline configuration](../../../ci/yaml/index.md).
- This pipeline configuration is run after the project's own configuration.
-- [Package Registry](continuous_integration.md#package-registry-configuration) -
- Settings related to the use and experience of using the GitLab Package Registry. Some
- [risks are involved](../../packages/container_registry/reduce_container_registry_storage.md#use-with-external-container-registries)
- in enabling some of these settings.
-
-## Security and Compliance settings
-
-- [License compliance settings](security_and_compliance.md#choose-package-registry-metadata-to-sync): Enable or disable synchronization of package metadata by a registry type.
-
-### Geo **(PREMIUM SELF)**
-
-The **Geo** setting contains:
-
-- [Geo](../../../administration/geo/index.md) - Replicate your GitLab instance to other
- geographical locations. Redirects to **Admin Area > Geo > Settings** are no
- longer available at **Admin Area > Settings > Geo** in [GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/issues/36896).
-
-### Integrations
-
-The **Integrations** settings contain:
-
-- [Elasticsearch](../../../integration/advanced_search/elasticsearch.md#enable-advanced-search) -
- Elasticsearch integration. Elasticsearch AWS IAM.
-- [Kroki](../../../administration/integration/kroki.md#enable-kroki-in-gitlab) -
- Allow rendering of diagrams in AsciiDoc and Markdown documents using [kroki.io](https://kroki.io).
-- [Mailgun](../../../administration/integration/mailgun.md) - Enable your GitLab instance
- to receive invite email bounce events from Mailgun, if it is your email provider.
-- [PlantUML](../../../administration/integration/plantuml.md) - Allow rendering of PlantUML
- diagrams in documents.
-- [Customer experience improvement and third-party offers](third_party_offers.md) -
- Control the display of customer experience improvement content and third-party offers.
-- [Snowplow](../../../development/internal_analytics/snowplow/index.md) - Configure the Snowplow integration.
-- [Google GKE](../../project/clusters/add_gke_clusters.md) - Google GKE integration enables
- you to provision GKE clusters from GitLab.
-- [Amazon EKS](../../project/clusters/add_eks_clusters.md) - Amazon EKS integration enables
- you to provision EKS clusters from GitLab.
-
-### Metrics and profiling
-
-The **Metrics and profiling** settings contain:
-
-- [Metrics - Prometheus](../../../administration/monitoring/prometheus/gitlab_metrics.md) -
- Enable and configure Prometheus metrics.
-- [Metrics - Grafana](../../../administration/monitoring/performance/grafana_configuration.md#integrate-with-gitlab-ui) -
- Enable and configure Grafana.
-- [Profiling - Performance bar](../../../administration/monitoring/performance/performance_bar.md#enable-the-performance-bar-for-non-administrators) -
- Enable access to the Performance Bar for non-administrator users in a given group.
-- [Usage statistics](usage_statistics.md) - Enable or disable version check and Service Ping.
-
-### Network
-
-The **Network** settings contain:
-
-- Performance optimization - Various settings that affect GitLab performance, including:
- - [Write to `authorized_keys` file](../../../administration/operations/fast_ssh_key_lookup.md#set-up-fast-lookup).
- - [Push event activities limit and bulk push events](push_event_activities_limit.md).
-- [User and IP rate limits](user_and_ip_rate_limits.md) - Configure limits for web and API requests.
- These rate limits can be overridden:
- - [Package Registry Rate Limits](package_registry_rate_limits.md) - Configure specific
- limits for Packages API requests that supersede the user and IP rate limits.
- - [Git LFS Rate Limits](git_lfs_rate_limits.md) - Configure specific limits for
- Git LFS requests that supersede the user and IP rate limits.
- - [Files API Rate Limits](files_api_rate_limits.md) - Configure specific limits for
- Files API requests that supersede the user and IP rate limits.
- - [Search rate limits](../../../administration/instance_limits.md#search-rate-limit) - Configure global search request rate limits for authenticated and unauthenticated users.
- - [Deprecated API Rate Limits](deprecated_api_rate_limits.md) - Configure specific limits
- for deprecated API requests that supersede the user and IP rate limits.
-- [Outbound requests](../../../security/webhooks.md) - Allow requests to the local network from webhooks and integrations, or deny all outbound requests.
-- [Protected Paths](protected_paths.md) - Configure paths to be protected by Rack Attack.
-- [Incident Management Limits](../../../operations/incident_management/index.md) - Limit the
- number of inbound alerts that can be sent to a project.
-- [Notes creation limit](rate_limit_on_notes_creation.md) - Set a rate limit on the note creation requests.
-- [Get single user limit](rate_limit_on_users_api.md) - Set a rate limit on users API endpoint to get a user by ID.
-- [Projects API rate limits for unauthenticated requests](rate_limit_on_projects_api.md) - Set a rate limit on Projects list API endpoint for unauthenticated requests.
-
-### Preferences
-
-The **Preferences** settings contain:
-
-- [Email](email.md) - Various email settings.
-- [What's new](../../../administration/whats-new.md) - Configure **What's new** drawer and content.
-- [Help page](help_page.md) - Help page text and support page URL.
-- [Pages](../../../administration/pages/index.md#custom-domain-verification) -
- Size and domain settings for static websites.
-- [Polling interval multiplier](../../../administration/polling.md) -
- Configure how frequently the GitLab UI polls for updates.
-- [Gitaly timeouts](gitaly_timeouts.md) - Configure Gitaly timeouts.
-- Localization:
- - [Default first day of the week](../../profile/preferences.md).
- - [Time tracking](../../project/time_tracking.md#limit-displayed-units-to-hours).
-- [Sidekiq Job Limits](sidekiq_job_limits.md) - Limit the size of Sidekiq jobs stored in Redis.
-
-### Reporting
-
-The **Reporting** settings contain:
-
-- Spam and Anti-bot protection:
- - Anti-spam services, such as [reCAPTCHA](../../../integration/recaptcha.md),
- [Akismet](../../../integration/akismet.md), or [Spamcheck](../reporting/spamcheck.md).
- - [IP address restrictions](../reporting/ip_addr_restrictions.md).
-- [Abuse reports](../review_abuse_reports.md) - Set notification email for abuse reports.
-- [Git abuse rate limit](../reporting/git_abuse_rate_limit.md) - Configure Git abuse rate limit settings. **(ULTIMATE SELF)**
-
-### Repository
-
-The **Repository** settings contain:
-
-- [Repository's custom initial branch name](../../project/repository/branches/default.md#instance-level-custom-initial-branch-name) -
- Set a custom branch name for new repositories created in your instance.
-- [Repository's initial default branch protection](../../project/repository/branches/default.md#instance-level-default-branch-protection) -
- Configure the branch protections to apply to every repository's default branch.
-- [Repository mirror](visibility_and_access_controls.md#enable-project-mirroring) -
- Configure repository mirroring.
-- [Repository storage](../../../administration/repository_storage_types.md) - Configure storage path settings.
-- Repository maintenance:
- - [Repository checks](../../../administration/repository_checks.md) - Configure
- automatic Git checks on repositories.
- - [Housekeeping](../../../administration/housekeeping.md). Configure automatic
- Git housekeeping on repositories.
- - [Inactive project deletion](../../../administration/inactive_project_deletion.md). Configure inactive
- project deletion.
-- [Repository static objects](../../../administration/static_objects_external_storage.md) -
- Serve repository static objects (for example, archives and blobs) from an external storage (for example, a CDN).
-
-### Templates **(PREMIUM SELF)**
-
-The **Templates** settings contain:
-
-- [Templates](instance_template_repository.md#configuration) - Set instance-wide template repository.
-- [Custom project templates](../custom_project_templates.md) - Select the custom project template source group.
-
-## Default first day of the week
-
-You can change the [Default first day of the week](../../profile/preferences.md)
-for the entire GitLab instance:
-
-1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
-1. Select **Admin Area**.
-1. Select **Settings > Preferences**.
-1. Scroll to the **Localization** section, and select your desired first day of the week.
-
-## Default language
-
-You can change the [Default language](../../profile/preferences.md)
-for the entire GitLab instance:
-
-1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
-1. Select **Admin Area**.
-1. Select **Settings > Preferences**.
-1. Scroll to the **Localization** section, and select your desired default language.
+<!-- This redirect file can be deleted after <2023-10-10>. -->
+<!-- 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/ee/development/documentation/redirects.html -->
diff --git a/doc/user/admin_area/settings/package_registry_rate_limits.md b/doc/user/admin_area/settings/package_registry_rate_limits.md
index a1bc339ddd9..269864bdd49 100644
--- a/doc/user/admin_area/settings/package_registry_rate_limits.md
+++ b/doc/user/admin_area/settings/package_registry_rate_limits.md
@@ -1,57 +1,11 @@
---
-stage: Package
-group: Package Registry
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
-type: reference
+redirect_to: '../../../administration/settings/package_registry_rate_limits.md'
+remove_date: '2023-10-11'
---
-# Package Registry Rate Limits **(FREE SELF)**
+This document was moved to [another location](../../../administration/settings/package_registry_rate_limits.md).
-With the [GitLab Package Registry](../../packages/package_registry/index.md),
-you can use GitLab as a private or public registry for a variety of common package managers. You can
-publish and share packages, which others can consume as a dependency in downstream projects through
-the [Packages API](../../../api/packages.md).
-
-If downstream projects frequently download such dependencies, many requests are made through the
-Packages API. You may therefore reach enforced [user and IP rate limits](user_and_ip_rate_limits.md).
-To address this issue, you can define specific rate limits for the Packages API:
-
-- [Unauthenticated requests (per IP)](#enable-unauthenticated-request-rate-limit-for-packages-api).
-- [Authenticated API requests (per user)](#enable-authenticated-api-request-rate-limit-for-packages-api).
-
-These limits are disabled by default.
-
-When enabled, they supersede the general user and IP rate limits for requests to
-the Packages API. You can therefore keep the general user and IP rate limits, and
-increase the rate limits for the Packages API. Besides this precedence, there is
-no difference in functionality compared to the general user and IP rate limits.
-
-## Enable unauthenticated request rate limit for packages API
-
-To enable the unauthenticated request rate limit:
-
-1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
-1. Select **Admin Area**.
-1. Select **Settings > Network**.
-1. Expand **Package registry rate limits**.
-1. Select **Enable unauthenticated request rate limit**.
-
- - Optional. Update the **Maximum unauthenticated requests per rate limit period per IP** value.
- Defaults to `800`.
- - Optional. Update the **Unauthenticated rate limit period in seconds** value.
- Defaults to `15`.
-
-## Enable authenticated API request rate limit for packages API
-
-To enable the authenticated API request rate limit:
-
-1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
-1. Select **Admin Area**.
-1. Select **Settings > Network**
-1. Expand **Package registry rate limits**.
-1. Select **Enable authenticated API request rate limit**.
-
- - Optional. Update the **Maximum authenticated API requests per rate limit period per user** value.
- Defaults to `1000`.
- - Optional. Update the **Authenticated API rate limit period in seconds** value.
- Defaults to `15`.
+<!-- This redirect file can be deleted after <2023-10-11>. -->
+<!-- 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/ee/development/documentation/redirects.html -->
diff --git a/doc/user/admin_area/settings/project_integration_management.md b/doc/user/admin_area/settings/project_integration_management.md
index 1bb4465020c..eff19caabbe 100644
--- a/doc/user/admin_area/settings/project_integration_management.md
+++ b/doc/user/admin_area/settings/project_integration_management.md
@@ -1,138 +1,11 @@
---
-stage: Manage
-group: Import and Integrate
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+redirect_to: '../../../administration/settings/project_integration_management.md'
+remove_date: '2023-10-11'
---
-# Project integration management **(FREE)**
+This document was moved to [another location](../../../administration/settings/project_integration_management.md).
-Project integrations can be configured and enabled by project administrators. As a GitLab instance
-administrator, you can set default configuration parameters for a given integration that all projects
-can inherit and use, enabling the integration for all projects that are not already using custom
-settings.
-
-You can update these default settings at any time, changing the settings used for all projects that
-are set to use instance-level or group-level defaults. Updating the default settings also enables the integration
-for all projects that didn't have it already enabled.
-
-Only the complete settings for an integration can be inherited. Per-field inheritance is [planned](https://gitlab.com/groups/gitlab-org/-/epics/2137).
-
-## Manage instance-level default settings for a project integration **(FREE SELF)**
-
-> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2137) in GitLab 13.3 for project-level integrations.
-> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2543) in GitLab 13.6 for group-level integrations.
-
-1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
-1. Select **Admin Area**.
-1. Select **Settings > Integrations**.
-1. Select an integration.
-1. Enter configuration details and select **Save changes**.
-
-WARNING:
-This may affect all or most of the groups and projects on your GitLab instance. Review the details
-below.
-
-If this is the first time you are setting up instance-level settings for an integration:
-
-- The integration is enabled for all groups and projects that don't already have this integration configured,
- if you have the **Enable integration** toggle turned on in the instance-level settings.
-- Groups and projects that already have the integration configured are not affected, but can choose to use the
- inherited settings at any time.
-
-When you make further changes to the instance defaults:
-
-- They are immediately applied to all groups and projects that have the integration set to use default settings.
-- They are immediately applied to newer groups and projects, created after you last saved defaults for the
- integration. If your instance-level default setting has the **Enable integration** toggle turned
- on, the integration is automatically enabled for all such groups and projects.
-- Groups and projects with custom settings selected for the integration are not immediately affected and may
- choose to use the latest defaults at any time.
-
-Only the complete settings for an integration can be inherited. Per-field inheritance
-is [planned](https://gitlab.com/groups/gitlab-org/-/epics/2137). This would allow
-administrators to update settings inherited by groups and projects without enabling the
-integration on all non-configured groups and projects by default.
-
-### Remove an instance-level default setting
-
-1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
-1. Select **Admin Area**.
-1. Select **Settings > Integrations**.
-1. Select an integration.
-1. Select **Reset** and confirm.
-
-Resetting an instance-level default setting removes the integration from all projects that have the integration set to use default settings.
-
-### View projects that override the default settings
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218252) in GitLab 14.2.
-
-You can view which projects in your instance use custom settings that [override the instance-level default settings](#use-custom-settings-for-a-group-or-project-integration)
-for an integration.
-
-1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
-1. Select **Admin Area**.
-1. Select **Settings > Integrations**.
-1. Select an integration.
-1. Select the **Projects using custom settings** tab.
-
-## Manage group-level default settings for a project integration
-
-> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2543) in GitLab 13.6.
-
-1. Navigate to the group's **Settings > Integrations**.
-1. Select an integration.
-1. Enter configuration details and select **Save changes**.
-
-WARNING:
-This may affect all or most of the subgroups and projects belonging to the group. Review the details below.
-
-If this is the first time you are setting up group-level settings for an integration:
-
-- The integration is enabled for all subgroups and projects belonging to the group that don't already have
- this integration configured, if you have the **Enable integration** toggle turned on in the group-level
- settings.
-- Subgroups and projects that already have the integration configured are not affected, but can choose to use
- the inherited settings at any time.
-
-When you make further changes to the group defaults:
-
-- They are immediately applied to all subgroups and projects belonging to the group that have the integration
- set to use default settings.
-- They are immediately applied to newer subgroups and projects, even those created after you last saved defaults for the
- integration. If your group-level default setting has the **Enable integration** toggle turned on,
- the integration is automatically enabled for all such subgroups and projects.
-
-- Subgroups and projects with custom settings selected for the integration are not immediately affected and
- may choose to use the latest defaults at any time.
-
-Only the complete settings for an integration can be inherited. Per-field inheritance
-is [planned](https://gitlab.com/groups/gitlab-org/-/epics/2137). This would allow
-administrators to update settings inherited by subgroups and projects without enabling the
-integration on all non-configured groups and projects by default.
-
-### Remove a group-level default setting
-
-1. Navigate to the group's **Settings > Integrations**.
-1. Select an integration.
-1. Select **Reset** and confirm.
-
-Resetting a group-level default setting removes integrations that use default settings and belong to a project or subgroup of the group.
-
-## Use instance-level or group-level default settings for a project integration
-
-> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2543) in GitLab 13.6 for group-level settings.
-
-1. Navigate to **Project > Settings > Integrations**.
-1. Choose the integration you want to enable or update.
-1. From the dropdown list, select **Use default settings**.
-1. Ensure the toggle is set to **Enable integration**.
-1. Select **Save changes**.
-
-## Use custom settings for a group or project integration
-
-1. Navigate to project or group's **Settings > Integrations**.
-1. Choose the integration you want to enable or update.
-1. From the dropdown list, select **Use custom settings**.
-1. Ensure the toggle is set to **Enable integration** and enter all required settings.
-1. Select **Save changes**.
+<!-- This redirect file can be deleted after <2023-10-11>. -->
+<!-- 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/ee/development/documentation/redirects.html -->
diff --git a/doc/user/organization/index.md b/doc/user/organization/index.md
index f5af26250f6..2a33543fea5 100644
--- a/doc/user/organization/index.md
+++ b/doc/user/organization/index.md
@@ -24,7 +24,7 @@ everything you do as a GitLab administrator, including:
- Aggregating data from all your groups, subgroups, and projects.
Our goal is to reach feature parity between SaaS and self-managed installations, with all
-[Admin Area settings](/ee/user/admin_area/settings/index.md) moving to either:
+[Admin Area settings](../../administration/settings/index.md) moving to either:
- Groups. Available in the Organization, and subgroups.
- Hardware Controls. For functionality that does not apply to groups, Hardware Controls are only
diff --git a/doc/user/profile/preferences.md b/doc/user/profile/preferences.md
index f6fcd274d7c..17dea99e5ef 100644
--- a/doc/user/profile/preferences.md
+++ b/doc/user/profile/preferences.md
@@ -157,7 +157,7 @@ You can choose one of the following options as the first day of the week:
- Sunday
- Monday
-If you select **System Default**, the [instance default](../admin_area/settings/index.md#default-first-day-of-the-week) setting is used.
+If you select **System Default**, the [instance default](../../administration/settings/index.md#default-first-day-of-the-week) setting is used.
## Time preferences
diff --git a/gems/click_house-client/Gemfile.lock b/gems/click_house-client/Gemfile.lock
index 088e39c07fb..a8e2b7ec0c6 100644
--- a/gems/click_house-client/Gemfile.lock
+++ b/gems/click_house-client/Gemfile.lock
@@ -33,7 +33,7 @@ GEM
parser (3.2.2.3)
ast (~> 2.4.1)
racc
- public_suffix (5.0.1)
+ public_suffix (5.0.3)
racc (1.7.1)
rack (3.0.8)
rainbow (3.1.1)
@@ -88,7 +88,7 @@ GEM
unicode-display_width (2.4.2)
PLATFORMS
- x86_64-linux
+ ruby
DEPENDENCIES
click_house-client!
@@ -99,4 +99,4 @@ DEPENDENCIES
rubocop-rspec
BUNDLED WITH
- 2.4.15
+ 2.4.16
diff --git a/lib/result.rb b/lib/result.rb
new file mode 100644
index 00000000000..5e72b3f13cb
--- /dev/null
+++ b/lib/result.rb
@@ -0,0 +1,209 @@
+# frozen_string_literal: true
+
+# A (partial) implementation of the functional Result type, with naming conventions based on the
+# Rust implementation (https://doc.rust-lang.org/std/result/index.html)
+#
+# Modern Ruby 3+ destructuring and pattern matching are supported.
+#
+# - See "Railway Oriented Programming and the Result Class" in `ee/lib/remote_development/README.md` for details
+# and example usage.
+# - See `spec/lib/result_spec.rb` for detailed executable example usage.
+# - See https://en.wikipedia.org/wiki/Result_type for a general description of the Result pattern.
+# - See https://fsharpforfunandprofit.com/rop/ for how this can be used with Railway Oriented Programming (ROP)
+# to improve design and architecture
+# - See https://doc.rust-lang.org/std/result/ for the Rust implementation.
+
+# NOTE: This class is intentionally not namespaced to allow for more concise, readable, and explicit usage.
+# It it a generic reusable implementation of the Result type, and is not specific to any domain
+# rubocop:disable Gitlab/NamespacedClass
+class Result
+ # The .ok and .err factory class methods are the only way to create a Result
+ #
+ # "self.ok" corresponds to Ok(T) in Rust: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Ok
+ #
+ # @param [Object, #new] ok_value
+ # @return [Result]
+ def self.ok(ok_value)
+ new(ok_value: ok_value)
+ end
+
+ # "self.err" corresponds to Err(E) in Rust: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Err
+ #
+ # @param [Object, #new] ok_value
+ # @return [Result]
+ def self.err(err_value)
+ new(err_value: err_value)
+ end
+
+ # "#unwrap" corresponds to "unwrap" in Rust.
+ #
+ # @return [Object]
+ # @raise [RuntimeError] if called on an "err" Result
+ def unwrap
+ ok? ? value : raise("Called Result#unwrap on an 'err' Result")
+ end
+
+ # "#unwrap" corresponds to "unwrap" in Rust.
+ #
+ # @return [Object]
+ # @raise [RuntimeError] if called on an "ok" Result
+ def unwrap_err
+ err? ? value : raise("Called Result#unwrap_err on an 'ok' Result")
+ end
+
+ # The `ok?` attribute will be true if the Result was constructed with .ok, and false if it was constructed with .err
+ #
+ # "#ok?" corresponds to "is_ok" in Rust.
+ # @return [Boolean]
+ def ok?
+ # We don't make `@ok` an attr_reader, because we don't want to confusingly shadow the class method `.ok`
+ @ok
+ end
+
+ # The `err?` attribute will be false if the Result was constructed with .ok, and true if it was constructed with .err
+ # "#err?" corresponds to "is_err" in Rust.
+ #
+ # @return [Boolean]
+ def err?
+ !ok?
+ end
+
+ # `and_then` is a functional way to chain together operations which may succeed or have errors. It is passed
+ # a lambda or class (singleton) method object, and must return a Result object representing "ok"
+ # or "err".
+ #
+ # If the Result object it is called on is "ok", then the passed lambda or singleton method
+ # is called with the value contained in the Result.
+ #
+ # If the Result object it is called on is "err", then it is returned without calling the passed
+ # lambda or method.
+ #
+ # It only supports being passed a lambda, or a class (singleton) method object
+ # which responds to `call` with a single argument (arity of 1). If multiple values are needed,
+ # pass a hash or array. Note that passing `Proc` objects is NOT supported, even though the YARD
+ # annotation contains `Proc` (because the type of a lambda is also `Proc`).
+ #
+ # Passing instance methods to `and_then` is not supported, because the methods in the chain should be
+ # stateless "pure functions", and should not be persisting or referencing any instance state anyway.
+ #
+ # "#and_then" corresponds to "and_then" in Rust: https://doc.rust-lang.org/std/result/enum.Result.html#method.and_then
+ #
+ # @param [Proc, Method] lambda_or_singleton_method
+ # @return [Result]
+ # @raise [TypeError]
+ def and_then(lambda_or_singleton_method)
+ validate_lambda_or_singleton_method(lambda_or_singleton_method)
+
+ # Return/passthough the Result itself if it is an err
+ return self if err?
+
+ # If the Result is ok, call the lambda or singleton method with the contained value
+ result = lambda_or_singleton_method.call(value)
+
+ unless result.is_a?(Result)
+ err_msg = "'Result##{__method__}' expects a lambda or singleton method object which returns a 'Result' type " \
+ ", but instead received '#{lambda_or_singleton_method.inspect}' which returned '#{result.class}'. " \
+ "Check that the previous method calls in the '#and_then' chain are correct."
+ raise(TypeError, err_msg)
+ end
+
+ result
+ end
+
+ # `map` is similar to `and_then`, but it is used for "single track" methods which always succeed,
+ # and have no possibility of returning an error (but they may still raise exceptions,
+ # which is unrelated to the Result handling). The passed lambda or singleton method must return
+ # a value, not a Result.
+ #
+ # If the Result object it is called on is "ok", then the passed lambda or singleton method
+ # is called with the value contained in the Result.
+ #
+ # If the Result object it is called on is "err", then it is returned without calling the passed
+ # lambda or method.
+ #
+ # "#map" corresponds to "map" in Rust: https://doc.rust-lang.org/std/result/enum.Result.html#method.map
+ #
+ # @param [Proc, Method] lambda_or_singleton_method
+ # @return [Result]
+ # @raise [TypeError]
+ def map(lambda_or_singleton_method)
+ validate_lambda_or_singleton_method(lambda_or_singleton_method)
+
+ # Return/passthrough the Result itself if it is an err
+ return self if err?
+
+ # If the Result is ok, call the lambda or singleton method with the contained value
+ mapped_value = lambda_or_singleton_method.call(value)
+
+ if mapped_value.is_a?(Result)
+ err_msg = "'Result##{__method__}' expects a lambda or singleton method object which returns an unwrapped " \
+ "value, not a 'Result', but instead received '#{lambda_or_singleton_method.inspect}' which returned " \
+ "a 'Result'."
+ raise(TypeError, err_msg)
+ end
+
+ # wrap the returned mapped_value in an "ok" Result.
+ Result.ok(mapped_value)
+ end
+
+ # `to_h` supports destructuring of a result object, for example: `result => { ok: }; puts ok`
+ #
+ # @return [Hash]
+ def to_h
+ ok? ? { ok: value } : { err: value }
+ end
+
+ # `deconstruct_keys` supports pattern matching on a Result object with a `case` statement. See specs for examples.
+ #
+ # @param [Array] keys
+ # @return [Hash]
+ # @raise [ArgumentError]
+ def deconstruct_keys(keys)
+ raise(ArgumentError, 'Use either :ok or :err for pattern matching') unless [[:ok], [:err]].include?(keys)
+
+ to_h
+ end
+
+ # @return [Boolean]
+ def ==(other)
+ # NOTE: The underlying `@ok` instance variable is a boolean, so we only need to check `ok?`, not `err?` too
+ self.class == other.class && other.ok? == ok? && other.instance_variable_get(:@value) == value
+ end
+
+ private
+
+ # The `value` attribute will contain either the ok_value or the err_value
+ attr_reader :value
+
+ def initialize(ok_value: nil, err_value: nil)
+ if (!ok_value.nil? && !err_value.nil?) || (ok_value.nil? && err_value.nil?)
+ raise(ArgumentError, 'Do not directly use private constructor, use Result.ok or Result.err')
+ end
+
+ @ok = err_value.nil?
+ @value = ok? ? ok_value : err_value
+ end
+
+ # @param [Proc, Method] lambda_or_singleton_method
+ # @return [void]
+ # @raise [TypeError]
+ def validate_lambda_or_singleton_method(lambda_or_singleton_method)
+ is_lambda = lambda_or_singleton_method.is_a?(Proc) && lambda_or_singleton_method.lambda?
+ is_singleton_method = lambda_or_singleton_method.is_a?(Method) && lambda_or_singleton_method.owner.singleton_class?
+ unless is_lambda || is_singleton_method
+ err_msg = "'Result##{__method__}' expects a lambda or singleton method object, " \
+ "but instead received '#{lambda_or_singleton_method.inspect}'."
+ raise(TypeError, err_msg)
+ end
+
+ arity = lambda_or_singleton_method.arity
+
+ return if arity == 1
+
+ err_msg = "'Result##{__method__}' expects a lambda or singleton method object with a single argument " \
+ "(arity of 1), but instead received '#{lambda_or_singleton_method.inspect}' with an arity of #{arity}."
+ raise(ArgumentError, err_msg)
+ end
+end
+
+# rubocop:enable Gitlab/NamespacedClass
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index daa40c5f621..7dd526b6588 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -1929,7 +1929,7 @@ msgstr ""
msgid "AI|Experiment features"
msgstr ""
-msgid "AI|Explain the code from %{filePath} in human understandable language presented in Markdown format. In the response add neither original code snippet nor any title. `%{text}`. If it is not programming code, say \"The selected text is not code. I am afraid this feature is for explaining code only. Would you like to ask a different question about the selected text?\" and wait for another question."
+msgid "AI|Explain the code from %{filePath} in human understandable language presented in Markdown format. In the response add neither original code snippet nor any title. `%{text}`. If it is not programming code, say `The selected text is not code. I am afraid this feature is for explaining code only. Would you like to ask a different question about the selected text?` and wait for another question."
msgstr ""
msgid "AI|Features that use third-party AI services require transmission of data, including personal data."
@@ -21454,7 +21454,7 @@ msgstr ""
msgid "Group URL"
msgstr ""
-msgid "Group access token creation is disabled in this group. You can still use and manage existing tokens. %{link_start}Learn more.%{link_end}"
+msgid "Group access token creation is disabled in this group."
msgstr ""
msgid "Group application: %{name}"
@@ -35520,7 +35520,7 @@ msgstr ""
msgid "Project URL"
msgstr ""
-msgid "Project access token creation is disabled in this group. You can still use and manage existing tokens. %{link_start}Learn more.%{link_end}"
+msgid "Project access token creation is disabled in this group."
msgstr ""
msgid "Project already deleted"
@@ -52621,6 +52621,9 @@ msgstr ""
msgid "You can specify notification level per group or per project."
msgstr ""
+msgid "You can still use and manage existing tokens. %{link_start}Learn more.%{link_end}"
+msgstr ""
+
msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
msgstr ""
diff --git a/scripts/allowed_warnings.txt b/scripts/allowed_warnings.txt
index cc7d14c1d3c..cb684166348 100644
--- a/scripts/allowed_warnings.txt
+++ b/scripts/allowed_warnings.txt
@@ -27,3 +27,11 @@ ruby\/2\.7\.0\/net\/protocol\.rb:66: warning: previous definition of ProtocRetry
# fine in both Ruby 2 and Ruby 3, it's unlikely it'll change again.
# This can be removed when support for Ruby 2 is dropped.
warning: Pattern matching is experimental, and the behavior may change in future versions of Ruby!
+
+# As of Ruby 3.1, one-line typesafe/destructuring pattern matching via "rightward assignment" has
+# been included for multiple years with no significant negative feedback or indications of removal.
+# In the event that it is removed in a future Ruby release, the changes required to fix it are
+# isolated and minor, and will have no fundamental effect on the logic. See the section
+# "Rightward assignment pattern matching and destructuring with types" in
+# ee/lib/remote_development/README.md for more information and context.
+warning: One-line pattern matching is experimental, and the behavior may change in future versions of Ruby!
diff --git a/scripts/remote_development/run-smoke-test-suite.sh b/scripts/remote_development/run-smoke-test-suite.sh
index 5c1c5532a05..a48b66d31ab 100755
--- a/scripts/remote_development/run-smoke-test-suite.sh
+++ b/scripts/remote_development/run-smoke-test-suite.sh
@@ -42,6 +42,7 @@ ee/spec/finders/remote_development/workspaces_finder_spec.rb \
ee/spec/graphql/types/query_type_spec.rb \
ee/spec/graphql/types/remote_development/workspace_type_spec.rb \
ee/spec/graphql/types/subscription_type_spec.rb \
+ee/spec/lib/remote_development/unmatched_result_error_spec.rb \
ee/spec/lib/remote_development/workspaces/create/create_processor_spec.rb \
ee/spec/lib/remote_development/workspaces/create/devfile_processor_spec.rb \
ee/spec/lib/remote_development/workspaces/create/devfile_validator_spec.rb \
@@ -53,19 +54,24 @@ ee/spec/lib/remote_development/workspaces/reconcile/params_parser_spec.rb \
ee/spec/lib/remote_development/workspaces/reconcile/reconcile_processor_scenarios_spec.rb \
ee/spec/lib/remote_development/workspaces/reconcile/reconcile_processor_spec.rb \
ee/spec/lib/remote_development/workspaces/states_spec.rb \
-ee/spec/lib/remote_development/workspaces/update/update_processor_spec.rb \
+ee/spec/lib/remote_development/workspaces/update/authorizer_spec.rb \
+ee/spec/lib/remote_development/workspaces/update/main_integration_spec.rb \
+ee/spec/lib/remote_development/workspaces/update/main_spec.rb \
+ee/spec/lib/remote_development/workspaces/update/updater_spec.rb \
ee/spec/models/remote_development/remote_development_agent_config_spec.rb \
ee/spec/models/remote_development/workspace_spec.rb \
ee/spec/requests/api/graphql/mutations/remote_development/workspaces/create_spec.rb \
ee/spec/requests/api/graphql/mutations/remote_development/workspaces/update_spec.rb \
ee/spec/requests/api/graphql/remote_development/current_user_workspaces_spec.rb \
-ee/spec/requests/api/graphql/remote_development/workspaces_by_ids_spec.rb \
ee/spec/requests/api/graphql/remote_development/workspace_by_id_spec.rb \
+ee/spec/requests/api/graphql/remote_development/workspaces_by_ids_spec.rb \
ee/spec/requests/api/internal/kubernetes_spec.rb \
ee/spec/services/remote_development/agent_config/update_service_spec.rb \
ee/spec/services/remote_development/workspaces/create_service_spec.rb \
ee/spec/services/remote_development/workspaces/reconcile_service_spec.rb \
ee/spec/services/remote_development/workspaces/update_service_spec.rb \
spec/graphql/types/subscription_type_spec.rb \
+spec/lib/result_spec.rb \
+spec/support_specs/matchers/result_matchers_spec.rb
printf "\n✅✅✅ ${BGreen}All Remote Development specs passed successfully!${Color_Off} ✅✅✅\n"
diff --git a/spec/features/groups/settings/access_tokens_spec.rb b/spec/features/groups/settings/access_tokens_spec.rb
index cb92f9abdf5..c7e81803694 100644
--- a/spec/features/groups/settings/access_tokens_spec.rb
+++ b/spec/features/groups/settings/access_tokens_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe 'Group > Settings > Access Tokens', :js, feature_category: :syste
it_behaves_like 'resource access tokens creation', 'group'
context 'when token creation is not allowed' do
- it_behaves_like 'resource access tokens creation disallowed', 'Group access token creation is disabled in this group. You can still use and manage existing tokens.'
+ it_behaves_like 'resource access tokens creation disallowed', 'Group access token creation is disabled in this group.'
end
end
diff --git a/spec/features/projects/settings/access_tokens_spec.rb b/spec/features/projects/settings/access_tokens_spec.rb
index a38c10c6bab..210815f341c 100644
--- a/spec/features/projects/settings/access_tokens_spec.rb
+++ b/spec/features/projects/settings/access_tokens_spec.rb
@@ -69,7 +69,7 @@ RSpec.describe 'Project > Settings > Access Tokens', :js, feature_category: :use
end
context 'when token creation is not allowed' do
- it_behaves_like 'resource access tokens creation disallowed', 'Project access token creation is disabled in this group. You can still use and manage existing tokens.'
+ it_behaves_like 'resource access tokens creation disallowed', 'Project access token creation is disabled in this group.'
context 'with a project in a personal namespace' do
let(:personal_project) { create(:project) }
diff --git a/spec/frontend/pipelines/components/pipelines_list/failure_widget/failed_jobs_list_spec.js b/spec/frontend/pipelines/components/pipelines_list/failure_widget/failed_jobs_list_spec.js
index 5c3d995f87f..fc8263c6c4d 100644
--- a/spec/frontend/pipelines/components/pipelines_list/failure_widget/failed_jobs_list_spec.js
+++ b/spec/frontend/pipelines/components/pipelines_list/failure_widget/failed_jobs_list_spec.js
@@ -31,6 +31,7 @@ describe('FailedJobsList component', () => {
const defaultProvide = {
fullPath: 'namespace/project/',
+ graphqlPath: 'api/graphql',
};
const createComponent = ({ props = {}, provide } = {}) => {
diff --git a/spec/frontend/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget_spec.js b/spec/frontend/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget_spec.js
index 222ac536361..c1a885391e9 100644
--- a/spec/frontend/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget_spec.js
+++ b/spec/frontend/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget_spec.js
@@ -1,24 +1,15 @@
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-
import { GlButton, GlIcon, GlPopover } from '@gitlab/ui';
-import createMockApollo from 'helpers/mock_apollo_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import waitForPromises from 'helpers/wait_for_promises';
import PipelineFailedJobsWidget from '~/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget.vue';
import FailedJobsList from '~/pipelines/components/pipelines_list/failure_widget/failed_jobs_list.vue';
-import getPipelineFailedJobsCount from '~/pipelines/graphql/queries/get_pipeline_failed_jobs_count.query.graphql';
-import { createFailedJobsMockCount } from './mock';
-
-Vue.use(VueApollo);
jest.mock('~/alert');
describe('PipelineFailedJobsWidget component', () => {
let wrapper;
- let mockFailedJobsResponse;
const defaultProps = {
+ failedJobsCount: 4,
isPipelineActive: false,
pipelineIid: 1,
pipelinePath: '/pipelines/1',
@@ -26,13 +17,9 @@ describe('PipelineFailedJobsWidget component', () => {
const defaultProvide = {
fullPath: 'namespace/project/',
- graphqlPath: '/api/graphql',
};
- const createComponent = ({ props = {}, provide } = {}) => {
- const handlers = [[getPipelineFailedJobsCount, mockFailedJobsResponse]];
- const mockApollo = createMockApollo(handlers);
-
+ const createComponent = ({ props = {}, provide = {} } = {}) => {
wrapper = shallowMountExtended(PipelineFailedJobsWidget, {
propsData: {
...defaultProps,
@@ -42,7 +29,6 @@ describe('PipelineFailedJobsWidget component', () => {
...defaultProvide,
...provide,
},
- apolloProvider: mockApollo,
});
};
@@ -51,13 +37,9 @@ describe('PipelineFailedJobsWidget component', () => {
const findInfoIcon = () => wrapper.findComponent(GlIcon);
const findInfoPopover = () => wrapper.findComponent(GlPopover);
- beforeEach(() => {
- mockFailedJobsResponse = jest.fn().mockResolvedValue(createFailedJobsMockCount());
- });
-
- describe('when it is loading', () => {
+ describe('when there are no failed jobs', () => {
beforeEach(() => {
- createComponent();
+ createComponent({ props: { failedJobsCount: 0 } });
});
it('renders the show failed jobs button with a count of 0', () => {
@@ -66,16 +48,16 @@ describe('PipelineFailedJobsWidget component', () => {
});
});
- describe('when the failed jobs have loaded', () => {
- beforeEach(async () => {
+ describe('when there are failed jobs', () => {
+ beforeEach(() => {
createComponent();
-
- await waitForPromises();
});
it('renders the show failed jobs button with correct count', () => {
expect(findFailedJobsButton().exists()).toBe(true);
- expect(findFailedJobsButton().text()).toBe('Show failed jobs (4)');
+ expect(findFailedJobsButton().text()).toBe(
+ `Show failed jobs (${defaultProps.failedJobsCount})`,
+ );
});
it('renders the info icon', () => {
@@ -94,8 +76,6 @@ describe('PipelineFailedJobsWidget component', () => {
describe('when the job button is clicked', () => {
beforeEach(async () => {
createComponent();
- await waitForPromises();
-
await findFailedJobsButton().vm.$emit('click');
});
@@ -104,77 +84,37 @@ describe('PipelineFailedJobsWidget component', () => {
});
});
- describe('polling', () => {
- it.each`
- isGraphqlActive | isExpanded | shouldPoll | text
- ${true} | ${false} | ${true} | ${'polls'}
- ${false} | ${false} | ${false} | ${'does not poll'}
- ${true} | ${true} | ${false} | ${'does not poll'}
- ${false} | ${true} | ${false} | ${'does not poll'}
- `(
- `$text when isGraphqlActive: $isGraphqlActive, isExpanded: $isExpanded`,
- async ({ isGraphqlActive, isExpanded, shouldPoll }) => {
- const defaultCount = 4;
- const newCount = 1;
- const expectedCount = shouldPoll ? newCount : defaultCount;
- const expectedCallCount = shouldPoll ? 2 : 1;
-
- // Second result is to simulate polling with a different response
- mockFailedJobsResponse.mockResolvedValueOnce(
- createFailedJobsMockCount({ active: isGraphqlActive, count: defaultCount }),
- );
- mockFailedJobsResponse.mockResolvedValueOnce(
- createFailedJobsMockCount({ active: isGraphqlActive, count: newCount }),
- );
-
- createComponent();
- await waitForPromises();
-
- // Initially, we get the first response which is always the default
- expect(mockFailedJobsResponse).toHaveBeenCalledTimes(1);
- expect(findFailedJobsButton().text()).toBe(`Show failed jobs (${defaultCount})`);
-
- // If the user expands the widget, polling stops
- if (isExpanded) {
- await findFailedJobsButton().vm.$emit('click');
- }
-
- jest.advanceTimersByTime(10000);
- await waitForPromises();
-
- expect(mockFailedJobsResponse).toHaveBeenCalledTimes(expectedCallCount);
- expect(findFailedJobsButton().text()).toBe(`Show failed jobs (${expectedCount})`);
- },
- );
- });
+ describe('when the job count changes', () => {
+ beforeEach(() => {
+ createComponent();
+ });
- describe('when a REST action occurs', () => {
- const defaultCount = 4;
- const newCount = 1;
+ describe('from the prop', () => {
+ it('updates the job count', async () => {
+ const newJobCount = 12;
- beforeEach(() => {
- // Second result is to simulate polling with a different response
- mockFailedJobsResponse.mockResolvedValueOnce(
- createFailedJobsMockCount({ active: false, count: defaultCount }),
- );
- mockFailedJobsResponse.mockResolvedValueOnce(
- createFailedJobsMockCount({ active: false, count: newCount }),
- );
+ expect(findFailedJobsButton().text()).toContain(String(defaultProps.failedJobsCount));
+
+ await wrapper.setProps({ failedJobsCount: newJobCount });
+
+ expect(findFailedJobsButton().text()).toContain(String(newJobCount));
+ });
});
- it.each([true, false])('triggers a refetch of the jobs count', async (isPipelineActive) => {
- createComponent({ props: { isPipelineActive } });
- await waitForPromises();
+ describe('from the event', () => {
+ beforeEach(async () => {
+ await findFailedJobsButton().vm.$emit('click');
+ });
+
+ it('updates the job count', async () => {
+ const newJobCount = 12;
- // Initially, we get the first response which is always the default
- expect(mockFailedJobsResponse).toHaveBeenCalledTimes(1);
- expect(findFailedJobsButton().text()).toBe(`Show failed jobs (${defaultCount})`);
+ expect(findFailedJobsButton().text()).toContain(String(defaultProps.failedJobsCount));
- await wrapper.setProps({ isPipelineActive: !isPipelineActive });
- await waitForPromises();
+ await findFailedJobsList().at(0).vm.$emit('failed-jobs-count', newJobCount);
- expect(mockFailedJobsResponse).toHaveBeenCalledTimes(2);
- expect(findFailedJobsButton().text()).toBe(`Show failed jobs (${newCount})`);
+ expect(findFailedJobsButton().text()).toContain(String(newJobCount));
+ });
});
});
});
diff --git a/spec/graphql/types/user_type_spec.rb b/spec/graphql/types/user_type_spec.rb
index 777972df88b..7c280b9da42 100644
--- a/spec/graphql/types/user_type_spec.rb
+++ b/spec/graphql/types/user_type_spec.rb
@@ -55,6 +55,7 @@ RSpec.describe GitlabSchema.types['User'], feature_category: :user_profile do
organization
jobTitle
createdAt
+ pronouns
]
expect(described_class).to include_graphql_fields(*expected_fields)
diff --git a/spec/lib/result_spec.rb b/spec/lib/result_spec.rb
new file mode 100644
index 00000000000..2b88521fe14
--- /dev/null
+++ b/spec/lib/result_spec.rb
@@ -0,0 +1,328 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+# NOTE:
+# This spec is intended to serve as documentation examples of idiomatic usage for the `Result` type.
+# These examples can be executed as-is in a Rails console to see the results.
+#
+# To support this, we have intentionally used some `rubocop:disable` comments to allow for more
+# explicit and readable examples.
+# rubocop:disable RSpec/DescribedClass, Lint/ConstantDefinitionInBlock, RSpec/LeakyConstantDeclaration
+RSpec.describe Result, feature_category: :remote_development do
+ describe 'usage of Result.ok and Result.err' do
+ context 'when checked with .ok? and .err?' do
+ it 'works with ok result' do
+ result = Result.ok(:success)
+ expect(result.ok?).to eq(true)
+ expect(result.err?).to eq(false)
+ expect(result.unwrap).to eq(:success)
+ end
+
+ it 'works with error result' do
+ result = Result.err(:failure)
+ expect(result.err?).to eq(true)
+ expect(result.ok?).to eq(false)
+ expect(result.unwrap_err).to eq(:failure)
+ end
+ end
+
+ context 'when checked with destructuring' do
+ it 'works with ok result' do
+ Result.ok(:success) => { ok: } # example of rightward assignment
+ expect(ok).to eq(:success)
+
+ Result.ok(:success) => { ok: success_value } # rightward assignment destructuring to different var
+ expect(success_value).to eq(:success)
+ end
+
+ it 'works with error result' do
+ Result.err(:failure) => { err: }
+ expect(err).to eq(:failure)
+
+ Result.err(:failure) => { err: error_value }
+ expect(error_value).to eq(:failure)
+ end
+ end
+
+ context 'when checked with pattern matching' do
+ def check_result_with_pattern_matching(result)
+ case result
+ in { ok: Symbol => ok_value }
+ { success: ok_value }
+ in { err: String => error_value }
+ { failure: error_value }
+ else
+ raise "Unmatched result type: #{result.unwrap.class.name}"
+ end
+ end
+
+ it 'works with ok result' do
+ ok_result = Result.ok(:success_symbol)
+ expect(check_result_with_pattern_matching(ok_result)).to eq({ success: :success_symbol })
+ end
+
+ it 'works with error result' do
+ error_result = Result.err('failure string')
+ expect(check_result_with_pattern_matching(error_result)).to eq({ failure: 'failure string' })
+ end
+
+ it 'raises error with unmatched type in pattern match' do
+ unmatched_type_result = Result.ok([])
+ expect do
+ check_result_with_pattern_matching(unmatched_type_result)
+ end.to raise_error(RuntimeError, 'Unmatched result type: Array')
+ end
+
+ it 'raises error with invalid pattern matching key' do
+ result = Result.ok(:success)
+ expect do
+ case result
+ in { invalid_pattern_match_because_it_is_not_ok_or_err: :value }
+ :unreachable_from_case
+ else
+ :unreachable_from_else
+ end
+ end.to raise_error(ArgumentError, 'Use either :ok or :err for pattern matching')
+ end
+ end
+ end
+
+ describe 'usage of #and_then' do
+ context 'when passed a proc' do
+ it 'returns last ok value in successful chain' do
+ initial_result = Result.ok(1)
+ final_result =
+ initial_result
+ .and_then(->(value) { Result.ok(value + 1) })
+ .and_then(->(value) { Result.ok(value + 1) })
+
+ expect(final_result.ok?).to eq(true)
+ expect(final_result.unwrap).to eq(3)
+ end
+
+ it 'short-circuits the rest of the chain on the first err value encountered' do
+ initial_result = Result.ok(1)
+ final_result =
+ initial_result
+ .and_then(->(value) { Result.err("invalid: #{value}") })
+ .and_then(->(value) { Result.ok(value + 1) })
+
+ expect(final_result.err?).to eq(true)
+ expect(final_result.unwrap_err).to eq('invalid: 1')
+ end
+ end
+
+ context 'when passed a module or class (singleton) method object' do
+ module MyModuleUsingResult
+ def self.double(value)
+ Result.ok(value * 2)
+ end
+
+ def self.return_err(value)
+ Result.err("invalid: #{value}")
+ end
+
+ class MyClassUsingResult
+ def self.triple(value)
+ Result.ok(value * 3)
+ end
+ end
+ end
+
+ it 'returns last ok value in successful chain' do
+ initial_result = Result.ok(1)
+ final_result =
+ initial_result
+ .and_then(::MyModuleUsingResult.method(:double))
+ .and_then(::MyModuleUsingResult::MyClassUsingResult.method(:triple))
+
+ expect(final_result.ok?).to eq(true)
+ expect(final_result.unwrap).to eq(6)
+ end
+
+ it 'returns first err value in failed chain' do
+ initial_result = Result.ok(1)
+ final_result =
+ initial_result
+ .and_then(::MyModuleUsingResult.method(:double))
+ .and_then(::MyModuleUsingResult::MyClassUsingResult.method(:triple))
+ .and_then(::MyModuleUsingResult.method(:return_err))
+ .and_then(::MyModuleUsingResult.method(:double))
+
+ expect(final_result.err?).to eq(true)
+ expect(final_result.unwrap_err).to eq('invalid: 6')
+ end
+ end
+
+ describe 'type checking validation' do
+ describe 'enforcement of argument type' do
+ it 'raises TypeError if passed anything other than a lambda or singleton method object' do
+ ex = TypeError
+ msg = /expects a lambda or singleton method object/
+ # noinspection RubyMismatchedArgumentType
+ expect { Result.ok(1).and_then('string') }.to raise_error(ex, msg)
+ expect { Result.ok(1).and_then(proc { Result.ok(1) }) }.to raise_error(ex, msg)
+ expect { Result.ok(1).and_then(1.method(:to_s)) }.to raise_error(ex, msg)
+ expect { Result.ok(1).and_then(Integer.method(:to_s)) }.to raise_error(ex, msg)
+ end
+ end
+
+ describe 'enforcement of argument arity' do
+ it 'raises ArgumentError if passed lambda or singleton method object with an arity other than 1' do
+ expect do
+ Result.ok(1).and_then(->(a, b) { Result.ok(a + b) })
+ end.to raise_error(ArgumentError, /expects .* with a single argument \(arity of 1\)/)
+ end
+ end
+
+ describe 'enforcement that passed lambda or method returns a Result type' do
+ it 'raises ArgumentError if passed lambda or singleton method object which returns non-Result type' do
+ expect do
+ Result.ok(1).and_then(->(a) { a + 1 })
+ end.to raise_error(TypeError, /expects .* which returns a 'Result' type/)
+ end
+ end
+ end
+ end
+
+ describe 'usage of #map' do
+ context 'when passed a proc' do
+ it 'returns last ok value in successful chain' do
+ initial_result = Result.ok(1)
+ final_result =
+ initial_result
+ .map(->(value) { value + 1 })
+ .map(->(value) { value + 1 })
+
+ expect(final_result.ok?).to eq(true)
+ expect(final_result.unwrap).to eq(3)
+ end
+
+ it 'returns first err value in failed chain' do
+ initial_result = Result.ok(1)
+ final_result =
+ initial_result
+ .and_then(->(value) { Result.err("invalid: #{value}") })
+ .map(->(value) { value + 1 })
+
+ expect(final_result.err?).to eq(true)
+ expect(final_result.unwrap_err).to eq('invalid: 1')
+ end
+ end
+
+ context 'when passed a module or class (singleton) method object' do
+ module MyModuleNotUsingResult
+ def self.double(value)
+ value * 2
+ end
+
+ class MyClassNotUsingResult
+ def self.triple(value)
+ value * 3
+ end
+ end
+ end
+
+ it 'returns last ok value in successful chain' do
+ initial_result = Result.ok(1)
+ final_result =
+ initial_result
+ .map(::MyModuleNotUsingResult.method(:double))
+ .map(::MyModuleNotUsingResult::MyClassNotUsingResult.method(:triple))
+
+ expect(final_result.ok?).to eq(true)
+ expect(final_result.unwrap).to eq(6)
+ end
+
+ it 'returns first err value in failed chain' do
+ initial_result = Result.ok(1)
+ final_result =
+ initial_result
+ .map(::MyModuleNotUsingResult.method(:double))
+ .and_then(->(value) { Result.err("invalid: #{value}") })
+ .map(::MyModuleUsingResult.method(:double))
+
+ expect(final_result.err?).to eq(true)
+ expect(final_result.unwrap_err).to eq('invalid: 2')
+ end
+ end
+
+ describe 'type checking validation' do
+ describe 'enforcement of argument type' do
+ it 'raises TypeError if passed anything other than a lambda or singleton method object' do
+ ex = TypeError
+ msg = /expects a lambda or singleton method object/
+ # noinspection RubyMismatchedArgumentType
+ expect { Result.ok(1).map('string') }.to raise_error(ex, msg)
+ expect { Result.ok(1).map(proc { 1 }) }.to raise_error(ex, msg)
+ expect { Result.ok(1).map(1.method(:to_s)) }.to raise_error(ex, msg)
+ expect { Result.ok(1).map(Integer.method(:to_s)) }.to raise_error(ex, msg)
+ end
+ end
+
+ describe 'enforcement of argument arity' do
+ it 'raises ArgumentError if passed lambda or singleton method object with an arity other than 1' do
+ expect do
+ Result.ok(1).map(->(a, b) { a + b })
+ end.to raise_error(ArgumentError, /expects .* with a single argument \(arity of 1\)/)
+ end
+ end
+
+ describe 'enforcement that passed lambda or method does not return a Result type' do
+ it 'raises TypeError if passed lambda or singleton method object which returns non-Result type' do
+ expect do
+ Result.ok(1).map(->(a) { Result.ok(a + 1) })
+ end.to raise_error(TypeError, /expects .* which returns an unwrapped value, not a 'Result'/)
+ end
+ end
+ end
+ end
+
+ describe '#unwrap' do
+ it 'returns wrapped value if ok' do
+ expect(Result.ok(1).unwrap).to eq(1)
+ end
+
+ it 'raises error if err' do
+ expect { Result.err('error').unwrap }.to raise_error(RuntimeError, /called.*unwrap.*on an 'err' Result/i)
+ end
+ end
+
+ describe '#unwrap_err' do
+ it 'returns wrapped value if err' do
+ expect(Result.err('error').unwrap_err).to eq('error')
+ end
+
+ it 'raises error if ok' do
+ expect { Result.ok(1).unwrap_err }.to raise_error(RuntimeError, /called.*unwrap_err.*on an 'ok' Result/i)
+ end
+ end
+
+ describe '#==' do
+ it 'implements equality' do
+ expect(Result.ok(1)).to eq(Result.ok(1))
+ expect(Result.err('error')).to eq(Result.err('error'))
+ expect(Result.ok(1)).not_to eq(Result.ok(2))
+ expect(Result.err('error')).not_to eq(Result.err('other error'))
+ expect(Result.ok(1)).not_to eq(Result.err(1))
+ end
+ end
+
+ describe 'validation' do
+ context 'for enforcing usage of only public interface' do
+ context 'when private constructor is called with invalid params' do
+ it 'raises ArgumentError if both ok_value and err_value are passed' do
+ expect { Result.new(ok_value: :ignored, err_value: :ignored) }
+ .to raise_error(ArgumentError, 'Do not directly use private constructor, use Result.ok or Result.err')
+ end
+
+ it 'raises ArgumentError if neither ok_value nor err_value are passed' do
+ expect { Result.new }
+ .to raise_error(ArgumentError, 'Do not directly use private constructor, use Result.ok or Result.err')
+ end
+ end
+ end
+ end
+end
+# rubocop:enable RSpec/DescribedClass, Lint/ConstantDefinitionInBlock, RSpec/LeakyConstantDeclaration
diff --git a/spec/migrations/20230523101514_finalize_user_type_migration_spec.rb b/spec/migrations/20230523101514_finalize_user_type_migration_spec.rb
index abf3a506748..01c05c38098 100644
--- a/spec/migrations/20230523101514_finalize_user_type_migration_spec.rb
+++ b/spec/migrations/20230523101514_finalize_user_type_migration_spec.rb
@@ -5,7 +5,14 @@ require_migration!
RSpec.describe FinalizeUserTypeMigration, feature_category: :devops_reports do
it 'finalizes MigrateHumanUserType migration' do
- expect(described_class).to be_finalize_background_migration_of('MigrateHumanUserType')
+ expect_next_instance_of(described_class) do |instance|
+ expect(instance).to receive(:ensure_batched_background_migration_is_finished).with(
+ job_class_name: 'MigrateHumanUserType',
+ table_name: :users,
+ column_name: :id,
+ job_arguments: []
+ )
+ end
migrate!
end
diff --git a/spec/models/ai/service_access_token_spec.rb b/spec/models/ai/service_access_token_spec.rb
index b7be96fec29..64de5bf7e6e 100644
--- a/spec/models/ai/service_access_token_spec.rb
+++ b/spec/models/ai/service_access_token_spec.rb
@@ -20,6 +20,7 @@ RSpec.describe Ai::ServiceAccessToken, type: :model, feature_category: :applicat
describe 'validations' do
it { is_expected.to validate_presence_of(:token) }
it { is_expected.to validate_presence_of(:category) }
+ it { is_expected.to validate_presence_of(:expires_at) }
end
end
end
diff --git a/spec/services/service_response_spec.rb b/spec/services/service_response_spec.rb
index 6171ca1a8a6..03fcc11b6bd 100644
--- a/spec/services/service_response_spec.rb
+++ b/spec/services/service_response_spec.rb
@@ -214,4 +214,17 @@ RSpec.describe ServiceResponse, feature_category: :shared do
end
end
end
+
+ describe '#deconstruct_keys' do
+ it 'supports pattern matching' do
+ status =
+ case described_class.error(message: 'Bad apple')
+ in { status: Symbol => status }
+ status
+ else
+ raise
+ end
+ expect(status).to eq(:error)
+ end
+ end
end
diff --git a/spec/support/matchers/result_matchers.rb b/spec/support/matchers/result_matchers.rb
new file mode 100644
index 00000000000..4fc2c06ba69
--- /dev/null
+++ b/spec/support/matchers/result_matchers.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+# Example usage:
+#
+# expect(Result.ok(1)).to be_ok_result(1)
+#
+# expect(Result.err('hello')).to be_err_result do |result_value|
+# expect(result_value).to match(/hello/i)
+# end
+#
+# Argument to matcher is the expected value to be matched via '=='.
+# For more complex matching, pass a block to the matcher which will receive the result value as an argument.
+
+module ResultMatchers
+ def be_ok_result(expected_value = nil)
+ BeResult.new(ok_or_err: :ok, expected_value: expected_value)
+ end
+
+ def be_err_result(expected_value = nil)
+ BeResult.new(ok_or_err: :err, expected_value: expected_value)
+ end
+
+ class BeResult
+ attr_reader :ok_or_err, :actual, :failure_message_suffix, :expected_value
+
+ def initialize(ok_or_err:, expected_value:)
+ @ok_or_err = ok_or_err
+ @expected_value = expected_value
+ end
+
+ def matches?(actual, &block)
+ @actual = actual
+
+ raise "#{actual} must be a #{::Result}, but it was a #{actual.class}" unless actual.is_a?(::Result)
+
+ @failure_message_suffix = "be an '#{ok_or_err}' type"
+ return false unless actual.ok? == ok?
+
+ actual_value = actual.ok? ? actual.unwrap : actual.unwrap_err
+
+ if expected_value
+ @failure_message_suffix =
+ "have a value of #{expected_value.inspect}, but it was #{actual_value.inspect}"
+ return false unless actual_value == expected_value
+ end
+
+ # NOTE: A block can be passed to the matcher to perform more sophisticated matching,
+ # or to provide more concise and specific failure messages.
+ block ? block.yield(actual_value) : true
+ end
+
+ def failure_message
+ "expected #{actual.inspect} to #{failure_message_suffix}"
+ end
+
+ def failure_message_when_negated
+ "expected #{actual.inspect} not to #{failure_message_suffix}"
+ end
+
+ private
+
+ def ok?
+ ok_or_err == :ok
+ end
+ end
+end
diff --git a/spec/support/shared_examples/graphql/types/merge_request_interactions_type_shared_examples.rb b/spec/support/shared_examples/graphql/types/merge_request_interactions_type_shared_examples.rb
index d8cc6f697d7..03a3f02ae1d 100644
--- a/spec/support/shared_examples/graphql/types/merge_request_interactions_type_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/types/merge_request_interactions_type_shared_examples.rb
@@ -50,6 +50,7 @@ RSpec.shared_examples "a user type with merge request interaction type" do
organization
jobTitle
createdAt
+ pronouns
]
# TODO: 'workspaces' needs to be included, but only when this spec is run in EE context, to account for the
diff --git a/spec/support_specs/matchers/result_matchers_spec.rb b/spec/support_specs/matchers/result_matchers_spec.rb
new file mode 100644
index 00000000000..0c30dd08009
--- /dev/null
+++ b/spec/support_specs/matchers/result_matchers_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+require_relative '../../../spec/support/matchers/result_matchers'
+
+RSpec.describe 'result matchers', feature_category: :remote_development do
+ include ResultMatchers
+
+ it 'works with value asserted via argument' do
+ expect(Result.ok(1)).to be_ok_result(1)
+ expect(Result.ok(1)).not_to be_ok_result(2)
+ expect(Result.ok(1)).not_to be_err_result(1)
+ end
+
+ it 'works with value asserted via block' do
+ expect(Result.err('hello')).to be_err_result do |result_value|
+ expect(result_value).to match(/hello/i)
+ end
+ end
+end