summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-10-29 12:08:50 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-10-29 12:08:50 +0000
commit5a7d44a955572b912d13ba8949e976f61b5c7f1b (patch)
tree544ed48a55f80871ca0dcf588cf873ffc8ff1bc3
parentc3ea5eada6f28b5e46cc4114f315729cde58de87 (diff)
downloadgitlab-ce-5a7d44a955572b912d13ba8949e976f61b5c7f1b.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/ci/rails.gitlab-ci.yml4
-rw-r--r--.gitlab/ci/rules.gitlab-ci.yml23
-rw-r--r--.gitlab/ci/yaml.gitlab-ci.yml6
-rw-r--r--app/assets/javascripts/boards/components/board_settings_sidebar.vue8
-rw-r--r--app/assets/javascripts/diffs/components/collapsed_files_warning.vue6
-rw-r--r--app/assets/javascripts/diffs/components/compare_versions.vue5
-rw-r--r--app/assets/javascripts/diffs/components/diff_file.vue14
-rw-r--r--app/assets/javascripts/diffs/event_hub.js3
-rw-r--r--app/assets/javascripts/diffs/store/actions.js4
-rw-r--r--app/assets/javascripts/diffs/store/mutation_types.js1
-rw-r--r--app/assets/javascripts/diffs/store/mutations.js11
-rw-r--r--app/assets/javascripts/frequent_items/index.js82
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue21
-rw-r--r--app/assets/javascripts/projects/pipelines/charts/components/statistics_list.vue54
-rw-r--r--app/assets/javascripts/projects/pipelines/charts/index.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author.vue16
-rw-r--r--app/controllers/projects/pipelines_controller.rb1
-rw-r--r--app/graphql/types/release_links_type.rb21
-rw-r--r--app/models/ci/pipeline.rb2
-rw-r--r--app/presenters/release_presenter.rb26
-rw-r--r--changelogs/unreleased/Add-Caching-To-BitBucket-Server-Importer.yml5
-rw-r--r--changelogs/unreleased/add-total-duration-to-cicd-analytics-page.yml5
-rw-r--r--changelogs/unreleased/nfriend-add-release-progress-link-paths.yml6
-rw-r--r--changelogs/unreleased/okr-tooltip-mr-widget-author-vue.yml5
-rw-r--r--changelogs/unreleased/revert-tz-defer-frequent-item-init.yml5
-rw-r--r--changelogs/unreleased/ss-add-wip-limits-license-check.yml5
-rw-r--r--doc/api/graphql/reference/gitlab_schema.graphql33
-rw-r--r--doc/api/graphql/reference/gitlab_schema.json76
-rw-r--r--doc/api/graphql/reference/index.md9
-rw-r--r--doc/development/testing_guide/frontend_testing.md52
-rw-r--r--lib/api/badges.rb2
-rw-r--r--lib/api/boards.rb2
-rw-r--r--lib/api/branches.rb2
-rw-r--r--lib/api/broadcast_messages.rb2
-rw-r--r--lib/api/ci/pipeline_schedules.rb2
-rw-r--r--lib/api/ci/pipelines.rb2
-rw-r--r--lib/api/ci/runner.rb2
-rw-r--r--lib/api/ci/runners.rb2
-rw-r--r--lib/api/commit_statuses.rb2
-rw-r--r--lib/api/commits.rb2
-rw-r--r--lib/api/container_registry_event.rb2
-rw-r--r--lib/api/deploy_keys.rb2
-rw-r--r--lib/api/deploy_tokens.rb2
-rw-r--r--lib/api/deployments.rb2
-rw-r--r--lib/api/entities/release.rb4
-rw-r--r--lib/api/environments.rb2
-rw-r--r--lib/api/error_tracking.rb2
-rw-r--r--lib/api/events.rb2
-rw-r--r--lib/api/feature_flag_scopes.rb2
-rw-r--r--lib/api/feature_flags.rb2
-rw-r--r--lib/api/feature_flags_user_lists.rb2
-rw-r--r--lib/api/features.rb2
-rw-r--r--lib/api/files.rb2
-rw-r--r--lib/api/freeze_periods.rb2
-rw-r--r--lib/api/group_boards.rb2
-rw-r--r--lib/api/group_clusters.rb2
-rw-r--r--lib/api/group_container_repositories.rb2
-rw-r--r--lib/api/group_export.rb2
-rw-r--r--lib/api/group_import.rb2
-rw-r--r--lib/api/group_labels.rb2
-rw-r--r--lib/api/group_milestones.rb2
-rw-r--r--lib/api/group_variables.rb2
-rw-r--r--lib/api/groups.rb2
-rw-r--r--lib/gitlab/bitbucket_server_import/importer.rb33
-rw-r--r--lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml4
-rw-r--r--locale/gitlab.pot3
-rw-r--r--spec/crystalball_env.rb2
-rw-r--r--spec/features/frequently_visited_projects_and_groups_spec.rb47
-rw-r--r--spec/features/issues/user_interacts_with_awards_spec.rb2
-rw-r--r--spec/frontend/diffs/components/collapsed_files_warning_spec.js7
-rw-r--r--spec/frontend/diffs/components/diff_file_spec.js26
-rw-r--r--spec/frontend/diffs/store/actions_spec.js18
-rw-r--r--spec/frontend/diffs/store/mutations_spec.js15
-rw-r--r--spec/frontend/projects/pipelines/charts/components/__snapshots__/statistics_list_spec.js.snap12
-rw-r--r--spec/frontend/projects/pipelines/charts/mock_data.js1
-rw-r--r--spec/graphql/types/release_links_type_spec.rb7
-rw-r--r--spec/lib/api/every_api_endpoint_spec.rb12
-rw-r--r--spec/lib/gitlab/bitbucket_server_import/importer_spec.rb47
-rw-r--r--spec/presenters/release_presenter_spec.rb73
-rw-r--r--spec/requests/api/graphql/project/release_spec.rb20
-rw-r--r--spec/requests/api/graphql/project/releases_spec.rb19
-rw-r--r--spec/support/shared_examples/uploaders/workers/object_storage/migrate_uploads_shared_examples.rb120
-rw-r--r--spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb114
-rw-r--r--tooling/lib/tooling/helm3_client.rb3
84 files changed, 779 insertions, 385 deletions
diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml
index 5a8f2651b6f..6735e08b020 100644
--- a/.gitlab/ci/rails.gitlab-ci.yml
+++ b/.gitlab/ci/rails.gitlab-ci.yml
@@ -287,7 +287,7 @@ db:migrate-from-v12.10.0:
- '[[ -d "ee/" ]] || export TAG_TO_CHECKOUT="v12.10.0"'
- git fetch https://gitlab.com/gitlab-org/$PROJECT_TO_CHECKOUT.git $TAG_TO_CHECKOUT
- git checkout -f FETCH_HEAD
- - sed -i -e "s/gem 'grpc', '~> 1.24.0'/gem 'grpc', '~> 1.30.2'/" Gemfile # Update gRPC for Ruby 2.7
+ - sed -i -e "s/gem 'grpc', '~> 1.24.0'/gem 'grpc', '~> 1.30.2'/" Gemfile # Update gRPC for Ruby 2.7
- sed -i -e "s/gem 'google-protobuf', '~> 3.8.0'/gem 'google-protobuf', '~> 3.12.0'/" Gemfile
- bundle update google-protobuf grpc bootsnap
- bundle install $BUNDLE_INSTALL_FLAGS
@@ -563,7 +563,7 @@ rspec-ee system pg12 geo:
# EE: Canonical MR pipelines
rspec fail-fast:
extends:
- - .rspec-ee-base-pg11 # This job also runs EE spec which needs elasticsearch
+ - .rspec-ee-base-pg11 # This job also runs EE spec which needs elasticsearch
- .rails:rules:rspec fail-fast
stage: test
needs: ["setup-test-env", "retrieve-tests-metadata", "compile-test-assets", "detect-tests"]
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml
index 57d4a2a4cb7..4c4a4b541f5 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -103,8 +103,11 @@
- ".gitlab/ci/build-images.gitlab-ci.yml"
- ".gitlab/ci/qa.gitlab-ci.yml"
-.yaml-patterns: &yaml-patterns
- - "**/*.yml"
+.yaml-lint-patterns: &yaml-lint-patterns
+ - ".gitlab-ci.yml"
+ - ".gitlab/ci/**/*.yml"
+ - "lib/gitlab/ci/templates/**/*.yml"
+ - "{,ee/}changelogs/**/*.yml"
.docs-patterns: &docs-patterns
- ".gitlab/route-map.yml"
@@ -142,8 +145,8 @@
- "{,ee/}{,spec/}lib/{,ee/}gitlab/database{,_spec}.rb"
- "{,ee/}{,spec/}lib/{,ee/}gitlab/background_migration/**/*"
- "{,ee/}{,spec/}lib/{,ee/}gitlab/background_migration{,_spec}.rb"
- - "config/prometheus/common_metrics.yml" # Used by Gitlab::DatabaseImporters::CommonMetrics::Importer
- - "{,ee/}app/models/project_statistics.rb" # Used to calculate sizes in migration specs
+ - "config/prometheus/common_metrics.yml" # Used by Gitlab::DatabaseImporters::CommonMetrics::Importer
+ - "{,ee/}app/models/project_statistics.rb" # Used to calculate sizes in migration specs
.backstage-patterns: &backstage-patterns
- "Dangerfile"
@@ -771,7 +774,7 @@
.review:rules:review-performance:
rules:
- - if: '$DAST_RUN == "true"' # Skip this job when DAST is run
+ - if: '$DAST_RUN == "true"' # Skip this job when DAST is run
when: never
- <<: *if-not-ee
when: never
@@ -905,10 +908,10 @@
- <<: *if-dot-com-ee-schedule
changes: *code-backstage-patterns
-##############
-# YAML rules #
-##############
-.yaml:rules:
+###################
+# yaml-lint rules #
+###################
+.yaml-lint:rules:
rules:
- <<: *if-default-refs
- changes: *yaml-patterns
+ changes: *yaml-lint-patterns
diff --git a/.gitlab/ci/yaml.gitlab-ci.yml b/.gitlab/ci/yaml.gitlab-ci.yml
index a650ee7e4b4..c597d992760 100644
--- a/.gitlab/ci/yaml.gitlab-ci.yml
+++ b/.gitlab/ci/yaml.gitlab-ci.yml
@@ -1,9 +1,9 @@
-# Yamllint of *.yml for .gitlab-ci.yml.
+# Yamllint of CI-related yaml and changelogs.
# This uses rules from project root `.yamllint`.
-lint-ci-gitlab:
+lint-yaml:
extends:
- .default-retry
- - .yaml:rules
+ - .yaml-lint:rules
image: pipelinecomponents/yamllint:latest
stage: test
needs: []
diff --git a/app/assets/javascripts/boards/components/board_settings_sidebar.vue b/app/assets/javascripts/boards/components/board_settings_sidebar.vue
index 392e056dcbf..06319df6ea5 100644
--- a/app/assets/javascripts/boards/components/board_settings_sidebar.vue
+++ b/app/assets/javascripts/boards/components/board_settings_sidebar.vue
@@ -36,6 +36,9 @@ export default {
computed: {
...mapGetters(['isSidebarOpen', 'shouldUseGraphQL']),
...mapState(['activeId', 'sidebarType', 'boardLists']),
+ isWipLimitsOn() {
+ return this.glFeatures.wipLimits;
+ },
activeList() {
/*
Warning: Though a computed property it is not reactive because we are
@@ -105,7 +108,10 @@ export default {
:active-list="activeList"
:board-list-type="boardListType"
/>
- <board-settings-sidebar-wip-limit :max-issue-count="activeList.maxIssueCount" />
+ <board-settings-sidebar-wip-limit
+ v-if="isWipLimitsOn"
+ :max-issue-count="activeList.maxIssueCount"
+ />
<div v-if="canAdminList && !activeList.preset && activeList.id" class="gl-m-4">
<gl-button
variant="danger"
diff --git a/app/assets/javascripts/diffs/components/collapsed_files_warning.vue b/app/assets/javascripts/diffs/components/collapsed_files_warning.vue
index 270bbfb99b7..1d4168449b2 100644
--- a/app/assets/javascripts/diffs/components/collapsed_files_warning.vue
+++ b/app/assets/javascripts/diffs/components/collapsed_files_warning.vue
@@ -1,9 +1,8 @@
<script>
-import { mapActions } from 'vuex';
-
import { GlAlert, GlButton } from '@gitlab/ui';
import { CENTERED_LIMITED_CONTAINER_CLASSES } from '../constants';
+import eventHub from '../event_hub';
export default {
components: {
@@ -36,13 +35,12 @@ export default {
},
methods: {
- ...mapActions('diffs', ['expandAllFiles']),
dismiss() {
this.isDismissed = true;
this.$emit('dismiss');
},
expand() {
- this.expandAllFiles();
+ eventHub.$emit('mr:diffs:expandAllFiles');
this.dismiss();
},
},
diff --git a/app/assets/javascripts/diffs/components/compare_versions.vue b/app/assets/javascripts/diffs/components/compare_versions.vue
index 59c64303c60..e30cbf8be4f 100644
--- a/app/assets/javascripts/diffs/components/compare_versions.vue
+++ b/app/assets/javascripts/diffs/components/compare_versions.vue
@@ -7,6 +7,7 @@ import CompareDropdownLayout from './compare_dropdown_layout.vue';
import SettingsDropdown from './settings_dropdown.vue';
import DiffStats from './diff_stats.vue';
import { CENTERED_LIMITED_CONTAINER_CLASSES } from '../constants';
+import eventHub from '../event_hub';
export default {
components: {
@@ -67,9 +68,11 @@ export default {
...mapActions('diffs', [
'setInlineDiffViewType',
'setParallelDiffViewType',
- 'expandAllFiles',
'toggleShowTreeList',
]),
+ expandAllFiles() {
+ eventHub.$emit('mr:diffs:expandAllFiles');
+ },
},
};
</script>
diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue
index 564d64c38c4..6d74952b7a1 100644
--- a/app/assets/javascripts/diffs/components/diff_file.vue
+++ b/app/assets/javascripts/diffs/components/diff_file.vue
@@ -6,13 +6,14 @@ import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { sprintf } from '~/locale';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import { hasDiff } from '~/helpers/diffs_helper';
-import eventHub from '../../notes/event_hub';
+import notesEventHub from '../../notes/event_hub';
import DiffFileHeader from './diff_file_header.vue';
import DiffContent from './diff_content.vue';
import { diffViewerErrors } from '~/ide/constants';
import { collapsedType, isCollapsed } from '../diff_file';
import { DIFF_FILE_AUTOMATIC_COLLAPSE, DIFF_FILE_MANUAL_COLLAPSE } from '../constants';
import { DIFF_FILE, GENERIC_ERROR } from '../i18n';
+import eventHub from '../event_hub';
export default {
components: {
@@ -151,7 +152,11 @@ export default {
},
},
created() {
- eventHub.$on(`loadCollapsedDiff/${this.file.file_hash}`, this.requestDiff);
+ notesEventHub.$on(`loadCollapsedDiff/${this.file.file_hash}`, this.requestDiff);
+ eventHub.$on('mr:diffs:expandAllFiles', this.expandAllListener);
+ },
+ beforeDestroy() {
+ eventHub.$off('mr:diffs:expandAllFiles', this.expandAllListener);
},
methods: {
...mapActions('diffs', [
@@ -160,6 +165,11 @@ export default {
'setRenderIt',
'setFileCollapsedByUser',
]),
+ expandAllListener() {
+ if (this.isCollapsed) {
+ this.handleToggle();
+ }
+ },
handleToggle() {
const currentCollapsedFlag = this.isCollapsed;
diff --git a/app/assets/javascripts/diffs/event_hub.js b/app/assets/javascripts/diffs/event_hub.js
new file mode 100644
index 00000000000..3e0c313f5e8
--- /dev/null
+++ b/app/assets/javascripts/diffs/event_hub.js
@@ -0,0 +1,3 @@
+import eventHubFactory from '~/helpers/event_hub_factory';
+
+export default eventHubFactory();
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index 322051cff05..e4f79e76cad 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -364,10 +364,6 @@ export const loadCollapsedDiff = ({ commit, getters, state }, file) =>
});
});
-export const expandAllFiles = ({ commit }) => {
- commit(types.EXPAND_ALL_FILES);
-};
-
/**
* Toggles the file discussions after user clicked on the toggle discussions button.
*
diff --git a/app/assets/javascripts/diffs/store/mutation_types.js b/app/assets/javascripts/diffs/store/mutation_types.js
index 5dba2e9d10d..19a9e65edc9 100644
--- a/app/assets/javascripts/diffs/store/mutation_types.js
+++ b/app/assets/javascripts/diffs/store/mutation_types.js
@@ -13,7 +13,6 @@ export const SET_MERGE_REQUEST_DIFFS = 'SET_MERGE_REQUEST_DIFFS';
export const TOGGLE_LINE_HAS_FORM = 'TOGGLE_LINE_HAS_FORM';
export const ADD_CONTEXT_LINES = 'ADD_CONTEXT_LINES';
export const ADD_COLLAPSED_DIFFS = 'ADD_COLLAPSED_DIFFS';
-export const EXPAND_ALL_FILES = 'EXPAND_ALL_FILES';
export const RENDER_FILE = 'RENDER_FILE';
export const SET_LINE_DISCUSSIONS_FOR_FILE = 'SET_LINE_DISCUSSIONS_FOR_FILE';
export const REMOVE_LINE_DISCUSSIONS_FOR_FILE = 'REMOVE_LINE_DISCUSSIONS_FOR_FILE';
diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js
index 5328845e4d9..2aeecf6e9f5 100644
--- a/app/assets/javascripts/diffs/store/mutations.js
+++ b/app/assets/javascripts/diffs/store/mutations.js
@@ -176,17 +176,6 @@ export default {
Object.assign(selectedFile, { ...newFileData });
},
- [types.EXPAND_ALL_FILES](state) {
- state.diffFiles.forEach(file => {
- Object.assign(file, {
- viewer: Object.assign(file.viewer, {
- automaticallyCollapsed: false,
- manuallyCollapsed: false,
- }),
- });
- });
- },
-
[types.SET_LINE_DISCUSSIONS_FOR_FILE](state, { discussion, diffPositionByLineCode, hash }) {
const { latestDiff } = state;
diff --git a/app/assets/javascripts/frequent_items/index.js b/app/assets/javascripts/frequent_items/index.js
index 5b03e1d19db..1998bf4358a 100644
--- a/app/assets/javascripts/frequent_items/index.js
+++ b/app/assets/javascripts/frequent_items/index.js
@@ -16,63 +16,53 @@ const frequentItemDropdowns = [
},
];
-const initFrequentItemList = (namespace, key) => {
- const el = document.getElementById(`js-${namespace}-dropdown`);
-
- // Don't do anything if element doesn't exist (No groups dropdown)
- // This is for when the user accesses GitLab without logging in
- if (!el) {
- return;
- }
-
- import('./components/app.vue')
- .then(({ default: FrequentItems }) => {
- // eslint-disable-next-line no-new
- new Vue({
- el,
- data() {
- const { dataset } = this.$options.el;
- const item = {
- id: Number(dataset[`${key}Id`]),
- name: dataset[`${key}Name`],
- namespace: dataset[`${key}Namespace`],
- webUrl: dataset[`${key}WebUrl`],
- avatarUrl: dataset[`${key}AvatarUrl`] || null,
- lastAccessedOn: Date.now(),
- };
-
- return {
- currentUserName: dataset.userName,
- currentItem: item,
- };
- },
- render(createElement) {
- return createElement(FrequentItems, {
- props: {
- namespace,
- currentUserName: this.currentUserName,
- currentItem: this.currentItem,
- },
- });
- },
- });
- })
- .catch(() => {});
-};
-
export default function initFrequentItemDropdowns() {
frequentItemDropdowns.forEach(dropdown => {
const { namespace, key } = dropdown;
+ const el = document.getElementById(`js-${namespace}-dropdown`);
const navEl = document.getElementById(`nav-${namespace}-dropdown`);
// Don't do anything if element doesn't exist (No groups dropdown)
// This is for when the user accesses GitLab without logging in
- if (!navEl) {
+ if (!el || !navEl) {
return;
}
+ import('./components/app.vue')
+ .then(({ default: FrequentItems }) => {
+ // eslint-disable-next-line no-new
+ new Vue({
+ el,
+ data() {
+ const { dataset } = this.$options.el;
+ const item = {
+ id: Number(dataset[`${key}Id`]),
+ name: dataset[`${key}Name`],
+ namespace: dataset[`${key}Namespace`],
+ webUrl: dataset[`${key}WebUrl`],
+ avatarUrl: dataset[`${key}AvatarUrl`] || null,
+ lastAccessedOn: Date.now(),
+ };
+
+ return {
+ currentUserName: dataset.userName,
+ currentItem: item,
+ };
+ },
+ render(createElement) {
+ return createElement(FrequentItems, {
+ props: {
+ namespace,
+ currentUserName: this.currentUserName,
+ currentItem: this.currentItem,
+ },
+ });
+ },
+ });
+ })
+ .catch(() => {});
+
$(navEl).on('shown.bs.dropdown', () => {
- initFrequentItemList(namespace, key);
eventHub.$emit(`${namespace}-dropdownOpen`);
});
});
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue b/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue
index dd09247337c..7fe495aa342 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue
@@ -1,7 +1,7 @@
<script>
import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
-import '~/lib/utils/datetime_utility';
import timeagoMixin from '~/vue_shared/mixins/timeago';
+import { formatTime } from '~/lib/utils/datetime_utility';
export default {
directives: {
@@ -27,24 +27,7 @@ export default {
return this.finishedTime !== '';
},
durationFormatted() {
- const date = new Date(this.duration * 1000);
-
- let hh = date.getUTCHours();
- let mm = date.getUTCMinutes();
- let ss = date.getSeconds();
-
- // left pad
- if (hh < 10) {
- hh = `0${hh}`;
- }
- if (mm < 10) {
- mm = `0${mm}`;
- }
- if (ss < 10) {
- ss = `0${ss}`;
- }
-
- return `${hh}:${mm}:${ss}`;
+ return formatTime(this.duration);
},
},
};
diff --git a/app/assets/javascripts/projects/pipelines/charts/components/statistics_list.vue b/app/assets/javascripts/projects/pipelines/charts/components/statistics_list.vue
index cd9e464c5ac..aa59717ddcd 100644
--- a/app/assets/javascripts/projects/pipelines/charts/components/statistics_list.vue
+++ b/app/assets/javascripts/projects/pipelines/charts/components/statistics_list.vue
@@ -1,4 +1,7 @@
<script>
+import { formatTime } from '~/lib/utils/datetime_utility';
+import { s__, n__ } from '~/locale';
+
export default {
props: {
counts: {
@@ -6,25 +9,44 @@ export default {
required: true,
},
},
+ computed: {
+ totalDuration() {
+ return formatTime(this.counts.totalDuration);
+ },
+ statistics() {
+ return [
+ {
+ title: s__('PipelineCharts|Total:'),
+ value: n__('1 pipeline', '%d pipelines', this.counts.total),
+ },
+ {
+ title: s__('PipelineCharts|Successful:'),
+ value: n__('1 pipeline', '%d pipelines', this.counts.success),
+ },
+ {
+ title: s__('PipelineCharts|Failed:'),
+ value: n__('1 pipeline', '%d pipelines', this.counts.failed),
+ },
+ {
+ title: s__('PipelineCharts|Success ratio:'),
+ value: `${this.counts.successRatio}%`,
+ },
+ {
+ title: s__('PipelineCharts|Total duration:'),
+ value: this.totalDuration,
+ },
+ ];
+ },
+ },
};
</script>
<template>
<ul>
- <li>
- <span>{{ s__('PipelineCharts|Total:') }}</span>
- <strong>{{ n__('1 pipeline', '%d pipelines', counts.total) }}</strong>
- </li>
- <li>
- <span>{{ s__('PipelineCharts|Successful:') }}</span>
- <strong>{{ n__('1 pipeline', '%d pipelines', counts.success) }}</strong>
- </li>
- <li>
- <span>{{ s__('PipelineCharts|Failed:') }}</span>
- <strong>{{ n__('1 pipeline', '%d pipelines', counts.failed) }}</strong>
- </li>
- <li>
- <span>{{ s__('PipelineCharts|Success ratio:') }}</span>
- <strong>{{ counts.successRatio }}%</strong>
- </li>
+ <template v-for="({ title, value }, index) in statistics">
+ <li :key="index">
+ <span>{{ title }}</span>
+ <strong>{{ value }}</strong>
+ </li>
+ </template>
</ul>
</template>
diff --git a/app/assets/javascripts/projects/pipelines/charts/index.js b/app/assets/javascripts/projects/pipelines/charts/index.js
index 4ae2b729200..eef1bc2d28b 100644
--- a/app/assets/javascripts/projects/pipelines/charts/index.js
+++ b/app/assets/javascripts/projects/pipelines/charts/index.js
@@ -7,6 +7,7 @@ export default () => {
countsFailed,
countsSuccess,
countsTotal,
+ countsTotalDuration,
successRatio,
timesChartLabels,
timesChartValues,
@@ -41,6 +42,7 @@ export default () => {
success: countsSuccess,
total: countsTotal,
successRatio,
+ totalDuration: countsTotalDuration,
},
timesChartData: {
labels: JSON.parse(timesChartLabels),
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author.vue
index 598b08f4c16..5ed699acddf 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author.vue
@@ -1,10 +1,10 @@
<script>
-import tooltip from '../../vue_shared/directives/tooltip';
+import { GlTooltipDirective } from '@gitlab/ui';
export default {
name: 'MrWidgetAuthor',
directives: {
- tooltip,
+ GlTooltip: GlTooltipDirective,
},
props: {
author: {
@@ -16,11 +16,6 @@ export default {
required: false,
default: true,
},
- showAuthorTooltip: {
- type: Boolean,
- required: false,
- default: false,
- },
},
computed: {
authorUrl() {
@@ -33,12 +28,7 @@ export default {
};
</script>
<template>
- <a
- :href="authorUrl"
- :v-tooltip="showAuthorTooltip"
- :title="author.name"
- class="author-link inline"
- >
+ <a v-gl-tooltip :href="authorUrl" :title="author.name" class="author-link inline">
<img :src="avatarUrl" class="avatar avatar-inline s16" />
<span v-if="showAuthorName" class="author">{{ author.name }}</span>
</a>
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index fa150412285..0944219a6e6 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -193,6 +193,7 @@ class Projects::PipelinesController < Projects::ApplicationController
@counts[:total] = @project.all_pipelines.count(:all)
@counts[:success] = @project.all_pipelines.success.count(:all)
@counts[:failed] = @project.all_pipelines.failed.count(:all)
+ @counts[:total_duration] = @project.all_pipelines.total_duration
end
def test_report
diff --git a/app/graphql/types/release_links_type.rb b/app/graphql/types/release_links_type.rb
index f61a16f5b67..b00fcf2ceb4 100644
--- a/app/graphql/types/release_links_type.rb
+++ b/app/graphql/types/release_links_type.rb
@@ -12,12 +12,25 @@ module Types
field :self_url, GraphQL::STRING_TYPE, null: true,
description: 'HTTP URL of the release'
- field :merge_requests_url, GraphQL::STRING_TYPE, null: true,
- description: 'HTTP URL of the merge request page filtered by this release'
- field :issues_url, GraphQL::STRING_TYPE, null: true,
- description: 'HTTP URL of the issues page filtered by this release'
field :edit_url, GraphQL::STRING_TYPE, null: true,
description: "HTTP URL of the release's edit page",
authorize: :update_release
+ field :open_merge_requests_url, GraphQL::STRING_TYPE, null: true,
+ description: 'HTTP URL of the merge request page, filtered by this release and `state=open`'
+ field :merged_merge_requests_url, GraphQL::STRING_TYPE, null: true,
+ description: 'HTTP URL of the merge request page , filtered by this release and `state=merged`'
+ field :closed_merge_requests_url, GraphQL::STRING_TYPE, null: true,
+ description: 'HTTP URL of the merge request page , filtered by this release and `state=closed`'
+ field :open_issues_url, GraphQL::STRING_TYPE, null: true,
+ description: 'HTTP URL of the issues page, filtered by this release and `state=open`'
+ field :closed_issues_url, GraphQL::STRING_TYPE, null: true,
+ description: 'HTTP URL of the issues page, filtered by this release and `state=closed`'
+
+ field :merge_requests_url, GraphQL::STRING_TYPE, null: true, method: :open_merge_requests_url,
+ description: 'HTTP URL of the merge request page filtered by this release',
+ deprecated: { reason: 'Use `open_merge_requests_url`', milestone: '13.6' }
+ field :issues_url, GraphQL::STRING_TYPE, null: true, method: :open_issues_url,
+ description: 'HTTP URL of the issues page filtered by this release',
+ deprecated: { reason: 'Use `open_issues_url`', milestone: '13.6' }
end
end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 684b6387ab1..8083d4ed48a 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -926,7 +926,7 @@ module Ci
def accessibility_reports
Gitlab::Ci::Reports::AccessibilityReports.new.tap do |accessibility_reports|
- builds.latest.with_reports(Ci::JobArtifact.accessibility_reports).each do |build|
+ latest_report_builds(Ci::JobArtifact.accessibility_reports).each do |build|
build.collect_accessibility_reports!(accessibility_reports)
end
end
diff --git a/app/presenters/release_presenter.rb b/app/presenters/release_presenter.rb
index c27059c6d63..1a89a76b4dc 100644
--- a/app/presenters/release_presenter.rb
+++ b/app/presenters/release_presenter.rb
@@ -23,18 +23,36 @@ class ReleasePresenter < Gitlab::View::Presenter::Delegated
project_release_url(project, release)
end
- def merge_requests_url
+ def open_merge_requests_url
return unless release_mr_issue_urls_available?
project_merge_requests_url(project, params_for_issues_and_mrs)
end
- def issues_url
+ def merged_merge_requests_url
+ return unless release_mr_issue_urls_available?
+
+ project_merge_requests_url(project, params_for_issues_and_mrs(state: 'merged'))
+ end
+
+ def closed_merge_requests_url
+ return unless release_mr_issue_urls_available?
+
+ project_merge_requests_url(project, params_for_issues_and_mrs(state: 'closed'))
+ end
+
+ def open_issues_url
return unless release_mr_issue_urls_available?
project_issues_url(project, params_for_issues_and_mrs)
end
+ def closed_issues_url
+ return unless release_mr_issue_urls_available?
+
+ project_issues_url(project, params_for_issues_and_mrs(state: 'closed'))
+ end
+
def edit_url
return unless release_edit_page_available?
@@ -59,8 +77,8 @@ class ReleasePresenter < Gitlab::View::Presenter::Delegated
can?(current_user, :download_code, project)
end
- def params_for_issues_and_mrs
- { scope: 'all', state: 'opened', release_tag: release.tag }
+ def params_for_issues_and_mrs(state: 'opened')
+ { scope: 'all', state: state, release_tag: release.tag }
end
def release_mr_issue_urls_available?
diff --git a/changelogs/unreleased/Add-Caching-To-BitBucket-Server-Importer.yml b/changelogs/unreleased/Add-Caching-To-BitBucket-Server-Importer.yml
new file mode 100644
index 00000000000..eb4c64fe8b9
--- /dev/null
+++ b/changelogs/unreleased/Add-Caching-To-BitBucket-Server-Importer.yml
@@ -0,0 +1,5 @@
+---
+title: Add Caching to BitBucket Server Import for pull requests
+merge_request: 45790
+author: Simon Schrottner
+type: performance
diff --git a/changelogs/unreleased/add-total-duration-to-cicd-analytics-page.yml b/changelogs/unreleased/add-total-duration-to-cicd-analytics-page.yml
new file mode 100644
index 00000000000..528ed67af24
--- /dev/null
+++ b/changelogs/unreleased/add-total-duration-to-cicd-analytics-page.yml
@@ -0,0 +1,5 @@
+---
+title: Add Total Duration to CI/CD Analytics Page
+merge_request: 44863
+author: Kev @KevSlashNull
+type: added
diff --git a/changelogs/unreleased/nfriend-add-release-progress-link-paths.yml b/changelogs/unreleased/nfriend-add-release-progress-link-paths.yml
new file mode 100644
index 00000000000..1b7532c554a
--- /dev/null
+++ b/changelogs/unreleased/nfriend-add-release-progress-link-paths.yml
@@ -0,0 +1,6 @@
+---
+title: Add links to GraphQL release object for searching related issues and merge
+ requests
+merge_request: 46161
+author:
+type: added
diff --git a/changelogs/unreleased/okr-tooltip-mr-widget-author-vue.yml b/changelogs/unreleased/okr-tooltip-mr-widget-author-vue.yml
new file mode 100644
index 00000000000..f2e357986df
--- /dev/null
+++ b/changelogs/unreleased/okr-tooltip-mr-widget-author-vue.yml
@@ -0,0 +1,5 @@
+---
+title: Migrate tooltip in app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author.vue
+merge_request: 46034
+author:
+type: other
diff --git a/changelogs/unreleased/revert-tz-defer-frequent-item-init.yml b/changelogs/unreleased/revert-tz-defer-frequent-item-init.yml
new file mode 100644
index 00000000000..b25ee863ec7
--- /dev/null
+++ b/changelogs/unreleased/revert-tz-defer-frequent-item-init.yml
@@ -0,0 +1,5 @@
+---
+title: Fix tracking of frequently visited projects / groups
+merge_request: 46348
+author:
+type: fixed
diff --git a/changelogs/unreleased/ss-add-wip-limits-license-check.yml b/changelogs/unreleased/ss-add-wip-limits-license-check.yml
new file mode 100644
index 00000000000..47d4b414bf4
--- /dev/null
+++ b/changelogs/unreleased/ss-add-wip-limits-license-check.yml
@@ -0,0 +1,5 @@
+---
+title: Add licensed check for wip limits
+merge_request: 46387
+author:
+type: fixed
diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql
index 9a54b70fd7b..cd134233f77 100644
--- a/doc/api/graphql/reference/gitlab_schema.graphql
+++ b/doc/api/graphql/reference/gitlab_schema.graphql
@@ -16856,19 +16856,44 @@ type ReleaseEvidenceEdge {
type ReleaseLinks {
"""
+ HTTP URL of the issues page, filtered by this release and `state=closed`
+ """
+ closedIssuesUrl: String
+
+ """
+ HTTP URL of the merge request page , filtered by this release and `state=closed`
+ """
+ closedMergeRequestsUrl: String
+
+ """
HTTP URL of the release's edit page
"""
editUrl: String
"""
- HTTP URL of the issues page filtered by this release
+ HTTP URL of the issues page filtered by this release. Deprecated in 13.6: Use `open_issues_url`
+ """
+ issuesUrl: String @deprecated(reason: "Use `open_issues_url`. Deprecated in 13.6")
+
+ """
+ HTTP URL of the merge request page filtered by this release. Deprecated in 13.6: Use `open_merge_requests_url`
+ """
+ mergeRequestsUrl: String @deprecated(reason: "Use `open_merge_requests_url`. Deprecated in 13.6")
+
+ """
+ HTTP URL of the merge request page , filtered by this release and `state=merged`
+ """
+ mergedMergeRequestsUrl: String
+
+ """
+ HTTP URL of the issues page, filtered by this release and `state=open`
"""
- issuesUrl: String
+ openIssuesUrl: String
"""
- HTTP URL of the merge request page filtered by this release
+ HTTP URL of the merge request page, filtered by this release and `state=open`
"""
- mergeRequestsUrl: String
+ openMergeRequestsUrl: String
"""
HTTP URL of the release
diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json
index 118dc03a9a4..cfca138011a 100644
--- a/doc/api/graphql/reference/gitlab_schema.json
+++ b/doc/api/graphql/reference/gitlab_schema.json
@@ -48542,6 +48542,34 @@
"description": null,
"fields": [
{
+ "name": "closedIssuesUrl",
+ "description": "HTTP URL of the issues page, filtered by this release and `state=closed`",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "closedMergeRequestsUrl",
+ "description": "HTTP URL of the merge request page , filtered by this release and `state=closed`",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "editUrl",
"description": "HTTP URL of the release's edit page",
"args": [
@@ -48557,7 +48585,35 @@
},
{
"name": "issuesUrl",
- "description": "HTTP URL of the issues page filtered by this release",
+ "description": "HTTP URL of the issues page filtered by this release. Deprecated in 13.6: Use `open_issues_url`",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": true,
+ "deprecationReason": "Use `open_issues_url`. Deprecated in 13.6"
+ },
+ {
+ "name": "mergeRequestsUrl",
+ "description": "HTTP URL of the merge request page filtered by this release. Deprecated in 13.6: Use `open_merge_requests_url`",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": true,
+ "deprecationReason": "Use `open_merge_requests_url`. Deprecated in 13.6"
+ },
+ {
+ "name": "mergedMergeRequestsUrl",
+ "description": "HTTP URL of the merge request page , filtered by this release and `state=merged`",
"args": [
],
@@ -48570,8 +48626,22 @@
"deprecationReason": null
},
{
- "name": "mergeRequestsUrl",
- "description": "HTTP URL of the merge request page filtered by this release",
+ "name": "openIssuesUrl",
+ "description": "HTTP URL of the issues page, filtered by this release and `state=open`",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "openMergeRequestsUrl",
+ "description": "HTTP URL of the merge request page, filtered by this release and `state=open`",
"args": [
],
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 6d6e815bb54..fb3086975b2 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -2230,9 +2230,14 @@ Evidence for a release.
| Field | Type | Description |
| ----- | ---- | ----------- |
+| `closedIssuesUrl` | String | HTTP URL of the issues page, filtered by this release and `state=closed` |
+| `closedMergeRequestsUrl` | String | HTTP URL of the merge request page , filtered by this release and `state=closed` |
| `editUrl` | String | HTTP URL of the release's edit page |
-| `issuesUrl` | String | HTTP URL of the issues page filtered by this release |
-| `mergeRequestsUrl` | String | HTTP URL of the merge request page filtered by this release |
+| `issuesUrl` **{warning-solid}** | String | **Deprecated:** Use `open_issues_url`. Deprecated in 13.6 |
+| `mergeRequestsUrl` **{warning-solid}** | String | **Deprecated:** Use `open_merge_requests_url`. Deprecated in 13.6 |
+| `mergedMergeRequestsUrl` | String | HTTP URL of the merge request page , filtered by this release and `state=merged` |
+| `openIssuesUrl` | String | HTTP URL of the issues page, filtered by this release and `state=open` |
+| `openMergeRequestsUrl` | String | HTTP URL of the merge request page, filtered by this release and `state=open` |
| `selfUrl` | String | HTTP URL of the release |
### ReleaseSource
diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md
index 730f8d5ad7d..67ac7934346 100644
--- a/doc/development/testing_guide/frontend_testing.md
+++ b/doc/development/testing_guide/frontend_testing.md
@@ -198,47 +198,33 @@ Following you'll find some general common practices you will find as part of our
When it comes to querying DOM elements in your tests, it is best to uniquely and semantically target
the element.
-Preferentially, this is done by targeting text the user actually sees using [DOM Testing Library](https://testing-library.com/docs/dom-testing-library/intro).
+Preferentially, this is done by targeting what the user actually sees using [DOM Testing Library](https://testing-library.com/docs/dom-testing-library/intro).
When selecting by text it is best to use [`getByRole` or `findByRole`](https://testing-library.com/docs/dom-testing-library/api-queries#byrole)
as these enforce accessibility best practices as well. The examples below demonstrate the order of preference.
-Sometimes this cannot be done feasibly. In these cases, adding test attributes to simplify the
-selectors might be the best option.
+When writing Vue component unit tests, it can be wise to query children by component, so that the unit test can focus on comprehensive value coverage
+rather than dealing with the complexity of a child component's behavior.
+
+Sometimes, neither of the above are feasible. In these cases, adding test attributes to simplify the selectors might be the best option. A list of
+possible selectors include:
- A semantic attribute like `name` (also verifies that `name` was setup properly)
- A `data-testid` attribute ([recommended by maintainers of `@vue/test-utils`](https://github.com/vuejs/vue-test-utils/issues/1498#issuecomment-610133465))
- a Vue `ref` (if using `@vue/test-utils`)
```javascript
-import { mount, shallowMount } from '@vue/test-utils'
import { getByRole, getByText } from '@testing-library/dom'
-let wrapper
-let el
-
-const createComponent = (mountFn = shallowMount) => {
- wrapper = mountFn(Component)
- el = wrapper.vm.$el // reference to the container element
-}
-
-beforeEach(() => {
- createComponent()
-})
-
-
+// In this example, `wrapper` is a `@vue/test-utils` wrapper returned from `mount` or `shallowMount`.
it('exists', () => {
- // Best
-
- // NOTE: both mount and shallowMount work as long as a DOM element is available
- // Finds a properly formatted link with an accessible name of "Click Me"
- getByRole(el, 'link', { name: /Click Me/i })
- getByRole(el, 'link', { name: 'Click Me' })
- // Finds any element with the text "Click Me"
- getByText(el, 'Click Me')
- // Regex is also available
- getByText(el, /Click Me/i)
-
- // Good
+ // Best (especially for integration tests)
+ getByRole(wrapper.element, 'link', { name: /Click Me/i })
+ getByRole(wrapper.element, 'link', { name: 'Click Me' })
+ getByText(wrapper.element, 'Click Me')
+ getByText(wrapper.element, /Click Me/i)
+
+ // Good (especially for unit tests)
+ wrapper.find(FooComponent);
wrapper.find('input[name=foo]');
wrapper.find('[data-testid="foo"]');
wrapper.find({ ref: 'foo'});
@@ -249,14 +235,6 @@ it('exists', () => {
wrapper.find('.qa-foo-component');
wrapper.find('[data-qa-selector="foo"]');
});
-
-// Good
-it('exists', () => {
- wrapper.find(FooComponent);
- wrapper.find('input[name=foo]');
- wrapper.find('[data-testid="foo"]');
- wrapper.find({ ref: 'foo'});
-});
```
It is not recommended that you add `.js-*` classes just for testing purposes. Only do this if there are no other feasible options available.
diff --git a/lib/api/badges.rb b/lib/api/badges.rb
index fc00594c9ec..04f155be4e1 100644
--- a/lib/api/badges.rb
+++ b/lib/api/badges.rb
@@ -8,6 +8,8 @@ module API
helpers ::API::Helpers::BadgesHelpers
+ feature_category :continuous_integration
+
helpers do
def find_source_if_admin(source_type)
source = find_source(source_type, params[:id])
diff --git a/lib/api/boards.rb b/lib/api/boards.rb
index d2d1628aff4..dbf0cef0484 100644
--- a/lib/api/boards.rb
+++ b/lib/api/boards.rb
@@ -9,6 +9,8 @@ module API
before { authenticate! }
+ feature_category :boards
+
helpers do
def board_parent
user_project
diff --git a/lib/api/branches.rb b/lib/api/branches.rb
index 37cce6eafba..6842e93a4de 100644
--- a/lib/api/branches.rb
+++ b/lib/api/branches.rb
@@ -10,6 +10,8 @@ module API
after_validation { content_type "application/json" }
+ feature_category :source_code_management
+
before do
require_repository_enabled!
authorize! :download_code, user_project
diff --git a/lib/api/broadcast_messages.rb b/lib/api/broadcast_messages.rb
index 8ce7694bbfd..0762c276aad 100644
--- a/lib/api/broadcast_messages.rb
+++ b/lib/api/broadcast_messages.rb
@@ -4,6 +4,8 @@ module API
class BroadcastMessages < ::API::Base
include PaginationParams
+ feature_category :navigation
+
resource :broadcast_messages do
helpers do
def find_message
diff --git a/lib/api/ci/pipeline_schedules.rb b/lib/api/ci/pipeline_schedules.rb
index 18caf85f109..a47d14a3feb 100644
--- a/lib/api/ci/pipeline_schedules.rb
+++ b/lib/api/ci/pipeline_schedules.rb
@@ -7,6 +7,8 @@ module API
before { authenticate! }
+ feature_category :continuous_integration
+
params do
requires :id, type: String, desc: 'The ID of a project'
end
diff --git a/lib/api/ci/pipelines.rb b/lib/api/ci/pipelines.rb
index dcbdfcd76f4..1b36e27f6c9 100644
--- a/lib/api/ci/pipelines.rb
+++ b/lib/api/ci/pipelines.rb
@@ -7,6 +7,8 @@ module API
before { authenticate_non_get! }
+ feature_category :continuous_integration
+
params do
requires :id, type: String, desc: 'The project ID'
end
diff --git a/lib/api/ci/runner.rb b/lib/api/ci/runner.rb
index 5b68208061b..c2e2b59d39f 100644
--- a/lib/api/ci/runner.rb
+++ b/lib/api/ci/runner.rb
@@ -7,6 +7,8 @@ module API
content_type :txt, 'text/plain'
+ feature_category :continuous_integration
+
resource :runners do
desc 'Registers a new Runner' do
success Entities::RunnerRegistrationDetails
diff --git a/lib/api/ci/runners.rb b/lib/api/ci/runners.rb
index d37f10fe631..44ffc941cfa 100644
--- a/lib/api/ci/runners.rb
+++ b/lib/api/ci/runners.rb
@@ -7,6 +7,8 @@ module API
before { authenticate! }
+ feature_category :continuous_integration
+
resource :runners do
desc 'Get runners available for user' do
success Entities::Runner
diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb
index af103b8c1f8..26af921432c 100644
--- a/lib/api/commit_statuses.rb
+++ b/lib/api/commit_statuses.rb
@@ -4,6 +4,8 @@ require 'mime/types'
module API
class CommitStatuses < ::API::Base
+ feature_category :continuous_integration
+
params do
requires :id, type: String, desc: 'The ID of a project'
end
diff --git a/lib/api/commits.rb b/lib/api/commits.rb
index 582ccd41847..a24848082a9 100644
--- a/lib/api/commits.rb
+++ b/lib/api/commits.rb
@@ -6,6 +6,8 @@ module API
class Commits < ::API::Base
include PaginationParams
+ feature_category :source_code_management
+
before do
require_repository_enabled!
authorize! :download_code, user_project
diff --git a/lib/api/container_registry_event.rb b/lib/api/container_registry_event.rb
index 6c4b80b612a..9bad31f6661 100644
--- a/lib/api/container_registry_event.rb
+++ b/lib/api/container_registry_event.rb
@@ -4,6 +4,8 @@ module API
class ContainerRegistryEvent < ::API::Base
DOCKER_DISTRIBUTION_EVENTS_V1_JSON = 'application/vnd.docker.distribution.events.v1+json'
+ feature_category :package_registry
+
before { authenticate_registry_notification! }
resource :container_registry_event do
diff --git a/lib/api/deploy_keys.rb b/lib/api/deploy_keys.rb
index 314f5b6ee1d..0a541620c3a 100644
--- a/lib/api/deploy_keys.rb
+++ b/lib/api/deploy_keys.rb
@@ -6,6 +6,8 @@ module API
before { authenticate! }
+ feature_category :continuous_delivery
+
helpers do
def add_deploy_keys_project(project, attrs = {})
project.deploy_keys_projects.create(attrs)
diff --git a/lib/api/deploy_tokens.rb b/lib/api/deploy_tokens.rb
index 1c156b8b3bb..5fab590eb4e 100644
--- a/lib/api/deploy_tokens.rb
+++ b/lib/api/deploy_tokens.rb
@@ -4,6 +4,8 @@ module API
class DeployTokens < ::API::Base
include PaginationParams
+ feature_category :continuous_delivery
+
helpers do
def scope_params
scopes = params.delete(:scopes)
diff --git a/lib/api/deployments.rb b/lib/api/deployments.rb
index ff06bdbae16..5346fcf03c9 100644
--- a/lib/api/deployments.rb
+++ b/lib/api/deployments.rb
@@ -7,6 +7,8 @@ module API
before { authenticate! }
+ feature_category :continuous_delivery
+
params do
requires :id, type: String, desc: 'The project ID'
end
diff --git a/lib/api/entities/release.rb b/lib/api/entities/release.rb
index afe14cf33cf..84c89b9f510 100644
--- a/lib/api/entities/release.rb
+++ b/lib/api/entities/release.rb
@@ -30,8 +30,8 @@ module API
expose :evidences, using: Entities::Releases::Evidence, expose_nil: false, if: ->(_, _) { can_download_code? }
expose :_links do
expose :self_url, as: :self, expose_nil: false
- expose :merge_requests_url, expose_nil: false
- expose :issues_url, expose_nil: false
+ expose :open_merge_requests_url, as: :merge_requests_url, expose_nil: false
+ expose :open_issues_url, as: :issues_url, expose_nil: false
expose :edit_url, expose_nil: false
end
diff --git a/lib/api/environments.rb b/lib/api/environments.rb
index 0e780d4ef36..5dd2fa22690 100644
--- a/lib/api/environments.rb
+++ b/lib/api/environments.rb
@@ -7,6 +7,8 @@ module API
before { authenticate! }
+ feature_category :continuous_delivery
+
params do
requires :id, type: String, desc: 'The project ID'
end
diff --git a/lib/api/error_tracking.rb b/lib/api/error_tracking.rb
index 03f83477954..0e44c8b1081 100644
--- a/lib/api/error_tracking.rb
+++ b/lib/api/error_tracking.rb
@@ -4,6 +4,8 @@ module API
class ErrorTracking < ::API::Base
before { authenticate! }
+ feature_category :error_tracking
+
params do
requires :id, type: String, desc: 'The ID of a project'
end
diff --git a/lib/api/events.rb b/lib/api/events.rb
index 43efacf9c0b..233c62b5389 100644
--- a/lib/api/events.rb
+++ b/lib/api/events.rb
@@ -8,6 +8,8 @@ module API
allow_access_with_scope :read_user, if: -> (request) { request.get? }
+ feature_category :users
+
resource :events do
desc "List currently authenticated user's events" do
detail 'This feature was introduced in GitLab 9.3.'
diff --git a/lib/api/feature_flag_scopes.rb b/lib/api/feature_flag_scopes.rb
index d77e243aa88..3f3bf4d9f42 100644
--- a/lib/api/feature_flag_scopes.rb
+++ b/lib/api/feature_flag_scopes.rb
@@ -7,6 +7,8 @@ module API
ENVIRONMENT_SCOPE_ENDPOINT_REQUIREMENTS = FeatureFlags::FEATURE_FLAG_ENDPOINT_REQUIREMENTS
.merge(environment_scope: API::NO_SLASH_URL_PART_REGEX)
+ feature_category :feature_flags
+
before do
authorize_read_feature_flags!
end
diff --git a/lib/api/feature_flags.rb b/lib/api/feature_flags.rb
index 613c3fb0f5b..67168ba9be6 100644
--- a/lib/api/feature_flags.rb
+++ b/lib/api/feature_flags.rb
@@ -7,6 +7,8 @@ module API
FEATURE_FLAG_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS
.merge(name: API::NO_SLASH_URL_PART_REGEX)
+ feature_category :feature_flags
+
before do
authorize_read_feature_flags!
end
diff --git a/lib/api/feature_flags_user_lists.rb b/lib/api/feature_flags_user_lists.rb
index e5218cfd7f1..88fa6dba418 100644
--- a/lib/api/feature_flags_user_lists.rb
+++ b/lib/api/feature_flags_user_lists.rb
@@ -8,6 +8,8 @@ module API
message.is_a?(String) ? { message: message }.to_json : message.to_json
}
+ feature_category :feature_flags
+
before do
authorize_admin_feature_flags_user_lists!
end
diff --git a/lib/api/features.rb b/lib/api/features.rb
index 70feef12ceb..2c2e3e3d0c9 100644
--- a/lib/api/features.rb
+++ b/lib/api/features.rb
@@ -4,6 +4,8 @@ module API
class Features < ::API::Base
before { authenticated_as_admin! }
+ feature_category :feature_flags
+
helpers do
def gate_value(params)
case params[:value]
diff --git a/lib/api/files.rb b/lib/api/files.rb
index 6833fc429e2..cb73bde73f5 100644
--- a/lib/api/files.rb
+++ b/lib/api/files.rb
@@ -9,6 +9,8 @@ module API
# Prevents returning plain/text responses for files with .txt extension
after_validation { content_type "application/json" }
+ feature_category :source_code_management
+
helpers ::API::Helpers::HeadersHelpers
helpers do
diff --git a/lib/api/freeze_periods.rb b/lib/api/freeze_periods.rb
index a83e36165a2..d001ced8581 100644
--- a/lib/api/freeze_periods.rb
+++ b/lib/api/freeze_periods.rb
@@ -6,6 +6,8 @@ module API
before { authenticate! }
+ feature_category :continuous_delivery
+
params do
requires :id, type: String, desc: 'The ID of a project'
end
diff --git a/lib/api/group_boards.rb b/lib/api/group_boards.rb
index d4574b22d99..ac5a1a2ce94 100644
--- a/lib/api/group_boards.rb
+++ b/lib/api/group_boards.rb
@@ -7,6 +7,8 @@ module API
prepend_if_ee('EE::API::BoardsResponses') # rubocop: disable Cop/InjectEnterpriseEditionModule
+ feature_category :boards
+
before do
authenticate!
end
diff --git a/lib/api/group_clusters.rb b/lib/api/group_clusters.rb
index 75429cf7a5c..a435b050042 100644
--- a/lib/api/group_clusters.rb
+++ b/lib/api/group_clusters.rb
@@ -6,6 +6,8 @@ module API
before { authenticate! }
+ feature_category :kubernetes_management
+
params do
requires :id, type: String, desc: 'The ID of the group'
end
diff --git a/lib/api/group_container_repositories.rb b/lib/api/group_container_repositories.rb
index 1bb26b3931c..4fede0ad583 100644
--- a/lib/api/group_container_repositories.rb
+++ b/lib/api/group_container_repositories.rb
@@ -8,6 +8,8 @@ module API
before { authorize_read_group_container_images! }
+ feature_category :package_registry
+
REPOSITORY_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(
tag_name: API::NO_SLASH_URL_PART_REGEX)
diff --git a/lib/api/group_export.rb b/lib/api/group_export.rb
index 6ebaa8de185..29ffbea687a 100644
--- a/lib/api/group_export.rb
+++ b/lib/api/group_export.rb
@@ -10,6 +10,8 @@ module API
authorize! :admin_group, user_group
end
+ feature_category :importers
+
params do
requires :id, type: String, desc: 'The ID of a group'
end
diff --git a/lib/api/group_import.rb b/lib/api/group_import.rb
index e703a217fd5..4a752732652 100644
--- a/lib/api/group_import.rb
+++ b/lib/api/group_import.rb
@@ -2,6 +2,8 @@
module API
class GroupImport < ::API::Base
+ feature_category :importers
+
helpers Helpers::FileUploadHelpers
helpers do
diff --git a/lib/api/group_labels.rb b/lib/api/group_labels.rb
index 8af073c614c..bf3ac8800b7 100644
--- a/lib/api/group_labels.rb
+++ b/lib/api/group_labels.rb
@@ -7,6 +7,8 @@ module API
before { authenticate! }
+ feature_category :issue_tracking
+
params do
requires :id, type: String, desc: 'The ID of a group'
end
diff --git a/lib/api/group_milestones.rb b/lib/api/group_milestones.rb
index aef9877b84c..dfffd3b1209 100644
--- a/lib/api/group_milestones.rb
+++ b/lib/api/group_milestones.rb
@@ -7,6 +7,8 @@ module API
before { authenticate! }
+ feature_category :issue_tracking
+
params do
requires :id, type: String, desc: 'The ID of a group'
end
diff --git a/lib/api/group_variables.rb b/lib/api/group_variables.rb
index ee110d67fa5..0c40db02eb5 100644
--- a/lib/api/group_variables.rb
+++ b/lib/api/group_variables.rb
@@ -7,6 +7,8 @@ module API
before { authenticate! }
before { authorize! :admin_build, user_group }
+ feature_category :continuous_integration
+
params do
requires :id, type: String, desc: 'The ID of a group'
end
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index bce73e17e49..a8b1cdab021 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -7,6 +7,8 @@ module API
before { authenticate_non_get! }
+ feature_category :subgroups
+
helpers Helpers::GroupsHelpers
helpers do
diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb
index cb1daa20ca7..68733c94a85 100644
--- a/lib/gitlab/bitbucket_server_import/importer.rb
+++ b/lib/gitlab/bitbucket_server_import/importer.rb
@@ -4,11 +4,14 @@ module Gitlab
module BitbucketServerImport
class Importer
attr_reader :recover_missing_commits
- attr_reader :project, :project_key, :repository_slug, :client, :errors, :users
+ attr_reader :project, :project_key, :repository_slug, :client, :errors, :users, :already_imported_cache_key
attr_accessor :logger
REMOTE_NAME = 'bitbucket_server'
BATCH_SIZE = 100
+ # The base cache key to use for tracking already imported objects.
+ ALREADY_IMPORTED_CACHE_KEY =
+ 'bitbucket_server-importer/already-imported/%{project}/%{collection}'
TempBranch = Struct.new(:name, :sha)
@@ -36,6 +39,12 @@ module Gitlab
@users = {}
@temp_branches = []
@logger = Gitlab::Import::Logger.build
+ @already_imported_cache_key = ALREADY_IMPORTED_CACHE_KEY %
+ { project: project.id, collection: collection_method }
+ end
+
+ def collection_method
+ :pull_requests
end
def execute
@@ -48,6 +57,7 @@ module Gitlab
log_info(stage: "complete")
+ Gitlab::Cache::Import::Caching.expire(already_imported_cache_key, 15.minutes.to_i)
true
end
@@ -167,6 +177,7 @@ module Gitlab
# on the remote server. Then we have to issue a `git fetch` to download these
# branches.
def import_pull_requests
+ log_info(stage: 'import_pull_requests', message: 'starting')
pull_requests = client.pull_requests(project_key, repository_slug).to_a
# Creating branches on the server and fetching the newly-created branches
@@ -176,7 +187,11 @@ module Gitlab
restore_branches(batch) if recover_missing_commits
batch.each do |pull_request|
- import_bitbucket_pull_request(pull_request)
+ if already_imported?(pull_request)
+ log_info(stage: 'import_pull_requests', message: 'already imported', iid: pull_request.iid)
+ else
+ import_bitbucket_pull_request(pull_request)
+ end
rescue StandardError => e
Gitlab::ErrorTracking.log_exception(
e,
@@ -189,6 +204,19 @@ module Gitlab
end
end
+ # Returns true if the given object has already been imported, false
+ # otherwise.
+ #
+ # object - The object to check.
+ def already_imported?(pull_request)
+ Gitlab::Cache::Import::Caching.set_includes?(already_imported_cache_key, pull_request.iid)
+ end
+
+ # Marks the given object as "already imported".
+ def mark_as_imported(pull_request)
+ Gitlab::Cache::Import::Caching.set_add(already_imported_cache_key, pull_request.iid)
+ end
+
def delete_temp_branches
@temp_branches.each do |branch|
client.delete_branch(project_key, repository_slug, branch.name, branch.sha)
@@ -236,6 +264,7 @@ module Gitlab
end
log_info(stage: 'import_bitbucket_pull_requests', message: 'finished', iid: pull_request.iid)
+ mark_as_imported(pull_request)
end
def import_pull_request_comments(pull_request, merge_request)
diff --git a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
index cba13f374f4..940291bd7b4 100644
--- a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
@@ -160,9 +160,9 @@ include:
- template: Jobs/Build.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Build.gitlab-ci.yml
- template: Jobs/Test.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Test.gitlab-ci.yml
- template: Jobs/Code-Quality.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml
- - template: Jobs/Code-Intelligence.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Code-Intelligence.gitlab-ci.yml
+ - template: Jobs/Code-Intelligence.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Code-Intelligence.gitlab-ci.yml
- template: Jobs/Deploy.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
- - template: Jobs/Deploy/ECS.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Deploy/ECS.gitlab-ci.yml
+ - template: Jobs/Deploy/ECS.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Deploy/ECS.gitlab-ci.yml
- template: Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml
- template: Jobs/Browser-Performance-Testing.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.gitlab-ci.yml
- template: Security/DAST.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 4a3e371b3a9..1f4a12b8b8c 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -19402,6 +19402,9 @@ msgstr ""
msgid "PipelineCharts|Successful:"
msgstr ""
+msgid "PipelineCharts|Total duration:"
+msgstr ""
+
msgid "PipelineCharts|Total:"
msgstr ""
diff --git a/spec/crystalball_env.rb b/spec/crystalball_env.rb
index 56498f07f85..a7748cd6627 100644
--- a/spec/crystalball_env.rb
+++ b/spec/crystalball_env.rb
@@ -6,7 +6,7 @@ module CrystalballEnv
extend self
def start!
- return unless ENV['CRYSTALBALL'] && ENV['CI_PIPELINE_SOURCE'] == 'schedule'
+ return unless ENV['CRYSTALBALL'] && ENV['CI_PIPELINE_SOURCE'] == 'schedule' && ENV['FREQUENCY'] == '2-hourly'
require 'crystalball'
require_relative '../tooling/lib/tooling/crystalball/coverage_lines_execution_detector'
diff --git a/spec/features/frequently_visited_projects_and_groups_spec.rb b/spec/features/frequently_visited_projects_and_groups_spec.rb
new file mode 100644
index 00000000000..b8797d9c139
--- /dev/null
+++ b/spec/features/frequently_visited_projects_and_groups_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Frequently visited items', :js do
+ let_it_be(:user) { create(:user) }
+
+ before do
+ sign_in(user)
+ end
+
+ context 'for projects' do
+ let_it_be(:project) { create(:project, :public) }
+
+ it 'increments localStorage counter when visiting the project' do
+ visit project_path(project)
+
+ frequent_projects = nil
+
+ wait_for('localStorage frequent-projects') do
+ frequent_projects = page.evaluate_script("localStorage['#{user.username}/frequent-projects']")
+
+ frequent_projects.present?
+ end
+
+ expect(Gitlab::Json.parse(frequent_projects)).to contain_exactly(a_hash_including('id' => project.id, 'frequency' => 1))
+ end
+ end
+
+ context 'for groups' do
+ let_it_be(:group) { create(:group, :public) }
+
+ it 'increments localStorage counter when visiting the group' do
+ visit group_path(group)
+
+ frequent_groups = nil
+
+ wait_for('localStorage frequent-groups') do
+ frequent_groups = page.evaluate_script("localStorage['#{user.username}/frequent-groups']")
+
+ frequent_groups.present?
+ end
+
+ expect(Gitlab::Json.parse(frequent_groups)).to contain_exactly(a_hash_including('id' => group.id, 'frequency' => 1))
+ end
+ end
+end
diff --git a/spec/features/issues/user_interacts_with_awards_spec.rb b/spec/features/issues/user_interacts_with_awards_spec.rb
index bb9bce9eccf..fec603e466a 100644
--- a/spec/features/issues/user_interacts_with_awards_spec.rb
+++ b/spec/features/issues/user_interacts_with_awards_spec.rb
@@ -294,7 +294,7 @@ RSpec.describe 'User interacts with awards' do
end
end
- it 'toggles the smiley emoji on a note', :js do
+ it 'toggles the smiley emoji on a note', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/267525' do
toggle_smiley_emoji(true)
within('.note-body') do
diff --git a/spec/frontend/diffs/components/collapsed_files_warning_spec.js b/spec/frontend/diffs/components/collapsed_files_warning_spec.js
index 7bbffb7a1cd..fbaaf8cf5a6 100644
--- a/spec/frontend/diffs/components/collapsed_files_warning_spec.js
+++ b/spec/frontend/diffs/components/collapsed_files_warning_spec.js
@@ -3,6 +3,7 @@ import { shallowMount, mount, createLocalVue } from '@vue/test-utils';
import createStore from '~/diffs/store/modules';
import CollapsedFilesWarning from '~/diffs/components/collapsed_files_warning.vue';
import { CENTERED_LIMITED_CONTAINER_CLASSES } from '~/diffs/constants';
+import eventHub from '~/diffs/event_hub';
const propsData = {
limited: true,
@@ -76,13 +77,13 @@ describe('CollapsedFilesWarning', () => {
expect(wrapper.find('[data-testid="root"]').exists()).toBe(false);
});
- it('triggers the expandAllFiles action when the alert action button is clicked', () => {
+ it('emits the `mr:diffs:expandAllFiles` event when the alert action button is clicked', () => {
createComponent({}, { full: true });
- jest.spyOn(wrapper.vm.$store, 'dispatch').mockReturnValue(undefined);
+ jest.spyOn(eventHub, '$emit');
getAlertActionButton().vm.$emit('click');
- expect(wrapper.vm.$store.dispatch).toHaveBeenCalledWith('diffs/expandAllFiles', undefined);
+ expect(eventHub.$emit).toHaveBeenCalledWith('mr:diffs:expandAllFiles');
});
});
diff --git a/spec/frontend/diffs/components/diff_file_spec.js b/spec/frontend/diffs/components/diff_file_spec.js
index 59aa6d811ee..4a41b5b4f98 100644
--- a/spec/frontend/diffs/components/diff_file_spec.js
+++ b/spec/frontend/diffs/components/diff_file_spec.js
@@ -9,6 +9,8 @@ import DiffFileComponent from '~/diffs/components/diff_file.vue';
import DiffFileHeaderComponent from '~/diffs/components/diff_file_header.vue';
import DiffContentComponent from '~/diffs/components/diff_content.vue';
+import eventHub from '~/diffs/event_hub';
+
import { diffViewerModes, diffViewerErrors } from '~/ide/constants';
function changeViewer(store, index, { automaticallyCollapsed, manuallyCollapsed, name }) {
@@ -138,6 +140,30 @@ describe('DiffFile', () => {
});
describe('collapsing', () => {
+ describe('`mr:diffs:expandAllFiles` event', () => {
+ beforeEach(() => {
+ jest.spyOn(wrapper.vm, 'handleToggle').mockImplementation(() => {});
+ });
+
+ it('performs the normal file toggle when the file is collapsed', async () => {
+ makeFileAutomaticallyCollapsed(store);
+
+ await wrapper.vm.$nextTick();
+
+ eventHub.$emit('mr:diffs:expandAllFiles');
+
+ expect(wrapper.vm.handleToggle).toHaveBeenCalledTimes(1);
+ });
+
+ it('does nothing when the file is not collapsed', async () => {
+ eventHub.$emit('mr:diffs:expandAllFiles');
+
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.vm.handleToggle).not.toHaveBeenCalled();
+ });
+ });
+
describe('user collapsed', () => {
beforeEach(() => {
makeFileManuallyCollapsed(store);
diff --git a/spec/frontend/diffs/store/actions_spec.js b/spec/frontend/diffs/store/actions_spec.js
index d41c4ae44eb..47d92d4a868 100644
--- a/spec/frontend/diffs/store/actions_spec.js
+++ b/spec/frontend/diffs/store/actions_spec.js
@@ -27,7 +27,6 @@ import {
scrollToLineIfNeededInline,
scrollToLineIfNeededParallel,
loadCollapsedDiff,
- expandAllFiles,
toggleFileDiscussions,
saveDiffDiscussion,
setHighlightedRow,
@@ -658,23 +657,6 @@ describe('DiffsStoreActions', () => {
});
});
- describe('expandAllFiles', () => {
- it('should change the collapsed prop from the diffFiles', done => {
- testAction(
- expandAllFiles,
- null,
- {},
- [
- {
- type: types.EXPAND_ALL_FILES,
- },
- ],
- [],
- done,
- );
- });
- });
-
describe('toggleFileDiscussions', () => {
it('should dispatch collapseDiscussion when all discussions are expanded', () => {
const getters = {
diff --git a/spec/frontend/diffs/store/mutations_spec.js b/spec/frontend/diffs/store/mutations_spec.js
index a84ad63c695..c0645faf89e 100644
--- a/spec/frontend/diffs/store/mutations_spec.js
+++ b/spec/frontend/diffs/store/mutations_spec.js
@@ -126,21 +126,6 @@ describe('DiffsStoreMutations', () => {
});
});
- describe('EXPAND_ALL_FILES', () => {
- it('should change the collapsed prop from diffFiles', () => {
- const diffFile = {
- viewer: {
- automaticallyCollapsed: true,
- },
- };
- const state = { expandAllFiles: true, diffFiles: [diffFile] };
-
- mutations[types.EXPAND_ALL_FILES](state);
-
- expect(state.diffFiles[0].viewer.automaticallyCollapsed).toEqual(false);
- });
- });
-
describe('ADD_CONTEXT_LINES', () => {
it('should call utils.addContextLines with proper params', () => {
const options = {
diff --git a/spec/frontend/projects/pipelines/charts/components/__snapshots__/statistics_list_spec.js.snap b/spec/frontend/projects/pipelines/charts/components/__snapshots__/statistics_list_spec.js.snap
index ff0351bd099..ac87fe893b9 100644
--- a/spec/frontend/projects/pipelines/charts/components/__snapshots__/statistics_list_spec.js.snap
+++ b/spec/frontend/projects/pipelines/charts/components/__snapshots__/statistics_list_spec.js.snap
@@ -11,7 +11,6 @@ exports[`StatisticsList matches the snapshot 1`] = `
4 pipelines
</strong>
</li>
-
<li>
<span>
Successful:
@@ -21,7 +20,6 @@ exports[`StatisticsList matches the snapshot 1`] = `
2 pipelines
</strong>
</li>
-
<li>
<span>
Failed:
@@ -31,7 +29,6 @@ exports[`StatisticsList matches the snapshot 1`] = `
2 pipelines
</strong>
</li>
-
<li>
<span>
Success ratio:
@@ -41,5 +38,14 @@ exports[`StatisticsList matches the snapshot 1`] = `
50%
</strong>
</li>
+ <li>
+ <span>
+ Total duration:
+ </span>
+
+ <strong>
+ 00:01:56
+ </strong>
+ </li>
</ul>
`;
diff --git a/spec/frontend/projects/pipelines/charts/mock_data.js b/spec/frontend/projects/pipelines/charts/mock_data.js
index db5164c8f99..84e0ccb828a 100644
--- a/spec/frontend/projects/pipelines/charts/mock_data.js
+++ b/spec/frontend/projects/pipelines/charts/mock_data.js
@@ -3,6 +3,7 @@ export const counts = {
success: 2,
total: 4,
successRatio: 50,
+ totalDuration: 116158,
};
export const timesChartData = {
diff --git a/spec/graphql/types/release_links_type_spec.rb b/spec/graphql/types/release_links_type_spec.rb
index d505f0a4b5c..e46dc0ddffe 100644
--- a/spec/graphql/types/release_links_type_spec.rb
+++ b/spec/graphql/types/release_links_type_spec.rb
@@ -8,9 +8,14 @@ RSpec.describe GitlabSchema.types['ReleaseLinks'] do
it 'has the expected fields' do
expected_fields = %w[
selfUrl
+ openMergeRequestsUrl
+ mergedMergeRequestsUrl
+ closedMergeRequestsUrl
+ openIssuesUrl
+ closedIssuesUrl
+ editUrl
mergeRequestsUrl
issuesUrl
- editUrl
]
expect(described_class).to include_graphql_fields(*expected_fields)
diff --git a/spec/lib/api/every_api_endpoint_spec.rb b/spec/lib/api/every_api_endpoint_spec.rb
index 2fcb09578c4..4a8998b89f9 100644
--- a/spec/lib/api/every_api_endpoint_spec.rb
+++ b/spec/lib/api/every_api_endpoint_spec.rb
@@ -22,7 +22,17 @@ RSpec.describe 'Every API endpoint' do
completed_classes = [
::API::Users, ::API::Issues, ::API::AccessRequests, ::API::Admin::Ci::Variables,
::API::Admin::InstanceClusters, ::API::Admin::Sidekiq, ::API::Appearance,
- ::API::Applications, ::API::Avatar, ::API::AwardEmoji
+ ::API::Applications, ::API::Avatar, ::API::AwardEmoji, API::Badges,
+ ::API::Boards, ::API::Branches, ::API::BroadcastMessages, ::API::Ci::Pipelines,
+ ::API::Ci::PipelineSchedules, ::API::Ci::Runners, ::API::Ci::Runner,
+ ::API::Commits, ::API::CommitStatuses, ::API::ContainerRegistryEvent,
+ ::API::DeployKeys, ::API::DeployTokens, ::API::Deployments, ::API::Environments,
+ ::API::ErrorTracking, ::API::Events, ::API::FeatureFlags, ::API::FeatureFlagScopes,
+ ::API::FeatureFlagsUserLists, ::API::Features, ::API::Files, ::API::FreezePeriods,
+ ::API::GroupBoards, ::API::GroupClusters, ::API::GroupExport, ::API::GroupImport,
+ ::API::GroupLabels, ::API::GroupMilestones, ::API::Groups,
+ ::API::GroupContainerRepositories, ::API::GroupVariables
+
]
next unless completed_classes.include?(klass)
diff --git a/spec/lib/gitlab/bitbucket_server_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_server_import/importer_spec.rb
index 18bcf779d31..3236ed0e41c 100644
--- a/spec/lib/gitlab/bitbucket_server_import/importer_spec.rb
+++ b/spec/lib/gitlab/bitbucket_server_import/importer_spec.rb
@@ -115,6 +115,12 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do
allow(subject.client).to receive(:pull_requests).and_return([pull_request])
end
+ # As we are using Caching with redis, it is best to clean the cache after each test run, else we need to wait for
+ # the expiration by the importer
+ after do
+ Gitlab::Cache::Import::Caching.expire(subject.already_imported_cache_key, 0)
+ end
+
it 'imports merge event' do
expect(subject.client).to receive(:activities).and_return([merge_event])
@@ -463,6 +469,47 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do
subject.execute
end
+
+ describe 'import pull requests with caching' do
+ let(:pull_request_already_imported) do
+ instance_double(
+ BitbucketServer::Representation::PullRequest,
+ iid: 11)
+ end
+
+ let(:pull_request_to_be_imported) do
+ instance_double(
+ BitbucketServer::Representation::PullRequest,
+ iid: 12,
+ source_branch_sha: sample.commits.last,
+ source_branch_name: Gitlab::Git::BRANCH_REF_PREFIX + sample.source_branch,
+ target_branch_sha: sample.commits.first,
+ target_branch_name: Gitlab::Git::BRANCH_REF_PREFIX + sample.target_branch,
+ title: 'This is a title',
+ description: 'This is a test pull request',
+ state: 'merged',
+ author: 'Test Author',
+ author_email: pull_request_author.email,
+ author_username: pull_request_author.username,
+ created_at: Time.now,
+ updated_at: Time.now,
+ raw: {},
+ merged?: true)
+ end
+
+ before do
+ Gitlab::Cache::Import::Caching.set_add(subject.already_imported_cache_key, pull_request_already_imported.iid)
+ allow(subject.client).to receive(:pull_requests).and_return([pull_request_to_be_imported, pull_request_already_imported])
+ end
+
+ it 'only imports one Merge Request, as the other on is in the cache' do
+ expect(subject.client).to receive(:activities).and_return([merge_event])
+ expect { subject.execute }.to change { MergeRequest.count }.by(1)
+
+ expect(Gitlab::Cache::Import::Caching.set_includes?(subject.already_imported_cache_key, pull_request_already_imported.iid)).to eq(true)
+ expect(Gitlab::Cache::Import::Caching.set_includes?(subject.already_imported_cache_key, pull_request_to_be_imported.iid)).to eq(true)
+ end
+ end
end
describe 'inaccessible branches' do
diff --git a/spec/presenters/release_presenter_spec.rb b/spec/presenters/release_presenter_spec.rb
index eb4d755205b..63c35bcf3ac 100644
--- a/spec/presenters/release_presenter_spec.rb
+++ b/spec/presenters/release_presenter_spec.rb
@@ -12,6 +12,11 @@ RSpec.describe ReleasePresenter do
let(:release) { create(:release, project: project) }
let(:presenter) { described_class.new(release, current_user: user) }
+ let(:base_url_params) { { scope: 'all', release_tag: release.tag } }
+ let(:opened_url_params) { { state: 'opened', **base_url_params } }
+ let(:merged_url_params) { { state: 'merged', **base_url_params } }
+ let(:closed_url_params) { { state: 'closed', **base_url_params } }
+
before do
project.add_developer(developer)
project.add_guest(guest)
@@ -55,15 +60,63 @@ RSpec.describe ReleasePresenter do
subject { presenter.self_url }
it 'returns its own url' do
- is_expected.to match /#{project_release_url(project, release)}/
+ is_expected.to eq(project_release_url(project, release))
+ end
+ end
+
+ describe '#open_merge_requests_url' do
+ subject { presenter.open_merge_requests_url }
+
+ it 'returns merge requests url with state=open' do
+ is_expected.to eq(project_merge_requests_url(project, opened_url_params))
+ end
+
+ context 'when release_mr_issue_urls feature flag is disabled' do
+ before do
+ stub_feature_flags(release_mr_issue_urls: false)
+ end
+
+ it { is_expected.to be_nil }
+ end
+ end
+
+ describe '#merged_merge_requests_url' do
+ subject { presenter.merged_merge_requests_url }
+
+ it 'returns merge requests url with state=merged' do
+ is_expected.to eq(project_merge_requests_url(project, merged_url_params))
+ end
+
+ context 'when release_mr_issue_urls feature flag is disabled' do
+ before do
+ stub_feature_flags(release_mr_issue_urls: false)
+ end
+
+ it { is_expected.to be_nil }
+ end
+ end
+
+ describe '#closed_merge_requests_url' do
+ subject { presenter.closed_merge_requests_url }
+
+ it 'returns merge requests url with state=closed' do
+ is_expected.to eq(project_merge_requests_url(project, closed_url_params))
+ end
+
+ context 'when release_mr_issue_urls feature flag is disabled' do
+ before do
+ stub_feature_flags(release_mr_issue_urls: false)
+ end
+
+ it { is_expected.to be_nil }
end
end
- describe '#merge_requests_url' do
- subject { presenter.merge_requests_url }
+ describe '#open_issues_url' do
+ subject { presenter.open_issues_url }
- it 'returns merge requests url' do
- is_expected.to match /#{project_merge_requests_url(project)}/
+ it 'returns issues url with state=open' do
+ is_expected.to eq(project_issues_url(project, opened_url_params))
end
context 'when release_mr_issue_urls feature flag is disabled' do
@@ -75,11 +128,11 @@ RSpec.describe ReleasePresenter do
end
end
- describe '#issues_url' do
- subject { presenter.issues_url }
+ describe '#closed_issues_url' do
+ subject { presenter.closed_issues_url }
- it 'returns merge requests url' do
- is_expected.to match /#{project_issues_url(project)}/
+ it 'returns issues url with state=closed' do
+ is_expected.to eq(project_issues_url(project, closed_url_params))
end
context 'when release_mr_issue_urls feature flag is disabled' do
@@ -95,7 +148,7 @@ RSpec.describe ReleasePresenter do
subject { presenter.edit_url }
it 'returns release edit url' do
- is_expected.to match /#{edit_project_release_url(project, release)}/
+ is_expected.to eq(edit_project_release_url(project, release))
end
context 'when a user is not allowed to update a release' do
diff --git a/spec/requests/api/graphql/project/release_spec.rb b/spec/requests/api/graphql/project/release_spec.rb
index 8fce29d0dc6..ebb588981d6 100644
--- a/spec/requests/api/graphql/project/release_spec.rb
+++ b/spec/requests/api/graphql/project/release_spec.rb
@@ -13,7 +13,11 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do
let_it_be(:link_filepath) { '/direct/asset/link/path' }
let_it_be(:released_at) { Time.now - 1.day }
- let(:params_for_issues_and_mrs) { { scope: 'all', state: 'opened', release_tag: release.tag } }
+ let(:base_url_params) { { scope: 'all', release_tag: release.tag } }
+ let(:opened_url_params) { { state: 'opened', **base_url_params } }
+ let(:merged_url_params) { { state: 'merged', **base_url_params } }
+ let(:closed_url_params) { { state: 'closed', **base_url_params } }
+
let(:post_query) { post_graphql(query, current_user: current_user) }
let(:path_prefix) { %w[project release] }
let(:data) { graphql_data.dig(*path) }
@@ -180,6 +184,11 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do
let(:release_fields) do
query_graphql_field(:links, nil, %{
selfUrl
+ openMergeRequestsUrl
+ mergedMergeRequestsUrl
+ closedMergeRequestsUrl
+ openIssuesUrl
+ closedIssuesUrl
mergeRequestsUrl
issuesUrl
})
@@ -190,8 +199,13 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do
expect(data).to eq(
'selfUrl' => project_release_url(project, release),
- 'mergeRequestsUrl' => project_merge_requests_url(project, params_for_issues_and_mrs),
- 'issuesUrl' => project_issues_url(project, params_for_issues_and_mrs)
+ 'openMergeRequestsUrl' => project_merge_requests_url(project, opened_url_params),
+ 'mergedMergeRequestsUrl' => project_merge_requests_url(project, merged_url_params),
+ 'closedMergeRequestsUrl' => project_merge_requests_url(project, closed_url_params),
+ 'openIssuesUrl' => project_issues_url(project, opened_url_params),
+ 'closedIssuesUrl' => project_issues_url(project, closed_url_params),
+ 'mergeRequestsUrl' => project_merge_requests_url(project, opened_url_params),
+ 'issuesUrl' => project_issues_url(project, opened_url_params)
)
end
end
diff --git a/spec/requests/api/graphql/project/releases_spec.rb b/spec/requests/api/graphql/project/releases_spec.rb
index 26700da262d..46ba1cdd1ea 100644
--- a/spec/requests/api/graphql/project/releases_spec.rb
+++ b/spec/requests/api/graphql/project/releases_spec.rb
@@ -10,6 +10,11 @@ RSpec.describe 'Query.project(fullPath).releases()' do
let_it_be(:reporter) { create(:user) }
let_it_be(:developer) { create(:user) }
+ let(:base_url_params) { { scope: 'all', release_tag: release.tag } }
+ let(:opened_url_params) { { state: 'opened', **base_url_params } }
+ let(:merged_url_params) { { state: 'merged', **base_url_params } }
+ let(:closed_url_params) { { state: 'closed', **base_url_params } }
+
let(:query) do
graphql_query_for(:project, { fullPath: project.full_path },
%{
@@ -37,6 +42,11 @@ RSpec.describe 'Query.project(fullPath).releases()' do
}
links {
selfUrl
+ openMergeRequestsUrl
+ mergedMergeRequestsUrl
+ closedMergeRequestsUrl
+ openIssuesUrl
+ closedIssuesUrl
mergeRequestsUrl
issuesUrl
}
@@ -101,8 +111,13 @@ RSpec.describe 'Query.project(fullPath).releases()' do
},
'links' => {
'selfUrl' => project_release_url(project, release),
- 'mergeRequestsUrl' => project_merge_requests_url(project, params_for_issues_and_mrs),
- 'issuesUrl' => project_issues_url(project, params_for_issues_and_mrs)
+ 'openMergeRequestsUrl' => project_merge_requests_url(project, opened_url_params),
+ 'mergedMergeRequestsUrl' => project_merge_requests_url(project, merged_url_params),
+ 'closedMergeRequestsUrl' => project_merge_requests_url(project, closed_url_params),
+ 'openIssuesUrl' => project_issues_url(project, opened_url_params),
+ 'closedIssuesUrl' => project_issues_url(project, closed_url_params),
+ 'mergeRequestsUrl' => project_merge_requests_url(project, opened_url_params),
+ 'issuesUrl' => project_issues_url(project, opened_url_params)
}
)
end
diff --git a/spec/support/shared_examples/uploaders/workers/object_storage/migrate_uploads_shared_examples.rb b/spec/support/shared_examples/uploaders/workers/object_storage/migrate_uploads_shared_examples.rb
deleted file mode 100644
index 5a9a3dfc2d2..00000000000
--- a/spec/support/shared_examples/uploaders/workers/object_storage/migrate_uploads_shared_examples.rb
+++ /dev/null
@@ -1,120 +0,0 @@
-# frozen_string_literal: true
-
-# Expects the calling spec to define:
-# - model_class
-# - mounted_as
-# - to_store
-RSpec.shared_examples 'uploads migration worker' do
- def perform(uploads, store = nil)
- described_class.new.perform(uploads.ids, model_class.to_s, mounted_as, store || to_store)
- rescue ObjectStorage::MigrateUploadsWorker::Report::MigrationFailures
- # swallow
- end
-
- describe '.enqueue!' do
- def enqueue!
- described_class.enqueue!(uploads, model_class, mounted_as, to_store)
- end
-
- it 'is guarded by .sanity_check!' do
- expect(described_class).to receive(:perform_async)
- expect(described_class).to receive(:sanity_check!)
-
- enqueue!
- end
-
- context 'sanity_check! fails' do
- include_context 'sanity_check! fails'
-
- it 'does not enqueue a job' do
- expect(described_class).not_to receive(:perform_async)
-
- expect { enqueue! }.to raise_error(described_class::SanityCheckError)
- end
- end
- end
-
- describe '.sanity_check!' do
- shared_examples 'raises a SanityCheckError' do |expected_message|
- let(:mount_point) { nil }
-
- it do
- expect { described_class.sanity_check!(uploads, model_class, mount_point) }
- .to raise_error(described_class::SanityCheckError).with_message(expected_message)
- end
- end
-
- context 'uploader types mismatch' do
- let!(:outlier) { create(:upload, uploader: 'GitlabUploader') }
-
- include_examples 'raises a SanityCheckError', /Multiple uploaders found/
- end
-
- context 'mount point not found' do
- include_examples 'raises a SanityCheckError', /Mount point [a-z:]+ not found in/ do
- let(:mount_point) { :potato }
- end
- end
- end
-
- describe '#perform' do
- shared_examples 'outputs correctly' do |success: 0, failures: 0|
- total = success + failures
-
- if success > 0
- it 'outputs the reports' do
- expect(Gitlab::AppLogger).to receive(:info).with(%r{Migrated #{success}/#{total} files})
-
- perform(uploads)
- end
- end
-
- if failures > 0
- it 'outputs upload failures' do
- expect(Gitlab::AppLogger).to receive(:warn).with(/Error .* I am a teapot/)
-
- perform(uploads)
- end
- end
- end
-
- it_behaves_like 'outputs correctly', success: 10
-
- it 'migrates files to remote storage' do
- perform(uploads)
-
- expect(Upload.where(store: ObjectStorage::Store::LOCAL).count).to eq(0)
- end
-
- context 'reversed' do
- let(:to_store) { ObjectStorage::Store::LOCAL }
-
- before do
- perform(uploads, ObjectStorage::Store::REMOTE)
- end
-
- it 'migrates files to local storage' do
- expect(Upload.where(store: ObjectStorage::Store::REMOTE).count).to eq(10)
-
- perform(uploads)
-
- expect(Upload.where(store: ObjectStorage::Store::LOCAL).count).to eq(10)
- end
- end
-
- context 'migration is unsuccessful' do
- before do
- allow_any_instance_of(ObjectStorage::Concern)
- .to receive(:migrate!).and_raise(CarrierWave::UploadError, 'I am a teapot.')
- end
-
- it_behaves_like 'outputs correctly', failures: 10
- end
- end
-end
-
-RSpec.shared_context 'sanity_check! fails' do
- before do
- expect(described_class).to receive(:sanity_check!).and_raise(described_class::SanityCheckError)
- end
-end
diff --git a/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb b/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb
index ef5459ce788..fd01a18e810 100644
--- a/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb
+++ b/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb
@@ -13,8 +13,103 @@ RSpec.describe ObjectStorage::MigrateUploadsWorker do
# swallow
end
+ # Expects the calling spec to define:
+ # - model_class
+ # - mounted_as
+ # - to_store
+ RSpec.shared_examples 'uploads migration worker' do
+ describe '.enqueue!' do
+ def enqueue!
+ described_class.enqueue!(uploads, model_class, mounted_as, to_store)
+ end
+
+ it 'is guarded by .sanity_check!' do
+ expect(described_class).to receive(:perform_async)
+ expect(described_class).to receive(:sanity_check!)
+
+ enqueue!
+ end
+
+ context 'sanity_check! fails' do
+ before do
+ expect(described_class).to receive(:sanity_check!).and_raise(described_class::SanityCheckError)
+ end
+
+ it 'does not enqueue a job' do
+ expect(described_class).not_to receive(:perform_async)
+
+ expect { enqueue! }.to raise_error(described_class::SanityCheckError)
+ end
+ end
+ end
+
+ describe '.sanity_check!' do
+ shared_examples 'raises a SanityCheckError' do |expected_message|
+ let(:mount_point) { nil }
+
+ it do
+ expect { described_class.sanity_check!(uploads, model_class, mount_point) }
+ .to raise_error(described_class::SanityCheckError).with_message(expected_message)
+ end
+ end
+
+ context 'uploader types mismatch' do
+ let!(:outlier) { create(:upload, uploader: 'GitlabUploader') }
+
+ include_examples 'raises a SanityCheckError', /Multiple uploaders found/
+ end
+
+ context 'mount point not found' do
+ include_examples 'raises a SanityCheckError', /Mount point [a-z:]+ not found in/ do
+ let(:mount_point) { :potato }
+ end
+ end
+ end
+
+ describe '#perform' do
+ it 'migrates files to remote storage' do
+ expect(Gitlab::AppLogger).to receive(:info).with(%r{Migrated 1/1 files})
+
+ perform(uploads)
+
+ expect(Upload.where(store: ObjectStorage::Store::LOCAL).count).to eq(0)
+ end
+
+ context 'reversed' do
+ let(:to_store) { ObjectStorage::Store::LOCAL }
+
+ before do
+ perform(uploads, ObjectStorage::Store::REMOTE)
+ end
+
+ it 'migrates files to local storage' do
+ expect(Upload.where(store: ObjectStorage::Store::REMOTE).count).to eq(1)
+
+ perform(uploads)
+
+ expect(Upload.where(store: ObjectStorage::Store::LOCAL).count).to eq(1)
+ end
+ end
+
+ context 'migration is unsuccessful' do
+ before do
+ allow_any_instance_of(ObjectStorage::Concern)
+ .to receive(:migrate!).and_raise(CarrierWave::UploadError, 'I am a teapot.')
+ end
+
+ it 'does not migrate files to remote storage' do
+ expect(Gitlab::AppLogger).to receive(:warn).with(/Error .* I am a teapot/)
+
+ perform(uploads)
+
+ expect(Upload.where(store: ObjectStorage::Store::LOCAL).count).to eq(1)
+ end
+ end
+ end
+ end
+
context "for AvatarUploader" do
- let!(:projects) { create_list(:project, 10, :with_avatar) }
+ let!(:project_with_avatar) { create(:project, :with_avatar) }
let(:mounted_as) { :avatar }
before do
@@ -27,16 +122,15 @@ RSpec.describe ObjectStorage::MigrateUploadsWorker do
it "to N*5" do
query_count = ActiveRecord::QueryRecorder.new { perform(uploads) }
- more_projects = create_list(:project, 3, :with_avatar)
+ create(:project, :with_avatar)
- expected_queries_per_migration = 5 * more_projects.count
- expect { perform(Upload.all) }.not_to exceed_query_limit(query_count).with_threshold(expected_queries_per_migration)
+ expect { perform(Upload.all) }.not_to exceed_query_limit(query_count).with_threshold(5)
end
end
end
context "for FileUploader" do
- let!(:projects) { create_list(:project, 10) }
+ let!(:project_with_file) { create(:project) }
let(:secret) { SecureRandom.hex }
let(:mounted_as) { nil }
@@ -48,7 +142,7 @@ RSpec.describe ObjectStorage::MigrateUploadsWorker do
before do
stub_uploads_object_storage(FileUploader)
- projects.map(&method(:upload_file))
+ upload_file(project_with_file)
end
it_behaves_like "uploads migration worker"
@@ -57,18 +151,16 @@ RSpec.describe ObjectStorage::MigrateUploadsWorker do
it "to N*5" do
query_count = ActiveRecord::QueryRecorder.new { perform(uploads) }
- more_projects = create_list(:project, 3)
- more_projects.map(&method(:upload_file))
+ upload_file(create(:project))
- expected_queries_per_migration = 5 * more_projects.count
- expect { perform(Upload.all) }.not_to exceed_query_limit(query_count).with_threshold(expected_queries_per_migration)
+ expect { perform(Upload.all) }.not_to exceed_query_limit(query_count).with_threshold(5)
end
end
end
context 'for DesignManagement::DesignV432x230Uploader' do
let(:model_class) { DesignManagement::Action }
- let!(:design_actions) { create_list(:design_action, 10, :with_image_v432x230) }
+ let!(:design_action) { create(:design_action, :with_image_v432x230) }
let(:mounted_as) { :image_v432x230 }
before do
diff --git a/tooling/lib/tooling/helm3_client.rb b/tooling/lib/tooling/helm3_client.rb
index 802ff9b9661..d6671688794 100644
--- a/tooling/lib/tooling/helm3_client.rb
+++ b/tooling/lib/tooling/helm3_client.rb
@@ -3,7 +3,6 @@
require 'time'
require 'json'
require_relative '../../../lib/gitlab/popen' unless defined?(Gitlab::Popen)
-require_relative '../../../lib/gitlab/json' unless defined?(Gitlab::Json)
module Tooling
class Helm3Client
@@ -67,7 +66,7 @@ module Tooling
%(--output json),
*args
]
- releases = Gitlab::Json.parse(run_command(command))
+ releases = JSON.parse(run_command(command)) # rubocop:disable Gitlab/Json
releases.map do |release|
Release.new(*release.values_at(*RELEASE_JSON_ATTRIBUTES))