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>2021-06-10 18:10:14 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-06-10 18:10:14 +0300
commitbe1b7b709e957f83b076d05c9672ab2ab5ff1940 (patch)
tree2390764106c8fb6152340565e47124889bb279a1
parentc68905777ea271aea120a58d2d1246e84aea4264 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/boards/boards_util.js2
-rw-r--r--app/assets/javascripts/boards/components/sidebar/board_sidebar_milestone_select.vue157
-rw-r--r--app/assets/javascripts/boards/graphql/issue.fragment.graphql4
-rw-r--r--app/assets/javascripts/boards/graphql/issue_set_milestone.mutation.graphql12
-rw-r--r--app/assets/javascripts/boards/graphql/lists_issues.query.graphql8
-rw-r--r--app/assets/javascripts/boards/stores/actions.js25
-rw-r--r--app/assets/javascripts/invite_members/components/invite_members_trigger.vue3
-rw-r--r--app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue8
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/sidebar_invite_members.vue12
-rw-r--r--app/assets/stylesheets/page_bundles/escalation_policies.scss26
-rw-r--r--app/models/ci/build_trace_chunk.rb27
-rw-r--r--app/models/ci/build_trace_chunks/database.rb4
-rw-r--r--app/models/ci/build_trace_chunks/fog.rb12
-rw-r--r--app/models/ci/build_trace_chunks/redis.rb87
-rw-r--r--app/models/ci/build_trace_chunks/redis_base.rb90
-rw-r--r--app/models/ci/build_trace_chunks/redis_trace_chunks.rb13
-rw-r--r--app/views/groups/group_members/index.html.haml5
-rw-r--r--app/views/projects/project_members/index.html.haml5
-rw-r--r--app/views/shared/issuable/_sidebar_assignees.html.haml3
-rw-r--r--app/views/shared/issuable/_sidebar_reviewers.html.haml3
-rw-r--r--app/views/shared/issuable/_sidebar_user_dropdown.html.haml1
-rw-r--r--config/feature_flags/ops/dedicated_redis_trace_chunks.yml8
-rw-r--r--doc/administration/geo/replication/datatypes.md26
-rw-r--r--doc/administration/geo/setup/database.md155
-rw-r--r--doc/api/projects.md6
-rw-r--r--doc/ci/environments/environments_dashboard.md4
-rw-r--r--doc/ci/quick_start/index.md4
-rw-r--r--doc/development/documentation/styleguide/index.md36
-rw-r--r--doc/development/stage_group_dashboards.md37
-rw-r--r--doc/subscriptions/index.md26
-rw-r--r--doc/user/application_security/security_dashboard/index.md3
-rw-r--r--doc/user/operations_dashboard/index.md2
-rw-r--r--doc/user/packages/composer_repository/index.md2
-rw-r--r--doc/user/packages/workflows/project_registry.md4
-rw-r--r--doc/user/project/badges.md2
-rw-r--r--doc/user/project/index.md2
-rw-r--r--doc/user/project/merge_requests/allow_collaboration.md50
-rw-r--r--doc/user/project/merge_requests/approvals/index.md2
-rw-r--r--doc/user/project/merge_requests/img/allow_collaboration.pngbin10806 -> 0 bytes
-rw-r--r--doc/user/project/merge_requests/img/allow_collaboration_after_save.pngbin17553 -> 0 bytes
-rw-r--r--doc/user/project/merge_requests/img/commit-button_v13_12.pngbin0 -> 8834 bytes
-rw-r--r--doc/user/project/milestones/index.md4
-rw-r--r--doc/user/project/pages/getting_started/pages_ci_cd_template.md2
-rw-r--r--doc/user/project/releases/index.md15
-rw-r--r--doc/user/project/repository/index.md6
-rw-r--r--doc/user/project/working_with_projects.md14
-rw-r--r--doc/user/snippets.md8
-rw-r--r--lib/api/invitations.rb2
-rw-r--r--lib/api/members.rb2
-rw-r--r--lib/gitlab/i18n.rb20
-rw-r--r--locale/gitlab.pot23
-rw-r--r--spec/features/admin/users/users_spec.rb41
-rw-r--r--spec/features/groups/members/manage_members_spec.rb4
-rw-r--r--spec/features/projects/members/list_spec.rb4
-rw-r--r--spec/frontend/boards/components/sidebar/board_sidebar_milestone_select_spec.js176
-rw-r--r--spec/frontend/boards/stores/actions_spec.js55
-rw-r--r--spec/frontend/invite_members/components/invite_members_trigger_spec.js16
-rw-r--r--spec/frontend/sidebar/components/assignees/sidebar_invite_members_spec.js11
-rw-r--r--spec/initializers/mailer_retries_spec.rb2
-rw-r--r--spec/models/ci/build_trace_chunk_spec.rb114
-rw-r--r--spec/models/ci/build_trace_chunks/database_spec.rb6
-rw-r--r--spec/models/ci/build_trace_chunks/redis_spec.rb6
-rw-r--r--spec/requests/api/invitations_spec.rb2
-rw-r--r--spec/requests/api/members_spec.rb4
-rw-r--r--spec/services/ci/append_build_trace_service_spec.rb2
-rw-r--r--spec/support/redis.rb8
-rw-r--r--spec/support/redis/redis_helpers.rb5
68 files changed, 595 insertions, 835 deletions
diff --git a/app/assets/javascripts/boards/boards_util.js b/app/assets/javascripts/boards/boards_util.js
index 3dc25560a85..e14a770411e 100644
--- a/app/assets/javascripts/boards/boards_util.js
+++ b/app/assets/javascripts/boards/boards_util.js
@@ -40,7 +40,7 @@ export function formatListIssues(listIssues) {
let listItemsCount;
const listData = listIssues.nodes.reduce((map, list) => {
- listItemsCount = list.issues.count;
+ listItemsCount = list.issuesCount;
let sortedIssues = list.issues.edges.map((issueNode) => ({
...issueNode.node,
}));
diff --git a/app/assets/javascripts/boards/components/sidebar/board_sidebar_milestone_select.vue b/app/assets/javascripts/boards/components/sidebar/board_sidebar_milestone_select.vue
deleted file mode 100644
index 2a1bda9278c..00000000000
--- a/app/assets/javascripts/boards/components/sidebar/board_sidebar_milestone_select.vue
+++ /dev/null
@@ -1,157 +0,0 @@
-<script>
-import {
- GlDropdown,
- GlDropdownItem,
- GlDropdownText,
- GlSearchBoxByType,
- GlDropdownDivider,
- GlLoadingIcon,
-} from '@gitlab/ui';
-import { mapGetters, mapActions } from 'vuex';
-import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue';
-import { __, s__ } from '~/locale';
-import projectMilestones from '../../graphql/project_milestones.query.graphql';
-
-export default {
- components: {
- BoardEditableItem,
- GlDropdown,
- GlLoadingIcon,
- GlDropdownItem,
- GlDropdownText,
- GlSearchBoxByType,
- GlDropdownDivider,
- },
- data() {
- return {
- milestones: [],
- searchTitle: '',
- loading: false,
- edit: false,
- };
- },
- apollo: {
- milestones: {
- query: projectMilestones,
- debounce: 250,
- skip() {
- return !this.edit;
- },
- variables() {
- return {
- fullPath: this.projectPath,
- searchTitle: this.searchTitle,
- state: 'active',
- includeAncestors: true,
- };
- },
- update(data) {
- const edges = data?.project?.milestones?.edges ?? [];
- return edges.map((item) => item.node);
- },
- error(error) {
- this.setError({ error, message: this.$options.i18n.fetchMilestonesError });
- },
- },
- },
- computed: {
- ...mapGetters(['activeBoardItem']),
- hasMilestone() {
- return this.activeBoardItem.milestone !== null;
- },
- groupFullPath() {
- const { referencePath = '' } = this.activeBoardItem;
- return referencePath.slice(0, referencePath.indexOf('/'));
- },
- projectPath() {
- const { referencePath = '' } = this.activeBoardItem;
- return referencePath.slice(0, referencePath.indexOf('#'));
- },
- dropdownText() {
- return this.activeBoardItem.milestone?.title ?? this.$options.i18n.noMilestone;
- },
- },
- methods: {
- ...mapActions(['setActiveIssueMilestone', 'setError']),
- handleOpen() {
- this.edit = true;
- this.$refs.dropdown.show();
- },
- handleClose() {
- this.edit = false;
- this.$refs.sidebarItem.collapse();
- },
- async setMilestone(milestoneId) {
- this.loading = true;
- this.searchTitle = '';
- this.handleClose();
-
- try {
- const input = { milestoneId, projectPath: this.projectPath };
- await this.setActiveIssueMilestone(input);
- } catch (e) {
- this.setError({ error: e, message: this.$options.i18n.updateMilestoneError });
- } finally {
- this.loading = false;
- }
- },
- },
- i18n: {
- milestone: __('Milestone'),
- noMilestone: __('No milestone'),
- assignMilestone: __('Assign milestone'),
- noMilestonesFound: s__('Milestones|No milestones found'),
- fetchMilestonesError: __('There was a problem fetching milestones.'),
- updateMilestoneError: __('An error occurred while updating the milestone.'),
- },
-};
-</script>
-
-<template>
- <board-editable-item
- ref="sidebarItem"
- :title="$options.i18n.milestone"
- :loading="loading"
- data-testid="sidebar-milestones"
- @open="handleOpen"
- @close="handleClose"
- >
- <template v-if="hasMilestone" #collapsed>
- <strong class="gl-text-gray-900">{{ activeBoardItem.milestone.title }}</strong>
- </template>
- <gl-dropdown
- ref="dropdown"
- :text="dropdownText"
- :header-text="$options.i18n.assignMilestone"
- block
- @hide="handleClose"
- >
- <gl-search-box-by-type ref="search" v-model.trim="searchTitle" class="gl-m-3" />
- <gl-dropdown-item
- data-testid="no-milestone-item"
- :is-check-item="true"
- :is-checked="!activeBoardItem.milestone"
- @click="setMilestone(null)"
- >
- {{ $options.i18n.noMilestone }}
- </gl-dropdown-item>
- <gl-dropdown-divider />
- <gl-loading-icon v-if="$apollo.loading" class="gl-py-4" />
- <template v-else-if="milestones.length > 0">
- <gl-dropdown-item
- v-for="milestone in milestones"
- :key="milestone.id"
- :is-check-item="true"
- :is-checked="activeBoardItem.milestone && milestone.id === activeBoardItem.milestone.id"
- data-testid="milestone-item"
- @click="setMilestone(milestone.id)"
- >
- {{ milestone.title }}
- </gl-dropdown-item>
- </template>
- <gl-dropdown-text v-else data-testid="no-milestones-found">
- {{ $options.i18n.noMilestonesFound }}
- </gl-dropdown-text>
- </gl-dropdown>
- </board-editable-item>
-</template>
diff --git a/app/assets/javascripts/boards/graphql/issue.fragment.graphql b/app/assets/javascripts/boards/graphql/issue.fragment.graphql
index 47ecb55c72b..0ff70703e1a 100644
--- a/app/assets/javascripts/boards/graphql/issue.fragment.graphql
+++ b/app/assets/javascripts/boards/graphql/issue.fragment.graphql
@@ -14,10 +14,6 @@ fragment IssueNode on Issue {
confidential
webUrl
relativePosition
- milestone {
- id
- title
- }
assignees {
nodes {
...User
diff --git a/app/assets/javascripts/boards/graphql/issue_set_milestone.mutation.graphql b/app/assets/javascripts/boards/graphql/issue_set_milestone.mutation.graphql
deleted file mode 100644
index 5dc78a03a06..00000000000
--- a/app/assets/javascripts/boards/graphql/issue_set_milestone.mutation.graphql
+++ /dev/null
@@ -1,12 +0,0 @@
-mutation issueSetMilestone($input: UpdateIssueInput!) {
- updateIssue(input: $input) {
- issue {
- milestone {
- id
- title
- description
- }
- }
- errors
- }
-}
diff --git a/app/assets/javascripts/boards/graphql/lists_issues.query.graphql b/app/assets/javascripts/boards/graphql/lists_issues.query.graphql
index 43af7d2b2f1..d1cb1ecf834 100644
--- a/app/assets/javascripts/boards/graphql/lists_issues.query.graphql
+++ b/app/assets/javascripts/boards/graphql/lists_issues.query.graphql
@@ -12,11 +12,11 @@ query ListIssues(
) {
group(fullPath: $fullPath) @include(if: $isGroup) {
board(id: $boardId) {
- lists(id: $id) {
+ lists(id: $id, issueFilters: $filters) {
nodes {
id
+ issuesCount
issues(first: $first, filters: $filters, after: $after) {
- count
edges {
node {
...IssueNode
@@ -33,11 +33,11 @@ query ListIssues(
}
project(fullPath: $fullPath) @include(if: $isProject) {
board(id: $boardId) {
- lists(id: $id) {
+ lists(id: $id, issueFilters: $filters) {
nodes {
id
+ issuesCount
issues(first: $first, filters: $filters, after: $after) {
- count
edges {
node {
...IssueNode
diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js
index 9842e3cb73a..694483e92ea 100644
--- a/app/assets/javascripts/boards/stores/actions.js
+++ b/app/assets/javascripts/boards/stores/actions.js
@@ -37,7 +37,6 @@ import groupProjectsQuery from '../graphql/group_projects.query.graphql';
import issueCreateMutation from '../graphql/issue_create.mutation.graphql';
import issueSetDueDateMutation from '../graphql/issue_set_due_date.mutation.graphql';
import issueSetLabelsMutation from '../graphql/issue_set_labels.mutation.graphql';
-import issueSetMilestoneMutation from '../graphql/issue_set_milestone.mutation.graphql';
import listsIssuesQuery from '../graphql/lists_issues.query.graphql';
import * as types from './mutation_types';
@@ -479,30 +478,6 @@ export default {
});
},
- setActiveIssueMilestone: async ({ commit, getters }, input) => {
- const { activeBoardItem } = getters;
- const { data } = await gqlClient.mutate({
- mutation: issueSetMilestoneMutation,
- variables: {
- input: {
- iid: String(activeBoardItem.iid),
- milestoneId: getIdFromGraphQLId(input.milestoneId),
- projectPath: input.projectPath,
- },
- },
- });
-
- if (data.updateIssue.errors?.length > 0) {
- throw new Error(data.updateIssue.errors);
- }
-
- commit(types.UPDATE_BOARD_ITEM_BY_ID, {
- itemId: activeBoardItem.id,
- prop: 'milestone',
- value: data.updateIssue.issue.milestone,
- });
- },
-
addListItem: ({ commit }, { list, item, position, inProgress = false }) => {
commit(types.ADD_BOARD_ITEM_TO_LIST, {
listId: list.id,
diff --git a/app/assets/javascripts/invite_members/components/invite_members_trigger.vue b/app/assets/javascripts/invite_members/components/invite_members_trigger.vue
index e297bb6c806..ec7d466336e 100644
--- a/app/assets/javascripts/invite_members/components/invite_members_trigger.vue
+++ b/app/assets/javascripts/invite_members/components/invite_members_trigger.vue
@@ -29,8 +29,7 @@ export default {
},
triggerSource: {
type: String,
- required: false,
- default: 'unknown',
+ required: true,
},
trackExperiment: {
type: String,
diff --git a/app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue b/app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue
index 137a2fc4b32..853842bf0d0 100644
--- a/app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue
+++ b/app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue
@@ -6,6 +6,7 @@ import {
GlInfiniteScroll,
GlLoadingIcon,
GlSearchBoxByType,
+ GlTooltipDirective,
} from '@gitlab/ui';
import { produce } from 'immer';
import { fetchPolicies } from '~/lib/graphql';
@@ -22,7 +23,7 @@ import getCurrentBranch from '~/pipeline_editor/graphql/queries/client/current_b
export default {
i18n: {
- dropdownHeader: s__('Switch Branch'),
+ dropdownHeader: s__('Switch branch'),
title: s__('Branches'),
fetchError: s__('Unable to fetch branch list for this project.'),
},
@@ -35,6 +36,9 @@ export default {
GlLoadingIcon,
GlSearchBoxByType,
},
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
inject: ['projectFullPath', 'totalBranches'],
props: {
paginationLimit: {
@@ -180,6 +184,8 @@ export default {
<template>
<gl-dropdown
v-if="showBranchSwitcher"
+ v-gl-tooltip.hover
+ :title="$options.i18n.dropdownHeader"
:header-text="$options.i18n.dropdownHeader"
:text="currentBranch"
icon="branch"
diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue
index abf690361cc..d9a974202a3 100644
--- a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue
@@ -248,7 +248,7 @@ export default {
>
<template #footer>
<gl-dropdown-item v-if="directlyInviteMembers">
- <sidebar-invite-members />
+ <sidebar-invite-members :issuable-type="issuableType" />
</gl-dropdown-item> </template
></user-select>
</template>
diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_invite_members.vue b/app/assets/javascripts/sidebar/components/assignees/sidebar_invite_members.vue
index 5c32d03e0d4..8ef65ef7308 100644
--- a/app/assets/javascripts/sidebar/components/assignees/sidebar_invite_members.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_invite_members.vue
@@ -9,6 +9,17 @@ export default {
components: {
InviteMembersTrigger,
},
+ props: {
+ issuableType: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ triggerSource() {
+ return `${this.issuableType}-assignee-dropdown`;
+ },
+ },
};
</script>
@@ -18,6 +29,7 @@ export default {
:display-text="$options.displayText"
:event="$options.dataTrackEvent"
:label="$options.dataTrackLabel"
+ :trigger-source="triggerSource"
classes="gl-display-block gl-pl-6 gl-hover-text-decoration-none gl-hover-text-blue-800!"
/>
</template>
diff --git a/app/assets/stylesheets/page_bundles/escalation_policies.scss b/app/assets/stylesheets/page_bundles/escalation_policies.scss
index 00aae78421c..f188dde1183 100644
--- a/app/assets/stylesheets/page_bundles/escalation_policies.scss
+++ b/app/assets/stylesheets/page_bundles/escalation_policies.scss
@@ -1,3 +1,5 @@
+@import 'mixins_and_variables_and_functions';
+
.escalation-policy-modal {
width: 640px;
}
@@ -9,3 +11,27 @@
.rule-close-icon {
right: 1rem;
}
+
+$stroke-size: 1px;
+
+.right-arrow {
+ @include gl-relative;
+ @include gl-mx-5;
+ @include gl-display-inline-block;
+ @include gl-vertical-align-middle;
+ height: $stroke-size;
+ background-color: var(--gray-900, $gray-900);
+ min-width: $gl-spacing-scale-7;
+
+ &-head {
+ @include gl-absolute;
+ top: -2*$stroke-size;
+ left: calc(100% - #{5*$stroke-size});
+ @include gl-display-inline-block;
+ @include gl-p-1;
+ @include gl-border-solid;
+ border-width: 0 $stroke-size $stroke-size 0;
+ border-color: var(--gray-900, $gray-900);
+ transform: rotate(-45deg);
+ }
+}
diff --git a/app/models/ci/build_trace_chunk.rb b/app/models/ci/build_trace_chunk.rb
index aaeff8bdd14..25f4a06088d 100644
--- a/app/models/ci/build_trace_chunk.rb
+++ b/app/models/ci/build_trace_chunk.rb
@@ -14,7 +14,13 @@ module Ci
belongs_to :build, class_name: "Ci::Build", foreign_key: :build_id
- default_value_for :data_store, :redis
+ default_value_for :data_store do
+ if Feature.enabled?(:dedicated_redis_trace_chunks, type: :ops)
+ :redis_trace_chunks
+ else
+ :redis
+ end
+ end
after_create { metrics.increment_trace_operation(operation: :chunked) }
@@ -25,22 +31,22 @@ module Ci
FailedToPersistDataError = Class.new(StandardError)
- # Note: The ordering of this hash is related to the precedence of persist store.
- # The bottom item takes the highest precedence, and the top item takes the lowest precedence.
DATA_STORES = {
redis: 1,
database: 2,
- fog: 3
+ fog: 3,
+ redis_trace_chunks: 4
}.freeze
STORE_TYPES = DATA_STORES.keys.to_h do |store|
- [store, "Ci::BuildTraceChunks::#{store.capitalize}".constantize]
+ [store, "Ci::BuildTraceChunks::#{store.to_s.camelize}".constantize]
end.freeze
+ LIVE_STORES = %i[redis redis_trace_chunks].freeze
enum data_store: DATA_STORES
- scope :live, -> { redis }
- scope :persisted, -> { not_redis.order(:chunk_index) }
+ scope :live, -> { where(data_store: LIVE_STORES) }
+ scope :persisted, -> { where.not(data_store: LIVE_STORES).order(:chunk_index) }
class << self
def all_stores
@@ -48,8 +54,7 @@ module Ci
end
def persistable_store
- # get first available store from the back of the list
- all_stores.reverse.find { |store| get_store_class(store).available? }
+ STORE_TYPES[:fog].available? ? :fog : :database
end
def get_store_class(store)
@@ -195,7 +200,7 @@ module Ci
end
def flushed?
- !redis?
+ !live?
end
def migrated?
@@ -203,7 +208,7 @@ module Ci
end
def live?
- redis?
+ LIVE_STORES.include?(data_store.to_sym)
end
def <=>(other)
diff --git a/app/models/ci/build_trace_chunks/database.rb b/app/models/ci/build_trace_chunks/database.rb
index 7448afba4c2..895028778a9 100644
--- a/app/models/ci/build_trace_chunks/database.rb
+++ b/app/models/ci/build_trace_chunks/database.rb
@@ -3,10 +3,6 @@
module Ci
module BuildTraceChunks
class Database
- def available?
- true
- end
-
def keys(relation)
[]
end
diff --git a/app/models/ci/build_trace_chunks/fog.rb b/app/models/ci/build_trace_chunks/fog.rb
index cbf0c0a1696..fab85fae33d 100644
--- a/app/models/ci/build_trace_chunks/fog.rb
+++ b/app/models/ci/build_trace_chunks/fog.rb
@@ -3,10 +3,18 @@
module Ci
module BuildTraceChunks
class Fog
- def available?
+ def self.available?
object_store.enabled
end
+ def self.object_store
+ Gitlab.config.artifacts.object_store
+ end
+
+ def available?
+ self.class.available?
+ end
+
def data(model)
files.get(key(model))&.body
rescue Excon::Error::NotFound
@@ -85,7 +93,7 @@ module Ci
end
def object_store
- Gitlab.config.artifacts.object_store
+ self.class.object_store
end
def object_store_raw_config
diff --git a/app/models/ci/build_trace_chunks/redis.rb b/app/models/ci/build_trace_chunks/redis.rb
index 003ec107895..46f275636e1 100644
--- a/app/models/ci/build_trace_chunks/redis.rb
+++ b/app/models/ci/build_trace_chunks/redis.rb
@@ -2,92 +2,11 @@
module Ci
module BuildTraceChunks
- class Redis
- CHUNK_REDIS_TTL = 1.week
- LUA_APPEND_CHUNK = <<~EOS
- local key, new_data, offset = KEYS[1], ARGV[1], ARGV[2]
- local length = new_data:len()
- local expire = #{CHUNK_REDIS_TTL.seconds}
- local current_size = redis.call("strlen", key)
- offset = tonumber(offset)
-
- if offset == 0 then
- -- overwrite everything
- redis.call("set", key, new_data, "ex", expire)
- return redis.call("strlen", key)
- elseif offset > current_size then
- -- offset range violation
- return -1
- elseif offset + length >= current_size then
- -- efficiently append or overwrite and append
- redis.call("expire", key, expire)
- return redis.call("setrange", key, offset, new_data)
- else
- -- append and truncate
- local current_data = redis.call("get", key)
- new_data = current_data:sub(1, offset) .. new_data
- redis.call("set", key, new_data, "ex", expire)
- return redis.call("strlen", key)
- end
- EOS
-
- def available?
- true
- end
-
- def data(model)
- Gitlab::Redis::SharedState.with do |redis|
- redis.get(key(model))
- end
- end
-
- def set_data(model, new_data)
- Gitlab::Redis::SharedState.with do |redis|
- redis.set(key(model), new_data, ex: CHUNK_REDIS_TTL)
- end
- end
-
- def append_data(model, new_data, offset)
- Gitlab::Redis::SharedState.with do |redis|
- redis.eval(LUA_APPEND_CHUNK, keys: [key(model)], argv: [new_data, offset])
- end
- end
-
- def size(model)
- Gitlab::Redis::SharedState.with do |redis|
- redis.strlen(key(model))
- end
- end
-
- def delete_data(model)
- delete_keys([[model.build_id, model.chunk_index]])
- end
-
- def keys(relation)
- relation.pluck(:build_id, :chunk_index)
- end
-
- def delete_keys(keys)
- return if keys.empty?
-
- keys = keys.map { |key| key_raw(*key) }
-
- Gitlab::Redis::SharedState.with do |redis|
- # https://gitlab.com/gitlab-org/gitlab/-/issues/224171
- Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
- redis.del(keys)
- end
- end
- end
-
+ class Redis < RedisBase
private
- def key(model)
- key_raw(model.build_id, model.chunk_index)
- end
-
- def key_raw(build_id, chunk_index)
- "gitlab:ci:trace:#{build_id.to_i}:chunks:#{chunk_index.to_i}"
+ def with_redis
+ Gitlab::Redis::SharedState.with { |redis| yield(redis) }
end
end
end
diff --git a/app/models/ci/build_trace_chunks/redis_base.rb b/app/models/ci/build_trace_chunks/redis_base.rb
new file mode 100644
index 00000000000..3b7a844d122
--- /dev/null
+++ b/app/models/ci/build_trace_chunks/redis_base.rb
@@ -0,0 +1,90 @@
+# frozen_string_literal: true
+
+module Ci
+ module BuildTraceChunks
+ class RedisBase
+ CHUNK_REDIS_TTL = 1.week
+ LUA_APPEND_CHUNK = <<~EOS
+ local key, new_data, offset = KEYS[1], ARGV[1], ARGV[2]
+ local length = new_data:len()
+ local expire = #{CHUNK_REDIS_TTL.seconds}
+ local current_size = redis.call("strlen", key)
+ offset = tonumber(offset)
+
+ if offset == 0 then
+ -- overwrite everything
+ redis.call("set", key, new_data, "ex", expire)
+ return redis.call("strlen", key)
+ elseif offset > current_size then
+ -- offset range violation
+ return -1
+ elseif offset + length >= current_size then
+ -- efficiently append or overwrite and append
+ redis.call("expire", key, expire)
+ return redis.call("setrange", key, offset, new_data)
+ else
+ -- append and truncate
+ local current_data = redis.call("get", key)
+ new_data = current_data:sub(1, offset) .. new_data
+ redis.call("set", key, new_data, "ex", expire)
+ return redis.call("strlen", key)
+ end
+ EOS
+
+ def data(model)
+ with_redis do |redis|
+ redis.get(key(model))
+ end
+ end
+
+ def set_data(model, new_data)
+ with_redis do |redis|
+ redis.set(key(model), new_data, ex: CHUNK_REDIS_TTL)
+ end
+ end
+
+ def append_data(model, new_data, offset)
+ with_redis do |redis|
+ redis.eval(LUA_APPEND_CHUNK, keys: [key(model)], argv: [new_data, offset])
+ end
+ end
+
+ def size(model)
+ with_redis do |redis|
+ redis.strlen(key(model))
+ end
+ end
+
+ def delete_data(model)
+ delete_keys([[model.build_id, model.chunk_index]])
+ end
+
+ def keys(relation)
+ relation.pluck(:build_id, :chunk_index)
+ end
+
+ def delete_keys(keys)
+ return if keys.empty?
+
+ keys = keys.map { |key| key_raw(*key) }
+
+ with_redis do |redis|
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/224171
+ Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
+ redis.del(keys)
+ end
+ end
+ end
+
+ private
+
+ def key(model)
+ key_raw(model.build_id, model.chunk_index)
+ end
+
+ def key_raw(build_id, chunk_index)
+ "gitlab:ci:trace:#{build_id.to_i}:chunks:#{chunk_index.to_i}"
+ end
+ end
+ end
+end
diff --git a/app/models/ci/build_trace_chunks/redis_trace_chunks.rb b/app/models/ci/build_trace_chunks/redis_trace_chunks.rb
new file mode 100644
index 00000000000..06e315b0aaf
--- /dev/null
+++ b/app/models/ci/build_trace_chunks/redis_trace_chunks.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Ci
+ module BuildTraceChunks
+ class RedisTraceChunks < RedisBase
+ private
+
+ def with_redis
+ Gitlab::Redis::TraceChunks.with { |redis| yield(redis) }
+ end
+ end
+ end
+end
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index 63459466b3b..2305bbd96f3 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -16,7 +16,10 @@
.gl-w-half.gl-xs-w-full
.gl-display-flex.gl-flex-wrap.gl-justify-content-end.gl-mb-3
.js-invite-group-trigger{ data: { classes: 'gl-mt-3 gl-sm-w-auto gl-w-full', display_text: _('Invite a group') } }
- .js-invite-members-trigger{ data: { variant: 'success', classes: 'gl-mt-3 gl-sm-w-auto gl-w-full gl-sm-ml-3', display_text: _('Invite members') } }
+ .js-invite-members-trigger{ data: { variant: 'success',
+ classes: 'gl-mt-3 gl-sm-w-auto gl-w-full gl-sm-ml-3',
+ trigger_source: 'group-members-page',
+ display_text: _('Invite members') } }
= render 'groups/invite_members_modal', group: @group
- if can_manage_members? && Feature.disabled?(:invite_members_group_modal, @group)
%hr.gl-mt-4
diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml
index 352d418efa9..d1b6db95392 100644
--- a/app/views/projects/project_members/index.html.haml
+++ b/app/views/projects/project_members/index.html.haml
@@ -26,7 +26,10 @@
- if @project.allowed_to_share_with_group?
.js-invite-group-trigger{ data: { classes: 'gl-mt-3 gl-sm-w-auto gl-w-full gl-sm-ml-3', display_text: _('Invite a group') } }
- if can_manage_project_members?(@project) && !membership_locked?
- .js-invite-members-trigger{ data: { variant: 'success', classes: 'gl-mt-3 gl-sm-w-auto gl-w-full gl-sm-ml-3', display_text: _('Invite members') } }
+ .js-invite-members-trigger{ data: { variant: 'success',
+ classes: 'gl-mt-3 gl-sm-w-auto gl-w-full gl-sm-ml-3',
+ trigger_source: 'project-members-page',
+ display_text: _('Invite members') } }
= render 'projects/invite_members_modal', project: @project
- else
diff --git a/app/views/shared/issuable/_sidebar_assignees.html.haml b/app/views/shared/issuable/_sidebar_assignees.html.haml
index 8216d48fcec..7416fda6b44 100644
--- a/app/views/shared/issuable/_sidebar_assignees.html.haml
+++ b/app/views/shared/issuable/_sidebar_assignees.html.haml
@@ -45,4 +45,5 @@
= render 'shared/issuable/sidebar_user_dropdown',
options: options,
wrapper_class: 'js-sidebar-assignee-dropdown',
- track_label: 'edit_assignee'
+ track_label: 'edit_assignee',
+ trigger_source: "#{issuable_type}-assignee-dropdown"
diff --git a/app/views/shared/issuable/_sidebar_reviewers.html.haml b/app/views/shared/issuable/_sidebar_reviewers.html.haml
index 95502b9fa60..bc76d292dd6 100644
--- a/app/views/shared/issuable/_sidebar_reviewers.html.haml
+++ b/app/views/shared/issuable/_sidebar_reviewers.html.haml
@@ -42,4 +42,5 @@
= render 'shared/issuable/sidebar_user_dropdown',
options: options,
wrapper_class: 'js-sidebar-reviewer-dropdown',
- track_label: 'edit_reviewer'
+ track_label: 'edit_reviewer',
+ trigger_source: "#{issuable_type}-reviewer-dropdown"
diff --git a/app/views/shared/issuable/_sidebar_user_dropdown.html.haml b/app/views/shared/issuable/_sidebar_user_dropdown.html.haml
index c8b9108d046..3a17db5acf8 100644
--- a/app/views/shared/issuable/_sidebar_user_dropdown.html.haml
+++ b/app/views/shared/issuable/_sidebar_user_dropdown.html.haml
@@ -15,6 +15,7 @@
.js-invite-members-trigger{ data: { trigger_element: 'anchor',
display_text: _('Invite Members'),
event: 'click_invite_members',
+ trigger_source: local_assigns.fetch(:trigger_source),
label: data['track-label'] } }
- else
= dropdown_tag(data['dropdown-title'], options: options)
diff --git a/config/feature_flags/ops/dedicated_redis_trace_chunks.yml b/config/feature_flags/ops/dedicated_redis_trace_chunks.yml
new file mode 100644
index 00000000000..1f3da11dfd5
--- /dev/null
+++ b/config/feature_flags/ops/dedicated_redis_trace_chunks.yml
@@ -0,0 +1,8 @@
+---
+name: dedicated_redis_trace_chunks
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62938
+rollout_issue_url: https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/1096
+milestone: '14.0'
+type: ops
+group: team::Scalability
+default_enabled: false
diff --git a/doc/administration/geo/replication/datatypes.md b/doc/administration/geo/replication/datatypes.md
index ddcb7372fc2..2d66de3d5da 100644
--- a/doc/administration/geo/replication/datatypes.md
+++ b/doc/administration/geo/replication/datatypes.md
@@ -47,8 +47,8 @@ verification methods:
| Blobs | Container registry _(file system)_ | Geo with API/Docker API | _Not implemented_ |
| Blobs | Container registry _(object storage)_ | Geo with API/Managed/Docker API (*2*) | _Not implemented_ |
| Blobs | Package registry _(file system)_ | Geo with API | SHA256 checksum |
-| Blobs | Package registry _(object storage)_ | Geo with API/Managed (*2*) | SHA256 checksum |
-| Blobs | Versioned Terraform State _(file system)_ | Geo with API | _Not implemented_ |
+| Blobs | Package registry _(object storage)_ | Geo with API/Managed (*2*) | _Not implemented_ |
+| Blobs | Versioned Terraform State _(file system)_ | Geo with API | SHA256 checksum |
| Blobs | Versioned Terraform State _(object storage)_ | Geo with API/Managed (*2*) | _Not implemented_ |
| Blobs | External Merge Request Diffs _(file system)_ | Geo with API | _Not implemented_ |
| Blobs | External Merge Request Diffs _(object storage)_ | Geo with API/Managed (*2*) | _Not implemented_ |
@@ -190,14 +190,14 @@ successfully, you must replicate their data using some other means.
|[Container Registry](../../packages/container_registry.md) | **Yes** (12.3) | No | No | Disabled by default. See [instructions](docker_registry.md) to enable. |
|[Content in object storage (beta)](object_storage.md) | **Yes** (12.4) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/13845) | No | |
|[Project designs repository](../../../user/project/issues/design_management.md) | **Yes** (12.7) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/32467) | Via Object Storage provider if supported. Native Geo support (Beta). | |
-|[Package Registry for npm](../../../user/packages/npm_registry/index.md) | **Yes** (13.2) | **Yes** (13.10) | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_package_file_replication`, enabled by default. |
-|[Package Registry for Maven](../../../user/packages/maven_repository/index.md) | **Yes** (13.2) | **Yes** (13.10) | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_package_file_replication`, enabled by default. |
-|[Package Registry for Conan](../../../user/packages/conan_repository/index.md) | **Yes** (13.2) | **Yes** (13.10) | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_package_file_replication`, enabled by default. |
-|[Package Registry for NuGet](../../../user/packages/nuget_repository/index.md) | **Yes** (13.2) | **Yes** (13.10) | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_package_file_replication`, enabled by default. |
-|[Package Registry for PyPI](../../../user/packages/pypi_repository/index.md) | **Yes** (13.2) | **Yes** (13.10) | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_package_file_replication`, enabled by default. |
-|[Package Registry for Composer](../../../user/packages/composer_repository/index.md) | **Yes** (13.2) | **Yes** (13.10) | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_package_file_replication`, enabled by default. |
-|[Package Registry for generic packages](../../../user/packages/generic_packages/index.md) | **Yes** (13.5) | **Yes** (13.10) | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_package_file_replication`, enabled by default. |
-|[Versioned Terraform State](../../terraform_state.md) | **Yes** (13.5) | No | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_terraform_state_version_replication`, enabled by default. |
+|[Package Registry for npm](../../../user/packages/npm_registry/index.md) | **Yes** (13.2) | [**Yes**](#limitation-of-verification-for-files-in-object-storage) (13.10) | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_package_file_replication`, enabled by default. |
+|[Package Registry for Maven](../../../user/packages/maven_repository/index.md) | **Yes** (13.2) | [**Yes**](#limitation-of-verification-for-files-in-object-storage) (13.10) | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_package_file_replication`, enabled by default. |
+|[Package Registry for Conan](../../../user/packages/conan_repository/index.md) | **Yes** (13.2) | [**Yes**](#limitation-of-verification-for-files-in-object-storage) (13.10) | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_package_file_replication`, enabled by default. |
+|[Package Registry for NuGet](../../../user/packages/nuget_repository/index.md) | **Yes** (13.2) | [**Yes**](#limitation-of-verification-for-files-in-object-storage) (13.10) | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_package_file_replication`, enabled by default. |
+|[Package Registry for PyPI](../../../user/packages/pypi_repository/index.md) | **Yes** (13.2) | [**Yes**](#limitation-of-verification-for-files-in-object-storage) (13.10) | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_package_file_replication`, enabled by default. |
+|[Package Registry for Composer](../../../user/packages/composer_repository/index.md) | **Yes** (13.2) | [**Yes**](#limitation-of-verification-for-files-in-object-storage) (13.10) | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_package_file_replication`, enabled by default. |
+|[Package Registry for generic packages](../../../user/packages/generic_packages/index.md) | **Yes** (13.5) | [**Yes**](#limitation-of-verification-for-files-in-object-storage) (13.10) | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_package_file_replication`, enabled by default. |
+|[Versioned Terraform State](../../terraform_state.md) | **Yes** (13.5) | [**Yes**](#limitation-of-verification-for-files-in-object-storage) (13.12) | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_terraform_state_version_replication`, enabled by default. |
|[External merge request diffs](../../merge_request_diffs.md) | **Yes** (13.5) | No | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_merge_request_diff_replication`, enabled by default. |
|[Versioned snippets](../../../user/snippets.md#versioned-snippets) | [**Yes** (13.7)](https://gitlab.com/groups/gitlab-org/-/epics/2809) | [No](https://gitlab.com/groups/gitlab-org/-/epics/2810) | No | |
|[Server-side Git hooks](../../server_hooks.md) | [No](https://gitlab.com/groups/gitlab-org/-/epics/1867) | No | No | |
@@ -205,3 +205,9 @@ successfully, you must replicate their data using some other means.
|[GitLab Pages](../../pages/index.md) | [No](https://gitlab.com/groups/gitlab-org/-/epics/589) | No | No | |
|[Dependency proxy images](../../../user/packages/dependency_proxy/index.md) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/259694) | No | No | Blocked on [Geo: Secondary Mimicry](https://gitlab.com/groups/gitlab-org/-/epics/1528). Note that replication of this cache is not needed for Disaster Recovery purposes because it can be recreated from external sources. |
|[Vulnerability Export](../../../user/application_security/vulnerability_report/#export-vulnerability-details) | [Not planned](https://gitlab.com/groups/gitlab-org/-/epics/3111) | No | Via Object Storage provider if supported. Native Geo support (Beta). | Not planned because they are ephemeral and sensitive. They can be regenerated on demand. |
+
+#### Limitation of verification for files in Object Storage
+
+GitLab managed Object Storage replication support [is in beta](object_storage.md#enabling-gitlab-managed-object-storage-replication).
+
+Locally stored files are verified but remote stored files are not.
diff --git a/doc/administration/geo/setup/database.md b/doc/administration/geo/setup/database.md
index 32fba4da370..5b53f881f29 100644
--- a/doc/administration/geo/setup/database.md
+++ b/doc/administration/geo/setup/database.md
@@ -25,7 +25,20 @@ size.
You are encouraged to first read through all the steps before executing them
in your testing/production environment.
-## PostgreSQL replication
+## Single instance database replication
+
+A single instance database replication is easier to set up and still provides the same Geo capabilities
+as a clusterized alternative. It's useful for setups running on a single machine
+or trying to evaluate Geo for a future clusterized installation.
+
+A single instance can be expanded to a clusterized version using Patroni, which is recommended for a
+highly available architecture.
+
+Follow below the instructions on how to set up PostgreSQL replication as a single instance database.
+Alternatively, you can look at the [Multi-node database replication](#multi-node-database-replication)
+instructions on setting up replication with a Patroni cluster.
+
+### PostgreSQL replication
The GitLab **primary** node where the write operations happen connects to
the **primary** database server, and **secondary** nodes
@@ -48,7 +61,7 @@ WARNING:
Geo works with streaming replication. Logical replication is not supported at this time.
There is an [issue where support is being discussed](https://gitlab.com/gitlab-org/gitlab/-/issues/7420).
-### Step 1. Configure the **primary** server
+#### Step 1. Configure the **primary** server
1. SSH into your GitLab **primary** server and login as root:
@@ -77,11 +90,7 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o
This command uses your defined `external_url` in `/etc/gitlab/gitlab.rb`.
-1. GitLab 10.4 and up only: Do the following to make sure the `gitlab` database user has a password defined:
-
- NOTE:
- Until FDW settings are removed in GitLab version 14.0, avoid using single or double quotes in the
- password for PostgreSQL as that leads to errors when reconfiguring.
+1. Define a password for the `gitlab` database user:
Generate a MD5 hash of the desired password:
@@ -103,18 +112,28 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o
# must be present in all application nodes.
gitlab_rails['db_password'] = '<your_password_here>'
```
+
+1. Define a password for the database [replication user](https://wiki.postgresql.org/wiki/Streaming_Replication).
-1. Omnibus GitLab already has a [replication user](https://wiki.postgresql.org/wiki/Streaming_Replication)
- called `gitlab_replicator`. You must set the password for this user manually.
- You are prompted to enter a password:
+ We will use the username defined in `/etc/gitlab/gitlab.rb` under the `postgresql['sql_replication_user']`
+ setting. The default value is `gitlab_replicator`, but if you changed it to something else, adapt
+ the instructions below.
+
+ Generate a MD5 hash of the desired password:
```shell
- gitlab-ctl set-replication-password
+ gitlab-ctl pg-password-md5 gitlab_replicator
+ # Enter password: <your_password_here>
+ # Confirm password: <your_password_here>
+ # 950233c0dfc2f39c64cf30457c3b7f1e
```
- This command also reads the `postgresql['sql_replication_user']` Omnibus
- setting in case you have changed `gitlab_replicator` username to something
- else.
+ Edit `/etc/gitlab/gitlab.rb`:
+
+ ```ruby
+ # Fill with the hash generated by `gitlab-ctl pg-password-md5 gitlab_replicator`
+ postgresql['sql_replication_password'] = '<md5_hash_of_your_password>'
+ ```
If you are using an external database not managed by Omnibus GitLab, you need
to create the replicator user and define a password to it manually:
@@ -275,7 +294,7 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o
need it when setting up the **secondary** node! The certificate is not sensitive
data.
-### Step 2. Configure the **secondary** server
+#### Step 2. Configure the **secondary** server
1. SSH into your GitLab **secondary** server and login as root:
@@ -376,6 +395,7 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o
## Database credentials password (defined previously in primary node)
## - replicate same values here as defined in primary node
##
+ postgresql['sql_replication_password'] = '<md5_hash_of_your_password>'
postgresql['sql_user_password'] = '<md5_hash_of_your_password>'
gitlab_rails['db_password'] = '<your_password_here>'
```
@@ -395,7 +415,7 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o
gitlab-ctl restart postgresql
```
-### Step 3. Initiate the replication process
+#### Step 3. Initiate the replication process
Below we provide a script that connects the database on the **secondary** node to
the database on the **primary** node, replicates the database, and creates the
@@ -461,37 +481,37 @@ data before running `pg_basebackup`.
The replication process is now complete.
-## PgBouncer support (optional)
+### PgBouncer support (optional)
[PgBouncer](https://www.pgbouncer.org/) may be used with GitLab Geo to pool
-PostgreSQL connections. We recommend using PgBouncer if you use GitLab in a
-high-availability configuration with a cluster of nodes supporting a Geo
-**primary** site and two other clusters of nodes supporting a Geo **secondary** site.
-One for the main database and the other for the tracking database. For more information,
+PostgreSQL connections, which can improve performance even when using in a
+single instance installation.
+
+We recommend using PgBouncer if you use GitLab in a highly available
+configuration with a cluster of nodes supporting a Geo **primary** site and
+two other clusters of nodes supporting a Geo **secondary** site. One for the
+main database and the other for the tracking database. For more information,
see [High Availability with Omnibus GitLab](../../postgresql/replication_and_failover.md).
-## Patroni support
+## Multi-node database replication
-Support for Patroni is intended to replace `repmgr` as a
-[highly available PostgreSQL solution](../../postgresql/replication_and_failover.md)
-on the primary node, but it can also be used for PostgreSQL HA on a secondary
-site. Similar to `repmgr`, using Patroni on a secondary node is optional.
+In GitLab 14.0, Patroni replaced `repmgr` as the supported
+[highly available PostgreSQL solution](../../postgresql/replication_and_failover.md).
-Starting with GitLab 13.5, Patroni is available for _experimental_ use with Geo
-primary and secondary sites. Due to its experimental nature, Patroni support is
-subject to change without notice.
+NOTE:
+If you still haven't [migrated from repmgr to Patroni](#migrating-from-repmgr-to-patroni) you're highly advised to do so.
-This experimental implementation has the following limitations:
+### Patroni support
-- Whenever `gitlab-ctl reconfigure` runs on a Patroni Leader instance, there's a
- chance of the node be demoted due to the required short-time restart. To
- avoid this, you can pause auto-failover by running `gitlab-ctl patroni pause`.
- After a reconfigure, it resumes on its own.
+Patroni is the official replication management solution for Geo. It
+can be used to build a highly available cluster on the **primary** and a **secondary** Geo site.
+Using Patroni on a **secondary** site is optional and you don't have to use the same amount of
+nodes on each Geo site.
For instructions about how to set up Patroni on the primary site, see the
[PostgreSQL replication and failover with Omnibus GitLab](../../postgresql/replication_and_failover.md#patroni) page.
-### Configuring Patroni cluster for a Geo secondary site
+#### Configuring Patroni cluster for a Geo secondary site
In a Geo secondary site, the main PostgreSQL database is a read-only replica of the primary site’s PostgreSQL database.
@@ -503,7 +523,7 @@ configuration for the secondary site. The internal load balancer provides a sing
endpoint for connecting to the Patroni cluster's leader whenever a new leader is
elected. Be sure to use [password credentials](../../postgresql/replication_and_failover.md#database-authorization-for-patroni) and other database best practices.
-#### Step 1. Configure Patroni permanent replication slot on the primary site
+##### Step 1. Configure Patroni permanent replication slot on the primary site
To set up database replication with Patroni on a secondary node, we need to
configure a _permanent replication slot_ on the primary node's Patroni cluster,
@@ -526,8 +546,8 @@ Leader instance**:
retry_join: %w[CONSUL_PRIMARY1_IP CONSUL_PRIMARY2_IP CONSUL_PRIMARY3_IP]
}
- repmgr['enable'] = false
-
+ roles ['patroni_role']
+
# You need one entry for each secondary, with a unique name following PostgreSQL slot_name constraints:
#
# Configuration syntax is: 'unique_slotname' => { 'type' => 'physical' },
@@ -539,15 +559,18 @@ Leader instance**:
patroni['use_pg_rewind'] = true
patroni['postgresql']['max_wal_senders'] = 8 # Use double of the amount of patroni/reserved slots (3 patronis + 1 reserved slot for a Geo secondary).
patroni['postgresql']['max_replication_slots'] = 8 # Use double of the amount of patroni/reserved slots (3 patronis + 1 reserved slot for a Geo secondary).
+ patroni['replication_password'] = 'PLAIN_TEXT_POSTGRESQL_REPLICATION_PASSWORD'
- postgresql['md5_auth_cidr_addresses'] = [
- 'PATRONI_PRIMARY1_IP/32', 'PATRONI_PRIMARY2_IP/32', 'PATRONI_PRIMARY3_IP/32', 'PATRONI_PRIMARY_PGBOUNCER/32',
- 'PATRONI_SECONDARY1_IP/32', 'PATRONI_SECONDARY2_IP/32', 'PATRONI_SECONDARY3_IP/32', 'PATRONI_SECONDARY_PGBOUNCER/32' # We list all secondary instances as they can all become a Standby Leader
+ # We list all secondary instances as they can all become a Standby Leader
+ postgresql['md5_auth_cidr_addresses'] = %w[
+ PATRONI_PRIMARY1_IP/32 PATRONI_PRIMARY2_IP/32 PATRONI_PRIMARY3_IP/32 PATRONI_PRIMARY_PGBOUNCER/32
+ PATRONI_SECONDARY1_IP/32 PATRONI_SECONDARY2_IP/32 PATRONI_SECONDARY3_IP/32 PATRONI_SECONDARY_PGBOUNCER/32
]
postgresql['pgbouncer_user_password'] = 'PGBOUNCER_PASSWORD_HASH'
postgresql['sql_replication_password'] = 'POSTGRESQL_REPLICATION_PASSWORD_HASH'
postgresql['sql_user_password'] = 'POSTGRESQL_PASSWORD_HASH'
+ postgresql['listen_address'] = '0.0.0.0' # You can use a public or VPC address here instead
```
1. Reconfigure GitLab for the changes to take effect:
@@ -556,7 +579,7 @@ Leader instance**:
gitlab-ctl reconfigure
```
-#### Step 2. Configure the internal load balancer on the primary site
+##### Step 2. Configure the internal load balancer on the primary site
To avoid reconfiguring the Standby Leader on the secondary site whenever a new
Leader is elected on the primary site, we need to set up a TCP internal load
@@ -600,7 +623,7 @@ backend postgresql
Refer to your preferred Load Balancer's documentation for further guidance.
-#### Step 3. Configure a PgBouncer node on the secondary site
+##### Step 3. Configure a PgBouncer node on the secondary site
A production-ready and highly available configuration requires at least
three Consul nodes, a minimum of one PgBouncer node, but it’s recommended to have
@@ -624,19 +647,23 @@ Follow the minimal configuration for the PgBouncer node:
roles ['pgbouncer_role']
# PgBouncer configuration
+ pgbouncer['admin_users'] = %w(pgbouncer gitlab-consul)
pgbouncer['users'] = {
+ 'gitlab-consul': {
+ # Generate it with: `gitlab-ctl pg-password-md5 gitlab-consul`
+ password: 'GITLAB_CONSUL_PASSWORD_HASH'
+ },
'pgbouncer': {
+ # Generate it with: `gitlab-ctl pg-password-md5 pgbouncer`
password: 'PGBOUNCER_PASSWORD_HASH'
}
}
# Consul configuration
consul['watchers'] = %w(postgresql)
-
consul['configuration'] = {
retry_join: %w[CONSUL_SECONDARY1_IP CONSUL_SECONDARY2_IP CONSUL_SECONDARY3_IP]
}
-
consul['monitoring_service_discovery'] = true
```
@@ -652,13 +679,13 @@ Follow the minimal configuration for the PgBouncer node:
gitlab-ctl write-pgpass --host 127.0.0.1 --database pgbouncer --user pgbouncer --hostuser gitlab-consul
```
-1. Restart the PgBouncer service:
+1. Reload the PgBouncer service:
```shell
- gitlab-ctl restart pgbouncer
+ gitlab-ctl hup pgbouncer
```
-#### Step 4. Configure a Standby cluster on the secondary site
+##### Step 4. Configure a Standby cluster on the secondary site
NOTE:
If you are converting a secondary site to a Patroni Cluster, you must start
@@ -676,21 +703,18 @@ For each Patroni instance on the secondary site:
1. Edit `/etc/gitlab/gitlab.rb` and add the following:
```ruby
- roles ['consul_role', 'postgres_role']
+ roles ['consul_role', 'patroni_role']
consul['enable'] = true
consul['configuration'] = {
retry_join: %w[CONSUL_SECONDARY1_IP CONSUL_SECONDARY2_IP CONSUL_SECONDARY3_IP]
}
- repmgr['enable'] = false
-
postgresql['md5_auth_cidr_addresses'] = [
'PATRONI_SECONDARY1_IP/32', 'PATRONI_SECONDARY2_IP/32', 'PATRONI_SECONDARY3_IP/32', 'PATRONI_SECONDARY_PGBOUNCER/32',
# Any other instance that needs access to the database as per documentation
]
- patroni['enable'] = false
patroni['standby_cluster']['enable'] = true
patroni['standby_cluster']['host'] = 'INTERNAL_LOAD_BALANCER_PRIMARY_IP'
patroni['standby_cluster']['port'] = INTERNAL_LOAD_BALANCER_PRIMARY_PORT
@@ -699,6 +723,11 @@ For each Patroni instance on the secondary site:
patroni['use_pg_rewind'] = true
patroni['postgresql']['max_wal_senders'] = 5 # A minimum of three for one replica, plus two for each additional replica
patroni['postgresql']['max_replication_slots'] = 5 # A minimum of three for one replica, plus two for each additional replica
+
+ postgresql['pgbouncer_user_password'] = 'PGBOUNCER_PASSWORD_HASH'
+ postgresql['sql_replication_password'] = 'POSTGRESQL_REPLICATION_PASSWORD_HASH'
+ postgresql['sql_user_password'] = 'POSTGRESQL_PASSWORD_HASH'
+ postgresql['listen_address'] = '0.0.0.0' # You can use a public or VPC address here instead
```
1. Reconfigure GitLab for the changes to take effect.
@@ -708,28 +737,6 @@ For each Patroni instance on the secondary site:
gitlab-ctl reconfigure
```
-1. Remove the PostgreSQL data directory:
-
- WARNING:
- If you are converting a secondary site to a Patroni Cluster, you must skip
- this step on the PostgreSQL instance.
-
- ```shell
- rm -rf /var/opt/gitlab/postgresql/data
- ```
-
-1. Edit `/etc/gitlab/gitlab.rb` to enable Patroni:
-
- ```ruby
- patroni['enable'] = true
- ```
-
-1. Reconfigure GitLab for the changes to take effect:
-
- ```shell
- gitlab-ctl reconfigure
- ```
-
### Migrating from repmgr to Patroni
1. Before migrating, it is recommended that there is no replication lag between the primary and secondary sites and that replication is paused. In GitLab 13.2 and later, you can pause and resume replication with `gitlab-ctl geo-replication-pause` and `gitlab-ctl geo-replication-resume` on a Geo secondary database node.
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 2d2e3285356..9f50c638386 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -1158,7 +1158,7 @@ POST /projects
| `path` | string | **{check-circle}** Yes (if name isn't provided) | Repository name for new project. Generated based on name if not provided (generated as lowercase with dashes). |
| `allow_merge_on_skipped_pipeline` | boolean | **{dotted-circle}** No | Set whether or not merge requests can be merged with skipped jobs. |
| `analytics_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private` or `enabled` |
-| `approvals_before_merge` **(PREMIUM)** | integer | **{dotted-circle}** No | How many approvers should approve merge requests by default. |
+| `approvals_before_merge` **(PREMIUM)** | integer | **{dotted-circle}** No | How many approvers should approve merge requests by default. To configure approval rules, see [Merge request approvals API](merge_request_approvals.md). |
| `auto_cancel_pending_pipelines` | string | **{dotted-circle}** No | Auto-cancel pending pipelines. This isn't a boolean, but enabled/disabled. |
| `auto_devops_deploy_strategy` | string | **{dotted-circle}** No | Auto Deploy strategy (`continuous`, `manual` or `timed_incremental`). |
| `auto_devops_enabled` | boolean | **{dotted-circle}** No | Enable Auto DevOps for this project. |
@@ -1233,7 +1233,7 @@ POST /projects/user/:user_id
| `name` | string | **{check-circle}** Yes | The name of the new project. |
| `allow_merge_on_skipped_pipeline` | boolean | **{dotted-circle}** No | Set whether or not merge requests can be merged with skipped jobs. |
| `analytics_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private` or `enabled` |
-| `approvals_before_merge` **(PREMIUM)** | integer | **{dotted-circle}** No | How many approvers should approve merge requests by default. |
+| `approvals_before_merge` **(PREMIUM)** | integer | **{dotted-circle}** No | How many approvers should approve merge requests by default. To configure approval rules, see [Merge request approvals API](merge_request_approvals.md). |
| `auto_cancel_pending_pipelines` | string | **{dotted-circle}** No | Auto-cancel pending pipelines. This isn't a boolean, but enabled/disabled. |
| `auto_devops_deploy_strategy` | string | **{dotted-circle}** No | Auto Deploy strategy (`continuous`, `manual` or `timed_incremental`). |
| `auto_devops_enabled` | boolean | **{dotted-circle}** No | Enable Auto DevOps for this project. |
@@ -1305,7 +1305,7 @@ PUT /projects/:id
|-------------------------------------------------------------|----------------|------------------------|-------------|
| `allow_merge_on_skipped_pipeline` | boolean | **{dotted-circle}** No | Set whether or not merge requests can be merged with skipped jobs. |
| `analytics_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private` or `enabled` |
-| `approvals_before_merge` **(PREMIUM)** | integer | **{dotted-circle}** No | How many approvers should approve merge request by default. |
+| `approvals_before_merge` **(PREMIUM)** | integer | **{dotted-circle}** No | How many approvers should approve merge request by default. To configure approval rules, see [Merge request approvals API](merge_request_approvals.md). |
| `auto_cancel_pending_pipelines` | string | **{dotted-circle}** No | Auto-cancel pending pipelines. This isn't a boolean, but enabled/disabled. |
| `auto_devops_deploy_strategy` | string | **{dotted-circle}** No | Auto Deploy strategy (`continuous`, `manual`, or `timed_incremental`). |
| `auto_devops_enabled` | boolean | **{dotted-circle}** No | Enable Auto DevOps for this project. |
diff --git a/doc/ci/environments/environments_dashboard.md b/doc/ci/environments/environments_dashboard.md
index 4ee9aa9a5ba..a89bc1c89aa 100644
--- a/doc/ci/environments/environments_dashboard.md
+++ b/doc/ci/environments/environments_dashboard.md
@@ -20,8 +20,8 @@ see which pipelines are green and which are red allowing you to
diagnose if there is a block at a particular point, or if there's
a more systemic problem you need to investigate.
-You can access the dashboard from the top bar by clicking
-**More > Environments**.
+You can access the dashboard on the top bar by selecting
+**Menu > Environments**.
![Environments Dashboard with projects](img/environments_dashboard_v12_5.png)
diff --git a/doc/ci/quick_start/index.md b/doc/ci/quick_start/index.md
index cd0be8b0561..225794aec17 100644
--- a/doc/ci/quick_start/index.md
+++ b/doc/ci/quick_start/index.md
@@ -21,7 +21,7 @@ If you are migrating from another CI/CD tool, view this documentation:
- [Migrate from Jenkins](../migration/jenkins.md).
> - <i class="fa fa-youtube-play youtube" aria-hidden="true"></i>&nbsp;Watch [First time GitLab & CI/CD](https://www.youtube.com/watch?v=kTNfi5z6Uvk&t=553s). This includes a quick introduction to GitLab, the first steps with CI/CD, building a Go project, running tests, using the CI/CD pipeline editor, detecting secrets and security vulnerabilities and offers more exercises for async practice.
-> - <i class="fa fa-youtube-play youtube" aria-hidden="true"></i>&nbsp;Watch [Intro to GitLab CI](https://www.youtube.com/watch?v=l5705U8s_nQ&t=358s). This workshop uses the Web IDE to quickly get going with building source code using CI/CD, and run unit tests.
+> - <i class="fa fa-youtube-play youtube" aria-hidden="true"></i>&nbsp;Watch [Intro to GitLab CI](https://www.youtube.com/watch?v=l5705U8s_nQ&t=358s). This workshop uses the Web IDE to quickly get going with building source code using CI/CD, and run unit tests.
## CI/CD process overview
@@ -76,7 +76,7 @@ All of this is defined in the `.gitlab-ci.yml` file.
To create a `.gitlab-ci.yml` file:
-1. Go to **Project overview > Details**.
+1. On the left sidebar, select **Project information > Details**.
1. Above the file list, select the branch you want to commit to,
click the plus icon, then select **New file**:
diff --git a/doc/development/documentation/styleguide/index.md b/doc/development/documentation/styleguide/index.md
index e75751a36bc..32a130aeeb8 100644
--- a/doc/development/documentation/styleguide/index.md
+++ b/doc/development/documentation/styleguide/index.md
@@ -1069,36 +1069,42 @@ document to ensure it links to the most recent version of the file.
## Navigation
-When documenting navigation through the user interface:
-
-- Use the exact wording as shown in the UI, including any capital letters as-is.
-- Use bold text for navigation items.
+When documenting navigation through the user interface, use these terms and styles.
### What to call the menus
Use these terms when referring to the main GitLab user interface
elements:
-- **Top menu**: This is the top menu that spans the width of the user interface.
- It includes the GitLab logo, search field, counters, and the user's avatar.
+- **Top bar**: This is the top bar that spans the width of the user interface.
+ It includes the menu, the GitLab logo, search field, counters, and the user's avatar.
- **Left sidebar**: This is the navigation sidebar on the left of the user
interface, specific to the project or group.
- **Right sidebar**: This is the navigation sidebar on the right of the user
interface, specific to the open issue, merge request, or epic.
-### How to document the left sidebar
+### How to document the menus
-To be consistent, use this format when you refer to the left sidebar.
+To be consistent, use this format when you write about UI navigation.
-- Go to your project and select **Settings > CI/CD**.
-- Go to your group and select **Settings > CI/CD**.
-- Go to the Admin Area (**{admin}**) and select **Overview > Projects**.
+1. On the top bar, select **Menu > Project** and find your project.
+1. On the left sidebar, select **Settings > CI/CD**.
+1. Expand **General pipelines**.
-For expandable menus, use this format:
+Another example:
-1. Go to your group and select **Settings > CI/CD**.
+1. On the top bar, select **Menu > Group** and find your group.
+1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **General pipelines**.
+An Admin Area example:
+
+`1. On the top bar, select **Menu >** **{admin}** **Admin**.`
+
+This text generates this HTML:
+
+1. On the top bar, select **Menu >** **{admin}** **Admin**.
+
## Images
Images, including screenshots, can help a reader better understand a concept.
@@ -1377,10 +1383,10 @@ readability of the text.
For example, this Markdown adds little to the accompanying text:
```markdown
-1. Go to **{home}** **Project overview > Details**.
+1. Go to **{home}** **Project information > Details**.
```
-1. Go to **{home}** **Project overview > Details**.
+1. Go to **{home}** **Project information > Details**.
However, these tables might help the reader connect the text to the user
interface:
diff --git a/doc/development/stage_group_dashboards.md b/doc/development/stage_group_dashboards.md
index ba2c83b7084..277c12fc938 100644
--- a/doc/development/stage_group_dashboards.md
+++ b/doc/development/stage_group_dashboards.md
@@ -163,14 +163,7 @@ stageGroupDashboards.dashboard('product_planning')
.stageGroupDashboardTrailer()
```
-We provide basic customization to filter out the components essential to your group's activities. By default, all components `web`, `api`, `git`, and `sidekiq` are available in the dashboard. We can change this to only show `web` and `api`, or only show `sidekiq`:
-
-```jsonnet
-stageGroupDashboards.dashboard('product_planning', components=['web', 'api']).stageGroupDashboardTrailer()
-# Or
-stageGroupDashboards.dashboard('product_planning', components=['sidekiq']).stageGroupDashboardTrailer()
-
-```
+We provide basic customization to filter out the components essential to your group's activities. By default, only the `web`, `api`, and `sidekiq` components are available in the dashboard, while `git` is hidden. See [how to enable available components and optional graphs](#optional-graphs).
You can also append further information or custom metrics to a dashboard. This is an example that adds some links and a total request rate on the top of the page:
@@ -219,3 +212,31 @@ If you want to see the workflow in action, we've recorded a pairing session on c
available on [GitLab Unfiltered](https://youtu.be/shEd_eiUjdI).
For deeper customization and more complicated metrics, visit the [Grafonnet lib](https://github.com/grafana/grafonnet-lib) project and the [GitLab Prometheus Metrics](../administration/monitoring/prometheus/gitlab_metrics.md#gitlab-prometheus-metrics) documentation.
+
+### Optional Graphs
+
+Some Graphs aren't relevant for all groups, so they aren't added to
+the dashboard by default. They can be added by customizing the
+dashboard.
+
+By default, only the `web`, `api`, and `sidekiq` metrics are
+shown. If you wish to see the metrics from the `git` fleet (or any
+other component that might be added in the future), this could be
+configured as follows:
+
+```jsonnet
+stageGroupDashboards
+.dashboard('source_code', components=stageGroupDashboards.supportedComponents)
+.stageGroupDashboardTrailer()
+```
+
+If your group is interested in Sidekiq job durations and their
+thresholds, these graphs can be added by calling the
+`.addSidekiqJobDurationByUrgency` function:
+
+```jsonnet
+stageGroupDashboards
+.dashboard('access')
+.addSidekiqJobDurationByUrgency()
+.stageGroupDashboardTrailer()
+```
diff --git a/doc/subscriptions/index.md b/doc/subscriptions/index.md
index 0af65a60c7e..7a2f6e1cae7 100644
--- a/doc/subscriptions/index.md
+++ b/doc/subscriptions/index.md
@@ -165,12 +165,12 @@ To change the password for this customers portal account:
1. Make the required changes to the **Your password** section.
1. Click **Save changes**.
-## Community program subscriptions
+## Community program subscriptions
### GitLab for Education
-For qualifying non-profit educational institutions, the [GitLab for Education](https://about.gitlab.com/solutions/education/) program provides
-the top GitLab tier, plus 50,000 CI minutes per month.
+For qualifying non-profit educational institutions, the [GitLab for Education](https://about.gitlab.com/solutions/education/) program provides
+the top GitLab tier, plus 50,000 CI minutes per month.
The GitLab for Education license can only be used for instructional-use or
non-commercial academic research.
@@ -180,8 +180,8 @@ Find more information on how to apply and renew at
### GitLab for Open Source
-For qualifying open source projects, the [GitLab for Open Source](https://about.gitlab.com/solutions/open-source/) program provides
-the top GitLab tier, plus 50,000 CI minutes per month.
+For qualifying open source projects, the [GitLab for Open Source](https://about.gitlab.com/solutions/open-source/) program provides
+the top GitLab tier, plus 50,000 CI minutes per month.
You can find more information about the [program requirements](https://about.gitlab.com/solutions/open-source/join/#requirements),
[renewals](https://about.gitlab.com/solutions/open-source/join/$renewals),
@@ -211,13 +211,13 @@ After you ensure that you are using OSI-approved licenses for your projects, you
###### License overview
-Go to **Project Overview > Details**. Take a screenshot that includes a view of the license you've chosen for your project.
+On the left sidebar, select **Project Information > Details**. Take a screenshot that includes a view of the license you've chosen for your project.
![License overview](img/license-overview.png)
###### License file
-Navigate to one of the license files that you uploaded. You can usually find the license file by selecting **Project Overview > Details** and scanning the page for the license.
+Navigate to one of the license files that you uploaded. You can usually find the license file by selecting **Project Information > Details** and scanning the page for the license.
Make sure the screenshot includes the title of the license.
![License file](img/license-file.png)
@@ -230,7 +230,7 @@ As a result, we ask that all projects under this license are publicly visible.
Follow these instructions to take a screenshot of the publicly visible settings:
- 1. Go to your project and select **Settings**.
+ 1. Go to your project and select **Settings**.
1. Expand **Visibility, project features, permissions**.
1. Set **Project Visibility** to **Public**.
1. Ensure others can request access by selecting the **Users can request access** checkbox.
@@ -241,14 +241,14 @@ Follow these instructions to take a screenshot of the publicly visible settings:
NOTE:
From time to time, GitLab allows exceptions. One or two projects within a group can be private if there is a legitimate need for it, for example,
-if a project holds sensitive data. Email `opensource@gitlab.com` with details of your use case to request written permission for exceptions.
+if a project holds sensitive data. Email `opensource@gitlab.com` with details of your use case to request written permission for exceptions.
### GitLab for Startups
-For qualifying startups, the [GitLab for Startups](https://about.gitlab.com/solutions/startups/) program provides
-the top GitLab tier, plus 50,000 CI minutes per month for 12 months.
+For qualifying startups, the [GitLab for Startups](https://about.gitlab.com/solutions/startups/) program provides
+the top GitLab tier, plus 50,000 CI minutes per month for 12 months.
-For more information, including program requirements, see the [Startup program's landing page](https://about.gitlab.com/solutions/startups/).
+For more information, including program requirements, see the [Startup program's landing page](https://about.gitlab.com/solutions/startups/).
Send all questions and requests related to the GitLab for Startups program to `startups@gitlab.com`.
@@ -257,7 +257,7 @@ Send all questions and requests related to the GitLab for Startups program to `s
Because these Community Programs are free of cost, regular Priority Support is not included. However, it can be purchased at a 95% discount in some cases.
If interested, email the relevant community program team: `education@gitlab.com`, `opensource@gitlab.com`, or `startups@gitlab.com`.
-As a community member, you can follow this diagram to find support:
+As a community member, you can follow this diagram to find support:
![Support diagram](img/support-diagram.png)
diff --git a/doc/user/application_security/security_dashboard/index.md b/doc/user/application_security/security_dashboard/index.md
index f0b3d895df5..01de066367c 100644
--- a/doc/user/application_security/security_dashboard/index.md
+++ b/doc/user/application_security/security_dashboard/index.md
@@ -150,8 +150,7 @@ the following:
![Security Center Dashboard with projects](img/security_center_dashboard_v13_4.png)
-To view the Security Center, from the navigation bar at the top of the page, select
-**More > Security**.
+To view the Security Center, on the top bar, select **Menu > Security**.
### Adding projects to the Security Center
diff --git a/doc/user/operations_dashboard/index.md b/doc/user/operations_dashboard/index.md
index be3454dbd02..29ca3a48e70 100644
--- a/doc/user/operations_dashboard/index.md
+++ b/doc/user/operations_dashboard/index.md
@@ -12,7 +12,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
The Operations Dashboard provides a summary of each project's operational health,
including pipeline and alert status.
-The dashboard can be accessed from the top bar, by clicking **More > Operations**.
+To access the dashboard, on the top bar, select **Menu > Operations**.
## Adding a project to the dashboard
diff --git a/doc/user/packages/composer_repository/index.md b/doc/user/packages/composer_repository/index.md
index 4d3d3cc24a3..b5170dfa55b 100644
--- a/doc/user/packages/composer_repository/index.md
+++ b/doc/user/packages/composer_repository/index.md
@@ -131,7 +131,7 @@ To view the published package, go to **Packages & Registries > Package Registry*
A more detailed Composer CI/CD file is also available as a `.gitlab-ci.yml` template:
-1. On the left sidebar, click **Project overview**.
+1. On the left sidebar, select **Project information**.
1. Above the file list, click **Set up CI/CD**. If this button is not available, select **CI/CD Configuration** and then **Edit**.
1. From the **Apply a template** list, select **Composer**.
diff --git a/doc/user/packages/workflows/project_registry.md b/doc/user/packages/workflows/project_registry.md
index 3e1c1e7f2ad..12978ad72a5 100644
--- a/doc/user/packages/workflows/project_registry.md
+++ b/doc/user/packages/workflows/project_registry.md
@@ -34,8 +34,8 @@ of each package management system to publish different package types to the same
Let's take a look at how you might create a public place to hold all of your public packages.
-1. Create a new project in GitLab. The project doesn't require any code or content. Note the project ID
- that's displayed on the project overview page.
+1. Create a new project in GitLab. The project doesn't require any code or content.
+1. On the left sidebar, select **Project information**, and note the project ID.
1. Create an access token. All package types in the Package Registry are accessible by using
[GitLab personal access tokens](../../profile/personal_access_tokens.md).
If you're using CI/CD, you can use CI job tokens (`CI_JOB_TOKEN`) to authenticate.
diff --git a/doc/user/project/badges.md b/doc/user/project/badges.md
index ee7c92818e8..64a375c6a1d 100644
--- a/doc/user/project/badges.md
+++ b/doc/user/project/badges.md
@@ -15,7 +15,7 @@ points to. Examples for badges can be the [pipeline status](../../ci/pipelines/s
[test coverage](../../ci/pipelines/settings.md#test-coverage-report-badge), or ways to contact the
project maintainers.
-![Badges on Project overview page](img/project_overview_badges_v13_10.png)
+![Badges on Project information page](img/project_overview_badges_v13_10.png)
## Project badges
diff --git a/doc/user/project/index.md b/doc/user/project/index.md
index df14c9d328e..0dcbf997452 100644
--- a/doc/user/project/index.md
+++ b/doc/user/project/index.md
@@ -97,7 +97,7 @@ Projects include the following [features](https://about.gitlab.com/features/):
- [Security Dashboard](../application_security/security_dashboard/index.md) **(ULTIMATE)**
- [Syntax highlighting](highlighting.md): Customize
your code blocks, overriding the default language choice.
-- [Badges](badges.md): Add an image to the project overview.
+- [Badges](badges.md): Add an image to the **Project information** page.
- [Releases](releases/index.md): Take a snapshot of
the source, build output, metadata, and artifacts
associated with a released version of your code.
diff --git a/doc/user/project/merge_requests/allow_collaboration.md b/doc/user/project/merge_requests/allow_collaboration.md
index 5917d67c398..63d5119c1b4 100644
--- a/doc/user/project/merge_requests/allow_collaboration.md
+++ b/doc/user/project/merge_requests/allow_collaboration.md
@@ -24,19 +24,17 @@ of the merge request.
## Enabling commit edits from upstream members
In [GitLab 13.7 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/23308),
-this setting is enabled by default. It can be changed by users with Developer
-permissions to the source project. Once enabled, upstream members can
-retry the pipelines and jobs of the merge request:
+this setting is enabled by default. It can be changed by users with the
+Developer [role](../../permissions.md) for the source project. After it's enabled,
+upstream members can retry the pipelines and jobs of the merge request:
-1. While creating or editing a merge request, select the checkbox **Allow
- commits from members who can merge to the target branch**.
+1. While creating or editing a merge request, scroll to **Contribution** and
+ then select the **Allow commits from members who can merge to the target branch**.
+ checkbox.
+1. Finish creating your merge request.
- ![Enable contribution](img/allow_collaboration.png)
-
-1. Once the merge request is created, you can see that commits from members who
- can merge to the target branch are allowed.
-
- ![Check that contribution is enabled](img/allow_collaboration_after_save.png)
+After you create the merge request, the merge request widget displays a message:
+**Members who can merge are allowed to add commits.**
## Pushing to the fork as the upstream member
@@ -48,41 +46,39 @@ Assuming that:
- The forked project URL is `git@gitlab.com:thedude/awesome-project.git`.
- The branch of the merge request is `update-docs`.
-Here's how the process would look like:
-
-1. First, you need to get the changes that the merge request has introduced.
- Click the **Check out branch** button that has some pre-populated
- commands that you can run.
-
- ![Check out branch button](img/checkout_button.png)
+To find and work with the changes from the fork:
-1. Use the copy button to copy the first command and paste them
- in your terminal:
+1. Open the merge request page, and select the **Overview** tab.
+1. Scroll to the merge request widget, and select **Check out branch**:
+ ![Check out branch button](img/commit-button_v13_12.png)
+1. In the modal window, select **{copy-to-clipboard}** (**Copy**) for step 1
+ to copy the `git fetch` and `git checkout` instructions to your clipboard.
+ Paste the commands (which look like this example) into your terminal:
```shell
git fetch git@gitlab.com:thedude/awesome-project.git update-docs
git checkout -b thedude-awesome-project-update-docs FETCH_HEAD
```
- This fetches the branch of the forked project and then create a local branch
+ These commands fetch the branch from the forked project, and create a local branch
based off the fetched branch.
-1. Make any changes you want and commit.
-1. Push to the forked project:
+1. Make your changes to the local copy of the branch, and then commit them.
+1. In your terminal, push your local changes back up to the forked project. This
+ command pushes the local branch `thedude-awesome-project-update-docs` to the
+ `update-docs` branch of the `git@gitlab.com:thedude/awesome-project.git` repository:
```shell
git push git@gitlab.com:thedude/awesome-project.git thedude-awesome-project-update-docs:update-docs
```
- Note the colon (`:`) between the two branches. The above command pushes the
- local branch `thedude-awesome-project-update-docs` to the
- `update-docs` branch of the `git@gitlab.com:thedude/awesome-project.git` repository.
+ Note the colon (`:`) between the two branches.
## Troubleshooting
### Pipeline status unavailable from MR page of forked project
-When a user forks a project, the permissions on the forked copy are not copied over
+When a user forks a project, the permissions of the forked copy are not copied
from the original project. The creator of the fork must grant permissions to the
forked copy before members in the upstream project can view or merge the changes
in the merge request.
diff --git a/doc/user/project/merge_requests/approvals/index.md b/doc/user/project/merge_requests/approvals/index.md
index ac48e44da52..daea0dff26d 100644
--- a/doc/user/project/merge_requests/approvals/index.md
+++ b/doc/user/project/merge_requests/approvals/index.md
@@ -42,7 +42,7 @@ for more control of the level of oversight and security your project needs, incl
- [Require security team approval.](settings.md#security-approvals-in-merge-requests)
You can configure your merge request approval rules and settings through the GitLab
-user interface or [with the API](../../../../api/merge_request_approvals.md).
+user interface or with the [Merge request approvals API](../../../../api/merge_request_approvals.md).
## Approve a merge request
diff --git a/doc/user/project/merge_requests/img/allow_collaboration.png b/doc/user/project/merge_requests/img/allow_collaboration.png
deleted file mode 100644
index cc13493646d..00000000000
--- a/doc/user/project/merge_requests/img/allow_collaboration.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/merge_requests/img/allow_collaboration_after_save.png b/doc/user/project/merge_requests/img/allow_collaboration_after_save.png
deleted file mode 100644
index 0d34071339c..00000000000
--- a/doc/user/project/merge_requests/img/allow_collaboration_after_save.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/merge_requests/img/commit-button_v13_12.png b/doc/user/project/merge_requests/img/commit-button_v13_12.png
new file mode 100644
index 00000000000..be154b9e60b
--- /dev/null
+++ b/doc/user/project/merge_requests/img/commit-button_v13_12.png
Binary files differ
diff --git a/doc/user/project/milestones/index.md b/doc/user/project/milestones/index.md
index 2399774c96d..3f7d5b6aac1 100644
--- a/doc/user/project/milestones/index.md
+++ b/doc/user/project/milestones/index.md
@@ -38,8 +38,8 @@ You can assign **group milestones** to any issue or merge request of any project
To view the group milestone list, in a group, go to **{issues}** **Issues > Milestones**.
You can also view all milestones you have access to in the dashboard milestones list.
-To view both project milestones and group milestones you have access to, select **More > Milestones**
-on the top navigation bar.
+To view both project milestones and group milestones you have access to, select **Menu > Milestones**
+on the top bar.
For information about project and group milestones API, see:
diff --git a/doc/user/project/pages/getting_started/pages_ci_cd_template.md b/doc/user/project/pages/getting_started/pages_ci_cd_template.md
index e2280cb0f39..4f2b62beab1 100644
--- a/doc/user/project/pages/getting_started/pages_ci_cd_template.md
+++ b/doc/user/project/pages/getting_started/pages_ci_cd_template.md
@@ -17,7 +17,7 @@ Your GitLab repository should contain files specific to an SSG, or plain HTML.
After you complete these steps, you may need to do additional
configuration for the Pages site to generate properly.
-1. In the left sidebar, click **Project overview**.
+1. On the left sidebar, select **Project information**.
1. Click **Set up CI/CD**.
![setup GitLab CI/CD](../img/setup_ci_v13_1.png)
diff --git a/doc/user/project/releases/index.md b/doc/user/project/releases/index.md
index 7ef39ecbab3..85b96fd9794 100644
--- a/doc/user/project/releases/index.md
+++ b/doc/user/project/releases/index.md
@@ -33,7 +33,7 @@ and attach [release assets](#release-assets), like runbooks or packages.
To view a list of releases:
-- Go to **Project overview > Releases**, or
+- On the left sidebar, select **Project information > Releases**, or
- On the project's overview page, if at least one release exists, click the number of releases.
@@ -64,8 +64,7 @@ Read more about [Release permissions](../../../user/permissions.md#project-membe
To create a new release through the GitLab UI:
-1. Navigate to **Project overview > Releases** and click the **New release**
- button.
+1. On the left sidebar, select **Project information > Releases** and select **New release**.
1. Open the [**Tag name**](#tag-name) dropdown. Select an existing tag or type
in a new tag name. Selecting an existing tag that is already associated with
a release will result in a validation error.
@@ -105,7 +104,7 @@ Read more about [Release permissions](../../../user/permissions.md#project-membe
To edit the details of a release:
-1. Navigate to **Project overview > Releases**.
+1. On the left sidebar, select **Project information > Releases**.
1. In the top-right corner of the release you want to modify, click **Edit this release** (the pencil icon).
1. On the **Edit Release** page, change the release's details.
1. Click **Save changes**.
@@ -151,12 +150,12 @@ the [Releases API](../../../api/releases/index.md#create-a-release).
In the user interface, to associate milestones to a release:
-1. Navigate to **Project overview > Releases**.
+1. On the left sidebar, select **Project information > Releases**.
1. In the top-right corner of the release you want to modify, click **Edit this release** (the pencil icon).
1. From the **Milestones** list, select each milestone you want to associate. You can select multiple milestones.
1. Click **Save changes**.
-On the **Project overview > Releases** page, the **Milestone** is listed in the top
+On the **Project information > Releases** page, the **Milestone** is listed in the top
section, along with statistics about the issues in the milestones.
![A Release with one associated milestone](img/release_with_milestone_v12_9.png)
@@ -176,7 +175,7 @@ You can be notified by email when a new release is created for your project.
To subscribe to notifications for releases:
-1. Navigate to **Project overview**.
+1. On the left sidebar, select **Project information**.
1. Click **Notification setting** (the bell icon).
1. In the list, click **Custom**.
1. Select the **New release** check box.
@@ -210,7 +209,7 @@ deploy_to_production:
To set a deploy freeze window in the UI, complete these steps:
1. Sign in to GitLab as a user with the [Maintainer role](../../permissions.md).
-1. Navigate to **Project overview**.
+1. On the left sidebar, select **Project information**.
1. In the left navigation menu, navigate to **Settings > CI/CD**.
1. Scroll to **Deploy freezes**.
1. Click **Expand** to see the deploy freeze table.
diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md
index 44ad592c07e..7919850b8cc 100644
--- a/doc/user/project/repository/index.md
+++ b/doc/user/project/repository/index.md
@@ -113,7 +113,7 @@ You can download the source code that's stored in a repository.
## Repository languages
For the default branch of each repository, GitLab determines which programming languages
-are used. This information is displayed on the project overview page.
+are used. This information is displayed on the **Project information** page.
![Repository Languages bar](img/repository_languages_v12_2.gif)
@@ -121,7 +121,7 @@ When new files are added, this information can take up to five minutes to update
### Add repository languages
-Not all files are detected and listed on the project overview page. Documentation,
+Not all files are detected and listed on the **Project information** page. Documentation,
vendor code, and most markup languages are excluded.
You can change this behavior by overriding the default settings.
@@ -200,7 +200,7 @@ To render an OpenAPI file:
## Repository size
-The project overview page shows the size of all files in the repository. The size is
+The **Project information** page shows the size of all files in the repository. The size is
updated, at most, every 15 minutes. The file size includes repository files, artifacts, and LFS.
The size can differ slightly from one instance to another due to compression, housekeeping, and other factors.
diff --git a/doc/user/project/working_with_projects.md b/doc/user/project/working_with_projects.md
index 3adcfc67117..e16d948aacf 100644
--- a/doc/user/project/working_with_projects.md
+++ b/doc/user/project/working_with_projects.md
@@ -228,10 +228,16 @@ Read through the documentation on [project settings](settings/index.md).
## Project activity
-To view the activity of a project, navigate to **Project overview > Activity**.
-From there, you can click on the tabs to see **All** the activity, or see it
-filtered by **Push events**, **Merge events**, **Issue events**, **Comments**,
-**Team**, and **Wiki**.
+To view the activity of a project:
+
+1. On the left sidebar, select **Project information > Activity**.
+1. Select a tab to view **All** the activity, or to filter it by any of these criteria:
+ - **Push events**
+ - **Merge events**
+ - **Issue events**
+ - **Comments**
+ - **Team**
+ - **Wiki**
### Leave a project
diff --git a/doc/user/snippets.md b/doc/user/snippets.md
index 45751e14cb8..4b3f9e78c7b 100644
--- a/doc/user/snippets.md
+++ b/doc/user/snippets.md
@@ -57,11 +57,11 @@ In GitLab versions 13.0 and later, snippets are [versioned by default](#versione
To discover all snippets visible to you in GitLab, you can:
-- **View all snippets visible to you**: In the top navigation bar of your GitLab
- instance, go to **More > Snippets** to view your snippets dashboard.
+- **View all snippets visible to you**: On the top bar of your GitLab
+ instance, select **Menu > Snippets** to view your snippets dashboard.
- **Visit [GitLab snippets](https://gitlab.com/dashboard/snippets)** for your snippets on GitLab.com.
-- **Explore all public snippets**: In the top navigation bar of your GitLab
- instance, go to **More > Snippets** and select **Explore snippets** to view
+- **Explore all public snippets**: On the top bar of your GitLab
+ instance, select **Menu > Snippets** and select **Explore snippets** to view
[all public snippets](https://gitlab.com/explore/snippets).
- **View a project's snippets**: In your project,
go to **Snippets**.
diff --git a/lib/api/invitations.rb b/lib/api/invitations.rb
index acd7f08f96d..46d8c0c958d 100644
--- a/lib/api/invitations.rb
+++ b/lib/api/invitations.rb
@@ -23,7 +23,7 @@ module API
requires :email, types: [String, Array[String]], email_or_email_list: true, desc: 'The email address to invite, or multiple emails separated by comma'
requires :access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'A valid access level (defaults: `30`, developer access level)'
optional :expires_at, type: DateTime, desc: 'Date string in the format YEAR-MONTH-DAY'
- optional :invite_source, type: String, desc: 'Source that triggered the member creation process', default: 'api'
+ optional :invite_source, type: String, desc: 'Source that triggered the member creation process', default: 'invitations-api'
end
post ":id/invitations" do
params[:source] = find_source(source_type, params[:id])
diff --git a/lib/api/members.rb b/lib/api/members.rb
index 0575eea1eab..0956806da5b 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -93,7 +93,7 @@ module API
requires :access_level, type: Integer, desc: 'A valid access level (defaults: `30`, developer access level)'
requires :user_id, types: [Integer, String], desc: 'The user ID of the new member or multiple IDs separated by commas.'
optional :expires_at, type: DateTime, desc: 'Date string in the format YEAR-MONTH-DAY'
- optional :invite_source, type: String, desc: 'Source that triggered the member creation process', default: 'api'
+ optional :invite_source, type: String, desc: 'Source that triggered the member creation process', default: 'members-api'
end
# rubocop: disable CodeReuse/ActiveRecord
post ":id/members" do
diff --git a/lib/gitlab/i18n.rb b/lib/gitlab/i18n.rb
index 023dbd1c601..30e72b58e21 100644
--- a/lib/gitlab/i18n.rb
+++ b/lib/gitlab/i18n.rb
@@ -40,24 +40,24 @@ module Gitlab
TRANSLATION_LEVELS = {
'bg' => 1,
'cs_CZ' => 1,
- 'de' => 19,
+ 'de' => 18,
'en' => 100,
'eo' => 1,
- 'es' => 41,
+ 'es' => 40,
'fil_PH' => 1,
- 'fr' => 14,
+ 'fr' => 13,
'gl_ES' => 1,
'id_ID' => 0,
'it' => 2,
- 'ja' => 45,
- 'ko' => 14,
+ 'ja' => 44,
+ 'ko' => 13,
'nl_NL' => 1,
- 'pl_PL' => 1,
- 'pt_BR' => 22,
- 'ru' => 32,
+ 'pl_PL' => 3,
+ 'pt_BR' => 21,
+ 'ru' => 30,
'tr_TR' => 17,
- 'uk' => 43,
- 'zh_CN' => 72,
+ 'uk' => 42,
+ 'zh_CN' => 69,
'zh_HK' => 3,
'zh_TW' => 4
}.freeze
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index c09e37b0137..f0d1964d021 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -3750,9 +3750,6 @@ msgstr ""
msgid "An error occurred while updating the configuration."
msgstr ""
-msgid "An error occurred while updating the milestone."
-msgstr ""
-
msgid "An error occurred while updating the notification settings. Please try again."
msgstr ""
@@ -13063,9 +13060,15 @@ msgstr ""
msgid "EscalationPolicies|Add escalation policy"
msgstr ""
+msgid "EscalationPolicies|Add policy"
+msgstr ""
+
msgid "EscalationPolicies|Create an escalation policy in GitLab"
msgstr ""
+msgid "EscalationPolicies|Delete escalation policy"
+msgstr ""
+
msgid "EscalationPolicies|Edit escalation policy"
msgstr ""
@@ -13075,12 +13078,18 @@ msgstr ""
msgid "EscalationPolicies|Email on-call user in schedule"
msgstr ""
+msgid "EscalationPolicies|Escalation policies"
+msgstr ""
+
msgid "EscalationPolicies|Escalation rules"
msgstr ""
msgid "EscalationPolicies|Failed to load oncall-schedules"
msgstr ""
+msgid "EscalationPolicies|IF alert is not %{alertStatus} in %{minutes} %{then} THEN %{doAction} %{schedule}"
+msgstr ""
+
msgid "EscalationPolicies|IF alert is not %{alertStatus} in %{minutes} minutes"
msgstr ""
@@ -13096,6 +13105,9 @@ msgstr ""
msgid "EscalationPolicies|THEN %{doAction} %{schedule}"
msgstr ""
+msgid "EscalationPolicies|mins"
+msgstr ""
+
msgid "Estimate"
msgstr ""
@@ -21249,9 +21261,6 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
-msgid "Milestones|No milestones found"
-msgstr ""
-
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -31641,7 +31650,7 @@ msgstr ""
msgid "Survey Response"
msgstr ""
-msgid "Switch Branch"
+msgid "Switch branch"
msgstr ""
msgid "Switch branch/tag"
diff --git a/spec/features/admin/users/users_spec.rb b/spec/features/admin/users/users_spec.rb
index 790c9a4fc74..187fa6fc2a4 100644
--- a/spec/features/admin/users/users_spec.rb
+++ b/spec/features/admin/users/users_spec.rb
@@ -308,6 +308,21 @@ RSpec.describe 'Admin::Users' do
end
end
end
+
+ context 'user group count', :js do
+ before do
+ group = create(:group)
+ group.add_developer(current_user)
+ project = create(:project, group: create(:group))
+ project.add_reporter(current_user)
+ end
+
+ it 'displays count of the users authorized groups' do
+ wait_for_requests
+
+ expect(page.find("[data-testid='user-group-count-#{current_user.id}']").text).to eq("2")
+ end
+ end
end
describe 'GET /admin/users/new' do
@@ -557,32 +572,6 @@ RSpec.describe 'Admin::Users' do
end
end
- # TODO: Move to main GET /admin/users block once feature flag is removed. Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/290737
- context 'with vue_admin_users feature flag enabled', :js do
- before do
- stub_feature_flags(vue_admin_users: true)
- end
-
- describe 'GET /admin/users' do
- context 'user group count', :js do
- before do
- group = create(:group)
- group.add_developer(current_user)
- project = create(:project, group: create(:group))
- project.add_reporter(current_user)
- end
-
- it 'displays count of the users authorized groups' do
- visit admin_users_path
-
- wait_for_requests
-
- expect(page.find("[data-testid='user-group-count-#{current_user.id}']").text).to eq("2")
- end
- end
- end
- end
-
def click_user_dropdown_toggle(user_id)
page.within("[data-testid='user-actions-#{user_id}']") do
find("[data-testid='dropdown-toggle']").click
diff --git a/spec/features/groups/members/manage_members_spec.rb b/spec/features/groups/members/manage_members_spec.rb
index 4241d20e6f7..ee18298e894 100644
--- a/spec/features/groups/members/manage_members_spec.rb
+++ b/spec/features/groups/members/manage_members_spec.rb
@@ -80,7 +80,7 @@ RSpec.describe 'Groups > Members > Manage members' do
expect_snowplow_event(
category: 'Members::CreateService',
action: 'create_member',
- label: 'unknown',
+ label: 'group-members-page',
property: 'existing_user',
user: user1
)
@@ -189,7 +189,7 @@ RSpec.describe 'Groups > Members > Manage members' do
expect_snowplow_event(
category: 'Members::InviteService',
action: 'create_member',
- label: 'unknown',
+ label: 'group-members-page',
property: 'net_new_user',
user: user1
)
diff --git a/spec/features/projects/members/list_spec.rb b/spec/features/projects/members/list_spec.rb
index 46583985509..25598146604 100644
--- a/spec/features/projects/members/list_spec.rb
+++ b/spec/features/projects/members/list_spec.rb
@@ -59,7 +59,7 @@ RSpec.describe 'Project members list', :js do
expect_snowplow_event(
category: 'Members::CreateService',
action: 'create_member',
- label: 'unknown',
+ label: 'project-members-page',
property: 'existing_user',
user: user1
)
@@ -117,7 +117,7 @@ RSpec.describe 'Project members list', :js do
expect_snowplow_event(
category: 'Members::InviteService',
action: 'create_member',
- label: 'unknown',
+ label: 'project-members-page',
property: 'net_new_user',
user: user1
)
diff --git a/spec/frontend/boards/components/sidebar/board_sidebar_milestone_select_spec.js b/spec/frontend/boards/components/sidebar/board_sidebar_milestone_select_spec.js
deleted file mode 100644
index 6cf750be972..00000000000
--- a/spec/frontend/boards/components/sidebar/board_sidebar_milestone_select_spec.js
+++ /dev/null
@@ -1,176 +0,0 @@
-import { GlLoadingIcon, GlDropdown } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import { mockMilestone as TEST_MILESTONE } from 'jest/boards/mock_data';
-import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue';
-import BoardSidebarMilestoneSelect from '~/boards/components/sidebar/board_sidebar_milestone_select.vue';
-import { createStore } from '~/boards/stores';
-
-const TEST_ISSUE = { id: 'gid://gitlab/Issue/1', iid: 9, referencePath: 'h/b#2' };
-
-describe('~/boards/components/sidebar/board_sidebar_milestone_select.vue', () => {
- let wrapper;
- let store;
-
- afterEach(() => {
- wrapper.destroy();
- store = null;
- wrapper = null;
- });
-
- const createWrapper = ({ milestone = null, loading = false } = {}) => {
- store = createStore();
- store.state.boardItems = { [TEST_ISSUE.id]: { ...TEST_ISSUE, milestone } };
- store.state.activeId = TEST_ISSUE.id;
-
- wrapper = shallowMount(BoardSidebarMilestoneSelect, {
- store,
- provide: {
- canUpdate: true,
- },
- data: () => ({
- milestones: [TEST_MILESTONE],
- }),
- stubs: {
- 'board-editable-item': BoardEditableItem,
- },
- mocks: {
- $apollo: {
- loading,
- },
- },
- });
- };
-
- const findCollapsed = () => wrapper.find('[data-testid="collapsed-content"]');
- const findLoader = () => wrapper.find(GlLoadingIcon);
- const findDropdown = () => wrapper.find(GlDropdown);
- const findBoardEditableItem = () => wrapper.find(BoardEditableItem);
- const findDropdownItem = () => wrapper.find('[data-testid="milestone-item"]');
- const findUnsetMilestoneItem = () => wrapper.find('[data-testid="no-milestone-item"]');
- const findNoMilestonesFoundItem = () => wrapper.find('[data-testid="no-milestones-found"]');
-
- describe('when not editing', () => {
- it('opens the milestone dropdown on clicking edit', async () => {
- createWrapper();
- wrapper.vm.$refs.dropdown.show = jest.fn();
-
- await findBoardEditableItem().vm.$emit('open');
-
- expect(wrapper.vm.$refs.dropdown.show).toHaveBeenCalledTimes(1);
- });
- });
-
- describe('when editing', () => {
- beforeEach(() => {
- createWrapper();
- jest.spyOn(wrapper.vm.$refs.sidebarItem, 'collapse');
- });
-
- it('collapses BoardEditableItem on clicking edit', async () => {
- await findBoardEditableItem().vm.$emit('close');
-
- expect(wrapper.vm.$refs.sidebarItem.collapse).toHaveBeenCalledTimes(1);
- });
-
- it('collapses BoardEditableItem on hiding dropdown', async () => {
- await findDropdown().vm.$emit('hide');
-
- expect(wrapper.vm.$refs.sidebarItem.collapse).toHaveBeenCalledTimes(1);
- });
- });
-
- it('renders "None" when no milestone is selected', () => {
- createWrapper();
-
- expect(findCollapsed().text()).toBe('None');
- });
-
- it('renders milestone title when set', () => {
- createWrapper({ milestone: TEST_MILESTONE });
-
- expect(findCollapsed().text()).toContain(TEST_MILESTONE.title);
- });
-
- it('shows loader while Apollo is loading', async () => {
- createWrapper({ milestone: TEST_MILESTONE, loading: true });
-
- expect(findLoader().exists()).toBe(true);
- });
-
- it('shows message when error or no milestones found', async () => {
- createWrapper();
-
- await wrapper.setData({ milestones: [] });
-
- expect(findNoMilestonesFoundItem().text()).toBe('No milestones found');
- });
-
- describe('when milestone is selected', () => {
- beforeEach(async () => {
- createWrapper();
-
- jest.spyOn(wrapper.vm, 'setActiveIssueMilestone').mockImplementation(() => {
- store.state.boardItems[TEST_ISSUE.id].milestone = TEST_MILESTONE;
- });
- findDropdownItem().vm.$emit('click');
- await wrapper.vm.$nextTick();
- });
-
- it('collapses sidebar and renders selected milestone', () => {
- expect(findCollapsed().isVisible()).toBe(true);
- expect(findCollapsed().text()).toContain(TEST_MILESTONE.title);
- });
-
- it('commits change to the server', () => {
- expect(wrapper.vm.setActiveIssueMilestone).toHaveBeenCalledWith({
- milestoneId: TEST_MILESTONE.id,
- projectPath: 'h/b',
- });
- });
- });
-
- describe('when milestone is set to "None"', () => {
- beforeEach(async () => {
- createWrapper({ milestone: TEST_MILESTONE });
-
- jest.spyOn(wrapper.vm, 'setActiveIssueMilestone').mockImplementation(() => {
- store.state.boardItems[TEST_ISSUE.id].milestone = null;
- });
- findUnsetMilestoneItem().vm.$emit('click');
- await wrapper.vm.$nextTick();
- });
-
- it('collapses sidebar and renders "None"', () => {
- expect(findCollapsed().isVisible()).toBe(true);
- expect(findCollapsed().text()).toBe('None');
- });
-
- it('commits change to the server', () => {
- expect(wrapper.vm.setActiveIssueMilestone).toHaveBeenCalledWith({
- milestoneId: null,
- projectPath: 'h/b',
- });
- });
- });
-
- describe('when the mutation fails', () => {
- const testMilestone = { id: '1', title: 'Former milestone' };
-
- beforeEach(async () => {
- createWrapper({ milestone: testMilestone });
-
- jest.spyOn(wrapper.vm, 'setActiveIssueMilestone').mockImplementation(() => {
- throw new Error(['failed mutation']);
- });
- jest.spyOn(wrapper.vm, 'setError').mockImplementation(() => {});
- findDropdownItem().vm.$emit('click');
- await wrapper.vm.$nextTick();
- });
-
- it('collapses sidebar and renders former milestone', () => {
- expect(findCollapsed().isVisible()).toBe(true);
- expect(findCollapsed().text()).toContain(testMilestone.title);
- expect(wrapper.vm.setError).toHaveBeenCalled();
- });
- });
-});
diff --git a/spec/frontend/boards/stores/actions_spec.js b/spec/frontend/boards/stores/actions_spec.js
index ad69dd4775a..63569333408 100644
--- a/spec/frontend/boards/stores/actions_spec.js
+++ b/spec/frontend/boards/stores/actions_spec.js
@@ -30,7 +30,6 @@ import {
mockIssue2,
rawIssue,
mockIssues,
- mockMilestone,
labels,
mockActiveIssue,
mockGroupProjects,
@@ -1495,60 +1494,6 @@ describe('setActiveItemSubscribed', () => {
});
});
-describe('setActiveIssueMilestone', () => {
- const state = { boardItems: { [mockIssue.id]: mockIssue } };
- const getters = { activeBoardItem: mockIssue };
- const testMilestone = {
- ...mockMilestone,
- id: 'gid://gitlab/Milestone/1',
- };
- const input = {
- milestoneId: testMilestone.id,
- projectPath: 'h/b',
- };
-
- it('should commit milestone after setting the issue', (done) => {
- jest.spyOn(gqlClient, 'mutate').mockResolvedValue({
- data: {
- updateIssue: {
- issue: {
- milestone: testMilestone,
- },
- errors: [],
- },
- },
- });
-
- const payload = {
- itemId: getters.activeBoardItem.id,
- prop: 'milestone',
- value: testMilestone,
- };
-
- testAction(
- actions.setActiveIssueMilestone,
- input,
- { ...state, ...getters },
- [
- {
- type: types.UPDATE_BOARD_ITEM_BY_ID,
- payload,
- },
- ],
- [],
- done,
- );
- });
-
- it('throws error if fails', async () => {
- jest
- .spyOn(gqlClient, 'mutate')
- .mockResolvedValue({ data: { updateIssue: { errors: ['failed mutation'] } } });
-
- await expect(actions.setActiveIssueMilestone({ getters }, input)).rejects.toThrow(Error);
- });
-});
-
describe('setActiveItemTitle', () => {
const state = {
boardItems: { [mockIssue.id]: mockIssue },
diff --git a/spec/frontend/invite_members/components/invite_members_trigger_spec.js b/spec/frontend/invite_members/components/invite_members_trigger_spec.js
index b569b6286e0..f57af61ad5b 100644
--- a/spec/frontend/invite_members/components/invite_members_trigger_spec.js
+++ b/spec/frontend/invite_members/components/invite_members_trigger_spec.js
@@ -7,6 +7,8 @@ import eventHub from '~/invite_members/event_hub';
jest.mock('~/experimentation/experiment_tracking');
const displayText = 'Invite team members';
+const triggerSource = '_trigger_source_';
+
let wrapper;
let triggerProps;
let findButton;
@@ -26,7 +28,7 @@ const createComponent = (props = {}) => {
};
describe.each(['button', 'anchor'])('with triggerElement as %s', (triggerElement) => {
- triggerProps = { triggerElement };
+ triggerProps = { triggerElement, triggerSource };
findButton = () => wrapper.findComponent(triggerComponent[triggerElement]);
afterEach(() => {
@@ -48,22 +50,14 @@ describe.each(['button', 'anchor'])('with triggerElement as %s', (triggerElement
spy = jest.spyOn(eventHub, '$emit');
});
- it('emits openModal from an unknown source', () => {
- createComponent();
-
- findButton().vm.$emit('click');
-
- expect(spy).toHaveBeenCalledWith('openModal', { inviteeType: 'members', source: 'unknown' });
- });
-
it('emits openModal from a named source', () => {
- createComponent({ triggerSource: '_trigger_source_' });
+ createComponent();
findButton().vm.$emit('click');
expect(spy).toHaveBeenCalledWith('openModal', {
inviteeType: 'members',
- source: '_trigger_source_',
+ source: triggerSource,
});
});
});
diff --git a/spec/frontend/sidebar/components/assignees/sidebar_invite_members_spec.js b/spec/frontend/sidebar/components/assignees/sidebar_invite_members_spec.js
index cfbe7227915..b738d931040 100644
--- a/spec/frontend/sidebar/components/assignees/sidebar_invite_members_spec.js
+++ b/spec/frontend/sidebar/components/assignees/sidebar_invite_members_spec.js
@@ -4,11 +4,16 @@ import SidebarInviteMembers from '~/sidebar/components/assignees/sidebar_invite_
describe('Sidebar invite members component', () => {
let wrapper;
+ const issuableType = 'issue';
const findDirectInviteLink = () => wrapper.findComponent(InviteMembersTrigger);
const createComponent = () => {
- wrapper = shallowMount(SidebarInviteMembers);
+ wrapper = shallowMount(SidebarInviteMembers, {
+ propsData: {
+ issuableType,
+ },
+ });
};
afterEach(() => {
@@ -23,5 +28,9 @@ describe('Sidebar invite members component', () => {
it('renders a direct link to project members path', () => {
expect(findDirectInviteLink().exists()).toBe(true);
});
+
+ it('has expected attributes on the trigger', () => {
+ expect(findDirectInviteLink().props('triggerSource')).toBe('issue-assignee-dropdown');
+ });
});
});
diff --git a/spec/initializers/mailer_retries_spec.rb b/spec/initializers/mailer_retries_spec.rb
index 59b8056aa0c..c1e56784ad9 100644
--- a/spec/initializers/mailer_retries_spec.rb
+++ b/spec/initializers/mailer_retries_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe 'Mailer retries' do
descendants.each { |a| a.queue_adapter = :test }
end
- it 'sets retries for mailers to 3' do
+ it 'sets retries for mailers to 3', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/332645' do
DeviseMailer.user_admin_approval(create(:user)).deliver_later
expect(Sidekiq::Queues['mailers'].first).to include('retry' => 3)
diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb
index c15910ef529..a16453f3d01 100644
--- a/spec/models/ci/build_trace_chunk_spec.rb
+++ b/spec/models/ci/build_trace_chunk_spec.rb
@@ -2,13 +2,13 @@
require 'spec_helper'
-RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
+RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state, :clean_gitlab_redis_trace_chunks do
include ExclusiveLeaseHelpers
let_it_be(:build) { create(:ci_build, :running) }
let(:chunk_index) { 0 }
- let(:data_store) { :redis }
+ let(:data_store) { :redis_trace_chunks }
let(:raw_data) { nil }
let(:build_trace_chunk) do
@@ -22,6 +22,13 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
stub_artifacts_object_storage
end
+ def redis_instance
+ {
+ redis: Gitlab::Redis::SharedState,
+ redis_trace_chunks: Gitlab::Redis::TraceChunks
+ }[data_store]
+ end
+
describe 'chunk creation' do
let(:metrics) { spy('metrics') }
@@ -85,7 +92,7 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
def external_data_counter
- Gitlab::Redis::SharedState.with do |redis|
+ redis_instance.with do |redis|
redis.scan_each(match: "gitlab:ci:trace:*:chunks:*").to_a.size
end
end
@@ -101,24 +108,16 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
subject { described_class.all_stores }
it 'returns a correctly ordered array' do
- is_expected.to eq(%i[redis database fog])
- end
-
- it 'returns redis store as the lowest precedence' do
- expect(subject.first).to eq(:redis)
- end
-
- it 'returns fog store as the highest precedence' do
- expect(subject.last).to eq(:fog)
+ is_expected.to eq(%i[redis database fog redis_trace_chunks])
end
end
describe '#data' do
subject { build_trace_chunk.data }
- context 'when data_store is redis' do
- let(:data_store) { :redis }
+ where(:data_store) { %i[redis redis_trace_chunks] }
+ with_them do
before do
build_trace_chunk.send(:unsafe_set_data!, +'Sample data in redis')
end
@@ -148,6 +147,22 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
end
+ describe '#data_store' do
+ subject { described_class.new.data_store }
+
+ context 'default value' do
+ it { expect(subject).to eq('redis_trace_chunks') }
+
+ context 'when dedicated_redis_trace_chunks is disabled' do
+ before do
+ stub_feature_flags(dedicated_redis_trace_chunks: false)
+ end
+
+ it { expect(subject).to eq('redis') }
+ end
+ end
+ end
+
describe '#get_store_class' do
using RSpec::Parameterized::TableSyntax
@@ -155,6 +170,7 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
:redis | Ci::BuildTraceChunks::Redis
:database | Ci::BuildTraceChunks::Database
:fog | Ci::BuildTraceChunks::Fog
+ :redis_trace_chunks | Ci::BuildTraceChunks::RedisTraceChunks
end
with_them do
@@ -302,9 +318,9 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
end
- context 'when data_store is redis' do
- let(:data_store) { :redis }
+ where(:data_store) { %i[redis redis_trace_chunks] }
+ with_them do
context 'when there are no data' do
let(:data) { +'' }
@@ -441,8 +457,9 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
end
- context 'when data_store is redis' do
- let(:data_store) { :redis }
+ where(:data_store) { %i[redis redis_trace_chunks] }
+
+ with_them do
let(:data) { +'Sample data in redis' }
before do
@@ -475,9 +492,9 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
describe '#size' do
subject { build_trace_chunk.size }
- context 'when data_store is redis' do
- let(:data_store) { :redis }
+ where(:data_store) { %i[redis redis_trace_chunks] }
+ with_them do
context 'when data exists' do
let(:data) { +'Sample data in redis' }
@@ -537,9 +554,14 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
subject { build_trace_chunk.persist_data! }
- context 'when data_store is redis' do
- let(:data_store) { :redis }
+ where(:data_store, :redis_class) do
+ [
+ [:redis, Ci::BuildTraceChunks::Redis],
+ [:redis_trace_chunks, Ci::BuildTraceChunks::RedisTraceChunks]
+ ]
+ end
+ with_them do
context 'when data exists' do
before do
build_trace_chunk.send(:unsafe_set_data!, data)
@@ -549,15 +571,15 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
let(:data) { +'a' * described_class::CHUNK_SIZE }
it 'persists the data' do
- expect(build_trace_chunk.redis?).to be_truthy
- expect(Ci::BuildTraceChunks::Redis.new.data(build_trace_chunk)).to eq(data)
+ expect(build_trace_chunk.data_store).to eq(data_store.to_s)
+ expect(redis_class.new.data(build_trace_chunk)).to eq(data)
expect(Ci::BuildTraceChunks::Database.new.data(build_trace_chunk)).to be_nil
expect(Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk)).to be_nil
subject
expect(build_trace_chunk.fog?).to be_truthy
- expect(Ci::BuildTraceChunks::Redis.new.data(build_trace_chunk)).to be_nil
+ expect(redis_class.new.data(build_trace_chunk)).to be_nil
expect(Ci::BuildTraceChunks::Database.new.data(build_trace_chunk)).to be_nil
expect(Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk)).to eq(data)
end
@@ -575,8 +597,8 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
it 'does not persist the data and the orignal data is intact' do
expect { subject }.to raise_error(described_class::FailedToPersistDataError)
- expect(build_trace_chunk.redis?).to be_truthy
- expect(Ci::BuildTraceChunks::Redis.new.data(build_trace_chunk)).to eq(data)
+ expect(build_trace_chunk.data_store).to eq(data_store.to_s)
+ expect(redis_class.new.data(build_trace_chunk)).to eq(data)
expect(Ci::BuildTraceChunks::Database.new.data(build_trace_chunk)).to be_nil
expect(Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk)).to be_nil
end
@@ -810,7 +832,7 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
shared_examples_for 'deletes all build_trace_chunk and data in redis' do
it 'deletes all build_trace_chunk and data in redis', :sidekiq_might_not_need_inline do
- Gitlab::Redis::SharedState.with do |redis|
+ redis_instance.with do |redis|
expect(redis.scan_each(match: "gitlab:ci:trace:*:chunks:*").to_a.size).to eq(3)
end
@@ -820,7 +842,7 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
expect(described_class.count).to eq(0)
- Gitlab::Redis::SharedState.with do |redis|
+ redis_instance.with do |redis|
expect(redis.scan_each(match: "gitlab:ci:trace:*:chunks:*").to_a.size).to eq(0)
end
end
@@ -902,4 +924,38 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
end
end
+
+ describe '#live?' do
+ subject { build_trace_chunk.live? }
+
+ where(:data_store, :value) do
+ [
+ [:redis, true],
+ [:redis_trace_chunks, true],
+ [:database, false],
+ [:fog, false]
+ ]
+ end
+
+ with_them do
+ it { is_expected.to eq(value) }
+ end
+ end
+
+ describe '#flushed?' do
+ subject { build_trace_chunk.flushed? }
+
+ where(:data_store, :value) do
+ [
+ [:redis, false],
+ [:redis_trace_chunks, false],
+ [:database, true],
+ [:fog, true]
+ ]
+ end
+
+ with_them do
+ it { is_expected.to eq(value) }
+ end
+ end
end
diff --git a/spec/models/ci/build_trace_chunks/database_spec.rb b/spec/models/ci/build_trace_chunks/database_spec.rb
index 313328ac037..d99aede853c 100644
--- a/spec/models/ci/build_trace_chunks/database_spec.rb
+++ b/spec/models/ci/build_trace_chunks/database_spec.rb
@@ -5,12 +5,6 @@ require 'spec_helper'
RSpec.describe Ci::BuildTraceChunks::Database do
let(:data_store) { described_class.new }
- describe '#available?' do
- subject { data_store.available? }
-
- it { is_expected.to be_truthy }
- end
-
describe '#data' do
subject { data_store.data(model) }
diff --git a/spec/models/ci/build_trace_chunks/redis_spec.rb b/spec/models/ci/build_trace_chunks/redis_spec.rb
index cb0b6baadeb..c004887d609 100644
--- a/spec/models/ci/build_trace_chunks/redis_spec.rb
+++ b/spec/models/ci/build_trace_chunks/redis_spec.rb
@@ -5,12 +5,6 @@ require 'spec_helper'
RSpec.describe Ci::BuildTraceChunks::Redis, :clean_gitlab_redis_shared_state do
let(:data_store) { described_class.new }
- describe '#available?' do
- subject { data_store.available? }
-
- it { is_expected.to be_truthy }
- end
-
describe '#data' do
subject { data_store.data(model) }
diff --git a/spec/requests/api/invitations_spec.rb b/spec/requests/api/invitations_spec.rb
index e4f394eed0e..f9f03c9e55c 100644
--- a/spec/requests/api/invitations_spec.rb
+++ b/spec/requests/api/invitations_spec.rb
@@ -161,7 +161,7 @@ RSpec.describe API::Invitations do
expect_snowplow_event(
category: 'Members::InviteService',
action: 'create_member',
- label: 'api',
+ label: 'invitations-api',
property: 'net_new_user',
user: maintainer
)
diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb
index 93fa36205ab..cac1b95e854 100644
--- a/spec/requests/api/members_spec.rb
+++ b/spec/requests/api/members_spec.rb
@@ -265,7 +265,7 @@ RSpec.describe API::Members do
expect_snowplow_event(
category: 'Members::CreateService',
action: 'create_member',
- label: 'api',
+ label: 'members-api',
property: 'existing_user',
user: maintainer
)
@@ -322,7 +322,7 @@ RSpec.describe API::Members do
expect_snowplow_event(
category: 'Members::CreateService',
action: 'create_member',
- label: 'api',
+ label: 'members-api',
property: 'existing_user',
user: maintainer
)
diff --git a/spec/services/ci/append_build_trace_service_spec.rb b/spec/services/ci/append_build_trace_service_spec.rb
index a0a7f594881..8812680b665 100644
--- a/spec/services/ci/append_build_trace_service_spec.rb
+++ b/spec/services/ci/append_build_trace_service_spec.rb
@@ -44,7 +44,7 @@ RSpec.describe Ci::AppendBuildTraceService do
expect(::Gitlab::ErrorTracking)
.to receive(:log_exception)
- .with(anything, hash_including(chunk_index: 0, chunk_store: 'redis'))
+ .with(anything, hash_including(chunk_index: 0, chunk_store: 'redis_trace_chunks'))
result = described_class
.new(build, content_range: '0-128')
diff --git a/spec/support/redis.rb b/spec/support/redis.rb
index 8539f202602..eeeb93fa811 100644
--- a/spec/support/redis.rb
+++ b/spec/support/redis.rb
@@ -30,4 +30,12 @@ RSpec.configure do |config|
redis_queues_cleanup!
end
+
+ config.around(:each, :clean_gitlab_redis_trace_chunks) do |example|
+ redis_trace_chunks_cleanup!
+
+ example.run
+
+ redis_trace_chunks_cleanup!
+ end
end
diff --git a/spec/support/redis/redis_helpers.rb b/spec/support/redis/redis_helpers.rb
index 7c571738a01..b8118bf94cc 100644
--- a/spec/support/redis/redis_helpers.rb
+++ b/spec/support/redis/redis_helpers.rb
@@ -17,4 +17,9 @@ module RedisHelpers
def redis_shared_state_cleanup!
Gitlab::Redis::SharedState.with(&:flushall)
end
+
+ # Usage: CI trace chunks
+ def redis_trace_chunks_cleanup!
+ Gitlab::Redis::TraceChunks.with(&:flushall)
+ end
end