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>2024-01-23 06:10:35 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2024-01-23 06:10:35 +0300
commit89ff92639b7ad6499fb1e9470e4151fb112a904e (patch)
treec03a3e8d5f5b905cd07932e7780e8f0d533e5ec6
parent422294262e50d47bee73c2e85bfbc21473c2508a (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js10
-rw-r--r--app/assets/javascripts/notes/components/comment_form.vue5
-rw-r--r--app/assets/stylesheets/components/content_editor.scss6
-rw-r--r--app/assets/stylesheets/components/detail_page.scss2
-rw-r--r--app/assets/stylesheets/components/related_items_list.scss2
-rw-r--r--app/assets/stylesheets/components/severity/icons.scss12
-rw-r--r--app/assets/stylesheets/components/whats_new.scss4
-rw-r--r--app/assets/stylesheets/framework/buttons.scss4
-rw-r--r--app/assets/stylesheets/framework/calendar.scss2
-rw-r--r--app/assets/stylesheets/framework/common.scss7
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss5
-rw-r--r--app/assets/stylesheets/framework/emojis.scss4
-rw-r--r--app/assets/stylesheets/framework/files.scss18
-rw-r--r--app/assets/stylesheets/framework/filters.scss4
-rw-r--r--app/assets/stylesheets/framework/flash.scss8
-rw-r--r--app/assets/stylesheets/framework/header.scss4
-rw-r--r--app/assets/stylesheets/framework/highlight.scss4
-rw-r--r--app/assets/stylesheets/framework/icons.scss2
-rw-r--r--app/assets/stylesheets/framework/labels.scss2
-rw-r--r--app/assets/stylesheets/framework/new_card.scss105
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss5
-rw-r--r--app/assets/stylesheets/framework/snippets.scss2
-rw-r--r--app/assets/stylesheets/framework/source_editor.scss24
-rw-r--r--app/assets/stylesheets/framework/super_sidebar.scss3
-rw-r--r--app/assets/stylesheets/framework/typography.scss6
-rw-r--r--app/assets/stylesheets/highlight/common.scss8
-rw-r--r--app/assets/stylesheets/page_bundles/alert_management_details.scss4
-rw-r--r--app/assets/stylesheets/page_bundles/cluster_agents.scss2
-rw-r--r--app/assets/stylesheets/page_bundles/clusters.scss4
-rw-r--r--app/assets/stylesheets/page_bundles/design_management.scss2
-rw-r--r--app/assets/stylesheets/page_bundles/escalation_policies.scss2
-rw-r--r--app/assets/stylesheets/page_bundles/incident_management_list.scss2
-rw-r--r--app/assets/stylesheets/page_bundles/incidents.scss10
-rw-r--r--app/assets/stylesheets/page_bundles/issuable_list.scss8
-rw-r--r--app/assets/stylesheets/page_bundles/merge_requests.scss24
-rw-r--r--app/assets/stylesheets/page_bundles/ml_experiment_tracking.scss2
-rw-r--r--app/assets/stylesheets/page_bundles/pipeline_editor.scss2
-rw-r--r--app/assets/stylesheets/page_bundles/projects.scss11
-rw-r--r--app/assets/stylesheets/page_bundles/todos.scss13
-rw-r--r--app/assets/stylesheets/page_bundles/work_items.scss14
-rw-r--r--app/assets/stylesheets/pages/colors.scss6
-rw-r--r--app/assets/stylesheets/pages/commits.scss2
-rw-r--r--app/assets/stylesheets/pages/groups.scss3
-rw-r--r--app/assets/stylesheets/pages/issues.scss2
-rw-r--r--app/assets/stylesheets/pages/notes.scss2
-rw-r--r--app/assets/stylesheets/pages/settings.scss10
-rw-r--r--app/assets/stylesheets/utilities.scss2
-rw-r--r--app/assets/stylesheets/vendors/atwho.scss25
-rw-r--r--app/models/note.rb2
-rw-r--r--app/policies/merge_request_policy.rb5
-rw-r--r--app/serializers/merge_request_noteable_entity.rb4
-rw-r--r--data/deprecations/16-9-deprecate-maven-3.6.x.yml11
-rw-r--r--db/docs/namespace_bans.yml10
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md2
-rw-r--r--doc/administration/package_information/omnibus_packages.md2
-rw-r--r--doc/administration/packages/container_registry.md46
-rw-r--r--doc/administration/packages/container_registry_metadata_database.md414
-rw-r--r--doc/api/boards.md62
-rw-r--r--doc/api/emoji_reactions.md2
-rw-r--r--doc/api/group_boards.md62
-rw-r--r--doc/api/group_clusters.md4
-rw-r--r--doc/api/group_level_variables.md18
-rw-r--r--doc/api/groups.md38
-rw-r--r--doc/api/issues.md40
-rw-r--r--doc/api/issues_statistics.md2
-rw-r--r--doc/api/job_artifacts.md8
-rw-r--r--doc/api/members.md10
-rw-r--r--doc/api/merge_requests.md18
-rw-r--r--doc/api/project_clusters.md2
-rw-r--r--doc/api/project_templates.md2
-rw-r--r--doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md4
-rw-r--r--doc/development/database/partitioning/date_range.md237
-rw-r--r--doc/development/database/partitioning/hash.md35
-rw-r--r--doc/development/database/partitioning/index.md71
-rw-r--r--doc/development/database/partitioning/int_range.md205
-rw-r--r--doc/development/database/partitioning/list.md372
-rw-r--r--doc/development/database/table_partitioning.md707
-rw-r--r--doc/install/docker.md188
-rw-r--r--doc/update/deprecations.md16
-rw-r--r--doc/user/application_security/dependency_scanning/index.md10
-rw-r--r--doc/user/clusters/agent/work_with_agent.md7
-rw-r--r--doc/user/markdown.md12
-rw-r--r--doc/user/project/integrations/gitlab_slack_application.md37
-rw-r--r--doc/user/project/quick_actions.md4
-rw-r--r--doc/user/workspace/index.md32
-rw-r--r--locale/gitlab.pot18
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request_noteable.json6
-rw-r--r--spec/frontend/notes/components/comment_form_spec.js2
-rw-r--r--spec/models/note_spec.rb10
-rw-r--r--spec/policies/merge_request_policy_spec.rb6
90 files changed, 1990 insertions, 1163 deletions
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index f1e46262b2f..57c82d34548 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -22,6 +22,9 @@ const LABELS_ALIAS = 'labels';
const SNIPPETS_ALIAS = 'snippets';
const CONTACTS_ALIAS = 'contacts';
+const CADENCE_REFERENCE_PREFIX = '[cadence:';
+const ITERATION_REFERENCE_PREFIX = '*iteration:';
+
export const AT_WHO_ACTIVE_CLASS = 'at-who-active';
export const CONTACT_STATE_ACTIVE = 'active';
export const CONTACTS_ADD_COMMAND = '/add_contacts';
@@ -220,6 +223,11 @@ class GfmAutoComplete {
const match = regexp.exec(value.params);
if (match) {
[referencePrefix] = match;
+
+ // EE-ONLY; iteration quick action should autocomplete iteration reference only
+ if (referencePrefix === CADENCE_REFERENCE_PREFIX)
+ referencePrefix = ITERATION_REFERENCE_PREFIX;
+
tpl += '<%- referencePrefix %>';
} else {
[[referencePrefix]] = value.params;
@@ -227,7 +235,7 @@ class GfmAutoComplete {
tpl += '<%- referencePrefix %>';
} else if (/^[*]/.test(referencePrefix)) {
// EE-ONLY
- referencePrefix = '*iteration:';
+ referencePrefix = ITERATION_REFERENCE_PREFIX;
tpl += '<%- referencePrefix %>';
}
}
diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue
index 17eded3bec0..dda153cb399 100644
--- a/app/assets/javascripts/notes/components/comment_form.vue
+++ b/app/assets/javascripts/notes/components/comment_form.vue
@@ -118,10 +118,7 @@ export default {
return this.getNoteableData.current_user.can_create_note;
},
canSetInternalNote() {
- return (
- this.getNoteableData.current_user.can_create_confidential_note &&
- (this.isIssue || this.isEpic)
- );
+ return this.getNoteableData.current_user.can_create_confidential_note;
},
issueActionButtonTitle() {
const openOrClose = this.isOpen ? 'close' : 'reopen';
diff --git a/app/assets/stylesheets/components/content_editor.scss b/app/assets/stylesheets/components/content_editor.scss
index c654eb16af5..18043c503f7 100644
--- a/app/assets/stylesheets/components/content_editor.scss
+++ b/app/assets/stylesheets/components/content_editor.scss
@@ -202,7 +202,7 @@
padding-right: 10px;
padding-left: 10px;
- @include gl-text-secondary;
+ color: $gl-text-color-secondary;
}
> code::after {
@@ -212,7 +212,7 @@
width: 20px;
text-align: center;
- @include gl-text-secondary;
+ color: $gl-text-color-secondary;
}
}
@@ -369,7 +369,7 @@
li.focused div.gl-new-dropdown-item-content {
@include gl-focus($inset: true);
- @include gl-bg-gray-50;
+ background-color: $gray-50;
}
}
diff --git a/app/assets/stylesheets/components/detail_page.scss b/app/assets/stylesheets/components/detail_page.scss
index 98ed7f590ea..95987dbb08b 100644
--- a/app/assets/stylesheets/components/detail_page.scss
+++ b/app/assets/stylesheets/components/detail_page.scss
@@ -45,7 +45,7 @@
}
.detail-page-header-meta {
- @include gl-flex-basis-full;
+ flex-basis: 100%;
}
.detail-page-description {
diff --git a/app/assets/stylesheets/components/related_items_list.scss b/app/assets/stylesheets/components/related_items_list.scss
index 4d53ae9ed4b..1cbd0faa900 100644
--- a/app/assets/stylesheets/components/related_items_list.scss
+++ b/app/assets/stylesheets/components/related_items_list.scss
@@ -160,7 +160,7 @@ $item-remove-button-space: 42px;
line-height: 0;
a:focus {
- @include gl-rounded-full;
+ border-radius: $gl-border-radius-full;
@include gl-focus;
}
}
diff --git a/app/assets/stylesheets/components/severity/icons.scss b/app/assets/stylesheets/components/severity/icons.scss
index 8ddf873196a..f58ad87a673 100644
--- a/app/assets/stylesheets/components/severity/icons.scss
+++ b/app/assets/stylesheets/components/severity/icons.scss
@@ -2,26 +2,26 @@
.incident-management-list,
.alert-management-details {
.icon-critical {
- @include gl-text-red-800;
+ color: $red-800;
}
.icon-high {
- @include gl-text-red-600;
+ color: $red-600;
}
.icon-medium {
- @include gl-text-orange-400;
+ color: $orange-400;
}
.icon-low {
- @include gl-text-orange-300;
+ color: $orange-300;
}
.icon-info {
- @include gl-text-blue-400;
+ color: $blue-400;
}
.icon-unknown {
- @include gl-text-gray-200;
+ color: $gray-200;
}
}
diff --git a/app/assets/stylesheets/components/whats_new.scss b/app/assets/stylesheets/components/whats_new.scss
index 2f6c7c43fc9..e8ed4971ea5 100644
--- a/app/assets/stylesheets/components/whats_new.scss
+++ b/app/assets/stylesheets/components/whats_new.scss
@@ -17,7 +17,7 @@
align-items: stretch;
.nav-item {
- @include gl-flex-shrink-0;
+ flex-shrink: 0;
a {
height: 100%;
@@ -38,7 +38,7 @@
&:hover,
&:focus,
&:active {
- @include gl-text-gray-900;
+ color: $gray-900;
}
}
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 260ef0ae380..56e4b23e80d 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -354,14 +354,14 @@ fieldset[disabled] .btn,
copy-code {
position: absolute;
@include gl-transition-medium;
- @include gl-opacity-0;
+ opacity: 0;
top: 7px;
right: $input-horizontal-padding;
.markdown-code-block:hover &,
&:focus-within {
- @include gl-opacity-10;
+ opacity: 1;
}
}
diff --git a/app/assets/stylesheets/framework/calendar.scss b/app/assets/stylesheets/framework/calendar.scss
index 65e378a79f3..6fe114fe337 100644
--- a/app/assets/stylesheets/framework/calendar.scss
+++ b/app/assets/stylesheets/framework/calendar.scss
@@ -5,7 +5,7 @@
}
&:focus {
- @include gl-outline-none;
+ outline: none;
stroke: $white;
filter: drop-shadow(1px 0 0.5px $blue-400) drop-shadow(0 1px 0.5px $blue-400) drop-shadow(-1px 0 0.5px $blue-400) drop-shadow(0 -1px 0.5px $blue-400);
}
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 66cb6a20ba5..9f0dc422795 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -527,7 +527,8 @@ li.note {
}
.border-section {
- @include gl-py-6;
+ padding-top: $gl-spacing-scale-6;
+ padding-bottom: $gl-spacing-scale-6;
margin: 0;
border-top: 1px solid $border-color;
@@ -562,8 +563,8 @@ See https://gitlab.com/gitlab-org/gitlab/issues/36857 for more details.
// --- moved from labels.scss when moving to page_bundles ---
// Fix scoped label padding in cases where old markdown uses the old label structure */
.gl-label-text + .gl-label-text {
- @include gl-pl-2;
- @include gl-pr-3;
+ padding-left: $gl-spacing-scale-2;
+ padding-right: $gl-spacing-scale-3;
}
// used in the Markdown rendering of labels
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 415d1532b15..f799683ba54 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -6,7 +6,7 @@
// need this style.
// Issue for the refactoring: https://gitlab.com/gitlab-org/gitlab/-/issues/213327
&.gl-dropdown button.dropdown-toggle {
- @include gl-display-inline-flex;
+ display: inline-flex;
}
.btn-link {
@@ -664,7 +664,8 @@
min-height: 30px;
padding-top: 0;
padding-bottom: 0;
- @include gl-px-3;
+ padding-left: $gl-spacing-scale-3;
+ padding-right: $gl-spacing-scale-3;
color: $gray-700;
line-height: 30px;
border: 1px solid $gray-400;
diff --git a/app/assets/stylesheets/framework/emojis.scss b/app/assets/stylesheets/framework/emojis.scss
index 9227028e3da..77956797817 100644
--- a/app/assets/stylesheets/framework/emojis.scss
+++ b/app/assets/stylesheets/framework/emojis.scss
@@ -26,7 +26,7 @@ gl-emoji {
}
.emoji-picker-category-header {
- @include gl-sticky;
+ position: sticky;
background: linear-gradient(to bottom, $white 50%, transparent 100%);
}
@@ -40,7 +40,7 @@ gl-emoji {
&:hover,
&:focus {
- @include gl-z-index-2;
+ z-index: 2;
transform: scale(1.3) !important;
}
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index 2c01f650ae3..c99eaa6a8fa 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -413,12 +413,10 @@ span.idiff {
}
.version-link {
- @include gl-display-inline-block;
- @include gl-align-self-center;
+ align-self: center;
margin-top: $gl-spacing-scale-2;
- @include gl-w-5;
- @include gl-h-5;
- @include gl-float-left;
+ width: $gl-spacing-scale-5;
+ height: $gl-spacing-scale-5;
background-color: $gray-400;
mask-image: url('icons-stacked.svg#doc-versions');
mask-repeat: no-repeat;
@@ -455,15 +453,15 @@ span.idiff {
.blob-viewer[data-loading] .file-content.code .line:nth-of-type(69):not(:last-of-type),
.blob-viewer[data-loading] .file-content.code .file-line-num:nth-of-type(69):not(:last-of-type) {
&::after {
- @include gl-display-block;
- @include gl-font-weight-bold;
+ display: block;
+ font-weight: $gl-font-weight-bold;
content: '\2026';
}
}
.blob-viewer[data-loading] .file-content.code .line:nth-of-type(69):not(:last-of-type) {
&::after {
- @include gl-text-center;
+ text-align: center;
}
}
@@ -481,7 +479,7 @@ span.idiff {
.tree-list-parent::before {
@include gl-content-empty;
position: absolute;
- @include gl-z-index-1;
+ z-index: 1;
pointer-events: none;
top: -4px;
@@ -570,7 +568,7 @@ span.idiff {
border-radius: 0;
.file-line-num {
- @include gl-min-w-9;
+ min-width: $gl-spacing-scale-9;
}
}
diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss
index 7c24cd0385c..0634196e007 100644
--- a/app/assets/stylesheets/framework/filters.scss
+++ b/app/assets/stylesheets/framework/filters.scss
@@ -347,12 +347,12 @@
}
.filtered-search-block .boards-switcher {
- @include gl-mr-0;
+ margin-right: 0;
margin-bottom: $gl-input-padding;
.boards-selector-wrapper,
.dropdown {
- @include gl-display-block;
+ display: block;
}
}
diff --git a/app/assets/stylesheets/framework/flash.scss b/app/assets/stylesheets/framework/flash.scss
index 259f806c315..a3e3b58e9c9 100644
--- a/app/assets/stylesheets/framework/flash.scss
+++ b/app/assets/stylesheets/framework/flash.scss
@@ -16,7 +16,7 @@ $notification-box-shadow-color: rgba(0, 0, 0, 0.25);
.flash-notice,
.flash-success,
.flash-warning {
- @include gl-mb-4;
+ margin-bottom: $gl-spacing-scale-4;
}
}
@@ -79,12 +79,14 @@ $notification-box-shadow-color: rgba(0, 0, 0, 0.25);
}
.gl-alert {
- @include gl-my-4;
+ margin-top: $gl-spacing-scale-4;
+ margin-bottom: $gl-spacing-scale-4;
}
&.flash-container-no-margin {
.gl-alert {
- @include gl-my-0;
+ margin-top:0;
+ margin-bottom: 0;
}
.flash-alert,
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index ddb13b73973..1f58964cfc0 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -123,9 +123,9 @@
padding: 6px 8px;
height: 32px;
border-radius: $border-radius-default;
- @include gl-text-gray-100;
+ color: $gray-100;
font-weight: $gl-font-weight-normal;
- @include gl-font-base;
+ font-size: $gl-font-size;
&:hover,
&:focus,
diff --git a/app/assets/stylesheets/framework/highlight.scss b/app/assets/stylesheets/framework/highlight.scss
index c499c9e9ffc..c15819e5b62 100644
--- a/app/assets/stylesheets/framework/highlight.scss
+++ b/app/assets/stylesheets/framework/highlight.scss
@@ -120,8 +120,8 @@ td.line-numbers {
.file-line-num {
@include gl-justify-content-end;
- @include gl-flex-grow-1;
- @include gl-pr-3;
+ flex-grow: 1;
+ padding-right: $gl-spacing-scale-3;
}
.file-line-blame {
diff --git a/app/assets/stylesheets/framework/icons.scss b/app/assets/stylesheets/framework/icons.scss
index 3399847c201..d5d29cb8217 100644
--- a/app/assets/stylesheets/framework/icons.scss
+++ b/app/assets/stylesheets/framework/icons.scss
@@ -47,7 +47,7 @@
// - app/assets/javascripts/vue_shared/components/ci_icon/ci_icon.vue
// - app/helpers/ci/status_helper.rb
.ci-icon-gl-icon-wrapper {
- @include gl-rounded-full;
+ border-radius: $gl-border-radius-full;
@include gl-line-height-0;
}
diff --git a/app/assets/stylesheets/framework/labels.scss b/app/assets/stylesheets/framework/labels.scss
index 116d9c489eb..045c825ad07 100644
--- a/app/assets/stylesheets/framework/labels.scss
+++ b/app/assets/stylesheets/framework/labels.scss
@@ -19,7 +19,7 @@
&:focus:active {
@include gl-reset-color;
@include gl-shadow-none;
- @include gl-outline-none;
+ outline: none;
}
.gl-label-text,
diff --git a/app/assets/stylesheets/framework/new_card.scss b/app/assets/stylesheets/framework/new_card.scss
index a244439d88e..821bda29e50 100644
--- a/app/assets/stylesheets/framework/new_card.scss
+++ b/app/assets/stylesheets/framework/new_card.scss
@@ -1,76 +1,82 @@
.gl-new-card {
- @include gl-mt-5;
+ margin-top: $gl-spacing-scale-5;
background-color: $gray-10;
- @include gl-border-1;
- @include gl-border-solid;
- @include gl-border-gray-100;
+ border-width: $gl-border-size-1;
+ border-style: solid;
+ border-color: $gray-100;
border-radius: $gl-border-radius-base;
&-header {
padding-left: $gl-spacing-scale-5;
padding-right: $gl-spacing-scale-5;
- @include gl-py-4;
+ padding-top: $gl-spacing-scale-4;
+ padding-bottom: $gl-spacing-scale-4;
display: flex;
- @include gl-justify-content-space-between;
- @include gl-bg-white;
- @include gl-border-b-1;
- @include gl-border-b-solid;
- @include gl-border-b-gray-100;
- @include gl-rounded-top-base;
+ justify-content: space-between;
+ background-color: $white;
+ border-bottom-width: $gl-border-size-1;
+ border-bottom-style: solid;
+ border-bottom-color: $gray-100;
+ border-top-left-radius: $gl-border-radius-base;
+ border-top-right-radius: $gl-border-radius-base;
}
&[aria-expanded=false] &-header {
- @include gl-border-bottom-0;
+ border-bottom: 0;
border-radius: $gl-border-radius-base;
}
&-title-wrapper {
display: flex;
- @include gl-flex-grow-1;
+ flex-grow: 1;
}
&-title {
display: flex;
- @include gl-font-base;
- @include gl-font-weight-bold;
+ font-size: $gl-font-size;
+ font-weight: $gl-font-weight-bold;
position: relative;
margin: 0;
- @include gl-line-height-24;
+ line-height: $gl-line-height-24;
}
&-count {
- @include gl-mx-3;
- @include gl-font-base;
- @include gl-font-weight-bold;
- @include gl-text-gray-500;
- @include gl-display-inline-flex;
+ margin-left: $gl-spacing-scale-3;
+ margin-right: $gl-spacing-scale-3;
+ font-size: $gl-font-size;
+ font-weight: $gl-font-weight-bold;
+ color: $gray-500;
+ display: inline-flex;
align-items: center;
}
&-description {
font-size: $gl-font-size-sm;
- @include gl-text-gray-500;
+ color: $gray-500;
margin: 0;
}
&-toggle {
- @include gl-pl-3;
+ padding-left: $gl-spacing-scale-3;
margin-left: $gl-spacing-scale-3;
- @include gl-mr-n2;
- @include gl-border-l-1;
- @include gl-border-l-solid;
- @include gl-border-l-gray-100;
+ margin-right: -$gl-spacing-scale-2;
+ border-left-width: $gl-border-size-1;
+ border-left-style: solid;
+ border-left-color: $gray-100;
}
&-body {
- @include gl-rounded-bottom-base;
- @include gl-px-3;
+ border-bottom-left-radius: $gl-border-radius-base;
+ border-bottom-right-radius: $gl-border-radius-base;
+ padding-left: $gl-spacing-scale-3;
+ padding-right: $gl-spacing-scale-3;
padding-top: 0;
padding-bottom: 0;
}
&-content {
- @include gl-px-2;
+ padding-left: $gl-spacing-scale-2;
+ padding-right: $gl-spacing-scale-2;
padding-top: $gl-spacing-scale-3;
padding-bottom: $gl-spacing-scale-3;
}
@@ -78,20 +84,21 @@
&-empty {
padding: $gl-spacing-scale-2;
margin-bottom: 0;
- @include gl-text-gray-500;
+ color: $gray-500;
}
&-footer {
- @include gl-bg-white;
+ background-color: $white;
}
&-add-form {
- @include gl-p-4;
- @include gl-my-2;
- @include gl-bg-white;
- @include gl-border-1;
- @include gl-border-solid;
- @include gl-border-gray-100;
+ padding: $gl-spacing-scale-4;
+ margin-top: $gl-spacing-scale-2;
+ margin-bottom: $gl-spacing-scale-2;
+ background-color: $white;
+ border-width: $gl-border-size-1;
+ border-style: solid;
+ border-color: $gray-100;
border-radius: $gl-border-radius-base;
}
}
@@ -102,27 +109,27 @@
tbody > tr {
&:first-of-type > td[data-label],
&:first-of-type > td:first-of-type:last-of-type {
- @include gl-border-t-0;
+ border-top-width: 0;
}
&:last-of-type td:not(:last-of-type) {
- @include gl-border-b-1;
+ border-bottom-width: $gl-border-size-1;
}
> td[data-label] {
- @include gl-border-left-0;
- @include gl-border-l-none;
- @include gl-border-right-0;
- @include gl-border-r-none;
+ border-left: 0;
+ border-left-style: none;
+ border-right: 0;
+ border-right-style: none;
}
> th {
- @include gl-border-t-1;
- @include gl-border-b-0;
+ border-top-width: $gl-border-size-1;
+ border-bottom-width: 0;
}
&::after {
- @include gl-bg-white;
+ background-color: $white;
}
&:last-child::after {
@@ -136,11 +143,11 @@
margin-bottom: 0;
tr:first-of-type th {
- @include gl-border-t-0;
+ border-top-width: 0;
}
tr:last-of-type td {
- @include gl-border-b-0;
+ border-bottom-width: 0;
}
}
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 04799a6b8f8..53eb5b2fbd0 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -69,7 +69,7 @@
}
.inline-block {
- @include gl-display-inline-block;
+ display: inline-block;
}
&.right-sidebar-merge-requests {
@@ -491,7 +491,8 @@
}
.issuable-sidebar-header {
- @include gl-py-5;
+ padding-top: $gl-spacing-scale-5;
+ padding-bottom: $gl-spacing-scale-5;
}
.value {
diff --git a/app/assets/stylesheets/framework/snippets.scss b/app/assets/stylesheets/framework/snippets.scss
index edd07dbaafa..1f752e70a13 100644
--- a/app/assets/stylesheets/framework/snippets.scss
+++ b/app/assets/stylesheets/framework/snippets.scss
@@ -21,7 +21,7 @@
}
+ .snippet-file-content {
- @include gl-mt-5;
+ margin-top: $gl-spacing-scale-5;
}
&:last-of-type {
diff --git a/app/assets/stylesheets/framework/source_editor.scss b/app/assets/stylesheets/framework/source_editor.scss
index 5a3863745ee..0d235e17191 100644
--- a/app/assets/stylesheets/framework/source_editor.scss
+++ b/app/assets/stylesheets/framework/source_editor.scss
@@ -3,7 +3,7 @@
display: flex;
@include gl-justify-content-center;
align-items: center;
- @include gl-z-index-0;
+ z-index: 0;
> * {
filter: blur(5px);
@@ -13,7 +13,7 @@
content: '';
@include spinner-deprecated(32px, 3px);
position: absolute;
- @include gl-z-index-1;
+ z-index: 1;
}
pre {
@@ -30,8 +30,10 @@
.md {
@include gl-overflow-scroll;
- @include gl-px-6;
- @include gl-py-4;
+ padding-left: $gl-spacing-scale-6;
+ padding-right: $gl-spacing-scale-6;
+ padding-top: $gl-spacing-scale-4;
+ padding-bottom: $gl-spacing-scale-4;
width: 100%;
}
@@ -73,11 +75,11 @@
&::before {
@include gl-visibility-hidden;
- @include gl-align-self-center;
- @include gl-bg-gray-400;
- @include gl-mr-2;
- @include gl-w-4;
- @include gl-h-4;
+ align-self: center;
+ background-color: $gray-400;
+ margin-right: $gl-spacing-scale-2;
+ width: $gl-spacing-scale-4;
+ height: $gl-spacing-scale-4;
mask-image: url('icons-stacked.svg#link');
mask-repeat: no-repeat;
mask-size: cover;
@@ -100,7 +102,7 @@
}
.link-anchor {
- @include gl-display-block;
+ display: block;
position: absolute;
width: 100%;
height: 100%;
@@ -115,6 +117,6 @@
}
.active-line-text {
- @include gl-bg-orange-600;
+ background-color: $orange-600;
@include gl-opacity-3;
}
diff --git a/app/assets/stylesheets/framework/super_sidebar.scss b/app/assets/stylesheets/framework/super_sidebar.scss
index 4d0b22928cc..ba4e7b612ee 100644
--- a/app/assets/stylesheets/framework/super_sidebar.scss
+++ b/app/assets/stylesheets/framework/super_sidebar.scss
@@ -501,7 +501,8 @@ $super-sidebar-transition-hint-duration: $super-sidebar-transition-duration / 4;
max-height: 30rem;
.gl-new-dropdown-item {
- @include gl-px-3;
+ padding-left: $gl-spacing-scale-3;
+ padding-right: $gl-spacing-scale-3;
}
// Target groups
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 25eeadfad45..e20c97bdf40 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -81,7 +81,7 @@
word-wrap: break-word;
overflow-wrap: break-word;
word-break: keep-all;
- @include gl-font-base;
+ font-size: $gl-font-size;
}
h1 {
@@ -665,7 +665,7 @@ pre {
display: block;
padding: $gl-padding-8 $input-horizontal-padding;
margin: 0 0 $gl-padding-8;
- @include gl-font-base;
+ font-size: $gl-font-size;
word-break: break-all;
word-wrap: break-word;
color: $gl-text-color;
@@ -712,7 +712,7 @@ code {
*/
textarea.js-gfm-input {
font-family: $monospace-font;
- @include gl-font-base;
+ font-size: $gl-font-size;
}
h1,
diff --git a/app/assets/stylesheets/highlight/common.scss b/app/assets/stylesheets/highlight/common.scss
index 3fd72904655..36b66f8d00d 100644
--- a/app/assets/stylesheets/highlight/common.scss
+++ b/app/assets/stylesheets/highlight/common.scss
@@ -113,10 +113,10 @@
@mixin line-link($color, $icon) {
&::before {
@include gl-visibility-hidden;
- @include gl-align-self-center;
- @include gl-mr-1;
- @include gl-w-5;
- @include gl-h-5;
+ align-self: center;
+ margin-right: $gl-spacing-scale-1;
+ width: $gl-spacing-scale-5;
+ height: $gl-spacing-scale-5;
background-color: rgba($color, 0.3);
mask-image: url('icons-stacked.svg##{$icon}');
mask-repeat: no-repeat;
diff --git a/app/assets/stylesheets/page_bundles/alert_management_details.scss b/app/assets/stylesheets/page_bundles/alert_management_details.scss
index 0890fefe910..bd81773d923 100644
--- a/app/assets/stylesheets/page_bundles/alert_management_details.scss
+++ b/app/assets/stylesheets/page_bundles/alert_management_details.scss
@@ -13,7 +13,7 @@
.dropdown-menu-toggle {
&:hover {
- @include gl-bg-white;
+ background-color: $white;
}
}
@@ -25,7 +25,7 @@
}
&::before {
- @include gl-pt-8;
+ padding-top: $gl-spacing-scale-8;
}
.gl-dropdown-item-text-wrapper {
diff --git a/app/assets/stylesheets/page_bundles/cluster_agents.scss b/app/assets/stylesheets/page_bundles/cluster_agents.scss
index f458b5a626a..956a9f6adcc 100644
--- a/app/assets/stylesheets/page_bundles/cluster_agents.scss
+++ b/app/assets/stylesheets/page_bundles/cluster_agents.scss
@@ -8,6 +8,6 @@
}
.timeline-entry::before {
- @include gl-mt-4;
+ margin-top: $gl-spacing-scale-4;
}
}
diff --git a/app/assets/stylesheets/page_bundles/clusters.scss b/app/assets/stylesheets/page_bundles/clusters.scss
index 7c46b836ddc..09611098bed 100644
--- a/app/assets/stylesheets/page_bundles/clusters.scss
+++ b/app/assets/stylesheets/page_bundles/clusters.scss
@@ -16,7 +16,7 @@
.cluster-card-item {
@include media-breakpoint-up(sm) {
- @include gl-pt-2;
+ padding-top: $gl-spacing-scale-2;
min-height: 372px;
}
}
@@ -27,6 +27,6 @@
.select-agent-dropdown {
.gl-button-text {
- @include gl-flex-grow-1;
+ flex-grow: 1;
}
}
diff --git a/app/assets/stylesheets/page_bundles/design_management.scss b/app/assets/stylesheets/page_bundles/design_management.scss
index e206b5e5b8b..e17640167b7 100644
--- a/app/assets/stylesheets/page_bundles/design_management.scss
+++ b/app/assets/stylesheets/page_bundles/design_management.scss
@@ -81,7 +81,7 @@ $t-gray-a-16-design-pin: rgba($black, 0.16);
@include gl-opacity-5;
&:hover {
- @include gl-opacity-10;
+ opacity: 1;
}
}
}
diff --git a/app/assets/stylesheets/page_bundles/escalation_policies.scss b/app/assets/stylesheets/page_bundles/escalation_policies.scss
index 604533e199c..6e70a44a4e4 100644
--- a/app/assets/stylesheets/page_bundles/escalation_policies.scss
+++ b/app/assets/stylesheets/page_bundles/escalation_policies.scss
@@ -35,7 +35,7 @@ $stroke-size: 1px;
.rule-condition {
@media (min-width: $breakpoint-lg) {
flex-basis: 25%;
- @include gl-flex-shrink-0;
+ flex-shrink: 0;
}
@media (max-width: $breakpoint-lg) {
diff --git a/app/assets/stylesheets/page_bundles/incident_management_list.scss b/app/assets/stylesheets/page_bundles/incident_management_list.scss
index ecfc481bb44..4e3fcf5044a 100644
--- a/app/assets/stylesheets/page_bundles/incident_management_list.scss
+++ b/app/assets/stylesheets/page_bundles/incident_management_list.scss
@@ -6,7 +6,7 @@
}
.gl-tabs-nav {
- @include gl-border-b-0;
+ border-bottom-width: 0;
.gl-tab-nav-item {
color: var(--gray-500, $gray-500);
diff --git a/app/assets/stylesheets/page_bundles/incidents.scss b/app/assets/stylesheets/page_bundles/incidents.scss
index 4be4cac2075..974d89a1fa0 100644
--- a/app/assets/stylesheets/page_bundles/incidents.scss
+++ b/app/assets/stylesheets/page_bundles/incidents.scss
@@ -46,7 +46,7 @@
&:last-child {
&::before {
top: - #{$gl-spacing-scale-5} !important; // Override default positioning
- @include gl-h-8;
+ height: $gl-spacing-scale-8;
}
&::after {
@@ -57,10 +57,10 @@
.timeline-entry:not(:last-child) {
.timeline-event-border {
- @include gl-pb-3;
+ padding-bottom: $gl-spacing-scale-3;
@include gl-border-gray-50;
- @include gl-border-1;
- @include gl-border-b-solid;
+ border-width: $gl-border-size-1;
+ border-bottom-style: solid;
}
}
@@ -69,7 +69,7 @@
.create-timeline-event {
.timeline-event-bottom-border {
@include gl-border-b;
- @include gl-pt-5;
+ padding-top: $gl-spacing-scale-5;
}
}
}
diff --git a/app/assets/stylesheets/page_bundles/issuable_list.scss b/app/assets/stylesheets/page_bundles/issuable_list.scss
index acf6e54efc8..8e885914d5e 100644
--- a/app/assets/stylesheets/page_bundles/issuable_list.scss
+++ b/app/assets/stylesheets/page_bundles/issuable_list.scss
@@ -90,9 +90,9 @@
.issuable-list li,
.issuable-info-container .controls {
.avatar-counter {
- @include gl-pl-1;
- @include gl-pr-2;
- @include gl-h-5;
+ padding-left: $gl-spacing-scale-1;
+ padding-right: $gl-spacing-scale-2;
+ height: $gl-spacing-scale-5;
@include gl-min-w-5;
line-height: 14px;
}
@@ -101,7 +101,7 @@
.merge-request {
.issuable-info-container .controls {
.avatar-counter {
- @include gl-line-height-normal;
+ line-height: $gl-line-height-16;
border: 0;
}
}
diff --git a/app/assets/stylesheets/page_bundles/merge_requests.scss b/app/assets/stylesheets/page_bundles/merge_requests.scss
index 8c14075bec2..b26b26b7734 100644
--- a/app/assets/stylesheets/page_bundles/merge_requests.scss
+++ b/app/assets/stylesheets/page_bundles/merge_requests.scss
@@ -331,7 +331,7 @@ $tabs-holder-z-index: 250;
width: 100% !important;
// avoid sticky elements overlap header and other elements
z-index: 1;
- @include gl-mb-3;
+ margin-bottom: $gl-spacing-scale-3;
}
.tree-list-holder {
@@ -814,7 +814,7 @@ $tabs-holder-z-index: 250;
@include media-breakpoint-down(sm) {
padding-top: $gl-spacing-scale-3;
padding-bottom: $gl-spacing-scale-3;
- @include gl-pr-3;
+ padding-right: $gl-spacing-scale-3;
}
}
@@ -842,7 +842,7 @@ $tabs-holder-z-index: 250;
// Adds a fix for the view app dropdown not showing up
// correctly.
position: relative;
- @include gl-z-index-1;
+ z-index: 1;
&.clickable:hover {
background-color: var(--gray-50, $gray-50);
@@ -856,9 +856,9 @@ $tabs-holder-z-index: 250;
@include gl-left-50p;
@include gl-top-half;
@include gl-opacity-3;
- @include gl-border-solid;
+ border-style: solid;
@include gl-border-4;
- @include gl-rounded-full;
+ border-radius: $gl-border-radius-full;
width: 24px;
height: 24px;
@@ -868,7 +868,7 @@ $tabs-holder-z-index: 250;
.mr-widget-extension-icon::after {
@include gl-content-empty;
position: absolute;
- @include gl-rounded-full;
+ border-radius: $gl-border-radius-full;
@include gl-left-50p;
@include gl-top-half;
@@ -1035,7 +1035,7 @@ $tabs-holder-z-index: 250;
}
p {
- @include gl-font-base;
+ font-size: $gl-font-size;
}
li:not(:last-child) {
@@ -1077,16 +1077,16 @@ $tabs-holder-z-index: 250;
@include gl-bottom-0;
@include gl-right-0;
@include gl-opacity-3;
- @include gl-rounded-full;
- @include gl-border-solid;
+ border-radius: $gl-border-radius-full;
+ border-style: solid;
@include gl-border-4;
}
.mr-widget-status-icon-level-1::after {
@include gl-content-empty;
position: absolute;
- @include gl-rounded-full;
- @include gl-border-solid;
+ border-radius: $gl-border-radius-full;
+ border-style: solid;
@include gl-border-4;
@include gl-left-2;
@include gl-right-2;
@@ -1208,7 +1208,7 @@ $tabs-holder-z-index: 250;
margin: 0;
@include gl-border-l-0;
@include gl-border-r-0;
- @include gl-border-b-0;
+ border-bottom-width: 0;
@include gl-rounded-top-left-none;
@include gl-rounded-top-right-none;
}
diff --git a/app/assets/stylesheets/page_bundles/ml_experiment_tracking.scss b/app/assets/stylesheets/page_bundles/ml_experiment_tracking.scss
index 685719071b5..82fb631036c 100644
--- a/app/assets/stylesheets/page_bundles/ml_experiment_tracking.scss
+++ b/app/assets/stylesheets/page_bundles/ml_experiment_tracking.scss
@@ -7,7 +7,7 @@ table.ml-candidate-table {
min-width: 100px;
> * {
- @include gl-display-block;
+ display: block;
@include gl-text-truncate;
}
}
diff --git a/app/assets/stylesheets/page_bundles/pipeline_editor.scss b/app/assets/stylesheets/page_bundles/pipeline_editor.scss
index ae832f61086..9e09980200f 100644
--- a/app/assets/stylesheets/page_bundles/pipeline_editor.scss
+++ b/app/assets/stylesheets/page_bundles/pipeline_editor.scss
@@ -17,7 +17,7 @@
}
.file-tree-includes-link:hover > svg {
- @include gl-display-block;
+ display: block;
top: 2px;
}
diff --git a/app/assets/stylesheets/page_bundles/projects.scss b/app/assets/stylesheets/page_bundles/projects.scss
index c560740a742..f7c8961c2c6 100644
--- a/app/assets/stylesheets/page_bundles/projects.scss
+++ b/app/assets/stylesheets/page_bundles/projects.scss
@@ -280,7 +280,8 @@
.project-cell {
@include gl-display-table-cell;
@include gl-vertical-align-top;
- @include gl-py-4;
+ padding-top: $gl-spacing-scale-4;
+ padding-bottom: $gl-spacing-scale-4;
border-bottom: 1px solid var(--gray-50, $gray-50);
}
@@ -323,7 +324,7 @@
.controls {
@include media-breakpoint-up(lg) {
@include gl-justify-content-start;
- @include gl-pr-9;
+ padding-right: $gl-spacing-scale-9;
&:not(.with-pipeline-status) {
.icon-wrapper:first-of-type {
@@ -354,7 +355,7 @@
.icon-wrapper {
@include media-breakpoint-down(md) {
- @include gl-mr-0;
+ margin-right: 0;
margin-left: $gl-spacing-scale-3;
}
@@ -397,7 +398,7 @@
.icon-wrapper {
margin-left: $gl-spacing-scale-3;
- @include gl-mr-0;
+ margin-right: 0;
}
.user-access-role {
@@ -407,7 +408,7 @@
@include media-breakpoint-down(md) {
.updated-note {
- @include gl-mt-3;
+ margin-top: $gl-spacing-scale-3;
@include gl-text-right;
}
}
diff --git a/app/assets/stylesheets/page_bundles/todos.scss b/app/assets/stylesheets/page_bundles/todos.scss
index 6fa92a2ea20..359d38bfa2d 100644
--- a/app/assets/stylesheets/page_bundles/todos.scss
+++ b/app/assets/stylesheets/page_bundles/todos.scss
@@ -37,7 +37,7 @@
}
&:hover {
- @include gl-border-t-1;
+ border-top-width: $gl-border-size-1;
@include gl-border-t-transparent;
@include gl-border-t-solid;
}
@@ -63,7 +63,7 @@
margin-right: 2.5rem;
@include media-breakpoint-up(sm) {
- @include gl-mr-0;
+ margin-right: 0;
@include gl-text-overflow-ellipsis;
@include gl-white-space-nowrap;
@include gl-overflow-hidden;
@@ -72,18 +72,19 @@
.todo-body {
p {
- @include gl-display-inline;
+ display: inline;
color: var(--gl-text-color, $gl-text-color);
}
pre.code.highlight {
padding-top: 0;
- padding-bottom: 0;
- @include gl-px-1;
+ padding-bottom: 0;
+ padding-left: $gl-spacing-scale-1;
+ padding-right: $gl-spacing-scale-1;
margin: 0;
@include gl-border-0;
border-radius: $gl-border-radius-base;
- @include gl-display-inline-flex;
+ display: inline-flex;
background: var(--gray-50, $gray-50);
color: var(--gl-text-color, $gl-text-color);
}
diff --git a/app/assets/stylesheets/page_bundles/work_items.scss b/app/assets/stylesheets/page_bundles/work_items.scss
index 102d2045af2..e0821b11bef 100644
--- a/app/assets/stylesheets/page_bundles/work_items.scss
+++ b/app/assets/stylesheets/page_bundles/work_items.scss
@@ -247,16 +247,16 @@ $disclosure-hierarchy-chevron-dimension: 1.2rem;
}
.disclosure-hierarchy-button {
- @include gl-pl-4;
+ padding-left: $gl-spacing-scale-4;
padding-top: $gl-spacing-scale-3;
padding-bottom: $gl-spacing-scale-3;
display: flex;
position: relative;
font-size: $gl-font-size-sm;
border: 1px solid var(--gray-100, $gray-100);
- @include gl-border-r-none;
- @include gl-border-l-none;
- @include gl-line-height-normal;
+ border-right-style: none;
+ border-left-style: none;
+ line-height: $gl-line-height-16;
padding-right: $grid-size;
max-width: $gl-spacing-scale-20;
background: var(--gray-10, $white);
@@ -288,7 +288,7 @@ $disclosure-hierarchy-chevron-dimension: 1.2rem;
}
.disclosure-hierarchy-item:first-child & {
- @include gl-pl-3;
+ padding-left: $gl-spacing-scale-3;
border-left: 1px solid var(--gray-100, $gray-100);
@include gl-rounded-top-left-base;
@include gl-rounded-bottom-left-base;
@@ -306,7 +306,7 @@ $disclosure-hierarchy-chevron-dimension: 1.2rem;
}
.disclosure-hierarchy-item:last-child & {
- @include gl-pr-4;
+ padding-right: $gl-spacing-scale-4;
border-right: 1px solid var(--gray-100, $gray-100);
@include gl-rounded-top-right-base;
@include gl-rounded-bottom-right-base;
@@ -343,7 +343,7 @@ $disclosure-hierarchy-chevron-dimension: 1.2rem;
border-bottom: 1px solid var(--gray-400, $gray-400);
@include hierarchy-active-item-color;
z-index: 2;
- @include gl-rounded-small;
+ border-radius: $gl-border-radius-small;
&::before, &::after {
box-shadow: 2px -2px 0 1px $blue-400;
diff --git a/app/assets/stylesheets/pages/colors.scss b/app/assets/stylesheets/pages/colors.scss
index ffc4e4992ab..5d3053a33b1 100644
--- a/app/assets/stylesheets/pages/colors.scss
+++ b/app/assets/stylesheets/pages/colors.scss
@@ -3,14 +3,14 @@
}
.color-item-color {
- @include gl-flex-shrink-0;
- @include gl-mr-3;
+ flex-shrink: 0;
+ margin-right: $gl-spacing-scale-3;
top: 0;
}
.right-sidebar-collapsed {
.color-item {
- @include gl-pt-3;
+ padding-top: $gl-spacing-scale-3;
}
.color-item-color {
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index 58fe4e8504c..b0519119bd8 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -259,7 +259,7 @@
}
.header-main-content & {
- @include gl-mr-2;
+ margin-right: $gl-spacing-scale-2;
}
}
diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss
index d01286bd209..91f123d7b26 100644
--- a/app/assets/stylesheets/pages/groups.scss
+++ b/app/assets/stylesheets/pages/groups.scss
@@ -91,7 +91,8 @@ table.pipeline-project-metrics tr td {
}
.user-access-role {
- @include gl-px-3;
+ padding-left: $gl-spacing-scale-3;
+ padding-right: $gl-spacing-scale-3;
display: inline-block;
color: $gl-text-color-secondary;
font-size: 12px;
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index f57a8519992..69fed5d59fe 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -198,7 +198,7 @@ ul.related-merge-requests > li gl-emoji {
display: flex;
.new-branch-col {
- @include gl-pb-0;
+ padding-bottom: 0;
align-self: flex-end;
}
}
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 1494e95c922..a706c003c51 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -1032,7 +1032,7 @@ $system-note-icon-m-left: $avatar-m-left + $icon-size-diff / $avatar-m-ratio;
&.is-active {
svg {
- @include gl-text-green-500;
+ color: $green-500;
}
&:hover,
diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss
index 1548a63ef6e..0d8fbc744c9 100644
--- a/app/assets/stylesheets/pages/settings.scss
+++ b/app/assets/stylesheets/pages/settings.scss
@@ -61,7 +61,7 @@
.settings-section::after {
content: '';
display: block;
- @include gl-mb-7;
+ margin-bottom: $gl-spacing-scale-7;
}
.settings-section,
@@ -71,11 +71,11 @@
// Fix for sticky header when there is no search bar.
.flash-container + .settings-section {
- @include gl-pt-3;
+ padding-top: $gl-spacing-scale-3;
}
.settings-section ~ .settings-section {
- @include gl-pt-6;
+ padding-top: $gl-spacing-scale-6;
}
.settings-section:not(.settings-section-no-bottom) ~ .settings-section {
@@ -83,10 +83,10 @@
}
.settings-section-no-bottom::after {
- @include gl-pb-0;
+ padding-bottom: 0;
@include media-breakpoint-up(sm) {
- @include gl-pb-5;
+ padding-bottom: $gl-spacing-scale-5;
}
}
diff --git a/app/assets/stylesheets/utilities.scss b/app/assets/stylesheets/utilities.scss
index 5fec8eb661d..27338c34741 100644
--- a/app/assets/stylesheets/utilities.scss
+++ b/app/assets/stylesheets/utilities.scss
@@ -134,7 +134,7 @@
}
.gl-last-of-type-border-b-0:last-of-type {
- @include gl-border-b-0;
+ border-bottom-width: 0;
}
.gl-md-h-9 {
diff --git a/app/assets/stylesheets/vendors/atwho.scss b/app/assets/stylesheets/vendors/atwho.scss
index 41f275ab8be..338ec571114 100644
--- a/app/assets/stylesheets/vendors/atwho.scss
+++ b/app/assets/stylesheets/vendors/atwho.scss
@@ -4,10 +4,10 @@
min-width: $gl-new-dropdown-min-width;
max-width: $gl-new-dropdown-max-width;
- @include gl-border-b-1;
- @include gl-border-b-solid;
- @include gl-border-b-gray-100;
- @include gl-rounded-lg;
+ border-bottom-width: $gl-border-size-1;
+ border-bottom-style: solid;
+ border-bottom-color: $gray-100;
+ border-radius: $gl-border-radius-large;
@include gl-shadow-md;
@@ -41,17 +41,20 @@
// TODO: fallback to global style
.atwho-view-ul {
- @include gl-py-2;
+ padding-top: $gl-spacing-scale-2;
+ padding-bottom: $gl-spacing-scale-2;
max-height: $gl-max-dropdown-max-height;
li {
border: 0;
padding: $gl-padding-6;
- @include gl-my-2;
- @include gl-mx-3;
- @include gl-rounded-small;
- @include gl-line-height-normal;
+ margin-top: $gl-spacing-scale-2;
+ margin-bottom: $gl-spacing-scale-2;
+ margin-left: $gl-spacing-scale-3;
+ margin-right: $gl-spacing-scale-3;
+ border-radius: $gl-border-radius-small;
+ line-height: $gl-line-height-16;
&.cur {
@include gl-focus;
@@ -78,7 +81,7 @@
}
gl-emoji {
- @include gl-mr-2;
+ margin-right: $gl-spacing-scale-2;
vertical-align: text-top;
img {
@@ -90,7 +93,7 @@
.dropdown-label-box {
position: relative;
top: -1px;
- @include gl-mr-2;
+ margin-right: $gl-spacing-scale-2;
}
}
}
diff --git a/app/models/note.rb b/app/models/note.rb
index 953632ba910..10ab85b24b7 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -935,7 +935,7 @@ class Note < ApplicationRecord
end
def noteable_can_have_confidential_note?
- for_issue?
+ for_issuable?
end
def set_internal_flag
diff --git a/app/policies/merge_request_policy.rb b/app/policies/merge_request_policy.rb
index 49f9225a1d3..27bcd816a08 100644
--- a/app/policies/merge_request_policy.rb
+++ b/app/policies/merge_request_policy.rb
@@ -14,6 +14,7 @@ class MergeRequestPolicy < IssuablePolicy
rule { ~can?(:read_merge_request) }.policy do
prevent :create_note
prevent :accept_merge_request
+ prevent :mark_note_as_internal
end
rule { can_approve }.policy do
@@ -43,6 +44,10 @@ class MergeRequestPolicy < IssuablePolicy
enable :set_merge_request_metadata
end
+ rule { can?(:reporter_access) }.policy do
+ enable :mark_note_as_internal
+ end
+
private
def can_approve?
diff --git a/app/serializers/merge_request_noteable_entity.rb b/app/serializers/merge_request_noteable_entity.rb
index 04b801e29ad..4122cb5f9a4 100644
--- a/app/serializers/merge_request_noteable_entity.rb
+++ b/app/serializers/merge_request_noteable_entity.rb
@@ -49,6 +49,10 @@ class MergeRequestNoteableEntity < IssuableEntity
expose :can_update do |merge_request|
can?(current_user, :update_merge_request, merge_request)
end
+
+ expose :can_create_confidential_note do |merge_request|
+ can?(request.current_user, :mark_note_as_internal, merge_request)
+ end
end
expose :locked_discussion_docs_path, if: -> (merge_request) { merge_request.discussion_locked? } do |merge_request|
diff --git a/data/deprecations/16-9-deprecate-maven-3.6.x.yml b/data/deprecations/16-9-deprecate-maven-3.6.x.yml
new file mode 100644
index 00000000000..d119c8b6380
--- /dev/null
+++ b/data/deprecations/16-9-deprecate-maven-3.6.x.yml
@@ -0,0 +1,11 @@
+- title: "Deprecate Maven versions below 3.8.8"
+ removal_milestone: "17.0"
+ announcement_milestone: "16.9"
+ breaking_change: true
+ reporter: thiagocsf
+ stage: Secure
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/438772
+ body: | # (required) Don't change this line.
+ GitLab 17.0 drops Dependency Scanning support for Maven versions below 3.8.8.
+
+ Maven versions below 3.6.3 have reached their end-of-life. Users are advised to upgrade to 3.8.8 or greater.
diff --git a/db/docs/namespace_bans.yml b/db/docs/namespace_bans.yml
index dbb1c012bda..eebfdc66efb 100644
--- a/db/docs/namespace_bans.yml
+++ b/db/docs/namespace_bans.yml
@@ -7,4 +7,12 @@ feature_categories:
description: Contains users banned from namespaces
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91271
milestone: '15.2'
-gitlab_schema: gitlab_main
+gitlab_schema: gitlab_main_cell
+allow_cross_joins:
+- gitlab_main_clusterwide
+allow_cross_transactions:
+- gitlab_main_clusterwide
+allow_cross_foreign_keys:
+- gitlab_main_clusterwide
+sharding_key:
+ namespace_id: namespaces
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index f1d1b504c9d..d46c1b71768 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -176,7 +176,6 @@ The following metrics are available:
| `gitlab_ci_current_queue_size` | Gauge | 16.3 | Current size of initialized CI/CD builds queue |
| `gitlab_ci_queue_iteration_duration_seconds` | Histogram | 16.3 | Time it takes to find a build in CI/CD queue |
| `gitlab_ci_queue_retrieval_duration_seconds` | Histogram | 16.3 | Time it takes to execute a SQL query to retrieve builds queue |
-| `gitlab_ci_queue_active_runners_total` | Histogram | 16.3 | The amount of active runners that can process queue in a project |
| `gitlab_connection_pool_size` | Gauge | 16.7 | Size of connection pool |
| `gitlab_connection_pool_available_count` | Gauge | 16.7 | Number of available connections in the pool |
| `gitlab_security_policies_scan_result_process_duration_seconds` | Histogram | 16.7 | The amount of time to process scan result policies |
@@ -407,6 +406,7 @@ configuration option in `gitlab.yml`. These metrics are served from the
| `geo_design_management_repositories_verification_total` | Gauge | 16.1 | Number of design repositories verifications tried on secondary | `url` |
| `geo_design_management_repositories_verified` | Gauge | 16.1 | Number of design repositories verified on secondary | `url` |
| `geo_design_management_repositories_verification_failed` | Gauge | 16.1 | Number of design repositories verifications failed on secondary | `url` |
+| `gitlab_ci_queue_active_runners_total` | Histogram | 16.3 | The number of active runners that can process the CI/CD queue in a project |
## Database load balancing metrics **(PREMIUM SELF)**
diff --git a/doc/administration/package_information/omnibus_packages.md b/doc/administration/package_information/omnibus_packages.md
index 8f8fa0097f7..12f7f6861ad 100644
--- a/doc/administration/package_information/omnibus_packages.md
+++ b/doc/administration/package_information/omnibus_packages.md
@@ -100,7 +100,7 @@ might be a better fit when the application has a number of moving parts.
## Docker image with multiple services
-[GitLab Docker image](../../install/docker.md#gitlab-docker-images) is based on the Linux package.
+[GitLab Docker image](../../install/docker.md) is based on the Linux package.
Considering that container spawned from this image contains multiple processes,
these types of containers are also referred to as 'fat containers'.
diff --git a/doc/administration/packages/container_registry.md b/doc/administration/packages/container_registry.md
index bfeca7c56b9..281e2f275ec 100644
--- a/doc/administration/packages/container_registry.md
+++ b/doc/administration/packages/container_registry.md
@@ -1069,6 +1069,12 @@ end
You can also [run cleanup on a schedule](../../user/packages/container_registry/reduce_container_registry_storage.md#cleanup-policy).
+## Container registry metadata database **(FREE SELF BETA)**
+
+The metadata database enables many new registry features, including
+online garbage collection, and increases the efficiency of many registry operations.
+See the [Container registry metadata database](container_registry_metadata_database.md) page for details.
+
## Container registry garbage collection
NOTE:
@@ -1260,11 +1266,11 @@ blobs start being deleted is anything permanent done.
### Continuous Zero Downtime Garbage Collection **(BETA)**
You can run garbage collection in the background without the need to schedule it or require read-only mode,
-if you migrate to the [metadata database (beta)](#use-a-postgresql-database-for-metadata).
+if you migrate to the [metadata database (beta)](container_registry_metadata_database.md).
NOTE:
If you would like to try this [beta feature](../../policy/experiment-beta-support.md#beta),
-you should review the [known limitations](#known-limitations). If you have any feedback,
+you should review the [known limitations](container_registry_metadata_database.md#known-limitations). If you have any feedback,
you can let us know in the [feedback issue](https://gitlab.com/gitlab-org/gitlab/-/issues/423459).
## Configure GitLab and Registry to run on separate nodes (Linux package installations)
@@ -1368,42 +1374,6 @@ including all the supported storage backends. To migrate to the GitLab container
you can follow the instructions on this page, and use the same storage backend as the Distribution Registry.
The GitLab container registry should accept the same configuration that you are using for the Distribution Registry.
-## Use a PostgreSQL database for metadata **(FREE SELF BETA)**
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/423459) in GitLab 16.4 as a [Beta feature](../../policy/experiment-beta-support.md) for self-managed GitLab instances.
-
-WARNING:
-While the metadata database is already in use on GitLab.com, it is in early beta for self-managed GitLab instances.
-
-By default, the container registry uses object storage to persist metadata
-related to container images. This method to store metadata limits how efficiently
-the data can be accessed, especially data spanning multiple images, such as when listing tags.
-By using a database to store this data, many new features are possible, including
-[online garbage collection](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/gitlab/online-garbage-collection.md)
-which removes old data automatically with zero downtime.
-
-This database works in conjunction with the object storage already used by the registry, but does not replace object storage.
-You must continue to maintain an object storage solution even after migrating to a metadata database.
-
-### Known Limitations
-
-- No support for online migrations.
-- Geo Support is not confirmed.
-- Registry database migrations must be ran manually when upgrading versions.
-
-### Migration Instructions and Feedback
-
-Instructions on how to migrate to the database may be found in the [feedback issue](https://gitlab.com/gitlab-org/gitlab/-/issues/423459) for the beta period.
-This issue also serves as a place to report issues and to get an overview of the beta status.
-
-### Metadata database feature support
-
-You can migrate existing registries to the metadata database, and use online garbage collection.
-
-Some database-enabled features are only enabled for GitLab.com and automatic database provisioning for
-the registry database is not available. Review the feature support table in the [feedback issue](https://gitlab.com/gitlab-org/gitlab/-/issues/423459#supported-feature-status)
-for the status of features related to the container registry database.
-
## Troubleshooting
Before diving in to the following sections, here's some basic troubleshooting:
diff --git a/doc/administration/packages/container_registry_metadata_database.md b/doc/administration/packages/container_registry_metadata_database.md
new file mode 100644
index 00000000000..e9477c74914
--- /dev/null
+++ b/doc/administration/packages/container_registry_metadata_database.md
@@ -0,0 +1,414 @@
+---
+stage: Package
+group: Container Registry
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+# Container registry metadata database **(FREE SELF BETA)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/423459) in GitLab 16.4 as a [Beta feature](../../policy/experiment-beta-support.md) for self-managed GitLab instances.
+
+WARNING:
+The metadata database is a [beta feature](../../policy/experiment-beta-support.md#beta).
+Carefully review the documentation before enabling the registry database in production!
+If you encounter a problem with either the import or operation of the
+registry, please add a comment in the [feedback issue](https://gitlab.com/gitlab-org/gitlab/-/issues/423459).
+
+The metadata database enables many new registry features, including
+online garbage collection, and increases the efficiency of many registry operations.
+The work on the self-managed release of the registry metadata database feature
+is tracked in [epic 5521](https://gitlab.com/groups/gitlab-org/-/epics/5521).
+
+By default, the container registry uses object storage to persist metadata
+related to container images. This method to store metadata limits how efficiently
+the data can be accessed, especially data spanning multiple images, such as when listing tags.
+By using a database to store this data, many new features are possible, including
+[online garbage collection](https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs/spec/gitlab/online-garbage-collection.md)
+which removes old data automatically with zero downtime.
+
+This database works in conjunction with the object storage already used by the registry, but does not replace object storage.
+You must continue to maintain an object storage solution even after migrating to a metadata database.
+
+## Known Limitations
+
+- No support for online migrations.
+- Geo Support is not confirmed.
+- Registry database migrations must be ran manually when upgrading versions.
+
+## Metadata database feature support
+
+You can migrate existing registries to the metadata database, and use online garbage collection.
+
+Some database-enabled features are only enabled for GitLab.com and automatic database provisioning for
+the registry database is not available. Review the feature support table in the [feedback issue](https://gitlab.com/gitlab-org/gitlab/-/issues/423459#supported-feature-status)
+for the status of features related to the container registry database.
+
+## Enable the metadata database for Linux package installations
+
+Prerequisites:
+
+- GitLab 16.7 or later.
+- PostgreSQL database version 12 or later. It must be accessible from the registry node.
+
+Follow the instructions that match your situation:
+
+- [New installation](#new-installations) or enabling the container registry for the first time.
+- Migrate existing container images to the metadata database:
+ - [One-step migration](#one-step-migration). Only recommended for relatively small registries or no requirement to avoid downtime.
+ - [Three-step migration](#three-step-migration). Recommended for larger container registries.
+
+### Before you start
+
+- After you enable the database, you must continue to use it. The database is
+ now the source of the registry metadata, disabling it after this point
+ causes the registry to lose visibility on all images written to it while
+ the database was active.
+- Never run [offline garbage collection](container_registry.md#container-registry-garbage-collection) at any point
+ after the import step has been completed. That command is not compatible with registries using
+ the metadata database, and it deletes data.
+- Verify you have not automated offline garbage collection.
+- You can first [reduce the storage of your registry](../../user/packages/container_registry/reduce_container_registry_storage.md)
+ to speed up the process.
+- Back up [your container registry data](../backup_restore/backup_gitlab.md#container-registry)
+ if possible.
+
+### New installations
+
+To enable the database:
+
+1. Edit `/etc/gitlab/gitlab.rb` by adding your database connection details, but start with the metadata database **disabled**:
+
+ ```ruby
+ registry['database'] = {
+ 'enabled' => false,
+ 'host' => 'localhost',
+ 'port' => 5432,
+ 'user' => 'registry-database-user',
+ 'password' => 'registry-database-password',
+ 'dbname' => 'registry-database-name',
+ 'sslmode' => 'require', # See the PostgreSQL documentation for additional information https://www.postgresql.org/docs/current/libpq-ssl.html.
+ 'sslcert' => '/path/to/cert.pem',
+ 'sslkey' => '/path/to/private.key',
+ 'sslrootcert' => '/path/to/ca.pem'
+ }
+ ```
+
+1. Save the file and [reconfigure GitLab](../restart_gitlab.md#reconfigure-a-linux-package-installation).
+1. [Apply schema migrations](#apply-schema-migrations).
+1. Enable the database by editing `/etc/gitlab/gitlab.rb` and setting `enabled` to `true`:
+
+ ```ruby
+ registry['database'] = {
+ 'enabled' => true,
+ 'host' => 'localhost',
+ 'port' => 5432,
+ 'user' => 'registry-database-user',
+ 'password' => 'registry-database-password',
+ 'dbname' => 'registry-database-name',
+ 'sslmode' => 'require', # See the PostgreSQL documentation for additional information https://www.postgresql.org/docs/current/libpq-ssl.html.
+ 'sslcert' => '/path/to/cert.pem',
+ 'sslkey' => '/path/to/private.key',
+ 'sslrootcert' => '/path/to/ca.pem'
+ }
+ ```
+
+1. Save the file and [reconfigure GitLab](../restart_gitlab.md#reconfigure-a-linux-package-installation).
+
+### Existing registries
+
+You can migrate your existing container registry data in one step or three steps.
+A few factors affect the duration of the migration:
+
+- The size of your existing registry data.
+- The specifications of your PostgresSQL instance.
+- The number of registry instances running.
+- Network latency between the registry, PostgresSQL and your configured Object Storage.
+
+Choose the one or three step method according to your registry installation.
+
+#### One-step migration
+
+WARNING:
+WARNING:
+The registry must be shut down or remain in `read-only` mode during the migration.
+Only choose this method if you do not need to write to the registry during the migration
+and your registry contains a relatively small amount of data.
+
+1. Add the `database` section to your `/etc/gitlab/gitlab.rb` file, but start with the metadata database **disabled**:
+
+ ```ruby
+ registry['database'] = {
+ 'enabled' => false, # Must be false!
+ 'host' => 'localhost',
+ 'port' => 5432,
+ 'user' => 'registry-database-user',
+ 'password' => 'registry-database-password',
+ 'dbname' => 'registry-database-name'
+ 'sslmode' => 'require', # See the PostgreSQL documentation for additional information https://www.postgresql.org/docs/current/libpq-ssl.html.
+ 'sslcert' => '/path/to/cert.pem',
+ 'sslkey' => '/path/to/private.key',
+ 'sslrootcert' => '/path/to/ca.pem'
+ }
+ ```
+
+1. Ensure the registry is set to `read-only` mode.
+
+ Edit your `/etc/gitlab/gitlab.rb` and add the `maintenance` section to the `registry['storage']` configuration.
+ For example, for a `gcs` backed registry using a `gs://my-company-container-registry` bucket,
+ the configuration could be:
+
+ ```ruby
+ ## Object Storage - Container Registry
+ registry['storage'] = {
+ 'gcs' => {
+ 'bucket' => "my-company-container-registry",
+ 'chunksize' => 5242880
+ },
+ 'maintenance' => {
+ 'readonly' => {
+ 'enabled' => true # Must be set to true.
+ }
+ }
+ }
+ ```
+
+1. Save the file and [reconfigure GitLab](../restart_gitlab.md#reconfigure-a-linux-package-installation).
+1. [Apply schema migrations](#apply-schema-migrations) if you have not done so.
+1. Run the following command:
+
+ ```shell
+ sudo gitlab-ctl registry-database import
+ ```
+
+1. If the command completed successfully, the registry is now fully imported. You
+ can now enable the database, turn off read-only mode in the configuration, and
+ start the registry service:
+
+ ```ruby
+ registry['database'] = {
+ 'enabled' => true, # Must be enabled now!
+ 'host' => 'localhost',
+ 'port' => 5432,
+ 'user' => 'registry-database-user',
+ 'password' => 'registry-database-password',
+ 'dbname' => 'registry-database-name',
+ 'sslmode' => 'require', # See the PostgreSQL documentation for additional information https://www.postgresql.org/docs/current/libpq-ssl.html.
+ 'sslcert' => '/path/to/cert.pem',
+ 'sslkey' => '/path/to/private.key',
+ 'sslrootcert' => '/path/to/ca.pem'
+ }
+
+ ## Object Storage - Container Registry
+ registry['storage'] = {
+ 'gcs' => {
+ 'bucket' => "my-company-container-registry",
+ 'chunksize' => 5242880
+ },
+ 'maintenance' => {
+ 'readonly' => {
+ 'enabled' => false
+ }
+ }
+ }
+ ```
+
+1. Save the file and [reconfigure GitLab](../restart_gitlab.md#reconfigure-a-linux-package-installation).
+
+You can now use the metadata database for all operations!
+
+#### Three-step migration
+
+Follow this guide to migrate your existing container registry data.
+This procedure is recommended for larger sets of data or if you are
+trying to minimize downtime while completing the migration.
+
+NOTE:
+Users have reported step one import completed at [rates of 2 to 4 TB per hour](https://gitlab.com/gitlab-org/gitlab/-/issues/423459).
+At the slower speed, registries with over 100TB of data could take longer than 48 hours.
+
+##### Pre-import repositories (step one)
+
+For larger instances, this command can take hours to days to complete, depending
+on the size of your registry. You may continue to use the registry as normal while
+step one is being completed.
+
+WARNING:
+It is [not yet possible](https://gitlab.com/gitlab-org/container-registry/-/issues/1162)
+to restart the migration, so it's important to let the migration run to completion.
+If you must halt the operation, you have to restart this step.
+
+1. Add the `database` section to your `/etc/gitlab/gitlab.rb` file, but start with the metadata database **disabled**:
+
+ ```ruby
+ registry['database'] = {
+ 'enabled' => false, # Must be false!
+ 'host' => 'localhost',
+ 'port' => 5432,
+ 'user' => 'registry-database-user',
+ 'password' => 'registry-database-password',
+ 'dbname' => 'registry-database-name'
+ 'sslmode' => 'require', # See the PostgreSQL documentation for additional information https://www.postgresql.org/docs/current/libpq-ssl.html.
+ 'sslcert' => '/path/to/cert.pem',
+ 'sslkey' => '/path/to/private.key',
+ 'sslrootcert' => '/path/to/ca.pem'
+ }
+ ```
+
+1. Save the file and [reconfigure GitLab](../restart_gitlab.md#reconfigure-a-linux-package-installation).
+1. [Apply schema migrations](#apply-schema-migrations) if you have not done so.
+1. Run the first step to begin the migration:
+
+ ```shell
+ sudo gitlab-ctl registry-database import --step-one
+ ```
+
+NOTE:
+You should try to schedule the following step as soon as possible
+to reduce the amount of downtime required. Ideally, less than one week
+after step one completes. Any new data written to the registry between steps one and two,
+causes step two to take more time.
+
+##### Import all repository data (step two)
+
+This step requires the registry to be shut down or set in `read-only` mode.
+Allow enough time for downtime while step two is being executed.
+
+1. Ensure the registry is set to `read-only` mode.
+
+ Edit your `/etc/gitlab/gitlab.rb` and add the `maintenance` section to the `registry['storage']`
+ configuration. For example, for a `gcs` backed registry using a `gs://my-company-container-registry`
+ bucket , the configuration could be:
+
+ ```ruby
+ ## Object Storage - Container Registry
+ registry['storage'] = {
+ 'gcs' => {
+ 'bucket' => "my-company-container-registry",
+ 'chunksize' => 5242880
+ },
+ 'maintenance' => {
+ 'readonly' => {
+ 'enabled' => true # Must be set to true.
+ }
+ }
+ }
+ ```
+
+1. Save the file and [reconfigure GitLab](../restart_gitlab.md#reconfigure-a-linux-package-installation).
+1. Run step two of the migration
+
+ ```shell
+ sudo gitlab-ctl registry-database import --step-two
+ ```
+
+1. If the command completed successfully, all images are now fully imported. You
+ can now enable the database, turn off read-only mode in the configuration, and
+ start the registry service:
+
+ ```ruby
+ registry['database'] = {
+ 'enabled' => true, # Must be set to true!
+ 'host' => 'localhost',
+ 'port' => 5432,
+ 'user' => 'registry-database-user',
+ 'password' => 'registry-database-password',
+ 'dbname' => 'registry-database-name',
+ 'sslmode' => 'require', # See the PostgreSQL documentation for additional information https://www.postgresql.org/docs/current/libpq-ssl.html.
+ 'sslcert' => '/path/to/cert.pem',
+ 'sslkey' => '/path/to/private.key',
+ 'sslrootcert' => '/path/to/ca.pem'
+ }
+
+ ## Object Storage - Container Registry
+ registry['storage'] = {
+ 'gcs' => {
+ 'bucket' => "my-company-container-registry",
+ 'chunksize' => 5242880
+ },
+ 'maintenance' => { # This section can be removed.
+ 'readonly' => {
+ 'enabled' => false
+ }
+ }
+ }
+ ```
+
+1. Save the file and [reconfigure GitLab](../restart_gitlab.md#reconfigure-a-linux-package-installation).
+
+You can now use the metadata database for all operations!
+
+##### Import the rest of the data (step three)
+
+Even though the registry is now fully using the database for its metadata, it
+does not yet have access to any potentially unused layer blobs.
+
+To complete the process, run the final step of the migration:
+
+```shell
+sudo gitlab-ctl registry-database import --step-three
+```
+
+After that command exists successfully, the registry is now fully migrated to the database!
+
+## Manage schema migrations
+
+Use the following commands to run the schema migrations for the Container registry metadata database.
+The registry must be enabled and the configuration section must have the database section filled.
+
+### Apply schema migrations
+
+1. Run the registry database schema migrations
+
+ ```shell
+ sudo gitlab-ctl registry-database migrate up
+ ```
+
+1. The registry must stop if it's running. Type `y` to confirm and wait for the process to finish.
+
+NOTE:
+The `migrate up` command offers some extra flags that can be used to control how the migrations are applied.
+Run `sudo gitlab-ctl registry-database migrate up --help` for details.
+
+### Undo schema migrations
+
+You can undo schema migrations in case anything goes wrong, but this is a non-recoverable action.
+If you pushed new images while the database was in use, they will no longer be accessible
+after this.
+
+1. Undo the registry database schema migrations:
+
+ ```shell
+ sudo gitlab-ctl registry-database migrate down
+ ```
+
+NOTE:
+The `migrate down` command offers some extra flags. Run `sudo gitlab-ctl registry-database migrate down --help` for details.
+
+## Troubleshooting
+
+### `there are pending database migrations` error
+
+If the registry has been updated and there are pending schema migrations,
+the registry fails to start with the following error message:
+
+```shell
+FATA[0000] configuring application: there are pending database migrations, use the 'registry database migrate' CLI command to check and apply them
+```
+
+To fix this issue, follow the steps to [apply schema migrations](#apply-schema-migrations).
+
+### `offline garbage collection is no longer possible` error
+
+If the registry uses the metadata database and you try to run
+[offline garbage collection](container_registry.md#container-registry-garbage-collection),
+the registry fails with the following error message:
+
+```shell
+ERRO[0000] this filesystem is managed by the metadata database, and offline garbage collection is no longer possible, if you are not using the database anymore, remove the file at the lock_path in this log message lock_path=/docker/registry/lockfiles/database-in-use
+```
+
+You must either:
+
+- Stop using offline garbage collection.
+- If you no longer use the metadata database, delete the indicated lock file at the `lock_path` shown in the error message.
+ For example, remove the `/docker/registry/lockfiles/database-in-use` file.
diff --git a/doc/api/boards.md b/doc/api/boards.md
index ef4a0838876..0562e59bcb6 100644
--- a/doc/api/boards.md
+++ b/doc/api/boards.md
@@ -21,7 +21,7 @@ GET /projects/:id/boards
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/boards"
@@ -105,8 +105,8 @@ GET /projects/:id/boards/:board_id
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user |
-| `board_id` | integer | yes | The ID of a board |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `board_id` | integer | yes | The ID of a board. |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/boards/1"
@@ -182,8 +182,8 @@ POST /projects/:id/boards
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user |
-| `name` | string | yes | The name of the new board |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `name` | string | yes | The name of the new board. |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/boards?name=newboard"
@@ -225,13 +225,13 @@ PUT /projects/:id/boards/:board_id
| Attribute | Type | Required | Description |
| ---------------------------- | -------------- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user |
-| `board_id` | integer | yes | The ID of a board |
-| `name` | string | no | The new name of the board |
-| `assignee_id` **(PREMIUM ALL)** | integer | no | The assignee the board should be scoped to |
-| `milestone_id` **(PREMIUM ALL)** | integer | no | The milestone the board should be scoped to |
-| `labels` **(PREMIUM ALL)** | string | no | Comma-separated list of label names which the board should be scoped to |
-| `weight` **(PREMIUM ALL)** | integer | no | The weight range from 0 to 9, to which the board should be scoped to |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `board_id` | integer | yes | The ID of a board. |
+| `name` | string | no | The new name of the board. |
+| `assignee_id` | integer | no | The assignee the board should be scoped to. Premium and Ultimate only. |
+| `milestone_id` | integer | no | The milestone the board should be scoped to. Premium and Ultimate only. |
+| `labels` | string | no | Comma-separated list of label names which the board should be scoped to. Premium and Ultimate only. |
+| `weight` | integer | no | The weight range from 0 to 9, to which the board should be scoped to. Premium and Ultimate only. |
```shell
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/boards/1?name=new_name&milestone_id=43&assignee_id=1&labels=Doing&weight=4"
@@ -305,8 +305,8 @@ DELETE /projects/:id/boards/:board_id
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user |
-| `board_id` | integer | yes | The ID of a board |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `board_id` | integer | yes | The ID of a board. |
```shell
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/boards/1"
@@ -323,8 +323,8 @@ GET /projects/:id/boards/:board_id/lists
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user |
-| `board_id` | integer | yes | The ID of a board |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `board_id` | integer | yes | The ID of a board. |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/boards/1/lists"
@@ -383,9 +383,9 @@ GET /projects/:id/boards/:board_id/lists/:list_id
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user |
-| `board_id` | integer | yes | The ID of a board |
-| `list_id`| integer | yes | The ID of a board's list |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `board_id` | integer | yes | The ID of a board. |
+| `list_id`| integer | yes | The ID of a board's list. |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/boards/1/lists/1"
@@ -418,11 +418,11 @@ POST /projects/:id/boards/:board_id/lists
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user |
-| `board_id` | integer | yes | The ID of a board |
-| `label_id` | integer | no | The ID of a label |
-| `assignee_id` **(PREMIUM ALL)** | integer | no | The ID of a user |
-| `milestone_id` **(PREMIUM ALL)** | integer | no | The ID of a milestone |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `board_id` | integer | yes | The ID of a board. |
+| `label_id` | integer | no | The ID of a label. |
+| `assignee_id` | integer | no | The ID of a user. Premium and Ultimate only. |
+| `milestone_id` | integer | no | The ID of a milestone. Premium and Ultimate only. |
NOTE:
Label, assignee and milestone arguments are mutually exclusive,
@@ -461,10 +461,10 @@ PUT /projects/:id/boards/:board_id/lists/:list_id
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user |
-| `board_id` | integer | yes | The ID of a board |
-| `list_id` | integer | yes | The ID of a board's list |
-| `position` | integer | yes | The position of the list |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `board_id` | integer | yes | The ID of a board. |
+| `list_id` | integer | yes | The ID of a board's list. |
+| `position` | integer | yes | The position of the list. |
```shell
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/boards/1/lists/1?position=2"
@@ -497,9 +497,9 @@ DELETE /projects/:id/boards/:board_id/lists/:list_id
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user |
-| `board_id` | integer | yes | The ID of a board |
-| `list_id` | integer | yes | The ID of a board's list |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `board_id` | integer | yes | The ID of a board. |
+| `list_id` | integer | yes | The ID of a board's list. |
```shell
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/boards/1/lists/1"
diff --git a/doc/api/emoji_reactions.md b/doc/api/emoji_reactions.md
index 7e45a9abc6a..dc944b54003 100644
--- a/doc/api/emoji_reactions.md
+++ b/doc/api/emoji_reactions.md
@@ -13,7 +13,7 @@ An [emoji reaction](../user/emoji_reactions.md) tells a thousand words.
We call GitLab objects on which you can react with an emoji "awardables".
You can react with emoji on the following:
-- [Epics](../user/group/epics/index.md) ([API](epics.md)). **(PREMIUM ALL)**
+- [Epics](../user/group/epics/index.md) ([API](epics.md)).
- [Issues](../user/project/issues/index.md) ([API](issues.md)).
- [Merge requests](../user/project/merge_requests/index.md) ([API](merge_requests.md)).
- [Snippets](../user/snippets.md) ([API](snippets.md)).
diff --git a/doc/api/group_boards.md b/doc/api/group_boards.md
index 48e1658e0ab..3c97c67b079 100644
--- a/doc/api/group_boards.md
+++ b/doc/api/group_boards.md
@@ -21,7 +21,7 @@ GET /groups/:id/boards
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) owned by the authenticated user |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/5/boards"
@@ -138,8 +138,8 @@ GET /groups/:id/boards/:board_id
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) owned by the authenticated user |
-| `board_id` | integer | yes | The ID of a board |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `board_id` | integer | yes | The ID of a board. |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/5/boards/1"
@@ -252,8 +252,8 @@ POST /groups/:id/boards
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) owned by the authenticated user |
-| `name` | string | yes | The name of the new board |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `name` | string | yes | The name of the new board. |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/5/boards?name=newboard"
@@ -291,15 +291,15 @@ PUT /groups/:id/boards/:board_id
| Attribute | Type | Required | Description |
| ---------------------------- | -------------- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) owned by the authenticated user |
-| `board_id` | integer | yes | The ID of a board |
-| `name` | string | no | The new name of the board |
-| `hide_backlog_list` | boolean | no | Hide the Open list |
-| `hide_closed_list` | boolean | no | Hide the Closed list |
-| `assignee_id` **(PREMIUM ALL)** | integer | no | The assignee the board should be scoped to |
-| `milestone_id` **(PREMIUM ALL)** | integer | no | The milestone the board should be scoped to |
-| `labels` **(PREMIUM ALL)** | string | no | Comma-separated list of label names which the board should be scoped to |
-| `weight` **(PREMIUM ALL)** | integer | no | The weight range from 0 to 9, to which the board should be scoped to |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `board_id` | integer | yes | The ID of a board. |
+| `name` | string | no | The new name of the board. |
+| `hide_backlog_list` | boolean | no | Hide the Open list. |
+| `hide_closed_list` | boolean | no | Hide the Closed list. |
+| `assignee_id` | integer | no | The assignee the board should be scoped to. Premium and Ultimate only. |
+| `milestone_id` | integer | no | The milestone the board should be scoped to. Premium and Ultimate only. |
+| `labels` | string | no | Comma-separated list of label names which the board should be scoped to. Premium and Ultimate only. |
+| `weight` | integer | no | The weight range from 0 to 9, to which the board should be scoped to. Premium and Ultimate only. |
```shell
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/5/boards/1?name=new_name&milestone_id=44&assignee_id=1&labels=GroupLabel&weight=4"
@@ -359,8 +359,8 @@ DELETE /groups/:id/boards/:board_id
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) owned by the authenticated user |
-| `board_id` | integer | yes | The ID of a board |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `board_id` | integer | yes | The ID of a board. |
```shell
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/5/boards/1"
@@ -377,8 +377,8 @@ GET /groups/:id/boards/:board_id/lists
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) owned by the authenticated user |
-| `board_id` | integer | yes | The ID of a board |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `board_id` | integer | yes | The ID of a board. |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/5/boards/1/lists"
@@ -428,9 +428,9 @@ GET /groups/:id/boards/:board_id/lists/:list_id
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) owned by the authenticated user |
-| `board_id` | integer | yes | The ID of a board |
-| `list_id` | integer | yes | The ID of a board's list |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `board_id` | integer | yes | The ID of a board. |
+| `list_id` | integer | yes | The ID of a board's list. |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/5/boards/1/lists/1"
@@ -460,9 +460,9 @@ POST /groups/:id/boards/:board_id/lists
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) owned by the authenticated user |
-| `board_id` | integer | yes | The ID of a board |
-| `label_id` | integer | yes | The ID of a label |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `board_id` | integer | yes | The ID of a board. |
+| `label_id` | integer | yes | The ID of a label. |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/4/boards/12/lists?milestone_id=7"
@@ -501,10 +501,10 @@ PUT /groups/:id/boards/:board_id/lists/:list_id
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) owned by the authenticated user |
-| `board_id` | integer | yes | The ID of a board |
-| `list_id` | integer | yes | The ID of a board's list |
-| `position` | integer | yes | The position of the list |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `board_id` | integer | yes | The ID of a board. |
+| `list_id` | integer | yes | The ID of a board's list. |
+| `position` | integer | yes | The position of the list. |
```shell
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/group/5/boards/1/lists/1?position=2"
@@ -534,9 +534,9 @@ DELETE /groups/:id/boards/:board_id/lists/:list_id
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) owned by the authenticated user |
-| `board_id` | integer | yes | The ID of a board |
-| `list_id` | integer | yes | The ID of a board's list |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `board_id` | integer | yes | The ID of a board. |
+| `list_id` | integer | yes | The ID of a board's list. |
```shell
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/5/boards/1/lists/1"
diff --git a/doc/api/group_clusters.md b/doc/api/group_clusters.md
index 6a10294d295..32a427f9a0f 100644
--- a/doc/api/group_clusters.md
+++ b/doc/api/group_clusters.md
@@ -179,7 +179,7 @@ Parameters:
| `platform_kubernetes_attributes[token]` | string | yes | The token to authenticate against Kubernetes |
| `platform_kubernetes_attributes[ca_cert]` | string | no | TLS certificate. Required if API is using a self-signed TLS certificate. |
| `platform_kubernetes_attributes[authorization_type]` | string | no | The cluster authorization type: `rbac`, `abac` or `unknown_authorization`. Defaults to `rbac`. |
-| `environment_scope` | string | no | The associated environment to the cluster. Defaults to `*` **(PREMIUM ALL)** |
+| `environment_scope` | string | no | The associated environment to the cluster. Defaults to `*`. Premium and Ultimate only. |
Example request:
@@ -250,7 +250,7 @@ Parameters:
| `platform_kubernetes_attributes[api_url]` | string | no | The URL to access the Kubernetes API |
| `platform_kubernetes_attributes[token]` | string | no | The token to authenticate against Kubernetes |
| `platform_kubernetes_attributes[ca_cert]` | string | no | TLS certificate. Required if API is using a self-signed TLS certificate. |
-| `environment_scope` | string | no | The associated environment to the cluster **(PREMIUM ALL)** |
+| `environment_scope` | string | no | The associated environment to the cluster. Premium and Ultimate only. |
NOTE:
`name`, `api_url`, `ca_cert` and `token` can only be updated if the cluster was added
diff --git a/doc/api/group_level_variables.md b/doc/api/group_level_variables.md
index aeab76b7212..68474a2aec7 100644
--- a/doc/api/group_level_variables.md
+++ b/doc/api/group_level_variables.md
@@ -91,15 +91,15 @@ POST /groups/:id/variables
| Attribute | Type | Required | Description |
|---------------------------------------|----------------|----------|-------------|
-| `id` | integer/string | Yes | The ID of a group or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) |
-| `key` | string | Yes | The `key` of a variable; must have no more than 255 characters; only `A-Z`, `a-z`, `0-9`, and `_` are allowed |
-| `value` | string | Yes | The `value` of a variable |
-| `description` | string | No | The `description` of the variable. Default: `null` |
-| `environment_scope` **(PREMIUM ALL)** | string | No | The [environment scope](../ci/environments/index.md#limit-the-environment-scope-of-a-cicd-variable) of a variable |
-| `masked` | boolean | No | Whether the variable is masked |
-| `protected` | boolean | No | Whether the variable is protected |
+| `id` | integer/string | Yes | The ID of a group or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding). |
+| `key` | string | Yes | The `key` of a variable; must have no more than 255 characters; only `A-Z`, `a-z`, `0-9`, and `_` are allowed. |
+| `value` | string | Yes | The `value` of a variable. |
+| `description` | string | No | The `description` of the variable. Default: `null`. |
+| `environment_scope` | string | No | The [environment scope](../ci/environments/index.md#limit-the-environment-scope-of-a-cicd-variable) of a variable. Premium and Ultimate only. |
+| `masked` | boolean | No | Whether the variable is masked. |
+| `protected` | boolean | No | Whether the variable is protected. |
| `raw` | boolean | No | Whether the variable is treated as a raw string. Default: `false`. When `true`, variables in the value are not [expanded](../ci/variables/index.md#prevent-cicd-variable-expansion). |
-| `variable_type` | string | No | The type of a variable. Available types are: `env_var` (default) and `file` |
+| `variable_type` | string | No | The type of a variable. Available types are: `env_var` (default) and `file`. |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
@@ -136,7 +136,7 @@ PUT /groups/:id/variables/:key
| `key` | string | Yes | The `key` of a variable |
| `value` | string | Yes | The `value` of a variable |
| `description` | string | No | The description of the variable. Default: `null`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/409641) in GitLab 16.2. |
-| `environment_scope` **(PREMIUM ALL)** | string | No | The [environment scope](../ci/environments/index.md#limit-the-environment-scope-of-a-cicd-variable) of a variable |
+| `environment_scope` | string | No | The [environment scope](../ci/environments/index.md#limit-the-environment-scope-of-a-cicd-variable) of a variable. Premium and Ultimate only. |
| `filter` | hash | No | Available filters: `[environment_scope]`. See the [`filter` parameter details](#the-filter-parameter). |
| `masked` | boolean | No | Whether the variable is masked |
| `protected` | boolean | No | Whether the variable is protected |
diff --git a/doc/api/groups.md b/doc/api/groups.md
index 2cfe6e0ead7..1193f36d2fe 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -38,7 +38,7 @@ Parameters:
| `owned` | boolean | no | Limit to groups explicitly owned by the current user |
| `min_access_level` | integer | no | Limit to groups where current user has at least this [role (`access_level`)](members.md#roles) |
| `top_level_only` | boolean | no | Limit to top level groups, excluding all subgroups |
-| `repository_storage` **(PREMIUM ALL)** | string | no | Filter by repository storage used by the group _(administrators only)_. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/419643) in GitLab 16.3 |
+| `repository_storage` | string | no | Filter by repository storage used by the group _(administrators only)_. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/419643) in GitLab 16.3. Premium and Ultimate only. |
```plaintext
GET /groups
@@ -844,10 +844,10 @@ Parameters:
| `subgroup_creation_level` | string | no | Allowed to [create subgroups](../user/group/subgroups/index.md#create-a-subgroup). Can be `owner` (Owners), or `maintainer` (users with the Maintainer role). |
| `two_factor_grace_period` | integer | no | Time before Two-factor authentication is enforced (in hours). |
| `visibility` | string | no | The group's visibility. Can be `private`, `internal`, or `public`. |
-| `membership_lock` **(PREMIUM ALL)** | boolean | no | Users cannot be added to projects in this group. |
-| `extra_shared_runners_minutes_limit` **(PREMIUM SELF)** | integer | no | Can be set by administrators only. Additional compute minutes for this group. |
-| `shared_runners_minutes_limit` **(PREMIUM SELF)** | integer | no | Can be set by administrators only. Maximum number of monthly compute minutes for this group. Can be `nil` (default; inherit system default), `0` (unlimited), or `> 0`. |
-| `wiki_access_level` **(PREMIUM ALL)** | string | no | The wiki access level. Can be `disabled`, `private`, or `enabled`. |
+| `membership_lock` | boolean | no | Users cannot be added to projects in this group. Premium and Ultimate only. |
+| `extra_shared_runners_minutes_limit` | integer | no | Can be set by administrators only. Additional compute minutes for this group. Self-managed, Premium and Ultimate only. |
+| `shared_runners_minutes_limit` | integer | no | Can be set by administrators only. Maximum number of monthly compute minutes for this group. Can be `nil` (default; inherit system default), `0` (unlimited), or `> 0`. Self-managed, Premium and Ultimate only. |
+| `wiki_access_level` | string | no | The wiki access level. Can be `disabled`, `private`, or `enabled`. Premium and Ultimate only. |
### Options for `default_branch_protection`
@@ -1017,18 +1017,18 @@ PUT /groups/:id
| `subgroup_creation_level` | string | no | Allowed to [create subgroups](../user/group/subgroups/index.md#create-a-subgroup). Can be `owner` (Owners), or `maintainer` (users with the Maintainer role). |
| `two_factor_grace_period` | integer | no | Time before Two-factor authentication is enforced (in hours). |
| `visibility` | string | no | The visibility level of the group. Can be `private`, `internal`, or `public`. |
-| `extra_shared_runners_minutes_limit` **(PREMIUM SELF)** | integer | no | Can be set by administrators only. Additional compute minutes for this group. |
-| `file_template_project_id` **(PREMIUM ALL)** | integer | no | The ID of a project to load custom file templates from. |
-| `membership_lock` **(PREMIUM ALL)** | boolean | no | Users cannot be added to projects in this group. |
-| `prevent_forking_outside_group` **(PREMIUM ALL)** | boolean | no | When enabled, users can **not** fork projects from this group to external namespaces. |
-| `shared_runners_minutes_limit` **(PREMIUM SELF)** | integer | no | Can be set by administrators only. Maximum number of monthly compute minutes for this group. Can be `nil` (default; inherit system default), `0` (unlimited), or `> 0`. |
-| `unique_project_download_limit` **(ULTIMATE ALL)** | integer | no | Maximum number of unique projects a user can download in the specified time period before they are banned. Available only on top-level groups. Default: 0, Maximum: 10,000. |
-| `unique_project_download_limit_interval_in_seconds` **(ULTIMATE ALL)** | integer | no | Time period during which a user can download a maximum amount of projects before they are banned. Available only on top-level groups. Default: 0, Maximum: 864,000 seconds (10 days). |
-| `unique_project_download_limit_allowlist` **(ULTIMATE ALL)** | array of strings | no | List of usernames excluded from the unique project download limit. Available only on top-level groups. Default: `[]`, Maximum: 100 usernames. |
-| `unique_project_download_limit_alertlist` **(ULTIMATE ALL)** | array of integers | no | List of user IDs that are emailed when the unique project download limit is exceeded. Available only on top-level groups. Default: `[]`, Maximum: 100 user IDs. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/110201) in GitLab 15.9. |
-| `auto_ban_user_on_excessive_projects_download` **(ULTIMATE ALL)** | boolean | no | When enabled, users are automatically banned from the group when they download more than the maximum number of unique projects specified by `unique_project_download_limit` and `unique_project_download_limit_interval_in_seconds`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/94159) in GitLab 15.4. |
-| `ip_restriction_ranges` **(PREMIUM ALL)** | string | no | Comma-separated list of IP addresses or subnet masks to restrict group access. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/351493) in GitLab 15.4. |
-| `wiki_access_level` **(PREMIUM ALL)** | string | no | The wiki access level. Can be `disabled`, `private`, or `enabled`. |
+| `extra_shared_runners_minutes_limit` | integer | no | Can be set by administrators only. Additional compute minutes for this group. Self-managed, Premium and Ultimate only. |
+| `file_template_project_id` | integer | no | The ID of a project to load custom file templates from. Premium and Ultimate only. |
+| `membership_lock` | boolean | no | Users cannot be added to projects in this group. Premium and Ultimate only. |
+| `prevent_forking_outside_group` | boolean | no | When enabled, users can **not** fork projects from this group to external namespaces. Premium and Ultimate only. |
+| `shared_runners_minutes_limit` | integer | no | Can be set by administrators only. Maximum number of monthly compute minutes for this group. Can be `nil` (default; inherit system default), `0` (unlimited), or `> 0`. Self-managed, Premium and Ultimate only. |
+| `unique_project_download_limit` | integer | no | Maximum number of unique projects a user can download in the specified time period before they are banned. Available only on top-level groups. Default: 0, Maximum: 10,000. Ultimate only. |
+| `unique_project_download_limit_interval_in_seconds` | integer | no | Time period during which a user can download a maximum amount of projects before they are banned. Available only on top-level groups. Default: 0, Maximum: 864,000 seconds (10 days). Ultimate only. |
+| `unique_project_download_limit_allowlist` | array of strings | no | List of usernames excluded from the unique project download limit. Available only on top-level groups. Default: `[]`, Maximum: 100 usernames. Ultimate only.|
+| `unique_project_download_limit_alertlist` | array of integers | no | List of user IDs that are emailed when the unique project download limit is exceeded. Available only on top-level groups. Default: `[]`, Maximum: 100 user IDs. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/110201) in GitLab 15.9. Ultimate only.|
+| `auto_ban_user_on_excessive_projects_download` | boolean | no | When enabled, users are automatically banned from the group when they download more than the maximum number of unique projects specified by `unique_project_download_limit` and `unique_project_download_limit_interval_in_seconds`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/94159) in GitLab 15.4. Ultimate only.|
+| `ip_restriction_ranges` | string | no | Comma-separated list of IP addresses or subnet masks to restrict group access. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/351493) in GitLab 15.4. Premium and Ultimate only.|
+| `wiki_access_level` | string | no | The wiki access level. Can be `disabled`, `private`, or `enabled`. Premium and Ultimate only.|
NOTE:
The `projects` and `shared_projects` attributes in the response are deprecated and [scheduled for removal in API v5](https://gitlab.com/gitlab-org/gitlab/-/issues/213797).
@@ -1192,8 +1192,8 @@ Parameters:
| Attribute | Type | Required | Description |
|----------------------|------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) |
-| `permanently_remove` **(PREMIUM ALL)** | boolean/string | no | Immediately deletes a subgroup if it is marked for deletion. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/368276) in GitLab 15.4 |
-| `full_path` **(PREMIUM ALL)** | string | no | Full path of subgroup to use with `permanently_remove`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/368276) in GitLab 15.4. To find the subgroup path, see the [group details](groups.md#details-of-a-group) |
+| `permanently_remove` | boolean/string | no | Immediately deletes a subgroup if it is marked for deletion. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/368276) in GitLab 15.4. Premium and Ultimate only. |
+| `full_path` | string | no | Full path of subgroup to use with `permanently_remove`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/368276) in GitLab 15.4. To find the subgroup path, see the [group details](groups.md#details-of-a-group). Premium and Ultimate only. |
The response is `202 Accepted` if the user has authorization.
diff --git a/doc/api/issues.md b/doc/api/issues.md
index 3c4ad7b6685..c58ccbcb8cb 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -59,13 +59,13 @@ Supported attributes:
| `created_after` | datetime | No | Return issues created on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
| `created_before` | datetime | No | Return issues created on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
| `due_date` | string | No | Return issues that have no due date, are overdue, or whose due date is this week, this month, or between two weeks ago and next month. Accepts: `0` (no due date), `any`, `today`, `tomorrow`, `overdue`, `week`, `month`, `next_month_and_previous_two_weeks`. |
-| `epic_id` **(PREMIUM ALL)** | integer | No | Return issues associated with the given epic ID. `None` returns issues that are not associated with an epic. `Any` returns issues that are associated with an epic. |
-| `health_status` **(ULTIMATE ALL)** | string | No | Return issues with the specified `health_status`. _([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/370721) in GitLab 15.4)._ In [GitLab 15.5 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/370721), `None` returns issues with no health status assigned, and `Any` returns issues with a health status assigned.
+| `epic_id` | integer | No | Return issues associated with the given epic ID. `None` returns issues that are not associated with an epic. `Any` returns issues that are associated with an epic. Premium and Ultimate only. |
+| `health_status` | string | No | Return issues with the specified `health_status`. _([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/370721) in GitLab 15.4)._ In [GitLab 15.5 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/370721), `None` returns issues with no health status assigned, and `Any` returns issues with a health status assigned. Ultimate only.
| `iids[]` | integer array | No | Return only the issues having the given `iid`. |
| `in` | string | No | Modify the scope of the `search` attribute. `title`, `description`, or a string joining them with comma. Default is `title,description`. |
| `issue_type` | string | No | Filter to a given type of issue. One of `issue`, `incident`, `test_case` or `task`. |
-| `iteration_id` **(PREMIUM ALL)** | integer | No | Return issues assigned to the given iteration ID. `None` returns issues that do not belong to an iteration. `Any` returns issues that belong to an iteration. Mutually exclusive with `iteration_title`. |
-| `iteration_title` **(PREMIUM ALL)** | string | No | Return issues assigned to the iteration with the given title. Similar to `iteration_id` and mutually exclusive with `iteration_id`. |
+| `iteration_id` | integer | No | Return issues assigned to the given iteration ID. `None` returns issues that do not belong to an iteration. `Any` returns issues that belong to an iteration. Mutually exclusive with `iteration_title`. Premium and Ultimate only. |
+| `iteration_title` | string | No | Return issues assigned to the iteration with the given title. Similar to `iteration_id` and mutually exclusive with `iteration_id`. Premium and Ultimate only. |
| `labels` | string | No | Comma-separated list of label names, issues must have all labels to be returned. `None` lists all issues with no labels. `Any` lists all issues with at least one label. `No+Label` (Deprecated) lists all issues with no labels. Predefined names are case-insensitive. |
| `milestone_id` | string | No | Returns issues assigned to milestones with a given timebox value (`None`, `Any`, `Upcoming`, and `Started`). `None` lists all issues with no milestone. `Any` lists all issues that have an assigned milestone. `Upcoming` lists all issues assigned to milestones due in the future. `Started` lists all issues assigned to open, started milestones. `milestone` and `milestone_id` are mutually exclusive. _([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/335939) in GitLab 14.3)_ |
| `milestone` | string | No | The milestone title. `None` lists all issues with no milestone. `Any` lists all issues that have an assigned milestone. Using `None` or `Any` will be [deprecated in the future](https://gitlab.com/gitlab-org/gitlab/-/issues/336044). Use `milestone_id` attribute instead. `milestone` and `milestone_id` are mutually exclusive. |
@@ -79,7 +79,7 @@ Supported attributes:
| `state` | string | No | Return `all` issues or just those that are `opened` or `closed`. |
| `updated_after` | datetime | No | Return issues updated on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
| `updated_before` | datetime | No | Return issues updated on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
-| `weight` **(PREMIUM ALL)** | integer | No | Return issues with the specified `weight`. `None` returns issues with no weight assigned. `Any` returns issues with a weight assigned. |
+| `weight` | integer | No | Return issues with the specified `weight`. `None` returns issues with no weight assigned. `Any` returns issues with a weight assigned. Premium and Ultimate only. |
| `with_labels_details` | boolean | No | If `true`, the response returns more details for each label in labels field: `:name`, `:color`, `:description`, `:description_html`, `:text_color`. Default is `false`. |
Example request:
@@ -293,11 +293,11 @@ Supported attributes:
| `created_after` | datetime | No | Return issues created on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
| `created_before` | datetime | No | Return issues created on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
| `due_date` | string | No | Return issues that have no due date, are overdue, or whose due date is this week, this month, or between two weeks ago and next month. Accepts: `0` (no due date), `any`, `today`, `tomorrow`, `overdue`, `week`, `month`, `next_month_and_previous_two_weeks`. |
-| `epic_id` **(PREMIUM ALL)** | integer | No | Return issues associated with the given epic ID. `None` returns issues that are not associated with an epic. `Any` returns issues that are associated with an epic. |
+| `epic_id` | integer | No | Return issues associated with the given epic ID. `None` returns issues that are not associated with an epic. `Any` returns issues that are associated with an epic. Premium and Ultimate only. |
| `iids[]` | integer array | No | Return only the issues having the given `iid`. |
| `issue_type` | string | No | Filter to a given type of issue. One of `issue`, `incident`, `test_case` or `task`. |
-| `iteration_id` **(PREMIUM ALL)** | integer | No | Return issues assigned to the given iteration ID. `None` returns issues that do not belong to an iteration. `Any` returns issues that belong to an iteration. Mutually exclusive with `iteration_title`. |
-| `iteration_title` **(PREMIUM ALL)** | string | No | Return issues assigned to the iteration with the given title. Similar to `iteration_id` and mutually exclusive with `iteration_id`. |
+| `iteration_id` | integer | No | Return issues assigned to the given iteration ID. `None` returns issues that do not belong to an iteration. `Any` returns issues that belong to an iteration. Mutually exclusive with `iteration_title`. Premium and Ultimate only. |
+| `iteration_title` | string | No | Return issues assigned to the iteration with the given title. Similar to `iteration_id` and mutually exclusive with `iteration_id`. Premium and Ultimate only.|
| `labels` | string | No | Comma-separated list of label names, issues must have all labels to be returned. `None` lists all issues with no labels. `Any` lists all issues with at least one label. `No+Label` (Deprecated) lists all issues with no labels. Predefined names are case-insensitive. |
| `milestone` | string | No | The milestone title. `None` lists all issues with no milestone. `Any` lists all issues that have an assigned milestone. |
| `my_reaction_emoji` | string | No | Return issues reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. |
@@ -310,7 +310,7 @@ Supported attributes:
| `state` | string | No | Return all issues or just those that are `opened` or `closed`. |
| `updated_after` | datetime | No | Return issues updated on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
| `updated_before` | datetime | No | Return issues updated on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
-| `weight` **(PREMIUM ALL)** | integer | No | Return issues with the specified `weight`. `None` returns issues with no weight assigned. `Any` returns issues with a weight assigned. |
+| `weight` | integer | No | Return issues with the specified `weight`. `None` returns issues with no weight assigned. `Any` returns issues with a weight assigned. Premium and Ultimate only. |
| `with_labels_details` | boolean | No | If `true`, the response returns more details for each label in labels field: `:name`, `:color`, `:description`, `:description_html`, `:text_color`. Default is `false`. |
Example request:
@@ -500,11 +500,11 @@ Supported attributes:
| `created_after` | datetime | No | Return issues created on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
| `created_before` | datetime | No | Return issues created on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
| `due_date` | string | No | Return issues that have no due date, are overdue, or whose due date is this week, this month, or between two weeks ago and next month. Accepts: `0` (no due date), `any`, `today`, `tomorrow`, `overdue`, `week`, `month`, `next_month_and_previous_two_weeks`. |
-| `epic_id` **(PREMIUM ALL)** | integer | No | Return issues associated with the given epic ID. `None` returns issues that are not associated with an epic. `Any` returns issues that are associated with an epic. |
+| `epic_id` | integer | No | Return issues associated with the given epic ID. `None` returns issues that are not associated with an epic. `Any` returns issues that are associated with an epic. Premium and Ultimate only. |
| `iids[]` | integer array | No | Return only the issues having the given `iid`. |
| `issue_type` | string | No | Filter to a given type of issue. One of `issue`, `incident`, `test_case` or `task`. |
-| `iteration_id` **(PREMIUM ALL)** | integer | No | Return issues assigned to the given iteration ID. `None` returns issues that do not belong to an iteration. `Any` returns issues that belong to an iteration. Mutually exclusive with `iteration_title`. |
-| `iteration_title` **(PREMIUM ALL)** | string | No | Return issues assigned to the iteration with the given title. Similar to `iteration_id` and mutually exclusive with `iteration_id`. |
+| `iteration_id` | integer | No | Return issues assigned to the given iteration ID. `None` returns issues that do not belong to an iteration. `Any` returns issues that belong to an iteration. Mutually exclusive with `iteration_title`. Premium and Ultimate only. |
+| `iteration_title` | string | No | Return issues assigned to the iteration with the given title. Similar to `iteration_id` and mutually exclusive with `iteration_id`. Premium and Ultimate only. |
| `labels` | string | No | Comma-separated list of label names, issues must have all labels to be returned. `None` lists all issues with no labels. `Any` lists all issues with at least one label. `No+Label` (Deprecated) lists all issues with no labels. Predefined names are case-insensitive. |
| `milestone` | string | No | The milestone title. `None` lists all issues with no milestone. `Any` lists all issues that have an assigned milestone. |
| `my_reaction_emoji` | string | No | Return issues reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. |
@@ -516,7 +516,7 @@ Supported attributes:
| `state` | string | No | Return all issues or just those that are `opened` or `closed`. |
| `updated_after` | datetime | No | Return issues updated on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
| `updated_before` | datetime | No | Return issues updated on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
-| `weight` **(PREMIUM ALL)** | integer | No | Return issues with the specified `weight`. `None` returns issues with no weight assigned. `Any` returns issues with a weight assigned. |
+| `weight` | integer | No | Return issues with the specified `weight`. `None` returns issues with no weight assigned. `Any` returns issues with a weight assigned. Premium and Ultimate only. |
| `with_labels_details` | boolean | No | If `true`, the response returns more details for each label in labels field: `:name`, `:color`, `:description`, `:description_html`, `:text_color`. Default is `false`. |
Example request:
@@ -1021,21 +1021,21 @@ Supported attributes:
|-------------------------------------------|----------------|----------|--------------|
| `id` | integer/string | Yes | The global ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
| `assignee_id` | integer | No | The ID of the user to assign the issue to. Only appears on GitLab Free. |
-| `assignee_ids` **(PREMIUM ALL)** | integer array | No | The IDs of the users to assign the issue to. |
+| `assignee_ids` | integer array | No | The IDs of the users to assign the issue to. Premium and Ultimate only.|
| `confidential` | boolean | No | Set an issue to be confidential. Default is `false`. |
| `created_at` | string | No | When the issue was created. Date time string, ISO 8601 formatted, for example `2016-03-11T03:45:40Z`. Requires administrator or project/group owner rights. |
| `description` | string | No | The description of an issue. Limited to 1,048,576 characters. |
| `discussion_to_resolve` | string | No | The ID of a discussion to resolve. This fills out the issue with a default description and mark the discussion as resolved. Use in combination with `merge_request_to_resolve_discussions_of`. |
| `due_date` | string | No | The due date. Date time string in the format `YYYY-MM-DD`, for example `2016-03-11`. |
-| `epic_id` **(PREMIUM ALL)** | integer | No | ID of the epic to add the issue to. Valid values are greater than or equal to 0. |
-| `epic_iid` **(PREMIUM ALL)** | integer | No | IID of the epic to add the issue to. Valid values are greater than or equal to 0. (deprecated, [scheduled for removal](https://gitlab.com/gitlab-org/gitlab/-/issues/35157) in API version 5). |
+| `epic_id` | integer | No | ID of the epic to add the issue to. Valid values are greater than or equal to 0. Premium and Ultimate only. |
+| `epic_iid` | integer | No | IID of the epic to add the issue to. Valid values are greater than or equal to 0. (deprecated, [scheduled for removal](https://gitlab.com/gitlab-org/gitlab/-/issues/35157) in API version 5). Premium and Ultimate only. |
| `iid` | integer/string | No | The internal ID of the project's issue (requires administrator or project owner rights). |
| `issue_type` | string | No | The type of issue. One of `issue`, `incident`, `test_case` or `task`. Default is `issue`. |
| `labels` | string | No | Comma-separated label names for an issue. |
| `merge_request_to_resolve_discussions_of` | integer | No | The IID of a merge request in which to resolve all issues. This fills out the issue with a default description and mark all discussions as resolved. When passing a description or title, these values take precedence over the default values.|
| `milestone_id` | integer | No | The global ID of a milestone to assign issue. To find the `milestone_id` associated with a milestone, view an issue with the milestone assigned and [use the API](#single-project-issue) to retrieve the issue's details. |
| `title` | string | Yes | The title of an issue. |
-| `weight` **(PREMIUM ALL)** | integer | No | The weight of the issue. Valid values are greater than or equal to 0. |
+| `weight` | integer | No | The weight of the issue. Valid values are greater than or equal to 0. Premium and Ultimate only. |
Example request:
@@ -1199,8 +1199,8 @@ Supported attributes:
| `description` | string | No | The description of an issue. Limited to 1,048,576 characters. |
| `discussion_locked` | boolean | No | Flag indicating if the issue's discussion is locked. If the discussion is locked only project members can add or edit comments. |
| `due_date` | string | No | The due date. Date time string in the format `YYYY-MM-DD`, for example `2016-03-11`. |
-| `epic_id` **(PREMIUM ALL)** | integer | No | ID of the epic to add the issue to. Valid values are greater than or equal to 0. |
-| `epic_iid` **(PREMIUM ALL)** | integer | No | IID of the epic to add the issue to. Valid values are greater than or equal to 0. (deprecated, [scheduled for removal](https://gitlab.com/gitlab-org/gitlab/-/issues/35157) in API version 5). |
+| `epic_id` | integer | No | ID of the epic to add the issue to. Valid values are greater than or equal to 0. Premium and Ultimate only. |
+| `epic_iid` | integer | No | IID of the epic to add the issue to. Valid values are greater than or equal to 0. (deprecated, [scheduled for removal](https://gitlab.com/gitlab-org/gitlab/-/issues/35157) in API version 5). Premium and Ultimate only. |
| `issue_type` | string | No | Updates the type of issue. One of `issue`, `incident`, `test_case` or `task`. |
| `labels` | string | No | Comma-separated label names for an issue. Set to an empty string to unassign all labels. |
| `milestone_id` | integer | No | The global ID of a milestone to assign the issue to. Set to `0` or provide an empty value to unassign a milestone.|
@@ -1208,7 +1208,7 @@ Supported attributes:
| `state_event` | string | No | The state event of an issue. To close the issue, use `close`, and to reopen it, use `reopen`. |
| `title` | string | No | The title of an issue. |
| `updated_at` | string | No | When the issue was updated. Date time string, ISO 8601 formatted, for example `2016-03-11T03:45:40Z` (requires administrator or project owner rights). Empty string or null values are not accepted.|
-| `weight` **(PREMIUM ALL)** | integer | No | The weight of the issue. Valid values are greater than or equal to 0. |
+| `weight` | integer | No | The weight of the issue. Valid values are greater than or equal to 0. Premium and Ultimate only. |
Example request:
diff --git a/doc/api/issues_statistics.md b/doc/api/issues_statistics.md
index 4e14a8f4f6c..6f82c9214f4 100644
--- a/doc/api/issues_statistics.md
+++ b/doc/api/issues_statistics.md
@@ -41,7 +41,7 @@ GET /issues_statistics?confidential=true
| `author_username` | string | no | Return issues created by the given `username`. Similar to `author_id` and mutually exclusive with `author_id`. |
| `assignee_id` | integer | no | Return issues assigned to the given user `id`. Mutually exclusive with `assignee_username`. `None` returns unassigned issues. `Any` returns issues with an assignee. |
| `assignee_username` | string array | no | Return issues assigned to the given `username`. Similar to `assignee_id` and mutually exclusive with `assignee_id`. In GitLab CE `assignee_username` array should only contain a single value or an invalid parameter error is returned otherwise. |
-| `epic_id` **(PREMIUM ALL)** | integer | no | Return issues associated with the given epic ID. `None` returns issues that are not associated with an epic. `Any` returns issues that are associated with an epic. _([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46887) in GitLab 13.6)_
+| `epic_id` | integer | no | Return issues associated with the given epic ID. `None` returns issues that are not associated with an epic. `Any` returns issues that are associated with an epic. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46887) in GitLab 13.6. Premium and Ultimate only.
| `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. |
| `iids[]` | integer array | no | Return only the issues having the given `iid` |
| `search` | string | no | Search issues against their `title` and `description` |
diff --git a/doc/api/job_artifacts.md b/doc/api/job_artifacts.md
index c7f0dfc28a1..2757aaf00db 100644
--- a/doc/api/job_artifacts.md
+++ b/doc/api/job_artifacts.md
@@ -28,7 +28,7 @@ GET /projects/:id/jobs/:job_id/artifacts
|-------------------------------|----------------|----------|-------------|
| `id` | integer/string | Yes | ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding). |
| `job_id` | integer | Yes | ID of a job. |
-| `job_token` **(PREMIUM ALL)** | string | No | To be used with [triggers](../ci/jobs/ci_job_token.md#download-an-artifact-from-a-different-pipeline) for multi-project pipelines. It should be invoked only in a CI/CD job defined in the `.gitlab-ci.yml` file. The value is always `$CI_JOB_TOKEN`. The job associated with the `$CI_JOB_TOKEN` must be running when this token is used. |
+| `job_token` | string | No | To be used with [triggers](../ci/jobs/ci_job_token.md#download-an-artifact-from-a-different-pipeline) for multi-project pipelines. It should be invoked only in a CI/CD job defined in the `.gitlab-ci.yml` file. The value is always `$CI_JOB_TOKEN`. The job associated with the `$CI_JOB_TOKEN` must be running when this token is used. Premium and Ultimate only. |
Example request using the `PRIVATE-TOKEN` header:
@@ -98,7 +98,7 @@ Parameters
| `id` | integer/string | Yes | ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding). |
| `job` | string | Yes | The name of the job. |
| `ref_name` | string | Yes | Branch or tag name in repository. HEAD or SHA references are not supported. |
-| `job_token` **(PREMIUM ALL)** | string | No | To be used with [triggers](../ci/jobs/ci_job_token.md#download-an-artifact-from-a-different-pipeline) for multi-project pipelines. It should be invoked only in a CI/CD job defined in the `.gitlab-ci.yml` file. The value is always `$CI_JOB_TOKEN`. The job associated with the `$CI_JOB_TOKEN` must be running when this token is used. |
+| `job_token` | string | No | To be used with [triggers](../ci/jobs/ci_job_token.md#download-an-artifact-from-a-different-pipeline) for multi-project pipelines. It should be invoked only in a CI/CD job defined in the `.gitlab-ci.yml` file. The value is always `$CI_JOB_TOKEN`. The job associated with the `$CI_JOB_TOKEN` must be running when this token is used. Premium and Ultimate only. |
Example request using the `PRIVATE-TOKEN` header:
@@ -161,7 +161,7 @@ Parameters
| `artifact_path` | string | Yes | Path to a file inside the artifacts archive. |
| `id` | integer/string | Yes | ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding). |
| `job_id` | integer | Yes | The unique job identifier. |
-| `job_token` **(PREMIUM ALL)** | string | No | To be used with [triggers](../ci/jobs/ci_job_token.md#download-an-artifact-from-a-different-pipeline) for multi-project pipelines. It should be invoked only in a CI/CD job defined in the `.gitlab-ci.yml` file. The value is always `$CI_JOB_TOKEN`. The job associated with the `$CI_JOB_TOKEN` must be running when this token is used. |
+| `job_token` | string | No | To be used with [triggers](../ci/jobs/ci_job_token.md#download-an-artifact-from-a-different-pipeline) for multi-project pipelines. It should be invoked only in a CI/CD job defined in the `.gitlab-ci.yml` file. The value is always `$CI_JOB_TOKEN`. The job associated with the `$CI_JOB_TOKEN` must be running when this token is used. Premium and Ultimate only. |
Example request:
@@ -208,7 +208,7 @@ Parameters:
| `id` | integer/string | Yes | ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding). |
| `job` | string | Yes | The name of the job. |
| `ref_name` | string | Yes | Branch or tag name in repository. `HEAD` or `SHA` references are not supported. |
-| `job_token` **(PREMIUM ALL)** | string | No | To be used with [triggers](../ci/jobs/ci_job_token.md#download-an-artifact-from-a-different-pipeline) for multi-project pipelines. It should be invoked only in a CI/CD job defined in the `.gitlab-ci.yml` file. The value is always `$CI_JOB_TOKEN`. The job associated with the `$CI_JOB_TOKEN` must be running when this token is used. |
+| `job_token` | string | No | To be used with [triggers](../ci/jobs/ci_job_token.md#download-an-artifact-from-a-different-pipeline) for multi-project pipelines. It should be invoked only in a CI/CD job defined in the `.gitlab-ci.yml` file. The value is always `$CI_JOB_TOKEN`. The job associated with the `$CI_JOB_TOKEN` must be running when this token is used. Premium and Ultimate only. |
Example request:
diff --git a/doc/api/members.md b/doc/api/members.md
index e1732716fac..9d7aa85ba93 100644
--- a/doc/api/members.md
+++ b/doc/api/members.md
@@ -132,11 +132,11 @@ GET /projects/:id/members/all
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project or group](rest/index.md#namespaced-path-encoding) owned by the authenticated user |
-| `query` | string | no | A query string to search for members |
-| `user_ids` | array of integers | no | Filter the results on the given user IDs |
-| `show_seat_info` | boolean | no | Show seat information for users |
-| `state` | string | no | Filter results by member state, one of `awaiting` or `active` **(PREMIUM ALL)** |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project or group](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `query` | string | no | A query string to search for members. |
+| `user_ids` | array of integers | no | Filter the results on the given user IDs. |
+| `show_seat_info` | boolean | no | Show seat information for users. |
+| `state` | string | no | Filter results by member state, one of `awaiting` or `active`. Premium and Ultimate only. |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/:id/members/all"
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 40b1521852d..58f972ed1c1 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -51,8 +51,8 @@ Supported attributes:
| Attribute | Type | Required | Description |
| ------------------------------- | -------------- | -------- | ----------- |
-| `approved_by_ids` **(PREMIUM ALL)** | integer array | No | Returns merge requests which have been approved by all the users with the given `id`. Maximum of 5. `None` returns merge requests with no approvals. `Any` returns merge requests with an approval. |
-| `approver_ids` **(PREMIUM ALL)** | integer array | No | Returns merge requests which have specified all the users with the given `id` as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. |
+| `approved_by_ids` | integer array | No | Returns merge requests which have been approved by all the users with the given `id`. Maximum of 5. `None` returns merge requests with no approvals. `Any` returns merge requests with an approval. Premium and Ultimate only. |
+| `approver_ids` | integer array | No | Returns merge requests which have specified all the users with the given `id` as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. Premium and Ultimate only. |
| `approved` | string | No | Filters merge requests by their `approved` status. `yes` returns only approved merge requests. `no` returns only non-approved merge requests. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/3159) in GitLab 15.11. Available only when the feature flag `mr_approved_filter` is enabled. |
| `assignee_id` | integer | No | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. |
| `author_id` | integer | No | Returns merge requests created by the given user `id`. Mutually exclusive with `author_username`. Combine with `scope=all` or `scope=assigned_to_me`. |
@@ -235,8 +235,8 @@ Supported attributes:
| Attribute | Type | Required | Description |
| ------------------------------- | -------------- | -------- | ----------- |
| `id` | integer or string | Yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
-| `approved_by_ids` **(PREMIUM ALL)** | integer array | No | Returns merge requests which have been approved by all the users with the given `id`, with a maximum of 5. `None` returns merge requests with no approvals. `Any` returns merge requests with an approval. |
-| `approver_ids` **(PREMIUM ALL)** | integer array | No | Returns merge requests which have specified all the users with the given `id` as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. |
+| `approved_by_ids` | integer array | No | Returns merge requests which have been approved by all the users with the given `id`, with a maximum of 5. `None` returns merge requests with no approvals. `Any` returns merge requests with an approval. Premium and Ultimate only. |
+| `approver_ids` | integer array | No | Returns merge requests which have specified all the users with the given `id` as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. Premium and Ultimate only. |
| `approved` | string | No | Filters merge requests by their `approved` status. `yes` returns only approved merge requests. `no` returns only non-approved merge requests. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/3159) in GitLab 15.11. Available only when the feature flag `mr_approved_filter` is enabled. |
| `assignee_id` | integer | No | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. |
| `author_id` | integer | No | Returns merge requests created by the given user `id`. Mutually exclusive with `author_username`. |
@@ -422,9 +422,9 @@ Supported attributes:
| Attribute | Type | Required | Description |
| ------------------------------- | -------------- | -------- | ----------- |
| `id` | integer or string | Yes | The ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
-| `approved_by_ids` **(PREMIUM ALL)** | integer array | No | Returns merge requests which have been approved by all the users with the given `id`, with a maximum of 5. `None` returns merge requests with no approvals. `Any` returns merge requests with an approval. |
-| `approved_by_usernames` **(PREMIUM ALL)** | string array | No | Returns merge requests which have been approved by all the users with the given `username`, with a maximum of 5. `None` returns merge requests with no approvals. `Any` returns merge requests with an approval. |
-| `approver_ids` **(PREMIUM ALL)** | integer array | No | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. |
+| `approved_by_ids` | integer array | No | Returns merge requests which have been approved by all the users with the given `id`, with a maximum of 5. `None` returns merge requests with no approvals. `Any` returns merge requests with an approval. Premium and Ultimate only. |
+| `approved_by_usernames` | string array | No | Returns merge requests which have been approved by all the users with the given `username`, with a maximum of 5. `None` returns merge requests with no approvals. `Any` returns merge requests with an approval. Premium and Ultimate only. |
+| `approver_ids` | integer array | No | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. Premium and Ultimate only. |
| `approved` | string | No | Filters merge requests by their `approved` status. `yes` returns only approved merge requests. `no` returns only non-approved merge requests. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/3159) in GitLab 15.11. Available only when the feature flag `mr_approved_filter` is enabled. |
| `assignee_id` | integer | No | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. |
| `author_id` | integer | No | Returns merge requests created by the given user `id`. Mutually exclusive with `author_username`. |
@@ -604,7 +604,7 @@ Supported attributes:
| Attribute | Type | Description |
|----------------------------------|------|-------------|
-| `approvals_before_merge`| integer | **(PREMIUM ALL)** Number of approvals required before this merge request can merge. To configure approval rules, see [Merge request approvals API](merge_request_approvals.md). [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/353097) in GitLab 16.0. |
+| `approvals_before_merge`| integer | Number of approvals required before this merge request can merge. To configure approval rules, see [Merge request approvals API](merge_request_approvals.md). [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/353097) in GitLab 16.0. Premium and Ultimate only. |
| `assignee` | object | First assignee of the merge request. |
| `assignees` | array | Assignees of the merge request. |
| `author` | object | User who created this merge request. |
@@ -1296,7 +1296,7 @@ POST /projects/:id/merge_requests
| `target_branch` | string | Yes | The target branch. |
| `title` | string | Yes | Title of MR. |
| `allow_collaboration` | boolean | No | Allow commits from members who can merge to the target branch. |
-| `approvals_before_merge` **(PREMIUM ALL)** | integer | No | Number of approvals required before this can be merged (see below). To configure approval rules, see [Merge request approvals API](merge_request_approvals.md). [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/353097) in GitLab 16.0. |
+| `approvals_before_merge` | integer | No | Number of approvals required before this can be merged (see below). To configure approval rules, see [Merge request approvals API](merge_request_approvals.md). [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/353097) in GitLab 16.0. Premium and Ultimate only. |
| `allow_maintainer_to_push` | boolean | No | Alias of `allow_collaboration`. |
| `assignee_id` | integer | No | Assignee user ID. |
| `assignee_ids` | integer array | No | The ID of the users to assign the merge request to. Set to `0` or provide an empty value to unassign all assignees. |
diff --git a/doc/api/project_clusters.md b/doc/api/project_clusters.md
index 1427b67a224..8909b6c473d 100644
--- a/doc/api/project_clusters.md
+++ b/doc/api/project_clusters.md
@@ -201,7 +201,7 @@ Parameters:
| `platform_kubernetes_attributes[ca_cert]` | string | no | TLS certificate. Required if API is using a self-signed TLS certificate. |
| `platform_kubernetes_attributes[namespace]` | string | no | The unique namespace related to the project |
| `platform_kubernetes_attributes[authorization_type]` | string | no | The cluster authorization type: `rbac`, `abac` or `unknown_authorization`. Defaults to `rbac`. |
-| `environment_scope` | string | no | The associated environment to the cluster. Defaults to `*` **(PREMIUM ALL)** |
+| `environment_scope` | string | no | The associated environment to the cluster. Defaults to `*`. Premium and Ultimate only. |
Example request:
diff --git a/doc/api/project_templates.md b/doc/api/project_templates.md
index 3960dba3d76..3585e3574b0 100644
--- a/doc/api/project_templates.md
+++ b/doc/api/project_templates.md
@@ -20,7 +20,7 @@ It deprecates these endpoints, which are scheduled for removal in API version 5.
In addition to templates common to the entire instance, project-specific
templates are also available from this API endpoint.
-Support is also available for [group-level file templates](../user/group/manage.md#group-file-templates). **(PREMIUM ALL)**
+Support is also available for [group-level file templates](../user/group/manage.md#group-file-templates).
## Get all templates of a particular type
diff --git a/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md b/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md
index 5dea1090507..704f7b038cb 100644
--- a/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md
+++ b/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md
@@ -318,11 +318,11 @@ This process can take up to 40 minutes.
With this strategy, we successfully acquired a lock on `ci_builds` table after 15 retries
during a low traffic period([after `00:00 UTC`](https://dashboards.gitlab.net/d/web-main/web-overview?orgId=1&viewPanel=537181794&from=now-2d&to=now)).
-See an example of this strategy in our [partition tooling](../../../development/database/table_partitioning.md#step-6---create-parent-table-and-attach-existing-table-as-the-initial-partition)).
+See an example of this strategy in our [partition tooling](../../../development/database/partitioning/list.md#step-6---create-parent-table-and-attach-existing-table-as-the-initial-partition)).
### Partitioning steps
-The database [partition tooling](../../../development/database/table_partitioning.md#partitioning-a-table-list)
+The database [partition tooling](../../../development/database/partitioning/list.md)
docs contain a list of steps to partition a table, but the steps are not enough
for our iterative strategy. As our dataset continues to grow we want to take
advantage of partitioning performance right away and not wait until all tables
diff --git a/doc/development/database/partitioning/date_range.md b/doc/development/database/partitioning/date_range.md
new file mode 100644
index 00000000000..5c68eb3d075
--- /dev/null
+++ b/doc/development/database/partitioning/date_range.md
@@ -0,0 +1,237 @@
+---
+stage: Data Stores
+group: Database
+info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review.
+---
+
+# Date range partitioning
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/32240) in GitLab 13.1.
+
+## Description
+
+The scheme best supported by the GitLab migration helpers is date-range partitioning,
+where each partition in the table contains data for a single month. In this case,
+the partitioning key must be a timestamp or date column. For this type of
+partitioning to work well, most queries must access data in a
+certain date range.
+
+For a more concrete example, consider using the `audit_events` table.
+It was the first table to be partitioned in the application database
+(scheduled for deployment with the GitLab 13.5 release). This
+table tracks audit entries of security events that happen in the
+application. In almost all cases, users want to see audit activity that
+occurs in a certain time frame. As a result, date-range partitioning
+was a natural fit for how the data would be accessed.
+
+To look at this in more detail, imagine a simplified `audit_events` schema:
+
+```sql
+CREATE TABLE audit_events (
+ id SERIAL NOT NULL PRIMARY KEY,
+ author_id INT NOT NULL,
+ details jsonb NOT NULL,
+ created_at timestamptz NOT NULL);
+```
+
+Now imagine typical queries in the UI would display the data in a
+certain date range, like a single week:
+
+```sql
+SELECT *
+FROM audit_events
+WHERE created_at >= '2020-01-01 00:00:00'
+ AND created_at < '2020-01-08 00:00:00'
+ORDER BY created_at DESC
+LIMIT 100
+```
+
+If the table is partitioned on the `created_at` column the base table would
+look like:
+
+```sql
+CREATE TABLE audit_events (
+ id SERIAL NOT NULL,
+ author_id INT NOT NULL,
+ details jsonb NOT NULL,
+ created_at timestamptz NOT NULL,
+ PRIMARY KEY (id, created_at))
+PARTITION BY RANGE(created_at);
+```
+
+NOTE:
+The primary key of a partitioned table must include the partition key as
+part of the primary key definition.
+
+And we might have a list of partitions for the table, such as:
+
+```sql
+audit_events_202001 FOR VALUES FROM ('2020-01-01') TO ('2020-02-01')
+audit_events_202002 FOR VALUES FROM ('2020-02-01') TO ('2020-03-01')
+audit_events_202003 FOR VALUES FROM ('2020-03-01') TO ('2020-04-01')
+```
+
+Each partition is a separate physical table, with the same structure as
+the base `audit_events` table, but contains only data for rows where the
+partition key falls in the specified range. For example, the partition
+`audit_events_202001` contains rows where the `created_at` column is
+greater than or equal to `2020-01-01` and less than `2020-02-01`.
+
+Now, if we look at the previous example query again, the database can
+use the `WHERE` to recognize that all matching rows are in the
+`audit_events_202001` partition. Rather than searching all of the data
+in all of the partitions, it can search only the single month's worth
+of data in the appropriate partition. In a large table, this can
+dramatically reduce the amount of data the database needs to access.
+However, imagine a query that does not filter based on the partitioning
+key, such as:
+
+```sql
+SELECT *
+FROM audit_events
+WHERE author_id = 123
+ORDER BY created_at DESC
+LIMIT 100
+```
+
+In this example, the database can't prune any partitions from the search,
+because matching data could exist in any of them. As a result, it has to
+query each partition individually, and aggregate the rows into a single result
+set. Because `author_id` would be indexed, the performance impact could
+likely be acceptable, but on more complex queries the overhead can be
+substantial. Partitioning should only be leveraged if the access patterns
+of the data support the partitioning strategy, otherwise performance
+suffers.
+
+## Example
+
+### Step 1: Creating the partitioned copy (Release N)
+
+The first step is to add a migration to create the partitioned copy of
+the original table. This migration creates the appropriate
+partitions based on the data in the original table, and install a
+trigger that syncs writes from the original table into the
+partitioned copy.
+
+An example migration of partitioning the `audit_events` table by its
+`created_at` column would look like:
+
+```ruby
+class PartitionAuditEvents < Gitlab::Database::Migration[2.1]
+ include Gitlab::Database::PartitioningMigrationHelpers
+
+ def up
+ partition_table_by_date :audit_events, :created_at
+ end
+
+ def down
+ drop_partitioned_table_for :audit_events
+ end
+end
+```
+
+After this has executed, any inserts, updates, or deletes in the
+original table are also duplicated in the new table. For updates and
+deletes, the operation only has an effect if the corresponding row
+exists in the partitioned table.
+
+### Step 2: Backfill the partitioned copy (Release N)
+
+The second step is to add a post-deployment migration that schedules
+the background jobs that backfill existing data from the original table
+into the partitioned copy.
+
+Continuing the above example, the migration would look like:
+
+```ruby
+class BackfillPartitionAuditEvents < Gitlab::Database::Migration[2.1]
+ include Gitlab::Database::PartitioningMigrationHelpers
+
+ disable_ddl_transaction!
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ def up
+ enqueue_partitioning_data_migration :audit_events
+ end
+
+ def down
+ cleanup_partitioning_data_migration :audit_events
+ end
+end
+```
+
+This step [queues a batched background migration](../batched_background_migrations.md#enqueue-a-batched-background-migration) internally with BATCH_SIZE and SUB_BATCH_SIZE as `50,000` and `2,500`. Refer [Batched Background migrations guide](../batched_background_migrations.md) for more details.
+
+### Step 3: Post-backfill cleanup (Release N+1)
+
+This step must occur at least one release after the release that
+includes step (2). This gives time for the background
+migration to execute properly in self-managed installations. In this step,
+add another post-deployment migration that cleans up after the
+background migration. This includes forcing any remaining jobs to
+execute, and copying data that may have been missed, due to dropped or
+failed jobs.
+
+WARNING:
+A required stop must occur between steps 2 and 3 to allow the background migration from step 2 to complete successfully.
+
+Once again, continuing the example, this migration would look like:
+
+```ruby
+class CleanupPartitionedAuditEventsBackfill < Gitlab::Database::Migration[2.1]
+ include Gitlab::Database::PartitioningMigrationHelpers
+
+ disable_ddl_transaction!
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ def up
+ finalize_backfilling_partitioned_table :audit_events
+ end
+
+ def down
+ # no op
+ end
+end
+```
+
+After this migration completes, the original table and partitioned
+table should contain identical data. The trigger installed on the
+original table guarantees that the data remains in sync going forward.
+
+### Step 4: Swap the partitioned and non-partitioned tables (Release N+1)
+
+This step replaces the non-partitioned table with its partitioned copy, this should be used only after all other migration steps have completed successfully.
+
+Some limitations to this method MUST be handled before, or during, the swap migration:
+
+- Secondary indexes and foreign keys are not automatically recreated on the partitioned table.
+- Some types of constraints (UNIQUE and EXCLUDE) which rely on indexes, are not automatically recreated
+ on the partitioned table, since the underlying index will not be present.
+- Foreign keys referencing the original non-partitioned table should be updated to reference the
+ partitioned table. This is not supported in PostgreSQL 11.
+- Views referencing the original table are not automatically updated to reference the partitioned table.
+
+```ruby
+# frozen_string_literal: true
+
+class SwapPartitionedAuditEvents < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::PartitioningMigrationHelpers
+
+ def up
+ replace_with_partitioned_table :audit_events
+ end
+
+ def down
+ rollback_replace_with_partitioned_table :audit_events
+ end
+end
+```
+
+After this migration completes:
+
+- The partitioned table replaces the non-partitioned (original) table.
+- The sync trigger created earlier is dropped.
+
+The partitioned table is now ready for use by the application.
diff --git a/doc/development/database/partitioning/hash.md b/doc/development/database/partitioning/hash.md
new file mode 100644
index 00000000000..8852097d89a
--- /dev/null
+++ b/doc/development/database/partitioning/hash.md
@@ -0,0 +1,35 @@
+---
+stage: Data Stores
+group: Database
+info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review.
+---
+
+# Hash Partitioning
+
+Hash partitioning is a method of dividing a large table into smaller, more manageable partitions based on a hash function applied to a specified column, typically the ID column. It offers unique advantages for certain use cases, but it also comes with limitations.
+
+Key points:
+
+- Data distribution: Rows are assigned to partitions based on the hash value of their ID and a modulus-remainder calculation.
+ For example, if partitioning by `HASH(ID)` with `MODULUS 64` and `REMAINDER 1`, rows with `hash(ID) % 64 == 1` would go into the corresponding partition.
+
+- Query requirements: Hash partitioning works best when most queries include a `WHERE hashed_column = ?` condition,
+ as this allows PostgreSQL to quickly identify the relevant partition.
+
+- ID uniqueness: It's the only partitioning method (aside from complex list partitioning) that can guarantee ID uniqueness across multiple partitions at the database level.
+
+Upfront decisions:
+
+- The number of partitions must be chosen before table creation and cannot be easily added later. This makes it crucial to anticipate future data growth.
+
+Unsupported query types:
+
+- Range queries `(WHERE id BETWEEN ? AND ?)` and lookups by other keys `(WHERE other_id = ?)` are not directly supported on hash-partitioned tables.
+
+Considerations:
+
+- Choose a large number of partitions to accommodate future growth.
+- Ensure application queries align with hash partitioning requirements.
+- Evaluate alternatives like range partitioning or list partitioning if range queries or lookups by other keys are essential.
+
+In summary, hash partitioning is a valuable tool for specific scenarios, particularly when ID uniqueness across partitions is crucial. However, it's essential to carefully consider its limitations and query patterns before implementation.
diff --git a/doc/development/database/partitioning/index.md b/doc/development/database/partitioning/index.md
new file mode 100644
index 00000000000..78b0c069705
--- /dev/null
+++ b/doc/development/database/partitioning/index.md
@@ -0,0 +1,71 @@
+---
+stage: Data Stores
+group: Database
+info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review.
+---
+
+# Database table partitioning
+
+WARNING:
+If you have questions not answered below, check for and add them
+to [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/398650).
+Tag `@gitlab-org/database-team/triage` and we'll get back to you with an
+answer as soon as possible. If you get an answer in Slack, document
+it on the issue as well so we can update this document in the future.
+
+Table partitioning is a powerful database feature that allows a table's
+data to be split into smaller physical tables that act as a single large
+table. If the application is designed to work with partitioning in mind,
+there can be multiple benefits, such as:
+
+- Query performance can be improved greatly, because the database can
+ cheaply eliminate much of the data from the search space, while still
+ providing full SQL capabilities.
+
+- Bulk deletes can be achieved with minimal impact on the database by
+ dropping entire partitions. This is a natural fit for features that need
+ to periodically delete data that falls outside the retention window.
+
+- Administrative tasks like `VACUUM` and index rebuilds can operate on
+ individual partitions, rather than across a single massive table.
+
+Unfortunately, not all models fit a partitioning scheme, and there are
+significant drawbacks if implemented incorrectly. Additionally,
+**tables can only be partitioned at their creation**, making it nontrivial
+to apply partitioning to a busy database. A suite of migration tools are available
+to enable backend developers to partition existing tables, but the
+migration process is rather heavy, taking multiple steps split across
+several releases. Due to the limitations of partitioning and the related
+migrations, you should understand how partitioning fits your use case
+before attempting to leverage this feature.
+
+The partitioning migration helpers work by creating a partitioned duplicate
+of the original table and using a combination of a trigger and a background
+migration to copy data into the new table. Changes to the original table
+schema can be made in parallel with the partitioning migration, but they
+must take care to not break the underlying mechanism that makes the migration
+work. For example, if a column is added to the table that is being
+partitioned, both the partitioned table and the trigger definition must
+be updated to match.
+
+## Determine when to use partitioning
+
+While partitioning can be very useful when properly applied, it's
+imperative to identify if the data and workload of a table naturally fit a
+partitioning scheme. Understand a few details to decide if partitioning
+is a good fit for your particular problem:
+
+- **Table partitioning**. A table is partitioned on a partition key, which is a
+ column or set of columns which determine how the data is split across the
+ partitions. The partition key is used by the database when reading or
+ writing data, to decide which partitions must be accessed. The
+ partition key should be a column that would be included in a `WHERE`
+ clause on almost all queries accessing that table.
+
+- **How the data is split**. What strategy does the database use
+ to split the data across the partitions? The available choices are `range`,
+ `hash`, and `list`.
+
+## Determine the appropriate partitioning strategy
+
+The available partitioning strategy choices are `date range`, `int range`, `hash`, and `list`.
diff --git a/doc/development/database/partitioning/int_range.md b/doc/development/database/partitioning/int_range.md
new file mode 100644
index 00000000000..7fbdd4da865
--- /dev/null
+++ b/doc/development/database/partitioning/int_range.md
@@ -0,0 +1,205 @@
+---
+stage: Data Stores
+group: Database
+info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review.
+---
+
+# Int range partitioning
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132148) in GitLab 16.8.
+
+## Description
+
+Int range partition is a technique for dividing a large table into smaller,
+more manageable chunks based on an integer column.
+This can be particularly useful for tables with large numbers of rows,
+as it can significantly improve query performance, reduce storage requirements, and simplify maintenance tasks.
+For this type of partitioning to work well, most queries must access data in a
+certain int range.
+
+To look at this in more detail, imagine a simplified `merge_request_diff_files` schema:
+
+```sql
+CREATE TABLE merge_request_diff_files (
+ merge_request_diff_id INT NOT NULL,
+ relative_order INT NOT NULL,
+ PRIMARY KEY (merge_request_diff_id, relative_order));
+```
+
+Now imagine typical queries in the UI would display the data in a certain int range:
+
+```sql
+SELECT *
+FROM merge_request_diff_files
+WHERE merge_request_diff_id > 1 AND merge_request_diff_id < 10
+LIMIT 100
+```
+
+If the table is partitioned on the `merge_request_diff_id` column the base table would look like:
+
+```sql
+CREATE TABLE merge_request_diff_files (
+ merge_request_diff_id INT NOT NULL,
+ relative_order INT NOT NULL,
+ PRIMARY KEY (merge_request_diff_id, relative_order))
+PARTITION BY RANGE(merge_request_diff_id);
+```
+
+NOTE:
+The primary key of a partitioned table must include the partition key as
+part of the primary key definition.
+
+And we might have a list of partitions for the table, such as:
+
+```sql
+merge_request_diff_files_1 FOR VALUES FROM (1) TO (20)
+merge_request_diff_files_20 FOR VALUES FROM (20) TO (40)
+merge_request_diff_files_40 FOR VALUES FROM (40) TO (60)
+```
+
+Each partition is a separate physical table, with the same structure as
+the base `merge_request_diff_files` table, but contains only data for rows where the
+partition key falls in the specified range. For example, the partition
+`merge_request_diff_files_1` contains rows where the `merge_request_diff_id` column is
+greater than or equal to `1` and less than `20`.
+
+Now, if we look at the previous example query again, the database can
+use the `WHERE` to recognize that all matching rows are in the
+`merge_request_diff_files_1` partition. Rather than searching all of the data
+in all of the partitions. In a large table, this can
+dramatically reduce the amount of data the database needs to access.
+
+## Example
+
+### Step 1: Creating the partitioned copy (Release N)
+
+The first step is to add a migration to create the partitioned copy of
+the original table. This migration creates the appropriate
+partitions based on the data in the original table, and install a
+trigger that syncs writes from the original table into the
+partitioned copy.
+
+An example migration of partitioning the `merge_request_diff_commits` table by its
+`merge_request_diff_id` column would look like:
+
+```ruby
+class PartitionAuditEvents < Gitlab::Database::Migration[2.1]
+ include Gitlab::Database::PartitioningMigrationHelpers
+
+ def up
+ partition_table_by_int_range('merge_request_diff_commits', 'merge_request_diff_id', partition_size: 10000000,
+ primary_key: %w[merge_request_diff_id relative_order])
+
+ end
+
+ def down
+ drop_partitioned_table_for('merge_request_diff_commits')
+ end
+end
+```
+
+After this has executed, any inserts, updates, or deletes in the
+original table are also duplicated in the new table. For updates and
+deletes, the operation only has an effect if the corresponding row
+exists in the partitioned table.
+
+### Step 2: Backfill the partitioned copy (Release N)
+
+The second step is to add a post-deployment migration that schedules
+the background jobs that backfill existing data from the original table
+into the partitioned copy.
+
+Continuing the above example, the migration would look like:
+
+```ruby
+class BackfillPartitionAuditEvents < Gitlab::Database::Migration[2.1]
+ include Gitlab::Database::PartitioningMigrationHelpers
+
+ disable_ddl_transaction!
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ def up
+ enqueue_partitioning_data_migration :merge_request_diff_commits
+ end
+
+ def down
+ cleanup_partitioning_data_migration :merge_request_diff_commits
+ end
+end
+```
+
+This step [queues a batched background migration](../batched_background_migrations.md#enqueue-a-batched-background-migration) internally with BATCH_SIZE and SUB_BATCH_SIZE as `50,000` and `2,500`. Refer [Batched Background migrations guide](../batched_background_migrations.md) for more details.
+
+### Step 3: Post-backfill cleanup (Release N+1)
+
+This step must occur at least one release after the release that
+includes step (2). This gives time for the background
+migration to execute properly in self-managed installations. In this step,
+add another post-deployment migration that cleans up after the
+background migration. This includes forcing any remaining jobs to
+execute, and copying data that may have been missed, due to dropped or
+failed jobs.
+
+WARNING:
+A required stop must occur between steps 2 and 3 to allow the background migration from step 2 to complete successfully.
+
+Once again, continuing the example, this migration would look like:
+
+```ruby
+class CleanupPartitionedAuditEventsBackfill < Gitlab::Database::Migration[2.1]
+ include Gitlab::Database::PartitioningMigrationHelpers
+
+ disable_ddl_transaction!
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ def up
+ finalize_backfilling_partitioned_table :merge_request_diff_commits
+ end
+
+ def down
+ # no op
+ end
+end
+```
+
+After this migration completes, the original table and partitioned
+table should contain identical data. The trigger installed on the
+original table guarantees that the data remains in sync going forward.
+
+### Step 4: Swap the partitioned and non-partitioned tables (Release N+1)
+
+This step replaces the non-partitioned table with its partitioned copy, this should be used only after all other migration steps have completed successfully.
+
+Some limitations to this method MUST be handled before, or during, the swap migration:
+
+- Secondary indexes and foreign keys are not automatically recreated on the partitioned table.
+- Some types of constraints (UNIQUE and EXCLUDE) which rely on indexes, are not automatically recreated
+ on the partitioned table, since the underlying index will not be present.
+- Foreign keys referencing the original non-partitioned table should be updated to reference the
+ partitioned table. This is not supported in PostgreSQL 11.
+- Views referencing the original table are not automatically updated to reference the partitioned table.
+
+```ruby
+# frozen_string_literal: true
+
+class SwapPartitionedAuditEvents < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::PartitioningMigrationHelpers
+
+ def up
+ replace_with_partitioned_table :audit_events
+ end
+
+ def down
+ rollback_replace_with_partitioned_table :audit_events
+ end
+end
+```
+
+After this migration completes:
+
+- The partitioned table replaces the non-partitioned (original) table.
+- The sync trigger created earlier is dropped.
+
+The partitioned table is now ready for use by the application.
diff --git a/doc/development/database/partitioning/list.md b/doc/development/database/partitioning/list.md
new file mode 100644
index 00000000000..74ce5a00f6a
--- /dev/null
+++ b/doc/development/database/partitioning/list.md
@@ -0,0 +1,372 @@
+---
+stage: Data Stores
+group: Database
+info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review.
+---
+
+# List partition
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96815) in GitLab 15.4.
+
+## Description
+
+Add the partitioning key column to the table you are partitioning.
+Include the partitioning key in the following constraints:
+
+- The primary key.
+- All foreign keys referencing the table to be partitioned.
+- All unique constraints.
+
+## Example
+
+### Step 1 - Add partition key
+
+Add the partitioning key column. For example, in a rails migration:
+
+```ruby
+class AddPartitionNumberForPartitioning < Gitlab::Database::Migration[2.1]
+ enable_lock_retries!
+
+ TABLE_NAME = :table_name
+ COLUMN_NAME = :partition_id
+ DEFAULT_VALUE = 100
+
+ def change
+ add_column(TABLE_NAME, COLUMN_NAME, :bigint, default: 100)
+ end
+end
+```
+
+### Step 2 - Create required indexes
+
+Add indexes including the partitioning key column. For example, in a rails migration:
+
+```ruby
+class PrepareIndexesForPartitioning < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ TABLE_NAME = :table_name
+ INDEX_NAME = :index_name
+
+ def up
+ add_concurrent_index(TABLE_NAME, [:id, :partition_id], unique: true, name: INDEX_NAME)
+ end
+
+ def down
+ remove_concurrent_index_by_name(TABLE_NAME, INDEX_NAME)
+ end
+end
+```
+
+### Step 3 - Enforce unique constraint
+
+Change all unique indexes to include the partitioning key column,
+including the primary key index. You can start by adding an unique
+index on `[primary_key_column, :partition_id]`, which will be
+required for the next two steps. For example, in a rails migration:
+
+```ruby
+class PrepareUniqueContraintForPartitioning < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ TABLE_NAME = :table_name
+ OLD_UNIQUE_INDEX_NAME = :index_name_unique
+ NEW_UNIQUE_INDEX_NAME = :new_index_name
+
+ def up
+ add_concurrent_index(TABLE_NAME, [:id, :partition_id], unique: true, name: NEW_UNIQUE_INDEX_NAME)
+
+ remove_concurrent_index_by_name(TABLE_NAME, OLD_UNIQUE_INDEX_NAME)
+ end
+
+ def down
+ add_concurrent_index(TABLE_NAME, :id, unique: true, name: OLD_UNIQUE_INDEX_NAME)
+
+ remove_concurrent_index_by_name(TABLE_NAME, NEW_UNIQUE_INDEX_NAME)
+ end
+end
+```
+
+### Step 4 - Enforce foreign key constraint
+
+Enforce foreign keys including the partitioning key column. For example, in a rails migration:
+
+```ruby
+class PrepareForeignKeyForPartitioning < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ SOURCE_TABLE_NAME = :source_table_name
+ TARGET_TABLE_NAME = :target_table_name
+ COLUMN = :foreign_key_id
+ TARGET_COLUMN = :id
+ FK_NAME = :fk_365d1db505_p
+ PARTITION_COLUMN = :partition_id
+
+ def up
+ add_concurrent_foreign_key(
+ SOURCE_TABLE_NAME,
+ TARGET_TABLE_NAME,
+ column: [PARTITION_COLUMN, COLUMN],
+ target_column: [PARTITION_COLUMN, TARGET_COLUMN],
+ validate: false,
+ on_update: :cascade,
+ name: FK_NAME
+ )
+
+ # This should be done in a separate post migration when dealing with a high traffic table
+ validate_foreign_key(TABLE_NAME, [PARTITION_COLUMN, COLUMN], name: FK_NAME)
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key_if_exists(SOURCE_TABLE_NAME, name: FK_NAME)
+ end
+ end
+end
+```
+
+The `on_update: :cascade` option is mandatory if we want the partitioning column
+to be updated. This will cascade the update to all dependent rows. Without
+specifying it, updating the partition column on the target table we would
+result in a `Key is still referenced from table ...` error and updating the
+partition column on the source table would raise a
+`Key is not present in table ...` error.
+
+This migration can be automatically generated using:
+
+```shell
+./scripts/partitioning/generate-fk --source source_table_name --target target_table_name
+```
+
+### Step 5 - Swap primary key
+
+Swap the primary key including the partitioning key column. This can be done only after
+including the partition key for all references foreign keys. For example, in a rails migration:
+
+```ruby
+class PreparePrimaryKeyForPartitioning < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ TABLE_NAME = :table_name
+ PRIMARY_KEY = :primary_key
+ OLD_INDEX_NAME = :old_index_name
+ NEW_INDEX_NAME = :new_index_name
+
+ def up
+ swap_primary_key(TABLE_NAME, PRIMARY_KEY, NEW_INDEX_NAME)
+ end
+
+ def down
+ add_concurrent_index(TABLE_NAME, :id, unique: true, name: OLD_INDEX_NAME)
+ add_concurrent_index(TABLE_NAME, [:id, :partition_id], unique: true, name: NEW_INDEX_NAME)
+
+ unswap_primary_key(TABLE_NAME, PRIMARY_KEY, OLD_INDEX_NAME)
+ end
+end
+```
+
+NOTE:
+Do not forget to set the primary key explicitly in your model as `ActiveRecord` does not support composite primary keys.
+
+```ruby
+class Model < ApplicationRecord
+ self.primary_key = :id
+end
+```
+
+### Step 6 - Create parent table and attach existing table as the initial partition
+
+You can now create the parent table attaching the existing table as the initial
+partition by using the following helpers provided by the database team.
+
+For example, using list partitioning in Rails post migrations:
+
+```ruby
+class PrepareTableConstraintsForListPartitioning < Gitlab::Database::Migration[2.1]
+ include Gitlab::Database::PartitioningMigrationHelpers::TableManagementHelpers
+
+ disable_ddl_transaction!
+
+ TABLE_NAME = :table_name
+ PARENT_TABLE_NAME = :p_table_name
+ FIRST_PARTITION = 100
+ PARTITION_COLUMN = :partition_id
+
+ def up
+ prepare_constraint_for_list_partitioning(
+ table_name: TABLE_NAME,
+ partitioning_column: PARTITION_COLUMN,
+ parent_table_name: PARENT_TABLE_NAME,
+ initial_partitioning_value: FIRST_PARTITION
+ )
+ end
+
+ def down
+ revert_preparing_constraint_for_list_partitioning(
+ table_name: TABLE_NAME,
+ partitioning_column: PARTITION_COLUMN,
+ parent_table_name: PARENT_TABLE_NAME,
+ initial_partitioning_value: FIRST_PARTITION
+ )
+ end
+end
+```
+
+```ruby
+class ConvertTableToListPartitioning < Gitlab::Database::Migration[2.1]
+ include Gitlab::Database::PartitioningMigrationHelpers::TableManagementHelpers
+
+ disable_ddl_transaction!
+
+ TABLE_NAME = :table_name
+ TABLE_FK = :table_references_by_fk
+ PARENT_TABLE_NAME = :p_table_name
+ FIRST_PARTITION = 100
+ PARTITION_COLUMN = :partition_id
+
+ def up
+ convert_table_to_first_list_partition(
+ table_name: TABLE_NAME,
+ partitioning_column: PARTITION_COLUMN,
+ parent_table_name: PARENT_TABLE_NAME,
+ initial_partitioning_value: FIRST_PARTITION,
+ lock_tables: [TABLE_FK, TABLE_NAME]
+ )
+ end
+
+ def down
+ revert_converting_table_to_first_list_partition(
+ table_name: TABLE_NAME,
+ partitioning_column: PARTITION_COLUMN,
+ parent_table_name: PARENT_TABLE_NAME,
+ initial_partitioning_value: FIRST_PARTITION
+ )
+ end
+end
+```
+
+NOTE:
+Do not forget to set the sequence name explicitly in your model because it will
+be owned by the routing table and `ActiveRecord` can't determine it. This can
+be cleaned up after the `table_name` is changed to the routing table.
+
+```ruby
+class Model < ApplicationRecord
+ self.sequence_name = 'model_id_seq'
+end
+```
+
+If the partitioning constraint migration takes [more than 10 minutes](../../migration_style_guide.md#how-long-a-migration-should-take) to finish,
+it can be made to run asynchronously to avoid running the post-migration during busy hours.
+
+Prepend the following migration `AsyncPrepareTableConstraintsForListPartitioning`
+and use `async: true` option. This change marks the partitioning constraint as `NOT VALID`
+and enqueues a scheduled job to validate the existing data in the table during the weekend.
+
+Then the second post-migration `PrepareTableConstraintsForListPartitioning` only
+marks the partitioning constraint as validated, because the existing data is already
+tested during the previous weekend.
+
+For example:
+
+```ruby
+class AsyncPrepareTableConstraintsForListPartitioning < Gitlab::Database::Migration[2.1]
+ include Gitlab::Database::PartitioningMigrationHelpers::TableManagementHelpers
+
+ disable_ddl_transaction!
+
+ TABLE_NAME = :table_name
+ PARENT_TABLE_NAME = :p_table_name
+ FIRST_PARTITION = 100
+ PARTITION_COLUMN = :partition_id
+
+ def up
+ prepare_constraint_for_list_partitioning(
+ table_name: TABLE_NAME,
+ partitioning_column: PARTITION_COLUMN,
+ parent_table_name: PARENT_TABLE_NAME,
+ initial_partitioning_value: FIRST_PARTITION,
+ async: true
+ )
+ end
+
+ def down
+ revert_preparing_constraint_for_list_partitioning(
+ table_name: TABLE_NAME,
+ partitioning_column: PARTITION_COLUMN,
+ parent_table_name: PARENT_TABLE_NAME,
+ initial_partitioning_value: FIRST_PARTITION
+ )
+ end
+end
+```
+
+### Step 7 - Re-point foreign keys to parent table
+
+The tables that reference the initial partition must be updated to point to the
+parent table now. Without this change, the records from those tables
+will not be able to locate the rows in the next partitions because they will look
+for them in the initial partition.
+
+Steps:
+
+- Add the foreign key to the partitioned table and validate it asynchronously,
+ [for example](https://gitlab.com/gitlab-org/gitlab/-/blob/65d63f6a00196c3a7d59f15191920f271ab2b145/db/post_migrate/20230524135543_replace_ci_build_pending_states_foreign_key.rb).
+- Validate it synchronously after the asynchronously validation was completed on GitLab.com,
+ [for example](https://gitlab.com/gitlab-org/gitlab/-/blob/65d63f6a00196c3a7d59f15191920f271ab2b145/db/post_migrate/20230530140456_validate_fk_ci_build_pending_states_p_ci_builds.rb).
+- Remove the old foreign key and rename the new one to the old name,
+ [for example](https://gitlab.com/gitlab-org/gitlab/-/blob/65d63f6a00196c3a7d59f15191920f271ab2b145/db/post_migrate/20230615083713_replace_old_fk_ci_build_pending_states_to_builds.rb#L9).
+
+### Step 8 - Ensure ID uniqueness across partitions
+
+All uniqueness constraints must include the partitioning key, so we can have
+duplicate IDs across partitions. To solve this we enforce that only the database
+can set the ID values and use a sequence to generate them because sequences are
+guaranteed to generate unique values.
+
+For example:
+
+```ruby
+class EnsureIdUniquenessForPCiBuilds < Gitlab::Database::Migration[2.1]
+ include Gitlab::Database::PartitioningMigrationHelpers::UniquenessHelpers
+
+ enable_lock_retries!
+
+ TABLE_NAME = :p_ci_builds
+ FUNCTION_NAME = :assign_p_ci_builds_id_value
+
+ def up
+ ensure_unique_id(TABLE_NAME)
+ end
+
+ def down
+ execute(<<~SQL.squish)
+ ALTER TABLE #{TABLE_NAME}
+ ALTER COLUMN id SET DEFAULT nextval('ci_builds_id_seq'::regclass);
+
+ DROP FUNCTION IF EXISTS #{FUNCTION_NAME} CASCADE;
+ SQL
+ end
+```
+
+### Step 9 - Analyze the partitioned table and create new partitions
+
+The autovacuum daemon does not process partitioned tables. It is necessary to
+periodically run a manual `ANALYZE` to keep the statistics of the table hierarchy
+up to date.
+
+Models that implement `Ci::Partitionable` with `partitioned: true` option are
+analyzed by default on a weekly basis. To enable this and create new partitions
+you need to register the model in the [PostgreSQL initializer](https://gitlab.com/gitlab-org/gitlab/-/blob/b7f0e3f1bcd2ffc220768bbc373364151775ca8e/config/initializers/postgres_partitioning.rb).
+
+### Step 10 - Update the application to use the partitioned table
+
+Now that the parent table is ready, we can update the application to use it:
+
+```ruby
+class Model < ApplicationRecord
+ self.table_name = :partitioned_table
+end
+```
+
+Depending on the model, it might be safer to use a [change management issue](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/16387).
diff --git a/doc/development/database/table_partitioning.md b/doc/development/database/table_partitioning.md
index cb159a404fd..b82bb11f662 100644
--- a/doc/development/database/table_partitioning.md
+++ b/doc/development/database/table_partitioning.md
@@ -1,704 +1,11 @@
---
-stage: Data Stores
-group: Database
-info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review.
+redirect_to: 'partitioning/index.md'
+remove_date: '2024-04-16'
---
-# Database table partitioning
+This document was moved to [another location](partitioning/index.md).
-WARNING:
-If you have questions not answered below, check for and add them
-to [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/398650).
-Tag `@gitlab-org/database-team/triage` and we'll get back to you with an
-answer as soon as possible. If you get an answer in Slack, document
-it on the issue as well so we can update this document in the future.
-
-Table partitioning is a powerful database feature that allows a table's
-data to be split into smaller physical tables that act as a single large
-table. If the application is designed to work with partitioning in mind,
-there can be multiple benefits, such as:
-
-- Query performance can be improved greatly, because the database can
- cheaply eliminate much of the data from the search space, while still
- providing full SQL capabilities.
-
-- Bulk deletes can be achieved with minimal impact on the database by
- dropping entire partitions. This is a natural fit for features that need
- to periodically delete data that falls outside the retention window.
-
-- Administrative tasks like `VACUUM` and index rebuilds can operate on
- individual partitions, rather than across a single massive table.
-
-Unfortunately, not all models fit a partitioning scheme, and there are
-significant drawbacks if implemented incorrectly. Additionally, tables
-can only be partitioned at their creation, making it nontrivial to apply
-partitioning to a busy database. A suite of migration tools are available
-to enable backend developers to partition existing tables, but the
-migration process is rather heavy, taking multiple steps split across
-several releases. Due to the limitations of partitioning and the related
-migrations, you should understand how partitioning fits your use case
-before attempting to leverage this feature.
-
-## Determine when to use partitioning
-
-While partitioning can be very useful when properly applied, it's
-imperative to identify if the data and workload of a table naturally fit a
-partitioning scheme. Understand a few details to decide if partitioning
-is a good fit for your particular problem:
-
-- **Table partitioning**. A table is partitioned on a partition key, which is a
- column or set of columns which determine how the data is split across the
- partitions. The partition key is used by the database when reading or
- writing data, to decide which partitions must be accessed. The
- partition key should be a column that would be included in a `WHERE`
- clause on almost all queries accessing that table.
-
-- **How the data is split**. What strategy does the database use
- to split the data across the partitions? The available choices are `range`,
- `hash`, and `list`.
-
-## Determine the appropriate partitioning strategy
-
-The available partitioning strategy choices are `range`, `hash`, and `list`.
-
-### Range partitioning
-
-The scheme best supported by the GitLab migration helpers is date-range partitioning,
-where each partition in the table contains data for a single month. In this case,
-the partitioning key must be a timestamp or date column. For this type of
-partitioning to work well, most queries must access data in a
-certain date range.
-
-For a more concrete example, consider using the `audit_events` table.
-It was the first table to be partitioned in the application database
-(scheduled for deployment with the GitLab 13.5 release). This
-table tracks audit entries of security events that happen in the
-application. In almost all cases, users want to see audit activity that
-occurs in a certain time frame. As a result, date-range partitioning
-was a natural fit for how the data would be accessed.
-
-To look at this in more detail, imagine a simplified `audit_events` schema:
-
-```sql
-CREATE TABLE audit_events (
- id SERIAL NOT NULL PRIMARY KEY,
- author_id INT NOT NULL,
- details jsonb NOT NULL,
- created_at timestamptz NOT NULL);
-```
-
-Now imagine typical queries in the UI would display the data in a
-certain date range, like a single week:
-
-```sql
-SELECT *
-FROM audit_events
-WHERE created_at >= '2020-01-01 00:00:00'
- AND created_at < '2020-01-08 00:00:00'
-ORDER BY created_at DESC
-LIMIT 100
-```
-
-If the table is partitioned on the `created_at` column the base table would
-look like:
-
-```sql
-CREATE TABLE audit_events (
- id SERIAL NOT NULL,
- author_id INT NOT NULL,
- details jsonb NOT NULL,
- created_at timestamptz NOT NULL,
- PRIMARY KEY (id, created_at))
-PARTITION BY RANGE(created_at);
-```
-
-NOTE:
-The primary key of a partitioned table must include the partition key as
-part of the primary key definition.
-
-And we might have a list of partitions for the table, such as:
-
-```sql
-audit_events_202001 FOR VALUES FROM ('2020-01-01') TO ('2020-02-01')
-audit_events_202002 FOR VALUES FROM ('2020-02-01') TO ('2020-03-01')
-audit_events_202003 FOR VALUES FROM ('2020-03-01') TO ('2020-04-01')
-```
-
-Each partition is a separate physical table, with the same structure as
-the base `audit_events` table, but contains only data for rows where the
-partition key falls in the specified range. For example, the partition
-`audit_events_202001` contains rows where the `created_at` column is
-greater than or equal to `2020-01-01` and less than `2020-02-01`.
-
-Now, if we look at the previous example query again, the database can
-use the `WHERE` to recognize that all matching rows are in the
-`audit_events_202001` partition. Rather than searching all of the data
-in all of the partitions, it can search only the single month's worth
-of data in the appropriate partition. In a large table, this can
-dramatically reduce the amount of data the database needs to access.
-However, imagine a query that does not filter based on the partitioning
-key, such as:
-
-```sql
-SELECT *
-FROM audit_events
-WHERE author_id = 123
-ORDER BY created_at DESC
-LIMIT 100
-```
-
-In this example, the database can't prune any partitions from the search,
-because matching data could exist in any of them. As a result, it has to
-query each partition individually, and aggregate the rows into a single result
-set. Because `author_id` would be indexed, the performance impact could
-likely be acceptable, but on more complex queries the overhead can be
-substantial. Partitioning should only be leveraged if the access patterns
-of the data support the partitioning strategy, otherwise performance
-suffers.
-
-### Hash Partitioning
-
-Hash partitioning splits a logical table into a series of partitioned
-tables. Each partition corresponds to the ID range that matches
-a hash and remainder. For example, if partitioning `BY HASH(id)`, rows
-with `hash(id) % 64 == 1` would end up in the partition
-`WITH (MODULUS 64, REMAINDER 1)`.
-
-When hash partitioning, you must include a `WHERE hashed_column = ?` condition in
-every performance-sensitive query issued by the application. If this is not possible,
-hash partitioning may not be the correct fit for your use case.
-
-Hash partitioning has one main advantage: it is the only type of partitioning that
-can enforce uniqueness on a single numeric `id` column. (While also possible with
-range partitioning, it's rarely the correct choice).
-
-Hash partitioning has downsides:
-
-- The number of partitions must be known up-front.
-- It's difficult to move new data to an extra partition if current partitions become too large.
-- Range queries, such as `WHERE id BETWEEN ? and ?`, are unsupported.
-- Lookups by other keys, such as `WHERE other_id = ?`, are unsupported.
-
-For this reason, it's often best to choose a large number of hash partitions to accommodate future table growth.
-
-## Partitioning a table (Range)
-
-Unfortunately, tables can only be partitioned at their creation, making
-it nontrivial to apply to a busy database. A suite of migration
-tools have been developed to enable backend developers to partition
-existing tables. This migration process takes multiple steps which must
-be split across several releases.
-
-### Caveats
-
-The partitioning migration helpers work by creating a partitioned duplicate
-of the original table and using a combination of a trigger and a background
-migration to copy data into the new table. Changes to the original table
-schema can be made in parallel with the partitioning migration, but they
-must take care to not break the underlying mechanism that makes the migration
-work. For example, if a column is added to the table that is being
-partitioned, both the partitioned table and the trigger definition must
-be updated to match.
-
-### Step 1: Creating the partitioned copy (Release N)
-
-The first step is to add a migration to create the partitioned copy of
-the original table. This migration creates the appropriate
-partitions based on the data in the original table, and install a
-trigger that syncs writes from the original table into the
-partitioned copy.
-
-An example migration of partitioning the `audit_events` table by its
-`created_at` column would look like:
-
-```ruby
-class PartitionAuditEvents < Gitlab::Database::Migration[2.1]
- include Gitlab::Database::PartitioningMigrationHelpers
-
- def up
- partition_table_by_date :audit_events, :created_at
- end
-
- def down
- drop_partitioned_table_for :audit_events
- end
-end
-```
-
-After this has executed, any inserts, updates, or deletes in the
-original table are also duplicated in the new table. For updates and
-deletes, the operation only has an effect if the corresponding row
-exists in the partitioned table.
-
-### Step 2: Backfill the partitioned copy (Release N)
-
-The second step is to add a post-deployment migration that schedules
-the background jobs that backfill existing data from the original table
-into the partitioned copy.
-
-Continuing the above example, the migration would look like:
-
-```ruby
-class BackfillPartitionAuditEvents < Gitlab::Database::Migration[2.1]
- include Gitlab::Database::PartitioningMigrationHelpers
-
- disable_ddl_transaction!
-
- restrict_gitlab_migration gitlab_schema: :gitlab_main
-
- def up
- enqueue_partitioning_data_migration :audit_events
- end
-
- def down
- cleanup_partitioning_data_migration :audit_events
- end
-end
-```
-
-This step [queues a batched background migration](batched_background_migrations.md#enqueue-a-batched-background-migration) internally with BATCH_SIZE and SUB_BATCH_SIZE as `50,000` and `2,500`. Refer [Batched Background migrations guide](batched_background_migrations.md) for more details.
-
-### Step 3: Post-backfill cleanup (Release N+1)
-
-This step must occur at least one release after the release that
-includes step (2). This gives time for the background
-migration to execute properly in self-managed installations. In this step,
-add another post-deployment migration that cleans up after the
-background migration. This includes forcing any remaining jobs to
-execute, and copying data that may have been missed, due to dropped or
-failed jobs.
-
-Once again, continuing the example, this migration would look like:
-
-```ruby
-class CleanupPartitionedAuditEventsBackfill < Gitlab::Database::Migration[2.1]
- include Gitlab::Database::PartitioningMigrationHelpers
-
- disable_ddl_transaction!
-
- restrict_gitlab_migration gitlab_schema: :gitlab_main
-
- def up
- finalize_backfilling_partitioned_table :audit_events
- end
-
- def down
- # no op
- end
-end
-```
-
-After this migration completes, the original table and partitioned
-table should contain identical data. The trigger installed on the
-original table guarantees that the data remains in sync going forward.
-
-### Step 4: Swap the partitioned and non-partitioned tables (Release N+1)
-
-This step replaces the non-partitioned table with its partitioned copy, this should be used only after all other migration steps have completed successfully.
-
-Some limitations to this method MUST be handled before, or during, the swap migration:
-
-- Secondary indexes and foreign keys are not automatically recreated on the partitioned table.
-- Some types of constraints (UNIQUE and EXCLUDE) which rely on indexes, are not automatically recreated
- on the partitioned table, since the underlying index will not be present.
-- Foreign keys referencing the original non-partitioned table should be updated to reference the
- partitioned table. This is not supported in PostgreSQL 11.
-- Views referencing the original table are not automatically updated to reference the partitioned table.
-
-```ruby
-# frozen_string_literal: true
-
-class SwapPartitionedAuditEvents < ActiveRecord::Migration[6.0]
- include Gitlab::Database::PartitioningMigrationHelpers
-
- def up
- replace_with_partitioned_table :audit_events
- end
-
- def down
- rollback_replace_with_partitioned_table :audit_events
- end
-end
-```
-
-After this migration completes:
-
-- The partitioned table replaces the non-partitioned (original) table.
-- The sync trigger created earlier is dropped.
-
-The partitioned table is now ready for use by the application.
-
-## Partitioning a table (Hash)
-
-Hash partitioning divides data into partitions based on a hash of their ID.
-It works well only if most queries against the table include a clause like `WHERE id = ?`,
-so that PostgreSQL can decide which partition to look in based on the ID or ids being requested.
-
-Another key downside is that hash partitioning does not allow adding additional partitions after table creation.
-The correct number of partitions must be chosen up-front.
-
-Hash partitioning is the only type of partitioning (aside from some complex uses of list partitioning) that can guarantee
-uniqueness of an ID across multiple partitions at the database level.
-
-## Partitioning a table (List)
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96815) in GitLab 15.4.
-
-Add the partitioning key column to the table you are partitioning.
-Include the partitioning key in the following constraints:
-
-- The primary key.
-- All foreign keys referencing the table to be partitioned.
-- All unique constraints.
-
-### Step 1 - Add partition key
-
-Add the partitioning key column. For example, in a rails migration:
-
-```ruby
-class AddPartitionNumberForPartitioning < Gitlab::Database::Migration[2.1]
- enable_lock_retries!
-
- TABLE_NAME = :table_name
- COLUMN_NAME = :partition_id
- DEFAULT_VALUE = 100
-
- def change
- add_column(TABLE_NAME, COLUMN_NAME, :bigint, default: 100)
- end
-end
-```
-
-### Step 2 - Create required indexes
-
-Add indexes including the partitioning key column. For example, in a rails migration:
-
-```ruby
-class PrepareIndexesForPartitioning < Gitlab::Database::Migration[2.1]
- disable_ddl_transaction!
-
- TABLE_NAME = :table_name
- INDEX_NAME = :index_name
-
- def up
- add_concurrent_index(TABLE_NAME, [:id, :partition_id], unique: true, name: INDEX_NAME)
- end
-
- def down
- remove_concurrent_index_by_name(TABLE_NAME, INDEX_NAME)
- end
-end
-```
-
-### Step 3 - Enforce unique constraint
-
-Change all unique indexes to include the partitioning key column,
-including the primary key index. You can start by adding an unique
-index on `[primary_key_column, :partition_id]`, which will be
-required for the next two steps. For example, in a rails migration:
-
-```ruby
-class PrepareUniqueContraintForPartitioning < Gitlab::Database::Migration[2.1]
- disable_ddl_transaction!
-
- TABLE_NAME = :table_name
- OLD_UNIQUE_INDEX_NAME = :index_name_unique
- NEW_UNIQUE_INDEX_NAME = :new_index_name
-
- def up
- add_concurrent_index(TABLE_NAME, [:id, :partition_id], unique: true, name: NEW_UNIQUE_INDEX_NAME)
-
- remove_concurrent_index_by_name(TABLE_NAME, OLD_UNIQUE_INDEX_NAME)
- end
-
- def down
- add_concurrent_index(TABLE_NAME, :id, unique: true, name: OLD_UNIQUE_INDEX_NAME)
-
- remove_concurrent_index_by_name(TABLE_NAME, NEW_UNIQUE_INDEX_NAME)
- end
-end
-```
-
-### Step 4 - Enforce foreign key constraint
-
-Enforce foreign keys including the partitioning key column. For example, in a rails migration:
-
-```ruby
-class PrepareForeignKeyForPartitioning < Gitlab::Database::Migration[2.1]
- disable_ddl_transaction!
-
- SOURCE_TABLE_NAME = :source_table_name
- TARGET_TABLE_NAME = :target_table_name
- COLUMN = :foreign_key_id
- TARGET_COLUMN = :id
- FK_NAME = :fk_365d1db505_p
- PARTITION_COLUMN = :partition_id
-
- def up
- add_concurrent_foreign_key(
- SOURCE_TABLE_NAME,
- TARGET_TABLE_NAME,
- column: [PARTITION_COLUMN, COLUMN],
- target_column: [PARTITION_COLUMN, TARGET_COLUMN],
- validate: false,
- on_update: :cascade,
- name: FK_NAME
- )
-
- # This should be done in a separate post migration when dealing with a high traffic table
- validate_foreign_key(TABLE_NAME, [PARTITION_COLUMN, COLUMN], name: FK_NAME)
- end
-
- def down
- with_lock_retries do
- remove_foreign_key_if_exists(SOURCE_TABLE_NAME, name: FK_NAME)
- end
- end
-end
-```
-
-The `on_update: :cascade` option is mandatory if we want the partitioning column
-to be updated. This will cascade the update to all dependent rows. Without
-specifying it, updating the partition column on the target table we would
-result in a `Key is still referenced from table ...` error and updating the
-partition column on the source table would raise a
-`Key is not present in table ...` error.
-
-This migration can be automatically generated using:
-
-```shell
-./scripts/partitioning/generate-fk --source source_table_name --target target_table_name
-```
-
-### Step 5 - Swap primary key
-
-Swap the primary key including the partitioning key column. This can be done only after
-including the partition key for all references foreign keys. For example, in a rails migration:
-
-```ruby
-class PreparePrimaryKeyForPartitioning < Gitlab::Database::Migration[2.1]
- disable_ddl_transaction!
-
- TABLE_NAME = :table_name
- PRIMARY_KEY = :primary_key
- OLD_INDEX_NAME = :old_index_name
- NEW_INDEX_NAME = :new_index_name
-
- def up
- swap_primary_key(TABLE_NAME, PRIMARY_KEY, NEW_INDEX_NAME)
- end
-
- def down
- add_concurrent_index(TABLE_NAME, :id, unique: true, name: OLD_INDEX_NAME)
- add_concurrent_index(TABLE_NAME, [:id, :partition_id], unique: true, name: NEW_INDEX_NAME)
-
- unswap_primary_key(TABLE_NAME, PRIMARY_KEY, OLD_INDEX_NAME)
- end
-end
-```
-
-NOTE:
-Do not forget to set the primary key explicitly in your model as `ActiveRecord` does not support composite primary keys.
-
-```ruby
-class Model < ApplicationRecord
- self.primary_key = :id
-end
-```
-
-### Step 6 - Create parent table and attach existing table as the initial partition
-
-You can now create the parent table attaching the existing table as the initial
-partition by using the following helpers provided by the database team.
-
-For example, using list partitioning in Rails post migrations:
-
-```ruby
-class PrepareTableConstraintsForListPartitioning < Gitlab::Database::Migration[2.1]
- include Gitlab::Database::PartitioningMigrationHelpers::TableManagementHelpers
-
- disable_ddl_transaction!
-
- TABLE_NAME = :table_name
- PARENT_TABLE_NAME = :p_table_name
- FIRST_PARTITION = 100
- PARTITION_COLUMN = :partition_id
-
- def up
- prepare_constraint_for_list_partitioning(
- table_name: TABLE_NAME,
- partitioning_column: PARTITION_COLUMN,
- parent_table_name: PARENT_TABLE_NAME,
- initial_partitioning_value: FIRST_PARTITION
- )
- end
-
- def down
- revert_preparing_constraint_for_list_partitioning(
- table_name: TABLE_NAME,
- partitioning_column: PARTITION_COLUMN,
- parent_table_name: PARENT_TABLE_NAME,
- initial_partitioning_value: FIRST_PARTITION
- )
- end
-end
-```
-
-```ruby
-class ConvertTableToListPartitioning < Gitlab::Database::Migration[2.1]
- include Gitlab::Database::PartitioningMigrationHelpers::TableManagementHelpers
-
- disable_ddl_transaction!
-
- TABLE_NAME = :table_name
- TABLE_FK = :table_references_by_fk
- PARENT_TABLE_NAME = :p_table_name
- FIRST_PARTITION = 100
- PARTITION_COLUMN = :partition_id
-
- def up
- convert_table_to_first_list_partition(
- table_name: TABLE_NAME,
- partitioning_column: PARTITION_COLUMN,
- parent_table_name: PARENT_TABLE_NAME,
- initial_partitioning_value: FIRST_PARTITION,
- lock_tables: [TABLE_FK, TABLE_NAME]
- )
- end
-
- def down
- revert_converting_table_to_first_list_partition(
- table_name: TABLE_NAME,
- partitioning_column: PARTITION_COLUMN,
- parent_table_name: PARENT_TABLE_NAME,
- initial_partitioning_value: FIRST_PARTITION
- )
- end
-end
-```
-
-NOTE:
-Do not forget to set the sequence name explicitly in your model because it will
-be owned by the routing table and `ActiveRecord` can't determine it. This can
-be cleaned up after the `table_name` is changed to the routing table.
-
-```ruby
-class Model < ApplicationRecord
- self.sequence_name = 'model_id_seq'
-end
-```
-
-If the partitioning constraint migration takes [more than 10 minutes](../migration_style_guide.md#how-long-a-migration-should-take) to finish,
-it can be made to run asynchronously to avoid running the post-migration during busy hours.
-
-Prepend the following migration `AsyncPrepareTableConstraintsForListPartitioning`
-and use `async: true` option. This change marks the partitioning constraint as `NOT VALID`
-and enqueues a scheduled job to validate the existing data in the table during the weekend.
-
-Then the second post-migration `PrepareTableConstraintsForListPartitioning` only
-marks the partitioning constraint as validated, because the existing data is already
-tested during the previous weekend.
-
-For example:
-
-```ruby
-class AsyncPrepareTableConstraintsForListPartitioning < Gitlab::Database::Migration[2.1]
- include Gitlab::Database::PartitioningMigrationHelpers::TableManagementHelpers
-
- disable_ddl_transaction!
-
- TABLE_NAME = :table_name
- PARENT_TABLE_NAME = :p_table_name
- FIRST_PARTITION = 100
- PARTITION_COLUMN = :partition_id
-
- def up
- prepare_constraint_for_list_partitioning(
- table_name: TABLE_NAME,
- partitioning_column: PARTITION_COLUMN,
- parent_table_name: PARENT_TABLE_NAME,
- initial_partitioning_value: FIRST_PARTITION,
- async: true
- )
- end
-
- def down
- revert_preparing_constraint_for_list_partitioning(
- table_name: TABLE_NAME,
- partitioning_column: PARTITION_COLUMN,
- parent_table_name: PARENT_TABLE_NAME,
- initial_partitioning_value: FIRST_PARTITION
- )
- end
-end
-```
-
-### Step 7 - Re-point foreign keys to parent table
-
-The tables that reference the initial partition must be updated to point to the
-parent table now. Without this change, the records from those tables
-will not be able to locate the rows in the next partitions because they will look
-for them in the initial partition.
-
-Steps:
-
-- Add the foreign key to the partitioned table and validate it asynchronously,
- [for example](https://gitlab.com/gitlab-org/gitlab/-/blob/65d63f6a00196c3a7d59f15191920f271ab2b145/db/post_migrate/20230524135543_replace_ci_build_pending_states_foreign_key.rb).
-- Validate it synchronously after the asynchronously validation was completed on GitLab.com,
- [for example](https://gitlab.com/gitlab-org/gitlab/-/blob/65d63f6a00196c3a7d59f15191920f271ab2b145/db/post_migrate/20230530140456_validate_fk_ci_build_pending_states_p_ci_builds.rb).
-- Remove the old foreign key and rename the new one to the old name,
- [for example](https://gitlab.com/gitlab-org/gitlab/-/blob/65d63f6a00196c3a7d59f15191920f271ab2b145/db/post_migrate/20230615083713_replace_old_fk_ci_build_pending_states_to_builds.rb#L9).
-
-### Step 8 - Ensure ID uniqueness across partitions
-
-All uniqueness constraints must include the partitioning key, so we can have
-duplicate IDs across partitions. To solve this we enforce that only the database
-can set the ID values and use a sequence to generate them because sequences are
-guaranteed to generate unique values.
-
-For example:
-
-```ruby
-class EnsureIdUniquenessForPCiBuilds < Gitlab::Database::Migration[2.1]
- include Gitlab::Database::PartitioningMigrationHelpers::UniquenessHelpers
-
- enable_lock_retries!
-
- TABLE_NAME = :p_ci_builds
- FUNCTION_NAME = :assign_p_ci_builds_id_value
-
- def up
- ensure_unique_id(TABLE_NAME)
- end
-
- def down
- execute(<<~SQL.squish)
- ALTER TABLE #{TABLE_NAME}
- ALTER COLUMN id SET DEFAULT nextval('ci_builds_id_seq'::regclass);
-
- DROP FUNCTION IF EXISTS #{FUNCTION_NAME} CASCADE;
- SQL
- end
-```
-
-### Step 9 - Analyze the partitioned table and create new partitions
-
-The autovacuum daemon does not process partitioned tables. It is necessary to
-periodically run a manual `ANALYZE` to keep the statistics of the table hierarchy
-up to date.
-
-Models that implement `Ci::Partitionable` with `partitioned: true` option are
-analyzed by default on a weekly basis. To enable this and create new partitions
-you need to register the model in the [PostgreSQL initializer](https://gitlab.com/gitlab-org/gitlab/-/blob/b7f0e3f1bcd2ffc220768bbc373364151775ca8e/config/initializers/postgres_partitioning.rb).
-
-### Step 10 - Update the application to use the partitioned table
-
-Now that the parent table is ready, we can update the application to use it:
-
-```ruby
-class Model < ApplicationRecord
- self.table_name = :partitioned_table
-end
-```
-
-Depending on the model, it might be safer to use a [change management issue](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/16387).
+<!-- This redirect file can be deleted after <2024-04-16>. -->
+<!-- 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 (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/install/docker.md b/doc/install/docker.md
index 76e5b9bd7b3..b627b3ea4d2 100644
--- a/doc/install/docker.md
+++ b/doc/install/docker.md
@@ -4,7 +4,7 @@ group: Distribution
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# GitLab Docker images **(FREE SELF)**
+# Install GitLab using Docker **(FREE SELF)**
The GitLab Docker images are monolithic images of GitLab running all the
necessary services in a single container.
@@ -19,9 +19,6 @@ container. As another option, you can install an MTA directly in the GitLab
container, but this adds maintenance overhead as you'll likely need to reinstall
the MTA after every upgrade or restart.
-In the following examples, if you want to use the latest RC image, use
-`gitlab/gitlab-ee:rc` instead.
-
You should not deploy the GitLab Docker image in Kubernetes as it creates a
single point of failure. If you want to deploy GitLab in Kubernetes, the
[GitLab Helm Chart](https://docs.gitlab.com/charts/) or [GitLab Operator](https://docs.gitlab.com/operator/)
@@ -37,9 +34,50 @@ to community resources (such as IRC or forums) to seek help from other users.
To use the GitLab Docker images:
-- You must install Docker.
+- You must [install Docker](https://docs.docker.com/engine/install/#server).
- You must use a valid externally-accessible hostname. Do not use `localhost`.
+## Configure the SSH port
+
+GitLab uses SSH to interact with Git over SSH. By default, GitLab uses port `22`.
+
+To use a different port when using the GitLab Docker image, you can either:
+
+- Change the server's SSH port (recommended).
+- Change the GitLab Shell SSH port.
+
+### Change the server's SSH port
+
+You can change the server's SSH port without making another SSH configuration
+change in GitLab. In that case, the SSH clone URLs looks like
+`ssh://git@gitlab.example.com/user/project.git`.
+
+To change the server's SSH port:
+
+1. Open `/etc/ssh/sshd_config` with your editor, and change the SSH port:
+
+ ```conf
+ Port = 2424
+ ```
+
+1. Save the file and restart the SSH service:
+
+ ```shell
+ sudo systemctl restart ssh
+ ```
+
+1. Open a new terminal session and verify that you can connect using SSH to the server using
+ the new port.
+
+### Change the GitLab Shell SSH port
+
+If you don't want to change the server's default SSH port, you can configure a
+different SSH port that GitLab uses for Git over SSH pushes. In that case,
+the SSH clone URLs looks like
+`ssh://git@gitlab.example.com:<portNumber>/user/project.git`.
+
+For more information, see how to [change the GitLab Shell SSH port](#expose-gitlab-on-different-ports).
+
## Set up the volumes location
Before setting everything else, create a directory where the configuration, logs,
@@ -74,6 +112,32 @@ The GitLab container uses host mounted volumes to store persistent data:
| `$GITLAB_HOME/logs` | `/var/log/gitlab` | For storing logs. |
| `$GITLAB_HOME/config`| `/etc/gitlab` | For storing the GitLab configuration files. |
+## Find the GitLab version and edition to use
+
+In a production environment, you should pin your deployment to a specific
+GitLab version. Find the version to use in the Docker tags page:
+
+- [GitLab Enterprise Edition tags](https://hub.docker.com/r/gitlab/gitlab-ee/tags/)
+- [GitLab Community Edition tags](https://hub.docker.com/r/gitlab/gitlab-ce/tags/)
+
+The tag name consists of the following:
+
+```plaintext
+gitlab/gitlab-ee:<version>-ee.0
+```
+
+Where `<version>` is the GitLab version, for example `16.5.3`. It always includes
+`<major>.<minor>.<patch>` in its name.
+
+For testing purposes, you can use the `latest` tag, such as `gitlab/gitlab-ee:latest`,
+which points to the latest stable release.
+
+In the following examples, we use a stable Enterprise Edition version, but
+if you want to use the Release Candidate (RC) or nightly image, use
+`gitlab/gitlab-ee:rc` or `gitlab/gitlab-ee:nightly` instead.
+
+To install the Community Edition, replace `ee` with `ce`.
+
## Installation
The GitLab Docker images can be run in multiple ways:
@@ -90,6 +154,7 @@ Once you've set up the `GITLAB_HOME` variable, you can run the image:
```shell
sudo docker run --detach \
--hostname gitlab.example.com \
+ --env GITLAB_OMNIBUS_CONFIG="external_url 'http://gitlab.example.com" \
--publish 443:443 --publish 80:80 --publish 22:22 \
--name gitlab \
--restart always \
@@ -97,18 +162,20 @@ sudo docker run --detach \
--volume $GITLAB_HOME/logs:/var/log/gitlab \
--volume $GITLAB_HOME/data:/var/opt/gitlab \
--shm-size 256m \
- gitlab/gitlab-ee:latest
+ gitlab/gitlab-ee:<version>-ee.0
```
-This will download and start a GitLab container and publish ports needed to
-access SSH, HTTP and HTTPS. All GitLab data will be stored as subdirectories of
-`$GITLAB_HOME`. The container will automatically `restart` after a system reboot.
+This command downloads and starts a GitLab container, and
+[publishes ports](https://docs.docker.com/network/#published-ports) needed to
+access SSH, HTTP and HTTPS. All GitLab data are stored as subdirectories of
+`$GITLAB_HOME`. The container automatically restarts after a system reboot.
If you are on SELinux, then run this instead:
```shell
sudo docker run --detach \
--hostname gitlab.example.com \
+ --env GITLAB_OMNIBUS_CONFIG="external_url 'http://gitlab.example.com" \
--publish 443:443 --publish 80:80 --publish 22:22 \
--name gitlab \
--restart always \
@@ -116,13 +183,13 @@ sudo docker run --detach \
--volume $GITLAB_HOME/logs:/var/log/gitlab:Z \
--volume $GITLAB_HOME/data:/var/opt/gitlab:Z \
--shm-size 256m \
- gitlab/gitlab-ee:latest
+ gitlab/gitlab-ee:<version>-ee.0
```
-This will ensure that the Docker process has enough permissions to create the
+This command ensures that the Docker process has enough permissions to create the
configuration files in the mounted volumes.
-If you're using the [Kerberos integration](../integration/kerberos.md) **(PREMIUM ONLY)**,
+If you're using the [Kerberos integration](../integration/kerberos.md),
you must also publish your Kerberos port (for example, `--publish 8443:8443`).
Failing to do so prevents Git operations with Kerberos.
@@ -133,9 +200,8 @@ process with:
sudo docker logs -f gitlab
```
-After starting a container you can visit `gitlab.example.com` (or
-`http://192.168.59.103` if you used boot2docker on macOS). It might take a while
-before the Docker container starts to respond to queries.
+After starting the container, you can visit `gitlab.example.com`. It might take
+a while before the Docker container starts to respond to queries.
Visit the GitLab URL, and sign in with the username `root`
and the password from the following command:
@@ -145,27 +211,28 @@ sudo docker exec -it gitlab grep 'Password:' /etc/gitlab/initial_root_password
```
NOTE:
-The password file will be automatically deleted in the first reconfigure run after 24 hours.
+The password file is automatically deleted in the first container restart after 24 hours.
### Install GitLab using Docker Compose
With [Docker Compose](https://docs.docker.com/compose/) you can easily configure,
install, and upgrade your Docker-based GitLab installation:
-1. [Install Docker Compose](https://docs.docker.com/compose/install/).
+1. [Install Docker Compose](https://docs.docker.com/compose/install/linux/).
1. Create a `docker-compose.yml` file:
```yaml
version: '3.6'
services:
- web:
- image: 'gitlab/gitlab-ee:latest'
+ gitlab:
+ image: gitlab/gitlab-ee:<version>-ee.0
+ name: gitlab
restart: always
hostname: 'gitlab.example.com'
environment:
GITLAB_OMNIBUS_CONFIG: |
- external_url 'https://gitlab.example.com'
# Add any other gitlab.rb configuration here, each on its own line
+ external_url 'https://gitlab.example.com'
ports:
- '80:80'
- '443:443'
@@ -185,7 +252,7 @@ install, and upgrade your Docker-based GitLab installation:
```
NOTE:
-Read the ["Pre-configure Docker container"](#pre-configure-docker-container) section
+Read the [Pre-configure Docker container](#pre-configure-docker-container) section
to see how the `GITLAB_OMNIBUS_CONFIG` variable works.
Below is another `docker-compose.yml` example with GitLab running on a custom
@@ -195,17 +262,19 @@ HTTP and SSH port. Notice how the `GITLAB_OMNIBUS_CONFIG` variables match the
```yaml
version: '3.6'
services:
- web:
- image: 'gitlab/gitlab-ee:latest'
+ gitlab:
+ image: gitlab/gitlab-ee:<version>-ee.0
+ name: gitlab
restart: always
hostname: 'gitlab.example.com'
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'http://gitlab.example.com:8929'
- gitlab_rails['gitlab_shell_ssh_port'] = 2224
+ gitlab_rails['gitlab_shell_ssh_port'] = 2424
ports:
- '8929:8929'
- - '2224:22'
+ - '443:443'
+ - '2424:2424'
volumes:
- '$GITLAB_HOME/config:/etc/gitlab'
- '$GITLAB_HOME/logs:/var/log/gitlab'
@@ -213,7 +282,7 @@ services:
shm_size: '256m'
```
-This is the same as using `--publish 8929:8929 --publish 2224:22`.
+This configuration is the same as using `--publish 8929:8929 --publish 2424:2424`.
### Install GitLab using Docker swarm mode
@@ -235,7 +304,10 @@ Here's an example that deploys GitLab with four runners as a [stack](https://doc
version: "3.6"
services:
gitlab:
- image: gitlab/gitlab-ee:latest
+ image: gitlab/gitlab-ee:<version>-ee.0
+ name: gitlab
+ restart: always
+ hostname: 'gitlab.example.com'
ports:
- "22:22"
- "80:80"
@@ -275,7 +347,7 @@ Here's an example that deploys GitLab with four runners as a [stack](https://doc
gitlab_rails['initial_root_password'] = File.read('/run/secrets/gitlab_root_password').gsub("\n", "")
```
-1. Create a `root_password.txt` file:
+1. Create a file called `root_password.txt` containing the password:
```plaintext
MySuperSecretAndSecurePassw0rd!
@@ -333,7 +405,8 @@ You can pre-configure the GitLab Docker image by adding the environment variable
and make database configuration or any other option from the
[Linux package template](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template).
The settings contained in `GITLAB_OMNIBUS_CONFIG` aren't written to the
-`gitlab.rb` configuration file, and are evaluated on load.
+`gitlab.rb` configuration file, and are evaluated on load. To provide multiple
+settings, separate them with a colon (`;`).
Here's an example that sets the external URL and enables LFS while starting
the container:
@@ -341,7 +414,7 @@ the container:
```shell
sudo docker run --detach \
--hostname gitlab.example.com \
- --env GITLAB_OMNIBUS_CONFIG="external_url 'http://my.domain.com/'; gitlab_rails['lfs_enabled'] = true;" \
+ --env GITLAB_OMNIBUS_CONFIG="external_url 'http://gitlab.example.com; gitlab_rails['lfs_enabled'] = true;" \
--publish 443:443 --publish 80:80 --publish 22:22 \
--name gitlab \
--restart always \
@@ -349,24 +422,13 @@ sudo docker run --detach \
--volume $GITLAB_HOME/logs:/var/log/gitlab \
--volume $GITLAB_HOME/data:/var/opt/gitlab \
--shm-size 256m \
- gitlab/gitlab-ee:latest
+ gitlab/gitlab-ee:<version>-ee.0
```
Every time you execute a `docker run` command, you need to provide
the `GITLAB_OMNIBUS_CONFIG` option. The content of `GITLAB_OMNIBUS_CONFIG` is
_not_ preserved between subsequent runs.
-### Use tagged versions of GitLab
-
-Tagged versions of the GitLab Docker images are also provided.
-To see all available tags see:
-
-- [GitLab CE tags](https://hub.docker.com/r/gitlab/gitlab-ce/tags/)
-- [GitLab EE tags](https://hub.docker.com/r/gitlab/gitlab-ee/tags/)
-
-To use a specific tagged version, replace `gitlab/gitlab-ee:latest` with
-the GitLab version you want to run, for example `gitlab/gitlab-ee:12.1.3-ce.0`.
-
### Run GitLab on a public IP address
You can make Docker to use your IP address and forward all traffic to the
@@ -377,6 +439,7 @@ To expose GitLab on IP `198.51.100.1`:
```shell
sudo docker run --detach \
--hostname gitlab.example.com \
+ --env GITLAB_OMNIBUS_CONFIG="external_url 'http://gitlab.example.com" \
--publish 198.51.100.1:443:443 \
--publish 198.51.100.1:80:80 \
--publish 198.51.100.1:22:22 \
@@ -386,7 +449,7 @@ sudo docker run --detach \
--volume $GITLAB_HOME/logs:/var/log/gitlab \
--volume $GITLAB_HOME/data:/var/opt/gitlab \
--shm-size 256m \
- gitlab/gitlab-ee:latest
+ gitlab/gitlab-ee:<version>-ee.0
```
You can then access your GitLab instance at `http://198.51.100.1/` and `https://198.51.100.1/`.
@@ -396,31 +459,32 @@ You can then access your GitLab instance at `http://198.51.100.1/` and `https://
GitLab will occupy [some ports](../administration/package_information/defaults.md)
inside the container.
-If you want to use a different host port than `80` (HTTP) or `443` (HTTPS),
+If you want to use a different host port than `80` (HTTP), `443` (HTTPS), or `22` (SSH),
you need to add a separate `--publish` directive to the `docker run` command.
For example, to expose the web interface on the host's port `8929`, and the SSH service on
-port `2289`:
+port `2424`:
1. Use the following `docker run` command:
```shell
sudo docker run --detach \
--hostname gitlab.example.com \
- --publish 8929:8929 --publish 2289:22 \
+ --env GITLAB_OMNIBUS_CONFIG="external_url 'http://gitlab.example.com:8929'; gitlab_rails['gitlab_shell_ssh_port'] = 2424" \
+ --publish 8929:8929 --publish 2424:2424 \
--name gitlab \
--restart always \
--volume $GITLAB_HOME/config:/etc/gitlab \
--volume $GITLAB_HOME/logs:/var/log/gitlab \
--volume $GITLAB_HOME/data:/var/opt/gitlab \
--shm-size 256m \
- gitlab/gitlab-ee:latest
+ gitlab/gitlab-ee:<version>-ee.0
```
NOTE:
The format for publishing ports is `hostPort:containerPort`. Read more in the
Docker documentation about
- [exposing incoming ports](https://docs.docker.com/engine/reference/run/#/expose-incoming-ports).
+ [exposing incoming ports](https://docs.docker.com/network/#published-ports).
1. Enter the running container:
@@ -445,10 +509,10 @@ port `2289`:
`nginx['listen_port']`, it will be pulled from the `external_url`.
For more information see the [NGINX documentation](https://docs.gitlab.com/omnibus/settings/nginx.html).
-1. Set `gitlab_shell_ssh_port`:
+1. Set the SSH port:
```ruby
- gitlab_rails['gitlab_shell_ssh_port'] = 2289
+ gitlab_rails['gitlab_shell_ssh_port'] = 2424
```
1. Finally, reconfigure GitLab:
@@ -497,7 +561,7 @@ and sign-up restrictions.
## Upgrade
In most cases, upgrading GitLab is as easy as downloading the newest Docker
-[image tag](#use-tagged-versions-of-gitlab).
+image tag.
### Upgrade GitLab using Docker Engine
@@ -518,10 +582,10 @@ To upgrade GitLab that was [installed using Docker Engine](#install-gitlab-using
sudo docker rm gitlab
```
-1. Pull the new image. For example, the latest GitLab image:
+1. Pull the new image:
```shell
- sudo docker pull gitlab/gitlab-ee:latest
+ sudo docker pull gitlab/gitlab-ee:<version>-ee.0
```
1. Ensure that the `GITLAB_HOME` environment variable is [defined](#set-up-the-volumes-location):
@@ -543,7 +607,7 @@ To upgrade GitLab that was [installed using Docker Engine](#install-gitlab-using
--volume $GITLAB_HOME/logs:/var/log/gitlab \
--volume $GITLAB_HOME/data:/var/opt/gitlab \
--shm-size 256m \
- gitlab/gitlab-ee:latest
+ gitlab/gitlab-ee:<version>-ee.0
```
On the first run, GitLab will reconfigure and upgrade itself.
@@ -557,7 +621,7 @@ To upgrade GitLab that was [installed using Docker Compose](#install-gitlab-usin
1. Take a [backup](#back-up-gitlab). As a minimum, back up [the database](#create-a-database-backup) and
the GitLab secrets file.
-
+1. Edit `docker-compose.yml` and change the version to pull.
1. Download the newest release and upgrade your GitLab instance:
```shell
@@ -565,9 +629,6 @@ To upgrade GitLab that was [installed using Docker Compose](#install-gitlab-usin
docker compose up -d
```
- If you have used [tags](#use-tagged-versions-of-gitlab) instead, you'll need
- to first edit `docker-compose.yml`.
-
### Convert Community Edition to Enterprise Edition
You can convert an existing Docker-based GitLab Community Edition (CE) container
@@ -591,8 +652,8 @@ The following steps assume that you are upgrading the same version.
To downgrade GitLab after an upgrade:
-1. Follow the upgrade procedure, but [specify the tag for the original version of GitLab](#use-tagged-versions-of-gitlab)
- instead of `latest`.
+1. Follow the upgrade procedure, by [specifying the version](#find-the-gitlab-version-and-edition-to-use)
+ to downgrade to.
1. Restore the [database backup you made](#create-a-database-backup) as part of the upgrade.
@@ -636,13 +697,6 @@ docker exec -t <container name> gitlab-backup create SKIP=artifacts,repositories
The backup is written to `/var/opt/gitlab/backups` which should be on a
[volume mounted by Docker](#set-up-the-volumes-location).
-## Installing GitLab Community Edition
-
-[GitLab CE Docker image](https://hub.docker.com/r/gitlab/gitlab-ce/)
-
-To install the Community Edition, replace `ee` with `ce` in the commands on this
-page.
-
## Troubleshooting
The following information will help if you encounter problems with an installation that used the Linux package and Docker.
diff --git a/doc/update/deprecations.md b/doc/update/deprecations.md
index bdc83c31c22..d099ff303e5 100644
--- a/doc/update/deprecations.md
+++ b/doc/update/deprecations.md
@@ -457,6 +457,22 @@ The GraphQL fields, `isTemporaryStorageIncreaseEnabled` and `temporaryStorageInc
<div class="deprecation breaking-change" data-milestone="17.0">
+### Deprecate Maven versions below 3.8.8
+
+<div class="deprecation-notes">
+- Announced in GitLab <span class="milestone">16.9</span>
+- Removal in GitLab <span class="milestone">17.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change))
+- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/438772).
+</div>
+
+GitLab 17.0 drops Dependency Scanning support for Maven versions below 3.8.8.
+
+Maven versions below 3.6.3 have reached their end-of-life. Users are advised to upgrade to 3.8.8 or greater.
+
+</div>
+
+<div class="deprecation breaking-change" data-milestone="17.0">
+
### Deprecate Windows CMD in GitLab Runner
<div class="deprecation-notes">
diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md
index 2570ce03005..a03dc265621 100644
--- a/doc/user/application_security/dependency_scanning/index.md
+++ b/doc/user/application_security/dependency_scanning/index.md
@@ -141,7 +141,7 @@ The following languages and dependency managers are supported:
<td>N</td>
</tr>
<tr>
- <td><a href="https://maven.apache.org/">Maven</a></td>
+ <td><a href="https://maven.apache.org/">Maven</a><sup><b><a href="#notes-regarding-supported-languages-and-package-managers-8">3</a></b></sup></td>
<td><code>pom.xml</code></td>
<td>N</td>
</tr>
@@ -283,6 +283,14 @@ The following languages and dependency managers are supported:
</ul>
</p>
</li>
+ <li>
+ <a id="notes-regarding-supported-languages-and-package-managers-8"></a>
+ <p>
+ <ul>
+ <li>Support for Maven below 3.8.8 was deprecated in GitLab 16.9 and will be removed in GitLab 17.0</li>
+ </ul>
+ </p>
+ </li>
</ol>
<!-- markdownlint-enable MD044 -->
diff --git a/doc/user/clusters/agent/work_with_agent.md b/doc/user/clusters/agent/work_with_agent.md
index e1f84197405..dc43eb29fb3 100644
--- a/doc/user/clusters/agent/work_with_agent.md
+++ b/doc/user/clusters/agent/work_with_agent.md
@@ -68,6 +68,13 @@ the first time or after more than an hour of inactivity.
View and provide feedback about the UI in [this epic](https://gitlab.com/groups/gitlab-org/-/epics/4739).
+## Manage an agent's workspaces
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/277323) in GitLab 16.8.
+
+You can view and manage all workspaces associated with an agent.
+For more information, see [Manage workspaces at the agent level](../../workspace/index.md#manage-workspaces-at-the-agent-level).
+
## Debug the agent
> The `grpc_level` was [introduced](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/merge_requests/669) in GitLab 15.1.
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index 372442bb53f..d5ca098c8c5 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -697,6 +697,9 @@ GitLab Flavored Markdown recognizes the following:
| Snippet | `$123` | `namespace/project$123` | `project$123` |
| [Epic](group/epics/index.md) | `&123` | `group1/subgroup&123` | |
| [Iteration](group/iterations/index.md) | `*iteration:"iteration title"` | | |
+| [Iteration cadence](group/iterations/index.md) by ID<sup>1</sup> | `[cadence:123]` | | |
+| [Iteration cadence](group/iterations/index.md) by title (one word)<sup>1</sup> | `[cadence:plan]` | | |
+| [Iteration cadence](group/iterations/index.md) by title (multiple words)<sup>1</sup> | `[cadence:"plan a"]` | | |
| [Vulnerability](application_security/vulnerabilities/index.md) | `[vulnerability:123]` | `[vulnerability:namespace/project/123]` | `[vulnerability:project/123]` |
| Feature flag | `[feature_flag:123]` | `[feature_flag:namespace/project/123]` | `[feature_flag:project/123]` |
| Label by ID | `~123` | `namespace/project~123` | `project~123` |
@@ -713,6 +716,15 @@ GitLab Flavored Markdown recognizes the following:
| [Alert](../operations/incident_management/alerts.md) | `^alert#123` | `namespace/project^alert#123` | `project^alert#123` |
| [Contact](crm/index.md#contacts) | `[contact:test@example.com]` | | |
+<ol>
+ <li>
+ <small>
+ <a href="https://gitlab.com/gitlab-org/gitlab/-/issues/384885">Introduced</a> in GitLab 16.9. Iteration cadence references are always rendered following the format <code>[cadence:&lt;ID>]</code>.
+ For example, the text reference <code>[cadence:"plan"]</code> renders as <code>[cadence:1]</code> if the referenced iterations cadence's ID is <code>1</code>.
+ </small>
+ </li>
+</ol>
+
For example, referencing an issue by using `#123` formats the output as a link
to issue number 123 with text `#123`. Likewise, a link to issue number 123 is
recognized and formatted with text `#123`. If you don't want `#123` to link to an issue,
diff --git a/doc/user/project/integrations/gitlab_slack_application.md b/doc/user/project/integrations/gitlab_slack_application.md
index 3664c56824d..79df07d5eb9 100644
--- a/doc/user/project/integrations/gitlab_slack_application.md
+++ b/doc/user/project/integrations/gitlab_slack_application.md
@@ -52,8 +52,7 @@ When GitLab releases new features for the GitLab for Slack app, you might have t
To reinstall the GitLab for Slack app:
-1. On the left sidebar, select **Search or go to** and find a project
- where the GitLab for Slack app is [installed](#install-the-gitlab-for-slack-app).
+1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Settings > Integrations**.
1. Select **GitLab for Slack app**.
1. Select **Reinstall GitLab for Slack app**.
@@ -69,27 +68,30 @@ You can use slash commands to run common GitLab operations.
For the GitLab for Slack app:
-- You must authorize your Slack user on GitLab.com when you run your first slash command.
-- You can [create a project alias](#create-a-project-alias) for slash commands.
+- You must authorize your Slack user when you run your first slash command.
+- You can replace `<project>` with a project full path or
+ [create a project alias](#create-a-project-alias) for slash commands.
-The following slash commands are available in GitLab:
+If you use [Slack slash commands](slack_slash_commands.md) or
+[Mattermost slash commands](mattermost_slash_commands.md) instead:
-- For [Slack slash commands](slack_slash_commands.md) on self-managed GitLab and [Mattermost slash commands](mattermost_slash_commands.md),
- replace `/gitlab` with the trigger name of the slash command you've configured for the integration.
-- Replace `<project>` with a project full path.
+- Replace `/gitlab` with the trigger name you've configured for these integrations.
+- Remove `<project>`.
+
+The following slash commands are available in GitLab:
| Command | Description |
| ------- | ----------- |
| `/gitlab help` | Shows all available slash commands. |
-| `/gitlab <project> issue new <title>` <kbd>Shift</kbd>+<kbd>Enter</kbd> `<description>` | Creates a new issue with the title `<title>` and description `<description>`. |
| `/gitlab <project> issue show <id>` | Shows the issue with the ID `<id>`. |
-| `/gitlab <project> issue close <id>` | Closes the issue with the ID `<id>`. |
+| `/gitlab <project> issue new <title>` <kbd>Shift</kbd>+<kbd>Enter</kbd> `<description>` | Creates an issue with the title `<title>` and description `<description>`. |
| `/gitlab <project> issue search <query>` | Shows up to five issues that match `<query>`. |
| `/gitlab <project> issue move <id> to <project>` | Moves the issue with the ID `<id>` to `<project>`. |
-| `/gitlab <project> issue comment <id>` <kbd>Shift</kbd>+<kbd>Enter</kbd> `<comment>` | Adds a new comment with the comment body `<comment>` to the issue with the ID `<id>`. |
+| `/gitlab <project> issue close <id>` | Closes the issue with the ID `<id>`. |
+| `/gitlab <project> issue comment <id>` <kbd>Shift</kbd>+<kbd>Enter</kbd> `<comment>` | Adds a comment with the comment body `<comment>` to the issue with the ID `<id>`. |
| `/gitlab <project> deploy <from> to <to>` | [Deploys](#deploy-command) from the `<from>` environment to the `<to>` environment. |
| `/gitlab <project> run <job name> <arguments>` | Executes the [ChatOps](../../../ci/chatops/index.md) job `<job name>` on the default branch. |
-| `/gitlab incident declare` | Opens a dialog to [create a new incident from Slack](../../../operations/incident_management/slack.md). |
+| `/gitlab incident declare` | Opens a dialog to [create an incident from Slack](../../../operations/incident_management/slack.md). |
### `deploy` command
@@ -103,10 +105,12 @@ The command returns an error if GitLab cannot find a matching deployment action.
### Create a project alias
-By default, slash commands expect a project full path. To create a project alias in the GitLab for Slack app:
+In the GitLab for Slack app, slash commands use a project full path by default.
+You can use a project alias instead.
+
+To create a project alias for slash commands in the GitLab for Slack app:
-1. On the left sidebar, select **Search or go to** and find a project
- where the GitLab for Slack app is [installed](#install-the-gitlab-for-slack-app).
+1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Settings > Integrations**.
1. Select **GitLab for Slack app**.
1. Next to the project path or alias, select **Edit**.
@@ -122,8 +126,7 @@ You can receive notifications to Slack channels for certain GitLab [events](#not
To configure Slack notifications:
-1. On the left sidebar, select **Search or go to** and find a project
- where the GitLab for Slack app is [installed](#install-the-gitlab-for-slack-app).
+1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Settings > Integrations**.
1. Select **GitLab for Slack app**.
1. In the **Trigger** section:
diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md
index bd1eab6a3e1..c5b6babe369 100644
--- a/doc/user/project/quick_actions.md
+++ b/doc/user/project/quick_actions.md
@@ -74,7 +74,9 @@ To auto-format this table, use the VS Code Markdown Table formatter: `https://do
| `/estimate <time>` or `/estimate_time <time>` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Set time estimate. For example, `/estimate 1mo 2w 3d 4h 5m`. For more information, see [Time tracking](time_tracking.md). Alias `/estimate_time` [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/16501) in GitLab 15.6. |
| `/health_status <value>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set [health status](issues/managing_issues.md#health-status). Valid options for `<value>` are `on_track`, `needs_attention`, and `at_risk` ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213814) in GitLab 14.7). |
| `/invite_email email1 email2` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add up to six email participants. This action is behind feature flag `issue_email_participants` and is not yet supported in issue templates. |
-| `/iteration *iteration:"iteration name"` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set iteration. For example, to set the `Late in July` iteration: `/iteration *iteration:"Late in July"`. |
+| `/iteration *iteration:<iteration ID> or <iteration name>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set iteration. For example, to set the `Late in July` iteration: `/iteration *iteration:"Late in July"`. |
+| `/iteration [cadence:<iteration cadence ID> or <iteration cadence name>] <--current or --next>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set iteration to the current or next upcoming iteration of the referenced iteration cadence. For example, `/iteration [cadence:"Team cadence"] --current` sets the iteration to the current iteration of the iteration cadence named "Team cadence". [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/384885) in GitLab 16.9. |
+| `/iteration <--current or --next>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set iteration to the current or next upcoming iteration when a group has one iteration cadence. For example, `/iteration --current` sets the iteration to the current iteration of the iteration cadence. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/384885) in GitLab 16.9. |
| `/label ~label1 ~label2` or `/labels ~label1 ~label2` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Add one or more labels. Label names can also start without a tilde (`~`), but mixed syntax is not supported. |
| `/link` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add a link and description to [linked resources](../../operations/incident_management/linked_resources.md) in an incident ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/374964) in GitLab 15.5). |
| `/lock` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Lock the discussions. |
diff --git a/doc/user/workspace/index.md b/doc/user/workspace/index.md
index 5fa6108de6f..35ad46d5385 100644
--- a/doc/user/workspace/index.md
+++ b/doc/user/workspace/index.md
@@ -55,6 +55,38 @@ To clean up orphaned resources, an administrator must manually delete the worksp
[Issue 414384](https://gitlab.com/gitlab-org/gitlab/-/issues/414384) proposes to change this behavior.
+## Manage workspaces at the agent level
+
+Prerequisites:
+
+- You must be a Kubernetes administrator.
+
+As a Kubernetes administrator, you might have to manage workspaces that you do not own. To see a list of all workspaces running on a particular agent:
+
+1. On the left sidebar, select **Search or go to** and find your project.
+1. Select **Operate > Kubernetes clusters**.
+1. Select **Agent** tab to view clusters connected to GitLab through the agent.
+1. Select the agent configured for remote development.
+1. Select **Workspaces** to view all active workspaces on the agent.
+
+From this list, you can terminate a workspace.
+
+NOTE:
+Terminating a workspace is a destructive and irreversible action. Any unsaved or uncommitted data in the workspace is immediately deleted and cannot be recovered.
+
+### Identify the agent from a running workspace
+
+Prerequisites:
+
+- You must be a Kubernetes administrator.
+
+In larger deployments that contain multiple agents, it might be difficult to identify the agent responsible for running the workspace with information provided at the workspace level. To identify the agent and view its status page, use one of the following GraphQL API endpoints:
+
+- `agent-id` to inspect the project the agent belongs to.
+- [`Query.workspaces`](../../api/graphql/reference/index.md#queryworkspaces) to search for workspaces and find their associated [cluster agent](../../api/graphql/reference/index.md#clusteragent) and the project the agent belongs to.
+
+You can then use the resulting project ID to view and manage the correct project.
+
## Devfile
A devfile is a file that defines a development environment by specifying the necessary
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 333b78b037d..bbb1e7679fe 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -14290,6 +14290,24 @@ msgstr ""
msgid "Could not apply %{name} command. %{message}."
msgstr ""
+msgid "Could not apply iteration command. Failed to find the referenced iteration cadence."
+msgstr ""
+
+msgid "Could not apply iteration command. Failed to find the referenced iteration."
+msgstr ""
+
+msgid "Could not apply iteration command. Missing option --current or --next."
+msgstr ""
+
+msgid "Could not apply iteration command. No current iteration found for the cadence."
+msgstr ""
+
+msgid "Could not apply iteration command. No upcoming iteration found for the cadence."
+msgstr ""
+
+msgid "Could not apply iteration command. There are multiple cadences but no cadence is specified."
+msgstr ""
+
msgid "Could not authorize chat nickname. Try again!"
msgstr ""
diff --git a/spec/fixtures/api/schemas/entities/merge_request_noteable.json b/spec/fixtures/api/schemas/entities/merge_request_noteable.json
index 6f3c29b16e9..2e0d7f998a2 100644
--- a/spec/fixtures/api/schemas/entities/merge_request_noteable.json
+++ b/spec/fixtures/api/schemas/entities/merge_request_noteable.json
@@ -24,11 +24,13 @@
"type": "object",
"required": [
"can_create_note",
- "can_update"
+ "can_update",
+ "can_create_confidential_note"
],
"properties": {
"can_create_note": { "type": "boolean" },
- "can_update": { "type": "boolean" }
+ "can_update": { "type": "boolean" },
+ "can_create_confidential_note": { "type": "boolean" }
},
"additionalProperties": false
},
diff --git a/spec/frontend/notes/components/comment_form_spec.js b/spec/frontend/notes/components/comment_form_spec.js
index 500032eac26..b69c604cb54 100644
--- a/spec/frontend/notes/components/comment_form_spec.js
+++ b/spec/frontend/notes/components/comment_form_spec.js
@@ -636,7 +636,7 @@ describe('issue_comment_form component', () => {
noteableType | rendered | message
${'Issue'} | ${true} | ${'render'}
${'Epic'} | ${true} | ${'render'}
- ${'MergeRequest'} | ${false} | ${'not render'}
+ ${'MergeRequest'} | ${true} | ${'render'}
`(
'should $message checkbox when noteableType is $noteableType',
({ noteableType, rendered }) => {
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index 59795059642..8f755e4741b 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -176,10 +176,18 @@ RSpec.describe Note, feature_category: :team_planning do
expect(subject).to be_valid
end
- context 'when noteable is not allowed to have confidential notes' do
+ context 'when noteable is a merge request' do
let_it_be(:noteable) { create(:merge_request) }
it 'can not be set confidential' do
+ expect(subject).to be_valid
+ end
+ end
+
+ context 'when noteable is not allowed to have confidential notes' do
+ let_it_be(:noteable) { create(:snippet) }
+
+ it 'can not be set confidential' do
expect(subject).not_to be_valid
expect(subject.errors[:confidential]).to include('can not be set for this resource')
end
diff --git a/spec/policies/merge_request_policy_spec.rb b/spec/policies/merge_request_policy_spec.rb
index c21e1244402..12b38fe1d93 100644
--- a/spec/policies/merge_request_policy_spec.rb
+++ b/spec/policies/merge_request_policy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequestPolicy do
+RSpec.describe MergeRequestPolicy, feature_category: :code_review_workflow do
include ExternalAuthorizationServiceHelpers
let_it_be(:guest) { create(:user) }
@@ -23,7 +23,8 @@ RSpec.describe MergeRequestPolicy do
create_todo
approve_merge_request
create_note
- update_subscription].freeze
+ update_subscription
+ mark_note_as_internal].freeze
shared_examples_for 'a denied user' do
let(:perms) { permissions(subject, merge_request) }
@@ -47,6 +48,7 @@ RSpec.describe MergeRequestPolicy do
:create_merge_request_from | false
:approve_merge_request | false
:update_merge_request | false
+ :mark_note_as_internal | true
end
with_them do