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:
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js30
-rw-r--r--app/assets/javascripts/boards/components/board_card_move_to_position.vue31
-rw-r--r--app/assets/javascripts/boards/components/board_list.vue13
-rw-r--r--app/assets/javascripts/boards/stores/actions.js25
-rw-r--r--app/assets/javascripts/boards/stores/mutation_types.js1
-rw-r--r--app/assets/javascripts/boards/stores/mutations.js41
-rw-r--r--app/assets/javascripts/boards/stores/state.js1
-rw-r--r--app/models/ci/bridge.rb2
-rw-r--r--app/models/ci/build.rb3
-rw-r--r--app/models/ci/partition.rb6
-rw-r--r--app/views/profiles/notifications/_email_settings.html.haml2
-rw-r--r--app/views/profiles/notifications/show.html.haml2
-rw-r--r--config/metrics/counts_all/20210216182006_source_code_pushes.yml2
-rw-r--r--db/docs/ci_partitions.yml9
-rw-r--r--db/migrate/20220902065314_create_ci_partitions.rb9
-rw-r--r--db/migrate/20220902065316_create_default_partition_record.rb21
-rw-r--r--db/migrate/20220902065317_add_partition_id_to_ci_builds.rb11
-rw-r--r--db/migrate/20220902065558_add_partition_id_to_ci_builds_metadata.rb9
-rw-r--r--db/migrate/20220902065611_add_partition_id_to_ci_job_artifacts.rb9
-rw-r--r--db/migrate/20220902065623_add_partition_id_to_ci_pipelines.rb9
-rw-r--r--db/migrate/20220902065635_add_partition_id_to_ci_stages.rb9
-rw-r--r--db/migrate/20220902065647_add_partition_id_to_ci_pipeline_variables.rb9
-rw-r--r--db/schema_migrations/202209020653141
-rw-r--r--db/schema_migrations/202209020653161
-rw-r--r--db/schema_migrations/202209020653171
-rw-r--r--db/schema_migrations/202209020655581
-rw-r--r--db/schema_migrations/202209020656111
-rw-r--r--db/schema_migrations/202209020656231
-rw-r--r--db/schema_migrations/202209020656351
-rw-r--r--db/schema_migrations/202209020656471
-rw-r--r--db/structure.sql30
-rw-r--r--doc/api/members.md24
-rw-r--r--doc/ci/mobile_devops.md42
-rw-r--r--doc/development/service_ping/implement.md2
-rw-r--r--doc/development/service_ping/metrics_instrumentation.md10
-rw-r--r--lib/gitlab/database/gitlab_schemas.yml1
-rw-r--r--lib/gitlab/usage/metrics/instrumentations/redis_metric.rb22
-rw-r--r--locale/gitlab.pot12
-rw-r--r--package.json4
-rw-r--r--spec/db/schema_spec.rb7
-rw-r--r--spec/frontend/boards/components/board_card_move_to_position_spec.js38
-rw-r--r--spec/frontend/boards/stores/actions_spec.js38
-rw-r--r--spec/frontend/boards/stores/mutations_spec.js25
-rw-r--r--spec/frontend/ide/stores/modules/commit/actions_spec.js2
-rw-r--r--spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb8
-rw-r--r--yarn.lock16
46 files changed, 420 insertions, 123 deletions
diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
index 82229b5aa8f..97ba9e15c0f 100644
--- a/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
@@ -1,9 +1,11 @@
import $ from 'jquery';
+import ClipboardJS from 'clipboard';
import Mousetrap from 'mousetrap';
-import { clickCopyToClipboardButton } from '~/behaviors/copy_to_clipboard';
import { getSelectedFragment } from '~/lib/utils/common_utils';
import { isElementVisible } from '~/lib/utils/dom_utils';
import { DEBOUNCE_DROPDOWN_DELAY } from '~/vue_shared/components/sidebar/labels_select_widget/constants';
+import toast from '~/vue_shared/plugins/global_toast';
+import { s__ } from '~/locale';
import Sidebar from '~/right_sidebar';
import { CopyAsGFM } from '../markdown/copy_as_gfm';
import {
@@ -21,6 +23,15 @@ export default class ShortcutsIssuable extends Shortcuts {
constructor() {
super();
+ this.inMemoryButton = document.createElement('button');
+ this.clipboardInstance = new ClipboardJS(this.inMemoryButton);
+ this.clipboardInstance.on('success', () => {
+ toast(s__('GlobalShortcuts|Copied source branch name to clipboard.'));
+ });
+ this.clipboardInstance.on('error', () => {
+ toast(s__('GlobalShortcuts|Unable to copy the source branch name at this time.'));
+ });
+
Mousetrap.bind(keysFor(ISSUE_MR_CHANGE_ASSIGNEE), () =>
ShortcutsIssuable.openSidebarDropdown('assignee'),
);
@@ -32,7 +43,7 @@ export default class ShortcutsIssuable extends Shortcuts {
);
Mousetrap.bind(keysFor(ISSUABLE_COMMENT_OR_REPLY), ShortcutsIssuable.replyWithSelectedText);
Mousetrap.bind(keysFor(ISSUABLE_EDIT_DESCRIPTION), ShortcutsIssuable.editIssue);
- Mousetrap.bind(keysFor(MR_COPY_SOURCE_BRANCH_NAME), ShortcutsIssuable.copyBranchName);
+ Mousetrap.bind(keysFor(MR_COPY_SOURCE_BRANCH_NAME), () => this.copyBranchName());
/**
* We're attaching a global focus event listener on document for
@@ -153,17 +164,14 @@ export default class ShortcutsIssuable extends Shortcuts {
return false;
}
- static copyBranchName() {
- // There are two buttons - one that is shown when the sidebar
- // is expanded, and one that is shown when it's collapsed.
- const allCopyBtns = Array.from(document.querySelectorAll('.js-source-branch-copy'));
+ async copyBranchName() {
+ const button = document.querySelector('.js-source-branch-copy');
+ const branchName = button?.dataset.clipboardText;
- // Select whichever button is currently visible so that
- // the "Copied" tooltip is shown when a click is simulated.
- const visibleBtn = allCopyBtns.find(isElementVisible);
+ if (branchName) {
+ this.inMemoryButton.dataset.clipboardText = branchName;
- if (visibleBtn) {
- clickCopyToClipboardButton(visibleBtn);
+ this.inMemoryButton.dispatchEvent(new CustomEvent('click'));
}
}
}
diff --git a/app/assets/javascripts/boards/components/board_card_move_to_position.vue b/app/assets/javascripts/boards/components/board_card_move_to_position.vue
index a0cc3756fc4..ff938219475 100644
--- a/app/assets/javascripts/boards/components/board_card_move_to_position.vue
+++ b/app/assets/javascripts/boards/components/board_card_move_to_position.vue
@@ -48,21 +48,12 @@ export default {
listHasNextPage() {
return this.pageInfoByListId[this.list.id]?.hasNextPage;
},
- firstItemInListId() {
- return this.listItems[0]?.id;
- },
lengthOfListItemsInBoard() {
return this.listItems?.length;
},
- lastItemInTheListId() {
- return this.listItems[this.lengthOfListItemsInBoard - 1]?.id;
- },
itemIdentifier() {
return `${this.item.id}-${this.item.iid}-${this.index}`;
},
- showMoveToEndOfList() {
- return !this.listHasNextPage;
- },
isFirstItemInList() {
return this.index === 0;
},
@@ -80,9 +71,8 @@ export default {
if (this.isFirstItemInList) {
return;
}
- const moveAfterId = this.firstItemInListId;
this.moveToPosition({
- moveAfterId,
+ positionInList: 0,
});
},
moveToEnd() {
@@ -93,20 +83,20 @@ export default {
if (this.isLastItemInList) {
return;
}
- const moveBeforeId = this.lastItemInTheListId;
this.moveToPosition({
- moveBeforeId,
+ positionInList: -1,
});
},
- moveToPosition({ moveAfterId, moveBeforeId }) {
+ moveToPosition({ positionInList }) {
this.moveItem({
itemId: this.item.id,
itemIid: this.item.iid,
itemPath: this.item.referencePath,
fromListId: this.list.id,
toListId: this.list.id,
- moveAfterId,
- moveBeforeId,
+ positionInList,
+ atIndex: this.index,
+ allItemsLoadedInList: !this.listHasNextPage,
});
},
},
@@ -117,7 +107,6 @@ export default {
<gl-dropdown
ref="dropdown"
:key="itemIdentifier"
- data-testid="move-card-dropdown"
icon="ellipsis_v"
:text="s__('Boards|Move card')"
:text-sr-only="true"
@@ -128,14 +117,10 @@ export default {
@keydown.esc.native="$emit('hide')"
>
<div>
- <gl-dropdown-item data-testid="action-move-to-first" @click.stop="moveToStart">
+ <gl-dropdown-item @click.stop="moveToStart">
{{ $options.i18n.moveToStartText }}
</gl-dropdown-item>
- <gl-dropdown-item
- v-if="showMoveToEndOfList"
- data-testid="action-move-to-end"
- @click.stop="moveToEnd"
- >
+ <gl-dropdown-item @click.stop="moveToEnd">
{{ $options.i18n.moveToEndText }}
</gl-dropdown-item>
</div>
diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue
index 93835519033..edf1a5ee7e6 100644
--- a/app/assets/javascripts/boards/components/board_list.vue
+++ b/app/assets/javascripts/boards/components/board_list.vue
@@ -66,7 +66,7 @@ export default {
},
},
computed: {
- ...mapState(['pageInfoByListId', 'listsFlags', 'filterParams']),
+ ...mapState(['pageInfoByListId', 'listsFlags', 'filterParams', 'isUpdateIssueOrderInProgress']),
...mapGetters(['isEpicBoard']),
listItemsCount() {
return this.isEpicBoard ? this.list.epicsCount : this.boardList?.issuesCount;
@@ -132,6 +132,9 @@ export default {
return this.canMoveIssue ? options : {};
},
+ disableScrollingWhenMutationInProgress() {
+ return this.hasNextPage && this.isUpdateIssueOrderInProgress;
+ },
},
watch: {
boardItems() {
@@ -285,9 +288,13 @@ export default {
v-bind="treeRootOptions"
:data-board="list.id"
:data-board-type="list.listType"
- :class="{ 'bg-danger-100': boardItemsSizeExceedsMax }"
+ :class="{
+ 'bg-danger-100': boardItemsSizeExceedsMax,
+ 'gl-overflow-hidden': disableScrollingWhenMutationInProgress,
+ 'gl-overflow-y-auto': !disableScrollingWhenMutationInProgress,
+ }"
draggable=".board-card"
- class="board-list gl-w-full gl-h-full gl-list-style-none gl-mb-0 gl-p-3 gl-pt-0 gl-overflow-y-auto gl-overflow-x-hidden"
+ class="board-list gl-w-full gl-h-full gl-list-style-none gl-mb-0 gl-p-3 gl-pt-0 gl-overflow-x-hidden"
data-testid="tree-root-wrapper"
@start="handleDragOnStart"
@end="handleDragOnEnd"
diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js
index f84274104b2..c2e346da606 100644
--- a/app/assets/javascripts/boards/stores/actions.js
+++ b/app/assets/javascripts/boards/stores/actions.js
@@ -479,16 +479,25 @@ export default {
toListId,
moveBeforeId,
moveAfterId,
+ positionInList,
+ allItemsLoadedInList,
} = moveData;
commit(types.REMOVE_BOARD_ITEM_FROM_LIST, { itemId, listId: fromListId });
+ if (reordering && !allItemsLoadedInList && positionInList === -1) {
+ return;
+ }
+
if (reordering) {
commit(types.ADD_BOARD_ITEM_TO_LIST, {
itemId,
listId: toListId,
moveBeforeId,
moveAfterId,
+ positionInList,
+ atIndex: originalIndex,
+ allItemsLoadedInList,
});
return;
@@ -500,6 +509,7 @@ export default {
listId: toListId,
moveBeforeId,
moveAfterId,
+ positionInList,
});
}
@@ -553,7 +563,15 @@ export default {
updateIssueOrder: async ({ commit, dispatch, state }, { moveData, mutationVariables = {} }) => {
try {
- const { itemId, fromListId, toListId, moveBeforeId, moveAfterId, itemNotInToList } = moveData;
+ const {
+ itemId,
+ fromListId,
+ toListId,
+ moveBeforeId,
+ moveAfterId,
+ itemNotInToList,
+ positionInList,
+ } = moveData;
const {
fullBoardId,
filterParams,
@@ -562,6 +580,8 @@ export default {
},
} = state;
+ commit(types.MUTATE_ISSUE_IN_PROGRESS, true);
+
const { data } = await gqlClient.mutate({
mutation: issueMoveListMutation,
variables: {
@@ -572,6 +592,7 @@ export default {
toListId: getIdFromGraphQLId(toListId),
moveBeforeId: moveBeforeId ? getIdFromGraphQLId(moveBeforeId) : undefined,
moveAfterId: moveAfterId ? getIdFromGraphQLId(moveAfterId) : undefined,
+ positionInList,
// 'mutationVariables' allows EE code to pass in extra parameters.
...mutationVariables,
},
@@ -643,7 +664,9 @@ export default {
}
commit(types.MUTATE_ISSUE_SUCCESS, { issue: data.issueMoveList.issue });
+ commit(types.MUTATE_ISSUE_IN_PROGRESS, false);
} catch {
+ commit(types.MUTATE_ISSUE_IN_PROGRESS, false);
commit(
types.SET_ERROR,
s__('Boards|An error occurred while moving the issue. Please try again.'),
diff --git a/app/assets/javascripts/boards/stores/mutation_types.js b/app/assets/javascripts/boards/stores/mutation_types.js
index 43268f21f96..0e496677b7b 100644
--- a/app/assets/javascripts/boards/stores/mutation_types.js
+++ b/app/assets/javascripts/boards/stores/mutation_types.js
@@ -44,3 +44,4 @@ export const ADD_LIST_TO_HIGHLIGHTED_LISTS = 'ADD_LIST_TO_HIGHLIGHTED_LISTS';
export const REMOVE_LIST_FROM_HIGHLIGHTED_LISTS = 'REMOVE_LIST_FROM_HIGHLIGHTED_LISTS';
export const RESET_BOARD_ITEM_SELECTION = 'RESET_BOARD_ITEM_SELECTION';
export const SET_ERROR = 'SET_ERROR';
+export const MUTATE_ISSUE_IN_PROGRESS = 'MUTATE_ISSUE_IN_PROGRESS';
diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js
index 26a98a645b3..44abb2030c7 100644
--- a/app/assets/javascripts/boards/stores/mutations.js
+++ b/app/assets/javascripts/boards/stores/mutations.js
@@ -20,17 +20,28 @@ export const removeItemFromList = ({ state, listId, itemId }) => {
updateListItemsCount({ state, listId, value: -1 });
};
-export const addItemToList = ({ state, listId, itemId, moveBeforeId, moveAfterId, atIndex }) => {
+export const addItemToList = ({
+ state,
+ listId,
+ itemId,
+ moveBeforeId,
+ moveAfterId,
+ atIndex,
+ positionInList,
+}) => {
const listIssues = state.boardItemsByListId[listId];
let newIndex = atIndex || 0;
+ const moveToStartOrLast = positionInList !== undefined;
if (moveBeforeId) {
newIndex = listIssues.indexOf(moveBeforeId) + 1;
} else if (moveAfterId) {
newIndex = listIssues.indexOf(moveAfterId);
+ } else if (moveToStartOrLast) {
+ newIndex = positionInList === -1 ? listIssues.length : 0;
}
listIssues.splice(newIndex, 0, itemId);
Vue.set(state.boardItemsByListId, listId, listIssues);
- updateListItemsCount({ state, listId, value: 1 });
+ updateListItemsCount({ state, listId, value: moveToStartOrLast ? 0 : 1 });
};
export default {
@@ -205,12 +216,34 @@ export default {
Vue.set(state.boardItems, issue.id, formatIssue(issue));
},
+ [mutationTypes.MUTATE_ISSUE_IN_PROGRESS](state, isLoading) {
+ state.isUpdateIssueOrderInProgress = isLoading;
+ },
+
[mutationTypes.ADD_BOARD_ITEM_TO_LIST]: (
state,
- { itemId, listId, moveBeforeId, moveAfterId, atIndex, inProgress = false },
+ {
+ itemId,
+ listId,
+ moveBeforeId,
+ moveAfterId,
+ atIndex,
+ positionInList,
+ allItemsLoadedInList,
+ inProgress = false,
+ },
) => {
Vue.set(state.listsFlags, listId, { ...state.listsFlags, addItemToListInProgress: inProgress });
- addItemToList({ state, listId, itemId, moveBeforeId, moveAfterId, atIndex });
+ addItemToList({
+ state,
+ listId,
+ itemId,
+ moveBeforeId,
+ moveAfterId,
+ atIndex,
+ positionInList,
+ allItemsLoadedInList,
+ });
},
[mutationTypes.REMOVE_BOARD_ITEM_FROM_LIST]: (state, { itemId, listId }) => {
diff --git a/app/assets/javascripts/boards/stores/state.js b/app/assets/javascripts/boards/stores/state.js
index b62c032b921..bf3f777ea7d 100644
--- a/app/assets/javascripts/boards/stores/state.js
+++ b/app/assets/javascripts/boards/stores/state.js
@@ -40,4 +40,5 @@ export default () => ({
},
// TODO: remove after ce/ee split of board_content.vue
isShowingEpicsSwimlanes: false,
+ isUpdateIssueOrderInProgress: false,
});
diff --git a/app/models/ci/bridge.rb b/app/models/ci/bridge.rb
index 0374d076da8..0223ad3818c 100644
--- a/app/models/ci/bridge.rb
+++ b/app/models/ci/bridge.rb
@@ -77,7 +77,7 @@ module Ci
%i[pipeline project ref tag options name
allow_failure stage stage_idx
yaml_variables when description needs_attributes
- scheduling_type ci_stage].freeze
+ scheduling_type ci_stage partition_id].freeze
end
def inherit_status_from_downstream!(pipeline)
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index a08fdf3652a..60a1d8b4b53 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -217,7 +217,8 @@ module Ci
allow_failure stage stage_idx trigger_request
yaml_variables when environment coverage_regex
description tag_list protected needs_attributes
- job_variables_attributes resource_group scheduling_type ci_stage].freeze
+ job_variables_attributes resource_group scheduling_type
+ ci_stage partition_id].freeze
end
end
diff --git a/app/models/ci/partition.rb b/app/models/ci/partition.rb
new file mode 100644
index 00000000000..d773038df01
--- /dev/null
+++ b/app/models/ci/partition.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+module Ci
+ class Partition < Ci::ApplicationRecord
+ end
+end
diff --git a/app/views/profiles/notifications/_email_settings.html.haml b/app/views/profiles/notifications/_email_settings.html.haml
index b4db99a8bd4..c4de33dcd9e 100644
--- a/app/views/profiles/notifications/_email_settings.html.haml
+++ b/app/views/profiles/notifications/_email_settings.html.haml
@@ -1,6 +1,6 @@
- form = local_assigns.fetch(:form)
.form-group
- = form.label :notification_email, class: "label-bold"
+ = form.label :notification_email, _('Notification Email'), class: "label-bold"
= form.select :notification_email, @user.public_verified_emails, { include_blank: _('Use primary email (%{email})') % { email: @user.email }, selected: @user.notification_email }, class: "select2", disabled: local_assigns.fetch(:email_change_disabled, nil)
.help-block
= local_assigns.fetch(:help_text, nil)
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index 26c9b2f0ee1..0f4b130a774 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -25,7 +25,7 @@
= gitlab_ui_form_for @user, url: profile_notifications_path, method: :put, html: { class: 'update-notifications gl-mt-3' } do |f|
= render_if_exists 'profiles/notifications/email_settings', form: f
- = label_tag :global_notification_level, "Global notification level", class: "label-bold"
+ = label_tag :global_notification_level, _('Global notification level'), class: "label-bold"
%br
.clearfix
.form-group.float-left.global-notification-setting
diff --git a/config/metrics/counts_all/20210216182006_source_code_pushes.yml b/config/metrics/counts_all/20210216182006_source_code_pushes.yml
index 5c3c70f2496..3e4ef3ec76c 100644
--- a/config/metrics/counts_all/20210216182006_source_code_pushes.yml
+++ b/config/metrics/counts_all/20210216182006_source_code_pushes.yml
@@ -12,7 +12,7 @@ time_frame: all
data_source: redis
instrumentation_class: RedisMetric
options:
- counter_class: SourceCodeCounter
+ prefix: source_code
event: pushes
distribution:
- ce
diff --git a/db/docs/ci_partitions.yml b/db/docs/ci_partitions.yml
new file mode 100644
index 00000000000..8dfa31f05f9
--- /dev/null
+++ b/db/docs/ci_partitions.yml
@@ -0,0 +1,9 @@
+---
+table_name: ci_partitions
+classes:
+- Ci::Partition
+feature_categories:
+- continuous_integration
+description: Database partitioning metadata for CI tables
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96856
+milestone: '15.4'
diff --git a/db/migrate/20220902065314_create_ci_partitions.rb b/db/migrate/20220902065314_create_ci_partitions.rb
new file mode 100644
index 00000000000..1a8a4f172f8
--- /dev/null
+++ b/db/migrate/20220902065314_create_ci_partitions.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class CreateCiPartitions < Gitlab::Database::Migration[2.0]
+ def change
+ create_table :ci_partitions do |t|
+ t.timestamps_with_timezone null: false
+ end
+ end
+end
diff --git a/db/migrate/20220902065316_create_default_partition_record.rb b/db/migrate/20220902065316_create_default_partition_record.rb
new file mode 100644
index 00000000000..6493fb23d4c
--- /dev/null
+++ b/db/migrate/20220902065316_create_default_partition_record.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class CreateDefaultPartitionRecord < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+ restrict_gitlab_migration gitlab_schema: :gitlab_ci
+
+ def up
+ execute(<<~SQL)
+ INSERT INTO "ci_partitions" ("id", "created_at", "updated_at")
+ VALUES (100, now(), now());
+ SQL
+
+ reset_pk_sequence!('ci_partitions')
+ end
+
+ def down
+ execute(<<~SQL)
+ DELETE FROM "ci_partitions" WHERE "ci_partitions"."id" = 100;
+ SQL
+ end
+end
diff --git a/db/migrate/20220902065317_add_partition_id_to_ci_builds.rb b/db/migrate/20220902065317_add_partition_id_to_ci_builds.rb
new file mode 100644
index 00000000000..6257164b44e
--- /dev/null
+++ b/db/migrate/20220902065317_add_partition_id_to_ci_builds.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class AddPartitionIdToCiBuilds < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ # rubocop:disable Migration/AddColumnsToWideTables
+ def change
+ add_column :ci_builds, :partition_id, :bigint, default: 100, null: false
+ end
+ # rubocop:enable Migration/AddColumnsToWideTables
+end
diff --git a/db/migrate/20220902065558_add_partition_id_to_ci_builds_metadata.rb b/db/migrate/20220902065558_add_partition_id_to_ci_builds_metadata.rb
new file mode 100644
index 00000000000..e04ea99539f
--- /dev/null
+++ b/db/migrate/20220902065558_add_partition_id_to_ci_builds_metadata.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddPartitionIdToCiBuildsMetadata < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :ci_builds_metadata, :partition_id, :bigint, default: 100, null: false
+ end
+end
diff --git a/db/migrate/20220902065611_add_partition_id_to_ci_job_artifacts.rb b/db/migrate/20220902065611_add_partition_id_to_ci_job_artifacts.rb
new file mode 100644
index 00000000000..1d9eeb0330e
--- /dev/null
+++ b/db/migrate/20220902065611_add_partition_id_to_ci_job_artifacts.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddPartitionIdToCiJobArtifacts < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :ci_job_artifacts, :partition_id, :bigint, default: 100, null: false
+ end
+end
diff --git a/db/migrate/20220902065623_add_partition_id_to_ci_pipelines.rb b/db/migrate/20220902065623_add_partition_id_to_ci_pipelines.rb
new file mode 100644
index 00000000000..bb3e7c27ee8
--- /dev/null
+++ b/db/migrate/20220902065623_add_partition_id_to_ci_pipelines.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddPartitionIdToCiPipelines < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :ci_pipelines, :partition_id, :bigint, default: 100, null: false
+ end
+end
diff --git a/db/migrate/20220902065635_add_partition_id_to_ci_stages.rb b/db/migrate/20220902065635_add_partition_id_to_ci_stages.rb
new file mode 100644
index 00000000000..0ddbf491ee9
--- /dev/null
+++ b/db/migrate/20220902065635_add_partition_id_to_ci_stages.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddPartitionIdToCiStages < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :ci_stages, :partition_id, :bigint, default: 100, null: false
+ end
+end
diff --git a/db/migrate/20220902065647_add_partition_id_to_ci_pipeline_variables.rb b/db/migrate/20220902065647_add_partition_id_to_ci_pipeline_variables.rb
new file mode 100644
index 00000000000..14f17b371b4
--- /dev/null
+++ b/db/migrate/20220902065647_add_partition_id_to_ci_pipeline_variables.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddPartitionIdToCiPipelineVariables < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :ci_pipeline_variables, :partition_id, :bigint, default: 100, null: false
+ end
+end
diff --git a/db/schema_migrations/20220902065314 b/db/schema_migrations/20220902065314
new file mode 100644
index 00000000000..8197a41403d
--- /dev/null
+++ b/db/schema_migrations/20220902065314
@@ -0,0 +1 @@
+d1ca445a17c742d435cba3d898e61242a3df9c92caeadecba147fce858d8cb80 \ No newline at end of file
diff --git a/db/schema_migrations/20220902065316 b/db/schema_migrations/20220902065316
new file mode 100644
index 00000000000..e9c3598206e
--- /dev/null
+++ b/db/schema_migrations/20220902065316
@@ -0,0 +1 @@
+910d87fbab226671b8e12b236be43970f6b2a3083f30df9586b3f8edf779f4af \ No newline at end of file
diff --git a/db/schema_migrations/20220902065317 b/db/schema_migrations/20220902065317
new file mode 100644
index 00000000000..fa60ee97fef
--- /dev/null
+++ b/db/schema_migrations/20220902065317
@@ -0,0 +1 @@
+11c65391a6744d7d7c303c6593dafa8e6dca392675974a2a1df2c164afbd4fe1 \ No newline at end of file
diff --git a/db/schema_migrations/20220902065558 b/db/schema_migrations/20220902065558
new file mode 100644
index 00000000000..2886e656d41
--- /dev/null
+++ b/db/schema_migrations/20220902065558
@@ -0,0 +1 @@
+cce779cc52b2bb175ccd3d07ac6a7df3711ae362fa0a5004bfc58fa1eb440e1f \ No newline at end of file
diff --git a/db/schema_migrations/20220902065611 b/db/schema_migrations/20220902065611
new file mode 100644
index 00000000000..365cb0f6194
--- /dev/null
+++ b/db/schema_migrations/20220902065611
@@ -0,0 +1 @@
+8ec0cc23559ba1b83042bed4abf8c47487ecb999fa66e602fbf4a9edac0569ec \ No newline at end of file
diff --git a/db/schema_migrations/20220902065623 b/db/schema_migrations/20220902065623
new file mode 100644
index 00000000000..cf75e086f31
--- /dev/null
+++ b/db/schema_migrations/20220902065623
@@ -0,0 +1 @@
+4f2076138e65849d60cf093f140afa1abaa7beea4d6c95048e6743168a7f17a9 \ No newline at end of file
diff --git a/db/schema_migrations/20220902065635 b/db/schema_migrations/20220902065635
new file mode 100644
index 00000000000..bd131598d78
--- /dev/null
+++ b/db/schema_migrations/20220902065635
@@ -0,0 +1 @@
+49a86fa87974f2c0cdc5a38726ab792f70c43e7f215495323d0999fd9f6e45f6 \ No newline at end of file
diff --git a/db/schema_migrations/20220902065647 b/db/schema_migrations/20220902065647
new file mode 100644
index 00000000000..31ee9352fe6
--- /dev/null
+++ b/db/schema_migrations/20220902065647
@@ -0,0 +1 @@
+812f25371d731d03bd4727328ad0daaf954595e24a314dd5f1adccdc3a4532c4 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 419a328269c..fe6c6f295c4 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -12632,6 +12632,7 @@ CREATE TABLE ci_builds (
scheduling_type smallint,
id bigint NOT NULL,
stage_id bigint,
+ partition_id bigint DEFAULT 100 NOT NULL,
CONSTRAINT check_1e2fbd1b39 CHECK ((lock_version IS NOT NULL))
);
@@ -12658,7 +12659,8 @@ CREATE TABLE ci_builds_metadata (
build_id bigint NOT NULL,
id bigint NOT NULL,
runtime_runner_features jsonb DEFAULT '{}'::jsonb NOT NULL,
- id_tokens jsonb DEFAULT '{}'::jsonb NOT NULL
+ id_tokens jsonb DEFAULT '{}'::jsonb NOT NULL,
+ partition_id bigint DEFAULT 100 NOT NULL
);
CREATE SEQUENCE ci_builds_metadata_id_seq
@@ -12823,6 +12825,7 @@ CREATE TABLE ci_job_artifacts (
job_id bigint NOT NULL,
locked smallint DEFAULT 2,
original_filename text,
+ partition_id bigint DEFAULT 100 NOT NULL,
CONSTRAINT check_27f0f6dbab CHECK ((file_store IS NOT NULL)),
CONSTRAINT check_85573000db CHECK ((char_length(original_filename) <= 512))
);
@@ -12928,6 +12931,21 @@ CREATE SEQUENCE ci_namespace_monthly_usages_id_seq
ALTER SEQUENCE ci_namespace_monthly_usages_id_seq OWNED BY ci_namespace_monthly_usages.id;
+CREATE TABLE ci_partitions (
+ id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL
+);
+
+CREATE SEQUENCE ci_partitions_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE ci_partitions_id_seq OWNED BY ci_partitions.id;
+
CREATE TABLE ci_pending_builds (
id bigint NOT NULL,
build_id bigint NOT NULL,
@@ -13071,7 +13089,8 @@ CREATE TABLE ci_pipeline_variables (
encrypted_value_iv character varying,
pipeline_id integer NOT NULL,
variable_type smallint DEFAULT 1 NOT NULL,
- raw boolean DEFAULT true NOT NULL
+ raw boolean DEFAULT true NOT NULL,
+ partition_id bigint DEFAULT 100 NOT NULL
);
CREATE SEQUENCE ci_pipeline_variables_id_seq
@@ -13113,6 +13132,7 @@ CREATE TABLE ci_pipelines (
external_pull_request_id bigint,
ci_ref_id bigint,
locked smallint DEFAULT 1 NOT NULL,
+ partition_id bigint DEFAULT 100 NOT NULL,
CONSTRAINT check_d7e99a025e CHECK ((lock_version IS NOT NULL))
);
@@ -13417,6 +13437,7 @@ CREATE TABLE ci_stages (
lock_version integer DEFAULT 0,
"position" integer,
id bigint NOT NULL,
+ partition_id bigint DEFAULT 100 NOT NULL,
CONSTRAINT check_81b431e49b CHECK ((lock_version IS NOT NULL))
);
@@ -23241,6 +23262,8 @@ ALTER TABLE ONLY ci_namespace_mirrors ALTER COLUMN id SET DEFAULT nextval('ci_na
ALTER TABLE ONLY ci_namespace_monthly_usages ALTER COLUMN id SET DEFAULT nextval('ci_namespace_monthly_usages_id_seq'::regclass);
+ALTER TABLE ONLY ci_partitions ALTER COLUMN id SET DEFAULT nextval('ci_partitions_id_seq'::regclass);
+
ALTER TABLE ONLY ci_pending_builds ALTER COLUMN id SET DEFAULT nextval('ci_pending_builds_id_seq'::regclass);
ALTER TABLE ONLY ci_pipeline_artifacts ALTER COLUMN id SET DEFAULT nextval('ci_pipeline_artifacts_id_seq'::regclass);
@@ -24976,6 +24999,9 @@ ALTER TABLE ONLY ci_namespace_mirrors
ALTER TABLE ONLY ci_namespace_monthly_usages
ADD CONSTRAINT ci_namespace_monthly_usages_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY ci_partitions
+ ADD CONSTRAINT ci_partitions_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY ci_pending_builds
ADD CONSTRAINT ci_pending_builds_pkey PRIMARY KEY (id);
diff --git a/doc/api/members.md b/doc/api/members.md
index b0992aafb7e..3ffe94e6f99 100644
--- a/doc/api/members.md
+++ b/doc/api/members.md
@@ -76,8 +76,7 @@ Example response:
},
"expires_at": "2012-10-22T14:13:35Z",
"access_level": 30,
- "group_saml_identity": null,
- "membership_state": "active"
+ "group_saml_identity": null
},
{
"id": 2,
@@ -102,8 +101,7 @@ Example response:
"extern_uid":"ABC-1234567890",
"provider": "group_saml",
"saml_provider_id": 10
- },
- "membership_state": "active"
+ }
}
]
```
@@ -163,8 +161,7 @@ Example response:
},
"expires_at": "2012-10-22T14:13:35Z",
"access_level": 30,
- "group_saml_identity": null,
- "membership_state": "active"
+ "group_saml_identity": null
},
{
"id": 2,
@@ -189,8 +186,7 @@ Example response:
"extern_uid":"ABC-1234567890",
"provider": "group_saml",
"saml_provider_id": 10
- },
- "membership_state": "active"
+ }
},
{
"id": 3,
@@ -210,8 +206,7 @@ Example response:
},
"expires_at": "2012-11-22T14:13:35Z",
"access_level": 30,
- "group_saml_identity": null,
- "membership_state": "active"
+ "group_saml_identity": null
}
]
```
@@ -257,8 +252,7 @@ Example response:
"web_url": "http://192.168.1.8:3000/root"
},
"expires_at": null,
- "group_saml_identity": null,
- "membership_state": "active"
+ "group_saml_identity": null
}
```
@@ -305,8 +299,7 @@ Example response:
},
"email": "john@example.com",
"expires_at": null,
- "group_saml_identity": null,
- "membership_state": "active"
+ "group_saml_identity": null
}
```
@@ -370,7 +363,6 @@ Example response:
"web_url": "http://192.168.1.8:3000/root",
"last_activity_on": "2021-01-27",
"membership_type": "group_member",
- "membership_state": "active",
"removable": true,
"created_at": "2021-01-03T12:16:02.000Z"
},
@@ -384,7 +376,6 @@ Example response:
"email": "john@example.com",
"last_activity_on": "2021-01-25",
"membership_type": "group_member",
- "membership_state": "active",
"removable": true,
"created_at": "2021-01-04T18:46:42.000Z"
},
@@ -397,7 +388,6 @@ Example response:
"web_url": "http://192.168.1.8:3000/root",
"last_activity_on": "2021-01-20",
"membership_type": "group_invite",
- "membership_state": "awaiting",
"removable": false,
"created_at": "2021-01-09T07:12:31.000Z"
}
diff --git a/doc/ci/mobile_devops.md b/doc/ci/mobile_devops.md
new file mode 100644
index 00000000000..6eb56434a1b
--- /dev/null
+++ b/doc/ci/mobile_devops.md
@@ -0,0 +1,42 @@
+---
+stage: none
+group: unassigned
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+type: reference
+---
+
+# Mobile DevOps
+
+GitLab Mobile DevOps is a collection of features and tools designed for mobile developers
+and teams to automate their build and release process using GitLab CI/CD. Mobile DevOps
+is an experimental feature developed by [GitLab Incubation Engineering](https://about.gitlab.com/handbook/engineering/incubation/).
+
+Mobile DevOps is still in development, but you can:
+
+- [Request a feature](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/feedback/-/issues/new?issuable_template=feature_request).
+- [Report a bug](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/feedback/-/issues/new?issuable_template=report_bug).
+- [Share feedback](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/feedback/-/issues/new?issuable_template=general_feedback).
+
+## Code Signing
+
+[Project-level Secure Files](secure_files/index.md) makes it easier to manage key stores, provision profiles,
+and signing certificates directly in a GitLab project.
+
+For a guided walkthrough of this feature, watch the [video demo](https://youtu.be/O7FbJu3H2YM).
+
+## Review Apps for Mobile
+
+You can use [Review Apps](review_apps/index.md) to preview changes directly from a merge request.
+Review Apps for Mobile brings that capability to mobile developers through an integration
+with [Appetize](https://appetize.io/).
+
+Watch a [video walkthrough](https://youtu.be/X15mI19TXa4) of this feature, or visit the
+[setup instructions](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/readme/-/issues/15)
+to get started.
+
+## Mobile SAST
+
+You can use [Static Application Security Testing (SAST)](../user/application_security/sast/index.md)
+to run static analyzers on code to check for known security vulnerabilities. Mobile SAST
+expands this functionality for mobile teams with an [experimental SAST feature](../user/application_security/sast/index.md#experimental-features)
+based on [Mobile Security Framework (MobSF)](https://github.com/MobSF/Mobile-Security-Framework-MobSF).
diff --git a/doc/development/service_ping/implement.md b/doc/development/service_ping/implement.md
index 8c04992fd67..4ef58fefcb9 100644
--- a/doc/development/service_ping/implement.md
+++ b/doc/development/service_ping/implement.md
@@ -272,7 +272,7 @@ Events are handled by counter classes in the `Gitlab::UsageDataCounters` namespa
1. Listed in [`Gitlab::UsageDataCounters::COUNTERS`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/usage_data_counters.rb#L5) to be then included in `Gitlab::UsageData`.
-1. Specified in the metric definition using the `RedisMetric` instrumentation class as a `counter_class` option to be picked up using the [metric instrumentation](metrics_instrumentation.md) framework. Refer to the [Redis metrics](metrics_instrumentation.md#redis-metrics) documentation for an example implementation.
+1. Specified in the metric definition using the `RedisMetric` instrumentation class by their `prefix` option to be picked up using the [metric instrumentation](metrics_instrumentation.md) framework. Refer to the [Redis metrics](metrics_instrumentation.md#redis-metrics) documentation for an example implementation.
Inheriting classes are expected to override `KNOWN_EVENTS` and `PREFIX` constants to build event names and associated metrics. For example, for prefix `issues` and events array `%w[create, update, delete]`, three metrics will be added to the Service Ping payload: `counts.issues_create`, `counts.issues_update` and `counts.issues_delete`.
diff --git a/doc/development/service_ping/metrics_instrumentation.md b/doc/development/service_ping/metrics_instrumentation.md
index ee0d701a5bb..860434ab2ad 100644
--- a/doc/development/service_ping/metrics_instrumentation.md
+++ b/doc/development/service_ping/metrics_instrumentation.md
@@ -154,14 +154,16 @@ end
You can use Redis metrics to track events not kept in the database, for example, a count of how many times the search bar has been used.
-[Example of a merge request that adds a `Redis` metric](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66582).
+[Example of a merge request that adds a `Redis` metric](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97009).
+
+Please note that `RedisMetric` class can only be used as the `instrumentation_class` for Redis metrics with simple counters classes (classes that only inherit `BaseCounter` and set `PREFIX` and `KNOWN_EVENTS` constants). In case the counter class has additional logic included in it, a new `instrumentation_class`, inheriting from `RedisMetric`, needs to be created. This new class needs to include the additional logic from the counter class.
Count unique values for `source_code_pushes` event.
Required options:
- `event`: the event name.
-- `counter_class`: one of the counter classes from the `Gitlab::UsageDataCounters` namespace; it should implement `read` method or inherit it from `BaseCounter`.
+- `prefix`: the value of the `PREFIX` constant used in the counter classes from the `Gitlab::UsageDataCounters` namespace.
```yaml
time_frame: all
@@ -169,7 +171,7 @@ data_source: redis
instrumentation_class: 'RedisMetric'
options:
event: pushes
- counter_class: SourceCodeCounter
+ prefix: source_code
```
### Availability-restrained Redis metrics
@@ -200,7 +202,7 @@ data_source: redis
instrumentation_class: 'MergeUsageCountRedisMetric'
options:
event: pushes
- counter_class: SourceCodeCounter
+ prefix: source_code
```
## Redis HyperLogLog metrics
diff --git a/lib/gitlab/database/gitlab_schemas.yml b/lib/gitlab/database/gitlab_schemas.yml
index 93fe5871a9b..1c2d04561b4 100644
--- a/lib/gitlab/database/gitlab_schemas.yml
+++ b/lib/gitlab/database/gitlab_schemas.yml
@@ -91,6 +91,7 @@ ci_job_artifact_states: :gitlab_ci
ci_minutes_additional_packs: :gitlab_ci
ci_namespace_monthly_usages: :gitlab_ci
ci_namespace_mirrors: :gitlab_ci
+ci_partitions: :gitlab_ci
ci_pending_builds: :gitlab_ci
ci_pipeline_artifacts: :gitlab_ci
ci_pipeline_chat_data: :gitlab_ci
diff --git a/lib/gitlab/usage/metrics/instrumentations/redis_metric.rb b/lib/gitlab/usage/metrics/instrumentations/redis_metric.rb
index a25bad2436b..c9449f10cc2 100644
--- a/lib/gitlab/usage/metrics/instrumentations/redis_metric.rb
+++ b/lib/gitlab/usage/metrics/instrumentations/redis_metric.rb
@@ -11,37 +11,41 @@ module Gitlab
# instrumentation_class: RedisMetric
# options:
# event: pushes
- # counter_class: SourceCodeCounter
+ # prefix: source_code
#
class RedisMetric < BaseMetric
+ include Gitlab::UsageDataCounters::RedisCounter
+
def initialize(time_frame:, options: {})
super
raise ArgumentError, "'event' option is required" unless metric_event.present?
- raise ArgumentError, "'counter class' option is required" unless counter_class.present?
+ raise ArgumentError, "'prefix' option is required" unless prefix.present?
end
def metric_event
options[:event]
end
- def counter_class_name
- options[:counter_class]
- end
-
- def counter_class
- "Gitlab::UsageDataCounters::#{counter_class_name}".constantize
+ def prefix
+ options[:prefix]
end
def value
redis_usage_data do
- counter_class.read(metric_event)
+ total_count(redis_key)
end
end
def suggested_name
Gitlab::Usage::Metrics::NameSuggestion.for(:redis)
end
+
+ private
+
+ def redis_key
+ "USAGE_#{prefix}_#{metric_event}".upcase
+ end
end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index c2a4609123d..0742b7bba2b 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -17962,6 +17962,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -18049,6 +18052,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -26698,6 +26707,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
diff --git a/package.json b/package.json
index 230f0a4d8f7..286a6a72d28 100644
--- a/package.json
+++ b/package.json
@@ -53,7 +53,7 @@
"@gitlab/at.js": "1.5.7",
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/svgs": "3.3.0",
- "@gitlab/ui": "43.9.3",
+ "@gitlab/ui": "43.13.0",
"@gitlab/visual-review-tools": "1.7.3",
"@gitlab/web-ide": "0.0.1-dev-20220815034418",
"@rails/actioncable": "6.1.4-7",
@@ -197,7 +197,7 @@
"yaml": "^2.0.0-10"
},
"devDependencies": {
- "@gitlab/eslint-plugin": "16.0.0",
+ "@gitlab/eslint-plugin": "17.0.0",
"@gitlab/stylelint-config": "4.1.0",
"@graphql-eslint/eslint-plugin": "3.10.7",
"@testing-library/dom": "^7.16.2",
diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb
index 4092f639eae..4aeafed5712 100644
--- a/spec/db/schema_spec.rb
+++ b/spec/db/schema_spec.rb
@@ -31,9 +31,14 @@ RSpec.describe 'Database schema' do
boards: %w[milestone_id iteration_id],
chat_names: %w[chat_id team_id user_id],
chat_teams: %w[team_id],
- ci_builds: %w[erased_by_id trigger_request_id],
+ ci_builds: %w[erased_by_id trigger_request_id partition_id],
+ ci_builds_metadata: %w[partition_id],
+ ci_job_artifacts: %w[partition_id],
ci_namespace_monthly_usages: %w[namespace_id],
+ ci_pipeline_variables: %w[partition_id],
+ ci_pipelines: %w[partition_id],
ci_runner_projects: %w[runner_id],
+ ci_stages: %w[partition_id],
ci_trigger_requests: %w[commit_id],
cluster_providers_aws: %w[security_group_id vpc_id access_key_id],
cluster_providers_gcp: %w[gcp_project_id operation_id],
diff --git a/spec/frontend/boards/components/board_card_move_to_position_spec.js b/spec/frontend/boards/components/board_card_move_to_position_spec.js
index 01bad53d9e1..7254b9486ef 100644
--- a/spec/frontend/boards/components/board_card_move_to_position_spec.js
+++ b/spec/frontend/boards/components/board_card_move_to_position_spec.js
@@ -1,8 +1,8 @@
+import { shallowMount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import BoardCardMoveToPosition from '~/boards/components/board_card_move_to_position.vue';
import { mockList, mockIssue2, mockIssue, mockIssue3, mockIssue4 } from 'jest/boards/mock_data';
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
@@ -19,6 +19,7 @@ describe('Board Card Move to position', () => {
let trackingSpy;
let store;
let dispatch;
+ const itemIndex = 1;
const createStoreOptions = () => {
const state = {
@@ -42,7 +43,7 @@ describe('Board Card Move to position', () => {
};
const createComponent = (propsData) => {
- wrapper = shallowMountExtended(BoardCardMoveToPosition, {
+ wrapper = shallowMount(BoardCardMoveToPosition, {
store,
propsData: {
item: mockIssue2,
@@ -66,7 +67,6 @@ describe('Board Card Move to position', () => {
wrapper.destroy();
});
- const findEllipsesButton = () => wrapper.findByTestId('move-card-dropdown');
const findMoveToPositionDropdown = () => wrapper.findComponent(GlDropdown);
const findDropdownItems = () => findMoveToPositionDropdown().findAllComponents(GlDropdownItem);
const findDropdownItemAtIndex = (index) => findDropdownItems().at(index);
@@ -74,7 +74,7 @@ describe('Board Card Move to position', () => {
describe('Dropdown', () => {
describe('Dropdown button', () => {
it('has an icon with vertical ellipsis', () => {
- expect(findEllipsesButton().exists()).toBe(true);
+ expect(findMoveToPositionDropdown().exists()).toBe(true);
expect(findMoveToPositionDropdown().props('icon')).toBe('ellipsis_v');
});
@@ -82,24 +82,11 @@ describe('Board Card Move to position', () => {
findMoveToPositionDropdown().vm.$emit('click');
expect(findDropdownItems()).toHaveLength(dropdownOptions.length);
});
-
- it('is opened on the click of vertical ellipsis and has 1 dropdown items when number of list items > 10', () => {
- wrapper.destroy();
-
- createComponent({
- list: {
- ...mockList,
- id: 'gid://gitlab/List/2',
- },
- });
- findMoveToPositionDropdown().vm.$emit('click');
- expect(findDropdownItems()).toHaveLength(1);
- });
});
describe('Dropdown options', () => {
beforeEach(() => {
- createComponent({ index: 1 });
+ createComponent({ index: itemIndex });
trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
dispatch = jest.spyOn(store, 'dispatch').mockImplementation(() => {});
});
@@ -109,13 +96,13 @@ describe('Board Card Move to position', () => {
});
it.each`
- dropdownIndex | dropdownLabel | trackLabel | moveAfterId | moveBeforeId
- ${0} | ${BoardCardMoveToPosition.i18n.moveToStartText} | ${'move_to_start'} | ${mockIssue.id} | ${undefined}
- ${1} | ${BoardCardMoveToPosition.i18n.moveToEndText} | ${'move_to_end'} | ${undefined} | ${mockIssue4.id}
+ dropdownIndex | dropdownLabel | trackLabel | positionInList
+ ${0} | ${BoardCardMoveToPosition.i18n.moveToStartText} | ${'move_to_start'} | ${0}
+ ${1} | ${BoardCardMoveToPosition.i18n.moveToEndText} | ${'move_to_end'} | ${-1}
`(
'on click of dropdown index $dropdownIndex with label $dropdownLabel should call moveItem action with tracking label $trackLabel',
- async ({ dropdownIndex, dropdownLabel, trackLabel, moveAfterId, moveBeforeId }) => {
- await findEllipsesButton().vm.$emit('click');
+ async ({ dropdownIndex, dropdownLabel, trackLabel, positionInList }) => {
+ await findMoveToPositionDropdown().vm.$emit('click');
expect(findDropdownItemAtIndex(dropdownIndex).text()).toBe(dropdownLabel);
await findDropdownItemAtIndex(dropdownIndex).vm.$emit('click', {
@@ -134,9 +121,10 @@ describe('Board Card Move to position', () => {
itemId: mockIssue2.id,
itemIid: mockIssue2.iid,
itemPath: mockIssue2.referencePath,
- moveBeforeId,
- moveAfterId,
+ positionInList,
toListId: mockList.id,
+ allItemsLoadedInList: true,
+ atIndex: itemIndex,
});
},
);
diff --git a/spec/frontend/boards/stores/actions_spec.js b/spec/frontend/boards/stores/actions_spec.js
index e48b946ff1b..e919300228a 100644
--- a/spec/frontend/boards/stores/actions_spec.js
+++ b/spec/frontend/boards/stores/actions_spec.js
@@ -1056,6 +1056,8 @@ describe('moveIssueCard and undoMoveIssueCard', () => {
originalIndex = 0,
moveBeforeId = undefined,
moveAfterId = undefined,
+ allItemsLoadedInList = true,
+ listPosition = undefined,
} = {}) => {
state = {
boardLists: {
@@ -1065,12 +1067,28 @@ describe('moveIssueCard and undoMoveIssueCard', () => {
boardItems: { [itemId]: originalIssue },
boardItemsByListId: { [fromListId]: [123] },
};
- params = { itemId, fromListId, toListId, moveBeforeId, moveAfterId };
+ params = {
+ itemId,
+ fromListId,
+ toListId,
+ moveBeforeId,
+ moveAfterId,
+ listPosition,
+ allItemsLoadedInList,
+ };
moveMutations = [
{ type: types.REMOVE_BOARD_ITEM_FROM_LIST, payload: { itemId, listId: fromListId } },
{
type: types.ADD_BOARD_ITEM_TO_LIST,
- payload: { itemId, listId: toListId, moveBeforeId, moveAfterId },
+ payload: {
+ itemId,
+ listId: toListId,
+ moveBeforeId,
+ moveAfterId,
+ listPosition,
+ allItemsLoadedInList,
+ atIndex: originalIndex,
+ },
},
];
undoMutations = [
@@ -1366,9 +1384,17 @@ describe('updateIssueOrder', () => {
state,
[
{
+ type: types.MUTATE_ISSUE_IN_PROGRESS,
+ payload: true,
+ },
+ {
type: types.MUTATE_ISSUE_SUCCESS,
payload: { issue: rawIssue },
},
+ {
+ type: types.MUTATE_ISSUE_IN_PROGRESS,
+ payload: false,
+ },
],
[],
);
@@ -1390,6 +1416,14 @@ describe('updateIssueOrder', () => {
state,
[
{
+ type: types.MUTATE_ISSUE_IN_PROGRESS,
+ payload: true,
+ },
+ {
+ type: types.MUTATE_ISSUE_IN_PROGRESS,
+ payload: false,
+ },
+ {
type: types.SET_ERROR,
payload: 'An error occurred while moving the issue. Please try again.',
},
diff --git a/spec/frontend/boards/stores/mutations_spec.js b/spec/frontend/boards/stores/mutations_spec.js
index 1606ca09d8f..87a183c0441 100644
--- a/spec/frontend/boards/stores/mutations_spec.js
+++ b/spec/frontend/boards/stores/mutations_spec.js
@@ -513,6 +513,31 @@ describe('Board Store Mutations', () => {
listState: [mockIssue2.id, mockIssue.id],
},
],
+ [
+ 'to the top of the list',
+ {
+ payload: {
+ itemId: mockIssue2.id,
+ listId: mockList.id,
+ positionInList: 0,
+ atIndex: 1,
+ },
+ listState: [mockIssue2.id, mockIssue.id],
+ },
+ ],
+ [
+ 'to the bottom of the list when the list is fully loaded',
+ {
+ payload: {
+ itemId: mockIssue2.id,
+ listId: mockList.id,
+ positionInList: -1,
+ atIndex: 0,
+ allItemsLoadedInList: true,
+ },
+ listState: [mockIssue.id, mockIssue2.id],
+ },
+ ],
])(`inserts an item into a list %s`, (_, { payload, listState }) => {
mutations.ADD_BOARD_ITEM_TO_LIST(state, payload);
diff --git a/spec/frontend/ide/stores/modules/commit/actions_spec.js b/spec/frontend/ide/stores/modules/commit/actions_spec.js
index d65039e89cc..4e8467de759 100644
--- a/spec/frontend/ide/stores/modules/commit/actions_spec.js
+++ b/spec/frontend/ide/stores/modules/commit/actions_spec.js
@@ -210,7 +210,7 @@ describe('IDE commit module actions', () => {
branch,
});
store.state.openFiles.forEach((entry) => {
- expect(entry.changed).toBeFalsy();
+ expect(entry.changed).toBe(false);
});
});
diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb
index 831f775ec9a..e228a0a7d72 100644
--- a/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb
+++ b/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb
@@ -11,18 +11,18 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::RedisMetric, :clean_git
let(:expected_value) { 4 }
- it_behaves_like 'a correct instrumented metric value', { options: { event: 'pushes', counter_class: 'SourceCodeCounter' } }
+ it_behaves_like 'a correct instrumented metric value', { options: { event: 'pushes', prefix: 'source_code' } }
it 'raises an exception if event option is not present' do
- expect { described_class.new(counter_class: 'SourceCodeCounter') }.to raise_error(ArgumentError)
+ expect { described_class.new(prefix: 'source_code') }.to raise_error(ArgumentError)
end
- it 'raises an exception if counter_class option is not present' do
+ it 'raises an exception if prefix option is not present' do
expect { described_class.new(event: 'pushes') }.to raise_error(ArgumentError)
end
describe 'children classes' do
- let(:options) { { event: 'pushes', counter_class: 'SourceCodeCounter' } }
+ let(:options) { { event: 'pushes', prefix: 'source_code' } }
context 'availability not defined' do
subject { Class.new(described_class).new(time_frame: nil, options: options) }
diff --git a/yarn.lock b/yarn.lock
index 7ecbe845056..954169a5f33 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1027,10 +1027,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/at.js/-/at.js-1.5.7.tgz#1ee6f838cc4410a1d797770934df91d90df8179e"
integrity sha512-c6ySRK/Ma7lxwpIVbSAF3P+xiTLrNTGTLRx4/pHK111AdFxwgUwrYF6aVZFXvmG65jHOJHoa0eQQ21RW6rm0Rg==
-"@gitlab/eslint-plugin@16.0.0":
- version "16.0.0"
- resolved "https://registry.yarnpkg.com/@gitlab/eslint-plugin/-/eslint-plugin-16.0.0.tgz#83b71bb3f749c6e52138d2c1c17ac623e7b2e3db"
- integrity sha512-2n7geoRPkeMAq4GCqyvFzcTgcSrTM7pdCOxfcqIeuTmh/PFGhh+m7YC+YC4enhGOCN8lo08buLZhXkSgWiHSqA==
+"@gitlab/eslint-plugin@17.0.0":
+ version "17.0.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/eslint-plugin/-/eslint-plugin-17.0.0.tgz#5451fbbad96b09d812af2afb247f6602fe0be6c6"
+ integrity sha512-c+sJtjzYl+KGPtZScU8Mji9seJw7dSEn31APyYEYTyWp72yMsFvXmg46txT2QCz+ueZlqk0/C2IQmgfe6fLcBw==
dependencies:
"@babel/core" "^7.17.0"
"@babel/eslint-parser" "^7.17.0"
@@ -1064,10 +1064,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.3.0.tgz#99b044484fcf3d5a6431281e320e2405540ff5a9"
integrity sha512-S8Hqf+ms8aNrSgmci9SVoIyj/0qQnizU5uV5vUPAOwiufMDFDyI5qfcgn4EYZ6mnju3LiO+ReSL/PPTD4qNgHA==
-"@gitlab/ui@43.9.3":
- version "43.9.3"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-43.9.3.tgz#2dd91b14da769a873e45ffe07b5863f6c47211ba"
- integrity sha512-TONSf+6UJYWTVs5qnItR1uLZ/0kBE8jGN8aLOVv4CDAsORvln0ZxtcZvMTCFp76YEtzXLkMUfvm7ZngQ26tIiA==
+"@gitlab/ui@43.13.0":
+ version "43.13.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-43.13.0.tgz#7e4e7d41287cfba8a46dbdd3c8ba998a853a9ad2"
+ integrity sha512-y0BrVKsqRBEQMrsJseakBeMrFHVMTg7DVMa3tbdkKkrruV8SYOsX8wLrv20taDhiMlceKRB8lF5gLTPPHLwCGA==
dependencies:
"@popperjs/core" "^2.11.2"
bootstrap-vue "2.20.1"