summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.checksum8
-rw-r--r--Gemfile.lock4
-rw-r--r--app/assets/javascripts/environments/components/confirm_rollback_modal.vue16
-rw-r--r--app/assets/javascripts/environments/environment_details/constants.js1
-rw-r--r--app/assets/javascripts/environments/environment_details/index.vue28
-rw-r--r--app/assets/javascripts/environments/graphql/client.js1
-rw-r--r--app/assets/javascripts/environments/mount_show.js1
-rw-r--r--app/assets/javascripts/graphql_shared/utils.js27
-rw-r--r--app/assets/javascripts/pipelines/components/graph/utils.js33
-rw-r--r--app/assets/javascripts/super_sidebar/components/menu_section.vue42
-rw-r--r--app/assets/javascripts/super_sidebar/components/nav_item.vue4
-rw-r--r--app/assets/javascripts/super_sidebar/components/pinned_section.vue4
-rw-r--r--app/assets/javascripts/super_sidebar/components/sidebar_menu.vue6
-rw-r--r--app/assets/stylesheets/framework/super_sidebar.scss2
-rw-r--r--app/controllers/admin/instance_review_controller.rb2
-rw-r--r--app/controllers/projects/repositories_controller.rb3
-rw-r--r--app/helpers/environment_helper.rb3
-rw-r--r--app/models/design_management/git_repository.rb22
-rw-r--r--app/models/design_management/repository.rb32
-rw-r--r--app/models/project.rb4
-rw-r--r--app/models/user.rb14
-rw-r--r--app/services/ci/job_token_scope/add_project_service.rb2
-rw-r--r--app/services/ci/job_token_scope/remove_project_service.rb2
-rw-r--r--app/services/merge_requests/after_create_service.rb1
-rw-r--r--app/services/merge_requests/base_service.rb4
-rw-r--r--app/views/admin/application_settings/appearances/show.html.haml1
-rw-r--r--app/views/admin/application_settings/ci_cd.html.haml1
-rw-r--r--app/views/admin/application_settings/general.html.haml1
-rw-r--r--app/views/admin/application_settings/integrations.html.haml1
-rw-r--r--app/views/admin/application_settings/metrics_and_profiling.html.haml1
-rw-r--r--app/views/admin/application_settings/network.html.haml1
-rw-r--r--app/views/admin/application_settings/preferences.html.haml1
-rw-r--r--app/views/admin/application_settings/reporting.html.haml1
-rw-r--r--app/views/admin/application_settings/repository.html.haml1
-rw-r--r--app/views/admin/application_settings/service_usage_data.html.haml1
-rw-r--r--app/workers/delete_user_worker.rb9
-rw-r--r--config/feature_flags/development/delay_delete_own_user.yml8
-rw-r--r--config/routes/directs.rb1
-rw-r--r--config/routes/directs/subscription_portal.rb19
-rw-r--r--config/routes/project.rb2
-rw-r--r--data/removals/16_0/16-0-pull-thru-cache-container-registry.yml11
-rw-r--r--doc/development/documentation/styleguide/word_list.md31
-rw-r--r--doc/policy/alpha-beta-support.md13
-rw-r--r--doc/update/removals.md8
-rw-r--r--lib/feature/gitaly.rb12
-rw-r--r--lib/gitlab/ci/reports/codequality_mr_diff.rb13
-rw-r--r--lib/gitlab/ci/templates/Jobs/Secret-Detection.latest.gitlab-ci.yml2
-rw-r--r--lib/gitlab/etag_caching/router/graphql.rb5
-rw-r--r--lib/gitlab/git_access_design.rb21
-rw-r--r--lib/gitlab/gl_repository.rb5
-rw-r--r--lib/gitlab/gl_repository/repo_type.rb8
-rw-r--r--lib/gitlab/patch/draw_route.rb2
-rw-r--r--lib/gitlab/subscription_portal.rb73
-rw-r--r--package.json2
-rw-r--r--spec/controllers/admin/instance_review_controller_spec.rb2
-rw-r--r--spec/controllers/projects/repositories_controller_spec.rb10
-rw-r--r--spec/factories/design_management/repositories.rb7
-rw-r--r--spec/features/nav/pinned_nav_items_spec.rb2
-rw-r--r--spec/fixtures/pipeline_artifacts/code_quality_mr_diff.json63
-rw-r--r--spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js8
-rw-r--r--spec/frontend/commit/components/commit_box_pipeline_status_spec.js6
-rw-r--r--spec/frontend/environments/confirm_rollback_modal_spec.js36
-rw-r--r--spec/frontend/environments/environment_details/index_spec.js (renamed from spec/frontend/environments/environment_details/page_spec.js)25
-rw-r--r--spec/frontend/graphql_shared/utils_spec.js53
-rw-r--r--spec/frontend/super_sidebar/components/menu_section_spec.js15
-rw-r--r--spec/graphql/mutations/design_management/delete_spec.rb31
-rw-r--r--spec/helpers/environment_helper_spec.rb3
-rw-r--r--spec/lib/backup/repositories_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/reports/codequality_mr_diff_spec.rb38
-rw-r--r--spec/lib/gitlab/gitaly_client/with_feature_flag_actors_spec.rb16
-rw-r--r--spec/lib/gitlab/gl_repository/identifier_spec.rb6
-rw-r--r--spec/lib/gitlab/gl_repository/repo_type_spec.rb23
-rw-r--r--spec/lib/gitlab/gl_repository_spec.rb9
-rw-r--r--spec/lib/gitlab/patch/draw_route_spec.rb4
-rw-r--r--spec/lib/gitlab/subscription_portal_spec.rb86
-rw-r--r--spec/models/design_management/design_collection_spec.rb2
-rw-r--r--spec/models/design_management/design_spec.rb2
-rw-r--r--spec/models/design_management/git_repository_spec.rb10
-rw-r--r--spec/models/design_management/repository_spec.rb10
-rw-r--r--spec/models/user_spec.rb28
-rw-r--r--spec/presenters/ci/pipeline_artifacts/code_quality_mr_diff_presenter_spec.rb49
-rw-r--r--spec/routing/directs/subscription_portal_spec.rb63
-rw-r--r--spec/routing/project_routing_spec.rb12
-rw-r--r--spec/serializers/ci/codequality_mr_diff_entity_spec.rb13
-rw-r--r--spec/services/design_management/save_designs_service_spec.rb5
-rw-r--r--spec/services/merge_requests/after_create_service_spec.rb7
-rw-r--r--spec/services/merge_requests/base_service_spec.rb34
-rw-r--r--spec/services/merge_requests/create_service_spec.rb7
-rw-r--r--spec/services/projects/transfer_service_spec.rb15
-rw-r--r--spec/support/shared_examples/lib/gitlab/repo_type_shared_examples.rb2
-rw-r--r--spec/workers/delete_user_worker_spec.rb50
-rw-r--r--yarn.lock8
93 files changed, 756 insertions, 528 deletions
diff --git a/Gemfile b/Gemfile
index 914a81cc5f0..297b3025507 100644
--- a/Gemfile
+++ b/Gemfile
@@ -28,7 +28,7 @@ gem 'sprockets', '~> 3.7.0'
gem 'view_component', '~> 2.82.0'
# Supported DBs
-gem 'pg', '~> 1.4.6'
+gem 'pg', '~> 1.5.3'
gem 'neighbor', '~> 0.2.3'
diff --git a/Gemfile.checksum b/Gemfile.checksum
index 0af30976daa..5471e4e7ea7 100644
--- a/Gemfile.checksum
+++ b/Gemfile.checksum
@@ -439,10 +439,10 @@
{"name":"parslet","version":"1.8.2","platform":"ruby","checksum":"08d1ab3721cd3f175bfbee8788b2ddff71f92038f2d69bd65454c22bb9fbd98a"},
{"name":"pastel","version":"0.8.0","platform":"ruby","checksum":"481da9fb7d2f6e6b1a08faf11fa10363172dc40fd47848f096ae21209f805a75"},
{"name":"peek","version":"1.1.0","platform":"ruby","checksum":"d6501ead8cde46d8d8ed0d59eb6f0ba713d0a41c11a2c4a81447b2dce37b3ecc"},
-{"name":"pg","version":"1.4.6","platform":"ruby","checksum":"d98f3dcb4a6ae29780a2219340cb0e55dbafbb7eb4ccc2b99f892f2569a7a61e"},
-{"name":"pg","version":"1.4.6","platform":"x64-mingw-ucrt","checksum":"1efb4f932d5579b87b1c37e0ef49d101925d4f0e3fcf282569aed0382a522b68"},
-{"name":"pg","version":"1.4.6","platform":"x64-mingw32","checksum":"26c4a010fe2cefe61f56f0c4ba9a86b6e99d0965af100f30eaba1602a167af56"},
-{"name":"pg","version":"1.4.6","platform":"x86-mingw32","checksum":"14376f8a122ec58b9e1b4123774e7eafb59222544b7c6cfaa379c6ef28785ae6"},
+{"name":"pg","version":"1.5.3","platform":"ruby","checksum":"6b9ee5e2d5aee975588232c41f8203e766157cf71dba54ee85b343a45ced9bfd"},
+{"name":"pg","version":"1.5.3","platform":"x64-mingw-ucrt","checksum":"1f2a6b2afaf0ccb8afe8b6a00131bce8151fbd6e8826b2d944288f6f2b615389"},
+{"name":"pg","version":"1.5.3","platform":"x64-mingw32","checksum":"ab7f5f3020323094a2b16f9638166b04c103e152a9079a1b8e795f4bf79765e0"},
+{"name":"pg","version":"1.5.3","platform":"x86-mingw32","checksum":"aa6ddda9887462d30a6d49d875eb9d27fca8cdb7185103b650e7351b38f15ddf"},
{"name":"pg_query","version":"2.2.1","platform":"ruby","checksum":"6086972bbf4eab86d8425b35f14ca8b6fe41e4341423582801c1ec86ff5f8cea"},
{"name":"plist","version":"3.6.0","platform":"ruby","checksum":"f468bcf6b72ec6d1585ed6744eb4817c1932a5bf91895ed056e69b7f12ca10f2"},
{"name":"png_quantizator","version":"0.2.1","platform":"ruby","checksum":"6023d4d064125c3a7e02929c95b7320ed6ac0d7341f9e8de0c9ea6576ef3106b"},
diff --git a/Gemfile.lock b/Gemfile.lock
index 82ac228c6cd..e2054d98a13 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1136,7 +1136,7 @@ GEM
tty-color (~> 0.5)
peek (1.1.0)
railties (>= 4.0.0)
- pg (1.4.6)
+ pg (1.5.3)
pg_query (2.2.1)
google-protobuf (>= 3.19.2)
plist (3.6.0)
@@ -1866,7 +1866,7 @@ DEPENDENCIES
parallel (~> 1.19)
parslet (~> 1.8)
peek (~> 1.1)
- pg (~> 1.4.6)
+ pg (~> 1.5.3)
pg_query (~> 2.2, >= 2.2.1)
png_quantizator (~> 0.2.1)
premailer-rails (~> 1.10.3)
diff --git a/app/assets/javascripts/environments/components/confirm_rollback_modal.vue b/app/assets/javascripts/environments/components/confirm_rollback_modal.vue
index 9db3011ba5d..2cf71de7ea2 100644
--- a/app/assets/javascripts/environments/components/confirm_rollback_modal.vue
+++ b/app/assets/javascripts/environments/components/confirm_rollback_modal.vue
@@ -3,6 +3,7 @@
* Render modal to confirm rollback/redeploy.
*/
import { GlModal, GlSprintf, GlLink } from '@gitlab/ui';
+import * as Sentry from '@sentry/browser';
import { escape } from 'lodash';
import csrf from '~/lib/utils/csrf';
import { __, s__, sprintf } from '~/locale';
@@ -125,10 +126,17 @@ export default {
},
onOk() {
if (this.graphql) {
- this.$apollo.mutate({
- mutation: rollbackEnvironment,
- variables: { environment: this.environment },
- });
+ this.$apollo
+ .mutate({
+ mutation: rollbackEnvironment,
+ variables: { environment: this.environment },
+ })
+ .then(() => {
+ this.$emit('rollback');
+ })
+ .catch((e) => {
+ Sentry.captureException(e);
+ });
} else {
eventHub.$emit('rollbackEnvironment', this.environment);
}
diff --git a/app/assets/javascripts/environments/environment_details/constants.js b/app/assets/javascripts/environments/environment_details/constants.js
index 07579092e23..e7b10aed20d 100644
--- a/app/assets/javascripts/environments/environment_details/constants.js
+++ b/app/assets/javascripts/environments/environment_details/constants.js
@@ -1,6 +1,7 @@
import { __, s__ } from '~/locale';
import { helpPagePath } from '~/helpers/help_page_helper';
+export const ENVIRONMENT_DETAILS_QUERY_POLLING_INTERVAL = 3000;
export const ENVIRONMENT_DETAILS_PAGE_SIZE = 20;
export const ENVIRONMENT_DETAILS_TABLE_FIELDS = [
{
diff --git a/app/assets/javascripts/environments/environment_details/index.vue b/app/assets/javascripts/environments/environment_details/index.vue
index f91e68e793f..1b7320df674 100644
--- a/app/assets/javascripts/environments/environment_details/index.vue
+++ b/app/assets/javascripts/environments/environment_details/index.vue
@@ -1,6 +1,7 @@
<script>
import { GlLoadingIcon } from '@gitlab/ui';
import { logError } from '~/lib/logger';
+import { toggleQueryPollingByVisibility, etagQueryHeaders } from '~/graphql_shared/utils';
import ConfirmRollbackModal from '~/environments/components/confirm_rollback_modal.vue';
import environmentDetailsQuery from '../graphql/queries/environment_details.query.graphql';
import environmentToRollbackQuery from '../graphql/queries/environment_to_rollback.query.graphql';
@@ -8,7 +9,10 @@ import { convertToDeploymentTableRow } from '../helpers/deployment_data_transfor
import EmptyState from './empty_state.vue';
import DeploymentsTable from './deployments_table.vue';
import Pagination from './pagination.vue';
-import { ENVIRONMENT_DETAILS_PAGE_SIZE } from './constants';
+import {
+ ENVIRONMENT_DETAILS_QUERY_POLLING_INTERVAL,
+ ENVIRONMENT_DETAILS_PAGE_SIZE,
+} from './constants';
export default {
components: {
@@ -18,6 +22,7 @@ export default {
EmptyState,
GlLoadingIcon,
},
+ inject: { graphqlEtagKey: { default: '' } },
props: {
projectFullPath: {
type: String,
@@ -51,6 +56,12 @@ export default {
before: this.before,
};
},
+ pollInterval() {
+ return this.graphqlEtagKey ? ENVIRONMENT_DETAILS_QUERY_POLLING_INTERVAL : null;
+ },
+ context() {
+ return etagQueryHeaders('environment_details', this.graphqlEtagKey);
+ },
},
environmentToRollback: {
query: environmentToRollbackQuery,
@@ -136,6 +147,19 @@ export default {
this.isPrefetchingPages = false;
},
},
+ mounted() {
+ if (this.graphqlEtagKey) {
+ toggleQueryPollingByVisibility(
+ this.$apollo.queries.project,
+ ENVIRONMENT_DETAILS_QUERY_POLLING_INTERVAL,
+ );
+ }
+ },
+ methods: {
+ resetPage() {
+ this.$router.push({ query: {} });
+ },
+ },
};
</script>
<template>
@@ -150,6 +174,6 @@ export default {
<pagination :page-info="pageInfo" :disabled="isPaginationDisabled" />
</div>
<empty-state v-if="!isDeploymentTableShown && !isLoading" />
- <confirm-rollback-modal :environment="environmentToRollback" graphql />
+ <confirm-rollback-modal :environment="environmentToRollback" graphql @rollback="resetPage" />
</div>
</template>
diff --git a/app/assets/javascripts/environments/graphql/client.js b/app/assets/javascripts/environments/graphql/client.js
index bb6f57e7e80..b7754558b10 100644
--- a/app/assets/javascripts/environments/graphql/client.js
+++ b/app/assets/javascripts/environments/graphql/client.js
@@ -13,6 +13,7 @@ import typeDefs from './typedefs.graphql';
export const apolloProvider = (endpoint) => {
const defaultClient = createDefaultClient(resolvers(endpoint), {
typeDefs,
+ useGet: true,
});
const { cache } = defaultClient;
diff --git a/app/assets/javascripts/environments/mount_show.js b/app/assets/javascripts/environments/mount_show.js
index 5e812c85c96..364f68cefb7 100644
--- a/app/assets/javascripts/environments/mount_show.js
+++ b/app/assets/javascripts/environments/mount_show.js
@@ -94,6 +94,7 @@ export const initPage = async () => {
router,
provide: {
projectPath: dataSet.projectFullPath,
+ graphqlEtagKey: dataSet.graphqlEtagPath,
},
render(createElement) {
return createElement('router-view');
diff --git a/app/assets/javascripts/graphql_shared/utils.js b/app/assets/javascripts/graphql_shared/utils.js
index 806e89d6e9f..198d9f980f0 100644
--- a/app/assets/javascripts/graphql_shared/utils.js
+++ b/app/assets/javascripts/graphql_shared/utils.js
@@ -1,4 +1,5 @@
import { isArray } from 'lodash';
+import Visibility from 'visibilityjs';
/**
* Ids generated by GraphQL endpoints are usually in the format
@@ -116,3 +117,29 @@ export const convertNodeIdsFromGraphQLIds = (nodes) => {
export const getNodesOrDefault = (queryData, nodesField = 'nodes') => {
return queryData?.[nodesField] ?? [];
};
+
+export const toggleQueryPollingByVisibility = (queryRef, interval = 10000) => {
+ const stopStartQuery = (query) => {
+ if (!Visibility.hidden()) {
+ query.startPolling(interval);
+ } else {
+ query.stopPolling();
+ }
+ };
+
+ stopStartQuery(queryRef);
+ Visibility.change(stopStartQuery.bind(null, queryRef));
+};
+
+export const etagQueryHeaders = (featureCorrelation, etagResource = '') => {
+ return {
+ fetchOptions: {
+ method: 'GET',
+ },
+ headers: {
+ 'X-GITLAB-GRAPHQL-FEATURE-CORRELATION': featureCorrelation,
+ 'X-GITLAB-GRAPHQL-RESOURCE-ETAG': etagResource,
+ 'X-Requested-With': 'XMLHttpRequest',
+ },
+ };
+};
diff --git a/app/assets/javascripts/pipelines/components/graph/utils.js b/app/assets/javascripts/pipelines/components/graph/utils.js
index 54985a24593..c888c8a5537 100644
--- a/app/assets/javascripts/pipelines/components/graph/utils.js
+++ b/app/assets/javascripts/pipelines/components/graph/utils.js
@@ -1,11 +1,12 @@
import { isEmpty } from 'lodash';
-import Visibility from 'visibilityjs';
-import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import { getIdFromGraphQLId, etagQueryHeaders } from '~/graphql_shared/utils';
import { reportToSentry } from '../../utils';
import { listByLayers } from '../parsing_utils';
import { unwrapStagesWithNeedsAndLookup } from '../unwrapping_utils';
import { beginPerfMeasure, finishPerfMeasureAndSend } from './perf_utils';
+export { toggleQueryPollingByVisibility } from '~/graphql_shared/utils';
+
const addMulti = (mainPipelineProjectPath, linkedPipeline) => {
return {
...linkedPipeline,
@@ -35,18 +36,8 @@ const calculatePipelineLayersInfo = (pipeline, componentName, metricsPath) => {
return layers;
};
-const getQueryHeaders = (etagResource) => {
- return {
- fetchOptions: {
- method: 'GET',
- },
- headers: {
- 'X-GITLAB-GRAPHQL-FEATURE-CORRELATION': 'verify/ci/pipeline-graph',
- 'X-GITLAB-GRAPHQL-RESOURCE-ETAG': etagResource,
- 'X-Requested-With': 'XMLHttpRequest',
- },
- };
-};
+const getQueryHeaders = (etagResource) =>
+ etagQueryHeaders('verify/ci/pipeline-graph', etagResource);
const serializeGqlErr = (gqlError) => {
const { locations = [], message = '', path = [] } = gqlError;
@@ -80,19 +71,6 @@ const serializeLoadErrors = (errors) => {
return message;
};
-const toggleQueryPollingByVisibility = (queryRef, interval = 10000) => {
- const stopStartQuery = (query) => {
- if (!Visibility.hidden()) {
- query.startPolling(interval);
- } else {
- query.stopPolling();
- }
- };
-
- stopStartQuery(queryRef);
- Visibility.change(stopStartQuery.bind(null, queryRef));
-};
-
const transformId = (linkedPipeline) => {
return { ...linkedPipeline, id: getIdFromGraphQLId(linkedPipeline.id) };
};
@@ -133,7 +111,6 @@ export {
getQueryHeaders,
serializeGqlErr,
serializeLoadErrors,
- toggleQueryPollingByVisibility,
unwrapPipelineData,
validateConfigPaths,
};
diff --git a/app/assets/javascripts/super_sidebar/components/menu_section.vue b/app/assets/javascripts/super_sidebar/components/menu_section.vue
index 86bf3837c10..49e481feb04 100644
--- a/app/assets/javascripts/super_sidebar/components/menu_section.vue
+++ b/app/assets/javascripts/super_sidebar/components/menu_section.vue
@@ -1,4 +1,5 @@
<script>
+import { kebabCase } from 'lodash';
import { GlCollapse, GlIcon } from '@gitlab/ui';
import NavItem from './nav_item.vue';
@@ -27,7 +28,7 @@ export default {
tag: {
type: String,
required: false,
- default: 'section',
+ default: 'div',
},
},
data() {
@@ -36,6 +37,13 @@ export default {
};
},
computed: {
+ buttonProps() {
+ return {
+ 'aria-controls': this.itemId,
+ 'aria-expanded': String(this.isExpanded),
+ 'data-qa-menu-item': this.item.title,
+ };
+ },
collapseIcon() {
return this.isExpanded ? 'chevron-up' : 'chevron-down';
},
@@ -44,16 +52,12 @@ export default {
'gl-bg-t-gray-a-08': this.isActive,
};
},
- linkProps() {
- return {
- 'aria-controls': this.itemId,
- 'aria-expanded': String(this.isExpanded),
- 'data-qa-menu-item': this.item.title,
- };
- },
isActive() {
return !this.isExpanded && this.item.is_active;
},
+ itemId() {
+ return kebabCase(this.item.title);
+ },
},
watch: {
isExpanded(newIsExpanded) {
@@ -66,10 +70,11 @@ export default {
<template>
<component :is="tag">
<button
- class="gl-rounded-base gl-relative gl-display-flex gl-align-items-center gl-py-3 gl-px-0 gl-line-height-normal gl-text-black-normal! gl-hover-bg-t-gray-a-08 gl-focus-bg-t-gray-a-08 gl-text-decoration-none! gl-appearance-none gl-border-0 gl-bg-transparent gl-text-left gl-w-full gl-focus--focus"
+ class="gl-rounded-base gl-relative gl-display-flex gl-align-items-center gl-mb-1 gl-py-3 gl-px-0 gl-line-height-normal gl-text-black-normal! gl-hover-bg-t-gray-a-08 gl-focus-bg-t-gray-a-08 gl-text-decoration-none! gl-appearance-none gl-border-0 gl-bg-transparent gl-text-left gl-w-full gl-focus--focus"
:class="computedLinkClasses"
data-qa-selector="menu_section_button"
:data-qa-section-name="item.title"
+ v-bind="buttonProps"
@click="isExpanded = !isExpanded"
>
<span
@@ -94,21 +99,22 @@ export default {
</button>
<gl-collapse
+ :id="itemId"
v-model="isExpanded"
:aria-label="item.title"
+ class="gl-list-style-none gl-p-0 gl-m-0"
data-qa-selector="menu_section"
:data-qa-section-name="item.title"
+ tag="ul"
>
<slot>
- <ul class="gl-list-style-none gl-p-0 gl-m-0">
- <nav-item
- v-for="subItem of item.items"
- :key="`${item.title}-${subItem.title}`"
- :item="subItem"
- @pin-add="(itemId) => $emit('pin-add', itemId)"
- @pin-remove="(itemId) => $emit('pin-remove', itemId)"
- />
- </ul>
+ <nav-item
+ v-for="subItem of item.items"
+ :key="`${item.title}-${subItem.title}`"
+ :item="subItem"
+ @pin-add="(itemId) => $emit('pin-add', itemId)"
+ @pin-remove="(itemId) => $emit('pin-remove', itemId)"
+ />
</slot>
</gl-collapse>
<hr v-if="separated" aria-hidden="true" class="gl-mx-4 gl-my-2" />
diff --git a/app/assets/javascripts/super_sidebar/components/nav_item.vue b/app/assets/javascripts/super_sidebar/components/nav_item.vue
index abf5d73069c..1d8c5b30e13 100644
--- a/app/assets/javascripts/super_sidebar/components/nav_item.vue
+++ b/app/assets/javascripts/super_sidebar/components/nav_item.vue
@@ -1,5 +1,4 @@
<script>
-import { kebabCase } from 'lodash';
import { GlButton, GlIcon, GlBadge } from '@gitlab/ui';
import { s__ } from '~/locale';
import {
@@ -47,9 +46,6 @@ export default {
},
},
computed: {
- itemId() {
- return kebabCase(this.item.title);
- },
pillData() {
return this.item.pill_count;
},
diff --git a/app/assets/javascripts/super_sidebar/components/pinned_section.vue b/app/assets/javascripts/super_sidebar/components/pinned_section.vue
index b93e8e2ed1d..4fc86e41ef2 100644
--- a/app/assets/javascripts/super_sidebar/components/pinned_section.vue
+++ b/app/assets/javascripts/super_sidebar/components/pinned_section.vue
@@ -89,8 +89,8 @@ export default {
@pin-remove="(itemId) => $emit('pin-remove', itemId)"
/>
</draggable>
- <div v-else class="gl-text-secondary gl-font-sm gl-py-3" style="margin-left: 2.5rem">
+ <li v-else class="gl-text-secondary gl-font-sm gl-py-3" style="margin-left: 2.5rem">
{{ $options.i18n.emptyHint }}
- </div>
+ </li>
</menu-section>
</template>
diff --git a/app/assets/javascripts/super_sidebar/components/sidebar_menu.vue b/app/assets/javascripts/super_sidebar/components/sidebar_menu.vue
index 16923e44a77..12abd727ef0 100644
--- a/app/assets/javascripts/super_sidebar/components/sidebar_menu.vue
+++ b/app/assets/javascripts/super_sidebar/components/sidebar_menu.vue
@@ -132,20 +132,18 @@ export default {
<template>
<nav class="gl-p-2 gl-relative">
- <section v-if="staticItems.length > 0">
+ <template v-if="staticItems.length > 0">
<ul class="gl-p-0 gl-m-0">
<nav-item v-for="item in staticItems" :key="item.id" :item="item" is-static />
</ul>
<hr aria-hidden="true" class="gl-my-2 gl-mx-4" />
- </section>
-
+ </template>
<pinned-section
v-if="supportsPins"
:items="pinnedItems"
@pin-remove="destroyPin"
@pin-reorder="movePin"
/>
-
<ul class="gl-p-0 gl-list-style-none">
<component
:is="item.items && item.items.length ? 'MenuSection' : 'NavItem'"
diff --git a/app/assets/stylesheets/framework/super_sidebar.scss b/app/assets/stylesheets/framework/super_sidebar.scss
index 3973be54795..9da860c838b 100644
--- a/app/assets/stylesheets/framework/super_sidebar.scss
+++ b/app/assets/stylesheets/framework/super_sidebar.scss
@@ -128,7 +128,7 @@
@include gl-font-sm;
}
- .context-switcher-toggle {
+ .gl-new-dropdown-custom-toggle .context-switcher-toggle {
&[aria-expanded='true'] {
background-color: $t-gray-a-08;
}
diff --git a/app/controllers/admin/instance_review_controller.rb b/app/controllers/admin/instance_review_controller.rb
index cc801bce5b7..87fb1fa1a66 100644
--- a/app/controllers/admin/instance_review_controller.rb
+++ b/app/controllers/admin/instance_review_controller.rb
@@ -5,7 +5,7 @@ class Admin::InstanceReviewController < Admin::ApplicationController
urgency :low
def index
- redirect_to("#{Gitlab::SubscriptionPortal.subscriptions_instance_review_url}?#{instance_review_params}")
+ redirect_to("#{subscription_portal_instance_review_url}?#{instance_review_params}")
end
def instance_review_params
diff --git a/app/controllers/projects/repositories_controller.rb b/app/controllers/projects/repositories_controller.rb
index 80bc92c0b69..4a9282432fd 100644
--- a/app/controllers/projects/repositories_controller.rb
+++ b/app/controllers/projects/repositories_controller.rb
@@ -27,9 +27,8 @@ class Projects::RepositoriesController < Projects::ApplicationController
end
def archive
- return render_404 if html_request?
-
set_cache_headers
+
return if archive_not_modified?
send_git_archive @repository, **repo_params
diff --git a/app/helpers/environment_helper.rb b/app/helpers/environment_helper.rb
index 2b3700a9f21..d914c63b753 100644
--- a/app/helpers/environment_helper.rb
+++ b/app/helpers/environment_helper.rb
@@ -88,7 +88,8 @@ module EnvironmentHelper
environment_terminal_path: terminal_project_environment_path(project, environment),
has_terminals: environment.has_terminals?,
is_environment_available: environment.available?,
- auto_stop_at: environment.auto_stop_at
+ auto_stop_at: environment.auto_stop_at,
+ graphql_etag_key: environment.etag_cache_key
}
end
diff --git a/app/models/design_management/git_repository.rb b/app/models/design_management/git_repository.rb
index 92db82f7bd1..38c457c7991 100644
--- a/app/models/design_management/git_repository.rb
+++ b/app/models/design_management/git_repository.rb
@@ -12,28 +12,6 @@ module DesignManagement
/#{DesignManagement.designs_directory}/* filter=lfs diff=lfs merge=lfs -text
GA
- # Passing the `project` explicitly saves on one query on the `project` table
- # in Mutations::DesignManagement::Delete
-
- def initialize(project)
- @project = project
-
- full_path = @project.full_path + Gitlab::GlRepository::DESIGN.path_suffix
- disk_path = @project.disk_path + Gitlab::GlRepository::DESIGN.path_suffix
-
- # Ideally a DesignManagement::Repository, not a project would be
- # the container to this Git repository.
- # See https://gitlab.com/gitlab-org/gitlab/-/issues/394816.
-
- super(
- full_path,
- @project,
- shard: @project.repository_storage,
- disk_path: disk_path,
- repo_type: Gitlab::GlRepository::DESIGN
- )
- end
-
# Override of a method called on Repository instances but sent via
# method_missing to Gitlab::Git::Repository where it is defined
def info_attributes
diff --git a/app/models/design_management/repository.rb b/app/models/design_management/repository.rb
index c1b14a875ac..33c5dc15fa4 100644
--- a/app/models/design_management/repository.rb
+++ b/app/models/design_management/repository.rb
@@ -3,22 +3,34 @@
module DesignManagement
class Repository < ApplicationRecord
include ::Gitlab::Utils::StrongMemoize
+ include HasRepository
belongs_to :project, inverse_of: :design_management_repository
validates :project, presence: true, uniqueness: true
- # This is so that git_repo is initialized once `project` has been
- # set. If it is not set after intialization and saving the record
- # fails for some reason, the first call to `git_repo`` (initiated by
- # `delegate_missing_to`) will throw an error because project would
- # be missing.
- after_initialize :git_repo
+ delegate :lfs_enabled?, :storage, :repository_storage, to: :project
- delegate_missing_to :git_repo
+ def repository
+ ::DesignManagement::GitRepository.new(
+ full_path,
+ self,
+ shard: repository_storage,
+ disk_path: disk_path,
+ repo_type: repo_type
+ )
+ end
+ strong_memoize_attr :repository
+
+ def full_path
+ project.full_path + repo_type.path_suffix
+ end
+
+ def disk_path
+ project.disk_path + repo_type.path_suffix
+ end
- def git_repo
- project ? GitRepository.new(project) : nil
+ def repo_type
+ Gitlab::GlRepository::DESIGN
end
- strong_memoize_attr :git_repo
end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 2f976e98003..5271fbb138d 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1202,6 +1202,10 @@ class Project < ApplicationRecord
@repository ||= Gitlab::GlRepository::PROJECT.repository_for(self)
end
+ def design_management_repository
+ super || create_design_management_repository
+ end
+
def design_repository
strong_memoize(:design_repository) do
Gitlab::GlRepository::DESIGN.repository_for(self)
diff --git a/app/models/user.rb b/app/models/user.rb
index 9e542e9f451..70d52ff801d 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1647,9 +1647,19 @@ class User < ApplicationRecord
end
# rubocop: enable CodeReuse/ServiceClass
+ DELETION_DELAY_IN_DAYS = 7.days
+
def delete_async(deleted_by:, params: {})
- block if params[:hard_delete]
- DeleteUserWorker.perform_async(deleted_by.id, id, params.to_h)
+ is_deleting_own_record = deleted_by.id == id
+
+ if is_deleting_own_record && ::Feature.enabled?(:delay_delete_own_user)
+ block
+ DeleteUserWorker.perform_in(DELETION_DELAY_IN_DAYS, deleted_by.id, id, params.to_h)
+ else
+ block if params[:hard_delete]
+
+ DeleteUserWorker.perform_async(deleted_by.id, id, params.to_h)
+ end
end
# rubocop: disable CodeReuse/ServiceClass
diff --git a/app/services/ci/job_token_scope/add_project_service.rb b/app/services/ci/job_token_scope/add_project_service.rb
index 704aa28f8c5..8fb543a2796 100644
--- a/app/services/ci/job_token_scope/add_project_service.rb
+++ b/app/services/ci/job_token_scope/add_project_service.rb
@@ -29,3 +29,5 @@ module Ci
end
end
end
+
+Ci::JobTokenScope::AddProjectService.prepend_mod_with('Ci::JobTokenScope::AddProjectService')
diff --git a/app/services/ci/job_token_scope/remove_project_service.rb b/app/services/ci/job_token_scope/remove_project_service.rb
index 864f9318c68..d6a2defd5b9 100644
--- a/app/services/ci/job_token_scope/remove_project_service.rb
+++ b/app/services/ci/job_token_scope/remove_project_service.rb
@@ -31,3 +31,5 @@ module Ci
end
end
end
+
+Ci::JobTokenScope::RemoveProjectService.prepend_mod_with('Ci::JobTokenScope::RemoveProjectService')
diff --git a/app/services/merge_requests/after_create_service.rb b/app/services/merge_requests/after_create_service.rb
index f3b1c663fa2..f174778e12e 100644
--- a/app/services/merge_requests/after_create_service.rb
+++ b/app/services/merge_requests/after_create_service.rb
@@ -7,6 +7,7 @@ module MergeRequests
def execute(merge_request)
merge_request.ensure_merge_request_diff
+ execute_hooks(merge_request)
prepare_for_mergeability(merge_request)
prepare_merge_request(merge_request)
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index 0d59e442dce..ec8a17162ca 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -26,6 +26,10 @@ module MergeRequests
end
def execute_hooks(merge_request, action = 'open', old_rev: nil, old_associations: {})
+ # NOTE: Due to the async merge request diffs generation, we need to skip this for CreateService and execute it in
+ # AfterCreateService instead so that the webhook consumers receive the update when diffs are ready.
+ return if merge_request.skip_ensure_merge_request_diff
+
merge_data = Gitlab::Lazy.new { hook_data(merge_request, action, old_rev: old_rev, old_associations: old_associations) }
merge_request.project.execute_hooks(merge_data, :merge_request_hooks)
merge_request.project.execute_integrations(merge_data, :merge_request_hooks)
diff --git a/app/views/admin/application_settings/appearances/show.html.haml b/app/views/admin/application_settings/appearances/show.html.haml
index cd255d961f4..82fdd13672a 100644
--- a/app/views/admin/application_settings/appearances/show.html.haml
+++ b/app/views/admin/application_settings/appearances/show.html.haml
@@ -1,4 +1,5 @@
- page_title _("Appearance")
- add_page_specific_style 'page_bundles/settings'
+- @force_desktop_expanded_sidebar = true
= render 'form'
diff --git a/app/views/admin/application_settings/ci_cd.html.haml b/app/views/admin/application_settings/ci_cd.html.haml
index c2dc3c3707e..a9a16f72ebe 100644
--- a/app/views/admin/application_settings/ci_cd.html.haml
+++ b/app/views/admin/application_settings/ci_cd.html.haml
@@ -1,6 +1,7 @@
- breadcrumb_title _("CI/CD")
- page_title _("CI/CD")
- add_page_specific_style 'page_bundles/settings'
+- @force_desktop_expanded_sidebar = true
%section.settings.no-animate#js-ci-cd-variables{ class: ('expanded' if expanded_by_default?) }
.settings-header
diff --git a/app/views/admin/application_settings/general.html.haml b/app/views/admin/application_settings/general.html.haml
index a7c80abdbc9..e6c27c1bc84 100644
--- a/app/views/admin/application_settings/general.html.haml
+++ b/app/views/admin/application_settings/general.html.haml
@@ -1,6 +1,7 @@
- breadcrumb_title _("General")
- page_title _("General")
- add_page_specific_style 'page_bundles/settings'
+- @force_desktop_expanded_sidebar = true
%section.settings.as-visibility-access.no-animate#js-visibility-settings{ class: ('expanded' if expanded_by_default?), data: { testid: 'admin-visibility-access-settings' } }
.settings-header
diff --git a/app/views/admin/application_settings/integrations.html.haml b/app/views/admin/application_settings/integrations.html.haml
index 396e6f3e7d6..68c62eb98ee 100644
--- a/app/views/admin/application_settings/integrations.html.haml
+++ b/app/views/admin/application_settings/integrations.html.haml
@@ -1,6 +1,7 @@
- breadcrumb_title s_('Integrations|Instance-level integration management')
- page_title s_('Integrations|Instance-level integration management')
- add_page_specific_style 'page_bundles/settings'
+- @force_desktop_expanded_sidebar = true
%h3= s_('Integrations|Instance-level integration management')
diff --git a/app/views/admin/application_settings/metrics_and_profiling.html.haml b/app/views/admin/application_settings/metrics_and_profiling.html.haml
index d0fb275e53a..8bc5d5cbaa6 100644
--- a/app/views/admin/application_settings/metrics_and_profiling.html.haml
+++ b/app/views/admin/application_settings/metrics_and_profiling.html.haml
@@ -3,6 +3,7 @@
- breadcrumb_title _("Metrics and profiling")
- page_title _("Metrics and profiling")
- add_page_specific_style 'page_bundles/settings'
+- @force_desktop_expanded_sidebar = true
%section.settings.as-prometheus.no-animate#js-prometheus-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
diff --git a/app/views/admin/application_settings/network.html.haml b/app/views/admin/application_settings/network.html.haml
index 32b10fd36e8..1809496bb9f 100644
--- a/app/views/admin/application_settings/network.html.haml
+++ b/app/views/admin/application_settings/network.html.haml
@@ -1,6 +1,7 @@
- breadcrumb_title _("Network")
- page_title _("Network")
- add_page_specific_style 'page_bundles/settings'
+- @force_desktop_expanded_sidebar = true
%section.settings.as-performance.no-animate#js-performance-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
diff --git a/app/views/admin/application_settings/preferences.html.haml b/app/views/admin/application_settings/preferences.html.haml
index 3843fc8e863..ab59e05c10f 100644
--- a/app/views/admin/application_settings/preferences.html.haml
+++ b/app/views/admin/application_settings/preferences.html.haml
@@ -1,6 +1,7 @@
- breadcrumb_title _("Preferences")
- page_title _("Preferences")
- add_page_specific_style 'page_bundles/settings'
+- @force_desktop_expanded_sidebar = true
%section.settings.as-email.no-animate#js-email-settings{ class: ('expanded' if expanded_by_default?), data: { qa_selector: 'email_content' } }
.settings-header
diff --git a/app/views/admin/application_settings/reporting.html.haml b/app/views/admin/application_settings/reporting.html.haml
index 0046275c7d1..6ea2fb80505 100644
--- a/app/views/admin/application_settings/reporting.html.haml
+++ b/app/views/admin/application_settings/reporting.html.haml
@@ -1,6 +1,7 @@
- breadcrumb_title _("Reporting")
- page_title _("Reporting")
- add_page_specific_style 'page_bundles/settings'
+- @force_desktop_expanded_sidebar = true
%section.settings.as-spam.no-animate#js-spam-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
diff --git a/app/views/admin/application_settings/repository.html.haml b/app/views/admin/application_settings/repository.html.haml
index 518b40a0326..1907544ea14 100644
--- a/app/views/admin/application_settings/repository.html.haml
+++ b/app/views/admin/application_settings/repository.html.haml
@@ -1,6 +1,7 @@
- breadcrumb_title _("Repository")
- page_title _("Repository")
- add_page_specific_style 'page_bundles/settings'
+- @force_desktop_expanded_sidebar = true
%section.settings.as-default-branch-name.no-animate#js-default-branch-name{ class: ('expanded' if expanded_by_default?) }
.settings-header
diff --git a/app/views/admin/application_settings/service_usage_data.html.haml b/app/views/admin/application_settings/service_usage_data.html.haml
index af646d79c29..ead2b2fd666 100644
--- a/app/views/admin/application_settings/service_usage_data.html.haml
+++ b/app/views/admin/application_settings/service_usage_data.html.haml
@@ -4,6 +4,7 @@
- page_title name
- add_page_specific_style 'page_bundles/settings'
- payload_class = 'js-service-ping-payload'
+- @force_desktop_expanded_sidebar = true
%section.js-search-settings-section
%h3= name
diff --git a/app/workers/delete_user_worker.rb b/app/workers/delete_user_worker.rb
index bca156ff84c..6a375a0cdd4 100644
--- a/app/workers/delete_user_worker.rb
+++ b/app/workers/delete_user_worker.rb
@@ -11,8 +11,13 @@ class DeleteUserWorker # rubocop:disable Scalability/IdempotentWorker
loggable_arguments 2
def perform(current_user_id, delete_user_id, options = {})
- delete_user = User.find(delete_user_id)
- current_user = User.find(current_user_id)
+ delete_user = User.find_by_id(delete_user_id)
+ return unless delete_user.present?
+
+ return if delete_user.banned? && ::Feature.enabled?(:delay_delete_own_user)
+
+ current_user = User.find_by_id(current_user_id)
+ return unless current_user.present?
Users::DestroyService.new(current_user).execute(delete_user, options.symbolize_keys)
rescue Gitlab::Access::AccessDeniedError => e
diff --git a/config/feature_flags/development/delay_delete_own_user.yml b/config/feature_flags/development/delay_delete_own_user.yml
new file mode 100644
index 00000000000..030ccd29c00
--- /dev/null
+++ b/config/feature_flags/development/delay_delete_own_user.yml
@@ -0,0 +1,8 @@
+---
+name: delay_delete_own_user
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118887
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/409025
+milestone: '16.0'
+type: development
+group: group::anti-abuse
+default_enabled: false
diff --git a/config/routes/directs.rb b/config/routes/directs.rb
index f28750b5c2e..407e2a9d8a5 100644
--- a/config/routes/directs.rb
+++ b/config/routes/directs.rb
@@ -3,3 +3,4 @@
# Custom URL definitions for the Community Edition.
draw 'directs/milestone'
+draw 'directs/subscription_portal'
diff --git a/config/routes/directs/subscription_portal.rb b/config/routes/directs/subscription_portal.rb
new file mode 100644
index 00000000000..188725d16c1
--- /dev/null
+++ b/config/routes/directs/subscription_portal.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+direct :subscription_portal_staging do
+ ENV.fetch('STAGING_CUSTOMER_PORTAL_URL', 'https://customers.staging.gitlab.com')
+end
+
+direct :subscription_portal do
+ default_subscriptions_url = if ::Gitlab.dev_or_test_env?
+ subscription_portal_staging_url
+ else
+ 'https://customers.gitlab.com'
+ end
+
+ ENV.fetch('CUSTOMER_PORTAL_URL', default_subscriptions_url)
+end
+
+direct :subscription_portal_instance_review do
+ Addressable::URI.join(subscription_portal_url, '/instance_review').to_s
+end
diff --git a/config/routes/project.rb b/config/routes/project.rb
index ef5f95eee75..93505d84aa6 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -28,7 +28,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
# Begin of the /-/ scope.
# Use this scope for all new project routes.
scope '-' do
- get 'archive/*id', constraints: { format: Gitlab::PathRegex.archive_formats_regex, id: /.+?/ }, to: 'repositories#archive', as: 'archive'
+ get 'archive/*id', format: true, constraints: { format: Gitlab::PathRegex.archive_formats_regex, id: /.+?/ }, to: 'repositories#archive', as: 'archive'
get 'metrics(/:dashboard_path)', constraints: { dashboard_path: /.+\.yml/ },
to: 'metrics_dashboard#show', as: :metrics_dashboard, format: false
get 'metrics(/:dashboard_path)/panel/new', constraints: { dashboard_path: /.+\.yml/ },
diff --git a/data/removals/16_0/16-0-pull-thru-cache-container-registry.yml b/data/removals/16_0/16-0-pull-thru-cache-container-registry.yml
new file mode 100644
index 00000000000..88f8c31eeef
--- /dev/null
+++ b/data/removals/16_0/16-0-pull-thru-cache-container-registry.yml
@@ -0,0 +1,11 @@
+# REQUIRED FIELDS
+#
+- title: "Container Registry pull-through cache is removed" # (required) Clearly explain the change. For example, "The `confidential` field for a `Note` is removed" or "CI/CD job names are limited to 250 characters."
+ announcement_milestone: "15.8" # (required) The milestone when this feature was deprecated.
+ removal_milestone: "16.0" # (required) The milestone when this feature is being removed.
+ breaking_change: true # (required) Change to false if this is not a breaking change.
+ reporter: trizzi # (required) GitLab username of the person reporting the removal
+ stage: Package # (required) String value of the stage that the feature was created in. e.g., Growth
+ issue_url: https://gitlab.com/gitlab-org/container-registry/-/issues/937 # (required) Link to the deprecation issue in GitLab
+ body: | # (required) Do not modify this line, instead modify the lines below.
+ The Container Registry [pull-through cache](https://docs.docker.com/registry/recipes/mirror/) was deprecated in GitLab 15.8 and removed in GitLab 16.0. This feature is part of the upstream [Docker Distribution project](https://github.com/distribution/distribution) but we are removing that code in favor of the GitLab Dependency Proxy. Use the GitLab Dependency Proxy to proxy and cache container images from Docker Hub.
diff --git a/doc/development/documentation/styleguide/word_list.md b/doc/development/documentation/styleguide/word_list.md
index eb0576d0c05..5f00aa355be 100644
--- a/doc/development/documentation/styleguide/word_list.md
+++ b/doc/development/documentation/styleguide/word_list.md
@@ -57,6 +57,7 @@ Instead of:
- In GitLab 14.4 and above...
- In GitLab 14.4 and higher...
+- In GitLab 14.4 and newer...
## access level
@@ -423,6 +424,7 @@ Use:
Instead of:
- In GitLab 14.1 and lower.
+- In GitLab 14.1 and older.
## easily
@@ -766,6 +768,7 @@ Instead of:
- In GitLab 14.1 and higher...
- In GitLab 14.1 and above...
+- In GitLab 14.1 and newer...
## list
@@ -814,6 +817,7 @@ Use:
Instead of:
- In GitLab 14.1 and lower.
+- In GitLab 14.1 and older.
## Maintainer
@@ -919,6 +923,20 @@ When the variable is **optional**:
- You can set the variable.
+## newer
+
+Do not use **newer** when talking about version numbers.
+
+Use:
+
+- In GitLab 14.4 and later...
+
+Instead of:
+
+- In GitLab 14.4 and higher...
+- In GitLab 14.4 and above...
+- In GitLab 14.4 and newer...
+
## normal, normally
Don't use **normal** to mean the usual, typical, or standard way of doing something.
@@ -949,6 +967,19 @@ Instead of:
- Note that you can change the settings.
+## older
+
+Do not use **older** when talking about version numbers.
+
+Use:
+
+- In GitLab 14.1 and earlier.
+
+Instead of:
+
+- In GitLab 14.1 and lower.
+- In GitLab 14.1 and older.
+
## Omnibus GitLab
When referring to the installation method that uses the Linux package, refer to it
diff --git a/doc/policy/alpha-beta-support.md b/doc/policy/alpha-beta-support.md
index 2eb07e1ddaf..e142fe9e908 100644
--- a/doc/policy/alpha-beta-support.md
+++ b/doc/policy/alpha-beta-support.md
@@ -20,8 +20,8 @@ Support is not provided for features listed as "Experimental" or "Alpha" or any
- Can be removed at any time.
- Data loss may occur.
- Documentation may not exist or just be in a blog format.
-- Behind a feature flag that is on by default and the UI reflects Experiment status.
-- Behind a toggle that is off by default and the UI reflects Experiment status.
+- Behind a feature flag that is on by default and the [UI reflects Experiment status](https://design.gitlab.com/usability/feature-management#highlighting-feature-versions).
+- Behind a toggle that is off by default and the [UI reflects Experiment status](https://design.gitlab.com/usability/feature-management#highlighting-feature-versions).
- Feedback issue to engage with team.
- UX not finalized, might be just quick action access.
- Not announced in a release post.
@@ -38,8 +38,8 @@ Commercially-reasonable efforts are made to provide limited support for features
- Support on a commercially-reasonable effort basis.
- Documentation reflects Beta status.
- UX complete or near completion.
-- Behind a feature flag that is on by default and the UI reflects Beta status.
-- Behind a toggle that is off by default and the UI reflects Beta status.
+- Behind a feature flag that is on by default and the [UI reflects Beta status](https://design.gitlab.com/usability/feature-management#highlighting-feature-versions).
+- Behind a toggle that is off by default and the [UI reflects Beta status](https://design.gitlab.com/usability/feature-management#highlighting-feature-versions).
- Can be announced in a release post that reflects Beta status.
## Generally Available (GA)
@@ -58,3 +58,8 @@ We will get higher quality (more diverse) feedback if people from different orga
We've also learned that internal only as a state slows us down more than it speeds us up.
Release the experiment instead of testing internally or waiting for the feature to be in a Beta state.
The experimental features are only shown when people/organizations opt-in to experiments, we are allowed to make mistakes here and literally experiment.
+
+## All features are in production
+
+All features that are available on GitLab.com are considered "in production."
+Because all Experiment, Beta, and Generally Available features are available on GitLab.com, they are all considered to be in production.
diff --git a/doc/update/removals.md b/doc/update/removals.md
index b92c90379cc..83a17c2e116 100644
--- a/doc/update/removals.md
+++ b/doc/update/removals.md
@@ -59,6 +59,14 @@ The Azure Storage Driver used to write to `//` as the default root directory. Th
In GitLab 16.0, the new default configuration for the storage driver uses `trimlegacyrootprefix: true`, and `/` is the default root directory. You can set your configuration to `trimlegacyrootprefix: false` if needed, to revert to the previous behavior.
+### Container Registry pull-through cache is removed
+
+WARNING:
+This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
+Review the details carefully before upgrading.
+
+The Container Registry [pull-through cache](https://docs.docker.com/registry/recipes/mirror/) was deprecated in GitLab 15.8 and removed in GitLab 16.0. This feature is part of the upstream [Docker Distribution project](https://github.com/distribution/distribution) but we are removing that code in favor of the GitLab Dependency Proxy. Use the GitLab Dependency Proxy to proxy and cache container images from Docker Hub.
+
### Project REST API field `operations_access_level` removed
WARNING:
diff --git a/lib/feature/gitaly.rb b/lib/feature/gitaly.rb
index fd798862fa8..eadf98e090e 100644
--- a/lib/feature/gitaly.rb
+++ b/lib/feature/gitaly.rb
@@ -53,11 +53,19 @@ module Feature
end
def project_actor(container)
- ::Feature::Gitaly::ActorWrapper.new(::Project, container.id) if container.is_a?(::Project)
+ return actor_wrapper(::Project, container.id) if container.is_a?(::Project)
+ return actor_wrapper(::Project, container.project.id) if container.is_a?(DesignManagement::Repository)
end
def group_actor(container)
- ::Feature::Gitaly::ActorWrapper.new(::Group, container.namespace_id) if container.is_a?(::Project)
+ return actor_wrapper(::Group, container.namespace_id) if container.is_a?(::Project)
+ return actor_wrapper(::Group, container.project.namespace_id) if container.is_a?(DesignManagement::Repository)
+ end
+
+ private
+
+ def actor_wrapper(actor_type, id)
+ ::Feature::Gitaly::ActorWrapper.new(actor_type, id)
end
end
end
diff --git a/lib/gitlab/ci/reports/codequality_mr_diff.rb b/lib/gitlab/ci/reports/codequality_mr_diff.rb
index bfa7b05e4a9..0595b6f966a 100644
--- a/lib/gitlab/ci/reports/codequality_mr_diff.rb
+++ b/lib/gitlab/ci/reports/codequality_mr_diff.rb
@@ -30,20 +30,9 @@ module Gitlab
codequality_files[degradation.dig(:location, :path)] << {
line: degradation.dig(:location, :lines, :begin) || degradation.dig(:location, :positions, :begin, :line),
description: degradation[:description],
- severity: degradation[:severity],
- engine_name: degradation[:engine_name],
- categories: degradation[:categories],
- content: convert_body(degradation[:content]),
- location: degradation[:location],
- other_locations: degradation[:other_locations],
- type: degradation[:type]
+ severity: degradation[:severity]
}
end
-
- def convert_body(content)
- content["body"] = ::MarkupHelper.markdown(content["body"])
- content
- end
end
end
end
diff --git a/lib/gitlab/ci/templates/Jobs/Secret-Detection.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Secret-Detection.latest.gitlab-ci.yml
index f343dfaa28f..56a8ad794dc 100644
--- a/lib/gitlab/ci/templates/Jobs/Secret-Detection.latest.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Secret-Detection.latest.gitlab-ci.yml
@@ -8,7 +8,7 @@ variables:
SECURE_ANALYZERS_PREFIX: "$CI_TEMPLATE_REGISTRY_HOST/security-products"
SECRET_DETECTION_IMAGE_SUFFIX: ""
- SECRETS_ANALYZER_VERSION: "4"
+ SECRETS_ANALYZER_VERSION: "5"
SECRET_DETECTION_EXCLUDED_PATHS: ""
.secret-analyzer:
diff --git a/lib/gitlab/etag_caching/router/graphql.rb b/lib/gitlab/etag_caching/router/graphql.rb
index b53164ac94c..92b21a0859d 100644
--- a/lib/gitlab/etag_caching/router/graphql.rb
+++ b/lib/gitlab/etag_caching/router/graphql.rb
@@ -22,6 +22,11 @@ module Gitlab
%r(\Aon_demand_scan/counts/),
'on_demand_scans',
'dynamic_application_security_testing'
+ ],
+ [
+ %r(\A/projects/.+/-/environments.json\z),
+ 'environment_details',
+ 'continuous_delivery'
]
].map { |attrs| build_graphql_route(*attrs) }.freeze
diff --git a/lib/gitlab/git_access_design.rb b/lib/gitlab/git_access_design.rb
index bf89c01305a..fb8df0d217a 100644
--- a/lib/gitlab/git_access_design.rb
+++ b/lib/gitlab/git_access_design.rb
@@ -4,6 +4,23 @@ module Gitlab
class GitAccessDesign < GitAccess
extend ::Gitlab::Utils::Override
+ # TODO Re-factor so that correct container is passed to the constructor
+ # and this method can be removed from here
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/409454
+ def initialize(
+ actor, container, protocol, authentication_abilities:, repository_path: nil, redirected_path: nil,
+ auth_result_type: nil)
+ super(
+ actor,
+ select_container(container),
+ protocol,
+ authentication_abilities: authentication_abilities,
+ repository_path: repository_path,
+ redirected_path: redirected_path,
+ auth_result_type: auth_result_type
+ )
+ end
+
def check(_cmd, _changes)
check_protocol!
check_can_create_design!
@@ -18,6 +35,10 @@ module Gitlab
private
+ def select_container(container)
+ container.is_a?(::DesignManagement::Repository) ? container.project : container
+ end
+
def check_protocol!
if protocol != 'web'
raise ::Gitlab::GitAccess::ForbiddenError, "Designs are only accessible using the web interface"
diff --git a/lib/gitlab/gl_repository.rb b/lib/gitlab/gl_repository.rb
index efdb205b8eb..268d5d3e564 100644
--- a/lib/gitlab/gl_repository.rb
+++ b/lib/gitlab/gl_repository.rb
@@ -34,8 +34,9 @@ module Gitlab
DESIGN = ::Gitlab::GlRepository::RepoType.new(
name: :design,
access_checker_class: ::Gitlab::GitAccessDesign,
- repository_resolver: -> (project) { ::DesignManagement::Repository.new(project: project) },
- suffix: :design
+ repository_resolver: -> (project) { project.design_management_repository.repository },
+ suffix: :design,
+ container_class: DesignManagement::Repository
).freeze
TYPES = {
diff --git a/lib/gitlab/gl_repository/repo_type.rb b/lib/gitlab/gl_repository/repo_type.rb
index 7792ef55b28..26b0ff86f67 100644
--- a/lib/gitlab/gl_repository/repo_type.rb
+++ b/lib/gitlab/gl_repository/repo_type.rb
@@ -55,11 +55,11 @@ module Gitlab
def repository_for(container)
return unless container
- repository_resolver.call(container)
+ repository_resolver.call(select_container(container))
end
def project_for(container)
- return container unless project_resolver
+ return select_container(container) unless project_resolver
project_resolver.call(container)
end
@@ -74,6 +74,10 @@ module Gitlab
private
+ def select_container(container)
+ container.is_a?(::DesignManagement::Repository) ? container.project : container
+ end
+
def default_container_class
Project
end
diff --git a/lib/gitlab/patch/draw_route.rb b/lib/gitlab/patch/draw_route.rb
index 61b25065e8f..67da6c9c943 100644
--- a/lib/gitlab/patch/draw_route.rb
+++ b/lib/gitlab/patch/draw_route.rb
@@ -27,7 +27,7 @@ module Gitlab
def draw_route(path)
if File.exist?(path)
- instance_eval(File.read(path))
+ instance_eval(File.read(path), path.to_s)
true
else
false
diff --git a/lib/gitlab/subscription_portal.rb b/lib/gitlab/subscription_portal.rb
index 723b8265479..1d9ecb624b2 100644
--- a/lib/gitlab/subscription_portal.rb
+++ b/lib/gitlab/subscription_portal.rb
@@ -2,22 +2,6 @@
module Gitlab
module SubscriptionPortal
- def self.default_subscriptions_url
- if ::Gitlab.dev_or_test_env?
- 'https://customers.staging.gitlab.com'
- else
- 'https://customers.gitlab.com'
- end
- end
-
- def self.subscriptions_url
- ENV.fetch('CUSTOMER_PORTAL_URL', default_subscriptions_url)
- end
-
- def self.payment_form_url
- "#{self.subscriptions_url}/payment_forms/cc_validation"
- end
-
def self.payment_validation_form_id
"payment_method_validation"
end
@@ -26,58 +10,6 @@ module Gitlab
"cc_registration_validation"
end
- def self.registration_validation_form_url
- "#{self.subscriptions_url}/payment_forms/cc_registration_validation"
- end
-
- def self.subscriptions_comparison_url
- 'https://about.gitlab.com/pricing/gitlab-com/feature-comparison'
- end
-
- def self.subscriptions_graphql_url
- "#{self.subscriptions_url}/graphql"
- end
-
- def self.subscriptions_more_minutes_url
- "#{self.subscriptions_url}/buy_pipeline_minutes"
- end
-
- def self.subscriptions_more_storage_url
- "#{self.subscriptions_url}/buy_storage"
- end
-
- def self.subscriptions_manage_url
- "#{self.subscriptions_url}/subscriptions"
- end
-
- def self.subscriptions_gitlab_plans_url
- "#{self.subscriptions_url}/gitlab_plans"
- end
-
- def self.subscriptions_instance_review_url
- "#{self.subscriptions_url}/instance_review"
- end
-
- def self.add_extra_seats_url(group_id)
- "#{self.subscriptions_url}/gitlab/namespaces/#{group_id}/extra_seats"
- end
-
- def self.upgrade_subscription_url(group_id, plan_id)
- "#{self.subscriptions_url}/gitlab/namespaces/#{group_id}/upgrade/#{plan_id}"
- end
-
- def self.renew_subscription_url(group_id)
- "#{self.subscriptions_url}/gitlab/namespaces/#{group_id}/renew"
- end
-
- def self.subscriptions_legacy_sign_in_url
- "#{self.subscriptions_url}/customers/sign_in?legacy=true"
- end
-
- def self.edit_account_url
- "#{self.subscriptions_url}/customers/edit"
- end
-
def self.subscription_portal_admin_email
ENV.fetch('SUBSCRIPTION_PORTAL_ADMIN_EMAIL', 'gl_com_api@gitlab.com')
end
@@ -93,13 +25,8 @@ module Gitlab
end
Gitlab::SubscriptionPortal.prepend_mod
-Gitlab::SubscriptionPortal::SUBSCRIPTIONS_URL = Gitlab::SubscriptionPortal.subscriptions_url.freeze
-Gitlab::SubscriptionPortal::SUBSCRIPTIONS_LEGACY_SIGN_IN_URL = Gitlab::SubscriptionPortal.subscriptions_legacy_sign_in_url.freeze
-Gitlab::SubscriptionPortal::PAYMENT_FORM_URL = Gitlab::SubscriptionPortal.payment_form_url.freeze
Gitlab::SubscriptionPortal::PAYMENT_VALIDATION_FORM_ID = Gitlab::SubscriptionPortal.payment_validation_form_id.freeze
Gitlab::SubscriptionPortal::RENEWAL_SERVICE_EMAIL = Gitlab::SubscriptionPortal.renewal_service_email.freeze
-Gitlab::SubscriptionPortal::REGISTRATION_VALIDATION_FORM_URL = Gitlab::SubscriptionPortal.registration_validation_form_url.freeze
Gitlab::SubscriptionPortal::REGISTRATION_VALIDATION_FORM_ID = Gitlab::SubscriptionPortal.registration_validation_form_id.freeze
Gitlab::SubscriptionPortal::SUBSCRIPTION_PORTAL_ADMIN_EMAIL = Gitlab::SubscriptionPortal.subscription_portal_admin_email.freeze
Gitlab::SubscriptionPortal::SUBSCRIPTION_PORTAL_ADMIN_TOKEN = Gitlab::SubscriptionPortal.subscription_portal_admin_token.freeze
-Gitlab::SubscriptionPortal::SUBSCRIPTIONS_MANAGE_URL = ::Gitlab::SubscriptionPortal.subscriptions_manage_url.freeze
diff --git a/package.json b/package.json
index c3bb53b8f28..9ffe1afd370 100644
--- a/package.json
+++ b/package.json
@@ -278,7 +278,7 @@
"timezone-mock": "^1.0.8",
"vue-loader-vue3": "npm:vue-loader@17",
"vue-test-utils-compat": "^0.0.11",
- "webpack-dev-server": "4.13.2",
+ "webpack-dev-server": "4.13.3",
"xhr-mock": "^2.5.1",
"yarn-check-webpack-plugin": "^1.2.0",
"yarn-deduplicate": "^6.0.0"
diff --git a/spec/controllers/admin/instance_review_controller_spec.rb b/spec/controllers/admin/instance_review_controller_spec.rb
index 6eab135b3a6..f0225a71e00 100644
--- a/spec/controllers/admin/instance_review_controller_spec.rb
+++ b/spec/controllers/admin/instance_review_controller_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe Admin::InstanceReviewController, feature_category: :service_ping
include UsageDataHelpers
let(:admin) { create(:admin) }
- let(:subscriptions_instance_review_url) { Gitlab::SubscriptionPortal.subscriptions_instance_review_url }
+ let(:subscriptions_instance_review_url) { ::Gitlab::Routing.url_helpers.subscription_portal_instance_review_url }
before do
sign_in(admin)
diff --git a/spec/controllers/projects/repositories_controller_spec.rb b/spec/controllers/projects/repositories_controller_spec.rb
index 8186176a46b..0efed45336f 100644
--- a/spec/controllers/projects/repositories_controller_spec.rb
+++ b/spec/controllers/projects/repositories_controller_spec.rb
@@ -106,19 +106,11 @@ RSpec.describe Projects::RepositoriesController, feature_category: :source_code_
end
end
- context "when the request format is HTML" do
- it "renders 404" do
- get :archive, params: { namespace_id: project.namespace, project_id: project, id: 'master' }, format: "html"
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
describe 'rate limiting' do
it 'rate limits user when thresholds hit' do
allow(Gitlab::ApplicationRateLimiter).to receive(:throttled?).and_return(true)
- get :archive, params: { namespace_id: project.namespace, project_id: project, id: 'master' }, format: "html"
+ get :archive, params: { namespace_id: project.namespace, project_id: project, id: 'master' }, format: "zip"
expect(response).to have_gitlab_http_status(:too_many_requests)
end
diff --git a/spec/factories/design_management/repositories.rb b/spec/factories/design_management/repositories.rb
new file mode 100644
index 00000000000..d903fd88c13
--- /dev/null
+++ b/spec/factories/design_management/repositories.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :design_management_repository, class: 'DesignManagement::Repository' do
+ project
+ end
+end
diff --git a/spec/features/nav/pinned_nav_items_spec.rb b/spec/features/nav/pinned_nav_items_spec.rb
index 2047eaa9659..ea0404e7cf6 100644
--- a/spec/features/nav/pinned_nav_items_spec.rb
+++ b/spec/features/nav/pinned_nav_items_spec.rb
@@ -94,7 +94,7 @@ RSpec.describe 'Navigation menu item pinning', :js, feature_category: :navigatio
it 'can be unpinned from within its section' do
section = find("button", text: 'Operate')
- within(section.sibling('div')) do
+ within(section.sibling('ul')) do
remove_pin('Terraform modules')
end
diff --git a/spec/fixtures/pipeline_artifacts/code_quality_mr_diff.json b/spec/fixtures/pipeline_artifacts/code_quality_mr_diff.json
index 97026cf1180..5489330fc1d 100644
--- a/spec/fixtures/pipeline_artifacts/code_quality_mr_diff.json
+++ b/spec/fixtures/pipeline_artifacts/code_quality_mr_diff.json
@@ -3,78 +3,21 @@
"files": {
"file_a.rb": [
{
- "categories": [
- "Complexity"
- ],
"line": 10,
"description": "Avoid parameter lists longer than 5 parameters. [12/5]",
- "severity": "major",
- "location": {
- "path": "file_a.rb",
- "lines": {
- "begin": 10,
- "end": 10
- }
- },
- "other_locations": [
-
- ],
- "content": {
- "body": ""
- },
- "type": "issue",
- "engine_name": "structure"
+ "severity": "major"
},
{
- "categories": [
- "Complexity"
- ],
"line": 10,
"description": "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.",
- "severity": "minor",
- "location": {
- "path": "file_a.rb",
- "lines": {
- "begin": 10,
- "end": 10
- }
- },
- "other_locations": [
-
- ],
- "content": {
- "body": ""
- },
- "type": "issue",
- "engine_name": "structure"
+ "severity": "minor"
}
],
"file_b.rb": [
{
- "categories": [
- "Complexity"
- ],
"line": 10,
"description": "This cop checks for methods with too many parameters.\nThe maximum number of parameters is configurable.\nKeyword arguments can optionally be excluded from the total count.",
- "severity": "minor",
- "location": {
- "path": "file_b.rb",
- "positions": {
- "begin": {
- "column": 14,
- "line": 10
- },
- "end": {
- "column": 39,
- "line": 10
- }
- }
- },
- "content": {
- "body": ""
- },
- "type": "Issue",
- "engine_name": "rubocop"
+ "severity": "minor"
}
]
}
diff --git a/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js b/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js
index cc251104811..7be68df61de 100644
--- a/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js
+++ b/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js
@@ -11,7 +11,7 @@ import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeli
import { COMMIT_BOX_POLL_INTERVAL } from '~/projects/commit_box/info/constants';
import getLinkedPipelinesQuery from '~/projects/commit_box/info/graphql/queries/get_linked_pipelines.query.graphql';
import getPipelineStagesQuery from '~/projects/commit_box/info/graphql/queries/get_pipeline_stages.query.graphql';
-import * as graphQlUtils from '~/pipelines/components/graph/utils';
+import * as sharedGraphQlUtils from '~/graphql_shared/utils';
import {
mockDownstreamQueryResponse,
mockPipelineStagesQueryResponse,
@@ -241,16 +241,16 @@ describe('Commit box pipeline mini graph', () => {
});
it('toggles query polling with visibility check', async () => {
- jest.spyOn(graphQlUtils, 'toggleQueryPollingByVisibility');
+ jest.spyOn(sharedGraphQlUtils, 'toggleQueryPollingByVisibility');
createComponent();
await waitForPromises();
- expect(graphQlUtils.toggleQueryPollingByVisibility).toHaveBeenCalledWith(
+ expect(sharedGraphQlUtils.toggleQueryPollingByVisibility).toHaveBeenCalledWith(
wrapper.vm.$apollo.queries.pipelineStages,
);
- expect(graphQlUtils.toggleQueryPollingByVisibility).toHaveBeenCalledWith(
+ expect(sharedGraphQlUtils.toggleQueryPollingByVisibility).toHaveBeenCalledWith(
wrapper.vm.$apollo.queries.pipeline,
);
});
diff --git a/spec/frontend/commit/components/commit_box_pipeline_status_spec.js b/spec/frontend/commit/components/commit_box_pipeline_status_spec.js
index 5df35cc6dda..80b75a0a65e 100644
--- a/spec/frontend/commit/components/commit_box_pipeline_status_spec.js
+++ b/spec/frontend/commit/components/commit_box_pipeline_status_spec.js
@@ -12,7 +12,7 @@ import {
PIPELINE_STATUS_FETCH_ERROR,
} from '~/projects/commit_box/info/constants';
import getLatestPipelineStatusQuery from '~/projects/commit_box/info/graphql/queries/get_latest_pipeline_status.query.graphql';
-import * as graphQlUtils from '~/pipelines/components/graph/utils';
+import * as sharedGraphQlUtils from '~/graphql_shared/utils';
import { mockPipelineStatusResponse } from '../mock_data';
const mockProvide = {
@@ -132,13 +132,13 @@ describe('Commit box pipeline status', () => {
});
it('toggles pipelineStatus polling with visibility check', async () => {
- jest.spyOn(graphQlUtils, 'toggleQueryPollingByVisibility');
+ jest.spyOn(sharedGraphQlUtils, 'toggleQueryPollingByVisibility');
createComponent();
await waitForPromises();
- expect(graphQlUtils.toggleQueryPollingByVisibility).toHaveBeenCalledWith(
+ expect(sharedGraphQlUtils.toggleQueryPollingByVisibility).toHaveBeenCalledWith(
wrapper.vm.$apollo.queries.pipelineStatus,
);
});
diff --git a/spec/frontend/environments/confirm_rollback_modal_spec.js b/spec/frontend/environments/confirm_rollback_modal_spec.js
index 2163814528a..d6601447cff 100644
--- a/spec/frontend/environments/confirm_rollback_modal_spec.js
+++ b/spec/frontend/environments/confirm_rollback_modal_spec.js
@@ -5,6 +5,7 @@ import VueApollo from 'vue-apollo';
import { trimText } from 'helpers/text_helper';
import ConfirmRollbackModal from '~/environments/components/confirm_rollback_modal.vue';
import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import eventHub from '~/environments/event_hub';
describe('Confirm Rollback Modal Component', () => {
@@ -53,6 +54,8 @@ describe('Confirm Rollback Modal Component', () => {
});
};
+ const findModal = () => component.findComponent(GlModal);
+
describe.each`
hasMultipleCommits | environmentData | retryUrl | primaryPropsAttrs
${true} | ${envWithLastDeployment} | ${null} | ${[{ variant: 'danger' }]}
@@ -73,7 +76,7 @@ describe('Confirm Rollback Modal Component', () => {
hasMultipleCommits,
retryUrl,
});
- const modal = component.findComponent(GlModal);
+ const modal = findModal();
expect(modal.attributes('title')).toContain('Rollback');
expect(modal.attributes('title')).toContain('test');
@@ -92,7 +95,7 @@ describe('Confirm Rollback Modal Component', () => {
hasMultipleCommits,
});
- const modal = component.findComponent(GlModal);
+ const modal = findModal();
expect(modal.attributes('title')).toContain('Re-deploy');
expect(modal.attributes('title')).toContain('test');
@@ -110,7 +113,7 @@ describe('Confirm Rollback Modal Component', () => {
});
const eventHubSpy = jest.spyOn(eventHub, '$emit');
- const modal = component.findComponent(GlModal);
+ const modal = findModal();
modal.vm.$emit('ok');
expect(eventHubSpy).toHaveBeenCalledWith('rollbackEnvironment', env);
@@ -155,7 +158,7 @@ describe('Confirm Rollback Modal Component', () => {
},
{ apolloProvider },
);
- const modal = component.findComponent(GlModal);
+ const modal = findModal();
expect(trimText(modal.text())).toContain('commit abc0123');
expect(modal.text()).toContain('Are you sure you want to continue?');
@@ -177,7 +180,7 @@ describe('Confirm Rollback Modal Component', () => {
},
{ apolloProvider },
);
- const modal = component.findComponent(GlModal);
+ const modal = findModal();
expect(modal.attributes('title')).toContain('Rollback');
expect(modal.attributes('title')).toContain('test');
@@ -201,7 +204,7 @@ describe('Confirm Rollback Modal Component', () => {
{ apolloProvider },
);
- const modal = component.findComponent(GlModal);
+ const modal = findModal();
expect(modal.attributes('title')).toContain('Re-deploy');
expect(modal.attributes('title')).toContain('test');
@@ -220,7 +223,7 @@ describe('Confirm Rollback Modal Component', () => {
{ apolloProvider },
);
- const modal = component.findComponent(GlModal);
+ const modal = findModal();
modal.vm.$emit('ok');
await nextTick();
@@ -231,6 +234,25 @@ describe('Confirm Rollback Modal Component', () => {
expect.anything(),
);
});
+
+ it('should emit the "rollback" event when "ok" is clicked', async () => {
+ const env = { ...environmentData, isLastDeployment: true };
+
+ createComponent(
+ {
+ environment: env,
+ hasMultipleCommits,
+ graphql: true,
+ },
+ { apolloProvider },
+ );
+
+ const modal = findModal();
+ modal.vm.$emit('ok');
+
+ await waitForPromises();
+ expect(component.emitted('rollback')).toEqual([[]]);
+ });
},
);
});
diff --git a/spec/frontend/environments/environment_details/page_spec.js b/spec/frontend/environments/environment_details/index_spec.js
index ed7e0feb6ed..4bf5194b86e 100644
--- a/spec/frontend/environments/environment_details/page_spec.js
+++ b/spec/frontend/environments/environment_details/index_spec.js
@@ -5,15 +5,19 @@ import resolvedEnvironmentDetails from 'test_fixtures/graphql/environments/graph
import emptyEnvironmentDetails from 'test_fixtures/graphql/environments/graphql/queries/environment_details.query.graphql.empty.json';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import EnvironmentsDetailPage from '~/environments/environment_details/index.vue';
+import ConfirmRollbackModal from '~/environments/components/confirm_rollback_modal.vue';
import EmptyState from '~/environments/environment_details/empty_state.vue';
import getEnvironmentDetails from '~/environments/graphql/queries/environment_details.query.graphql';
-import createMockApollo from '../../__helpers__/mock_apollo_helper';
-import waitForPromises from '../../__helpers__/wait_for_promises';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
-describe('~/environments/environment_details/page.vue', () => {
+const GRAPHQL_ETAG_KEY = '/graphql/environments';
+
+describe('~/environments/environment_details/index.vue', () => {
Vue.use(VueApollo);
let wrapper;
+ let routerMock;
const emptyEnvironmentToRollbackData = { id: '', name: '', lastDeployment: null, retryUrl: '' };
const environmentToRollbackMock = jest.fn();
@@ -41,16 +45,23 @@ describe('~/environments/environment_details/page.vue', () => {
environmentToRollbackData || emptyEnvironmentToRollbackData,
);
const projectFullPath = 'gitlab-group/test-project';
+ routerMock = {
+ push: jest.fn(),
+ };
return mountExtended(EnvironmentsDetailPage, {
apolloProvider: mockApollo,
provide: {
projectPath: projectFullPath,
+ graphqlEtagKey: GRAPHQL_ETAG_KEY,
},
propsData: {
projectFullPath,
environmentName: 'test-environment-name',
},
+ mocks: {
+ $router: routerMock,
+ },
});
};
@@ -73,6 +84,14 @@ describe('~/environments/environment_details/page.vue', () => {
expect(wrapper.findComponent(GlLoadingIcon).exists()).not.toBe(true);
expect(wrapper.findComponent(GlTableLite).exists()).toBe(true);
});
+
+ describe('on rollback', () => {
+ it('sets the page back to default', () => {
+ wrapper.findComponent(ConfirmRollbackModal).vm.$emit('rollback');
+
+ expect(routerMock.push).toHaveBeenCalledWith({ query: {} });
+ });
+ });
});
describe('and there are no deployments', () => {
diff --git a/spec/frontend/graphql_shared/utils_spec.js b/spec/frontend/graphql_shared/utils_spec.js
index cd334ef0d97..35ae8de1b1f 100644
--- a/spec/frontend/graphql_shared/utils_spec.js
+++ b/spec/frontend/graphql_shared/utils_spec.js
@@ -1,3 +1,5 @@
+import Visibility from 'visibilityjs';
+
import {
isGid,
getIdFromGraphQLId,
@@ -6,6 +8,8 @@ import {
convertFromGraphQLIds,
convertNodeIdsFromGraphQLIds,
getNodesOrDefault,
+ toggleQueryPollingByVisibility,
+ etagQueryHeaders,
} from '~/graphql_shared/utils';
const mockType = 'Group';
@@ -160,3 +164,52 @@ describe('getNodesOrDefault', () => {
expect(result).toEqual(expected);
});
});
+
+describe('toggleQueryPollingByVisibility', () => {
+ let query;
+ let changeFn;
+ let interval;
+ let hidden;
+
+ beforeEach(() => {
+ hidden = jest.spyOn(Visibility, 'hidden').mockReturnValue(true);
+ jest.spyOn(Visibility, 'change').mockImplementation((fn) => {
+ changeFn = fn;
+ });
+
+ query = { startPolling: jest.fn(), stopPolling: jest.fn() };
+ interval = 5000;
+
+ toggleQueryPollingByVisibility(query, 5000);
+ });
+
+ it('starts polling not hidden', () => {
+ hidden.mockReturnValue(false);
+
+ changeFn();
+ expect(query.startPolling).toHaveBeenCalledWith(interval);
+ });
+
+ it('stops polling when hidden', () => {
+ query.stopPolling.mockReset();
+ hidden.mockReturnValue(true);
+
+ changeFn();
+ expect(query.stopPolling).toHaveBeenCalled();
+ });
+});
+
+describe('etagQueryHeaders', () => {
+ it('returns headers necessary for etag caching', () => {
+ expect(etagQueryHeaders('myFeature', 'myResource')).toEqual({
+ fetchOptions: {
+ method: 'GET',
+ },
+ headers: {
+ 'X-GITLAB-GRAPHQL-FEATURE-CORRELATION': 'myFeature',
+ 'X-GITLAB-GRAPHQL-RESOURCE-ETAG': 'myResource',
+ 'X-Requested-With': 'XMLHttpRequest',
+ },
+ });
+ });
+});
diff --git a/spec/frontend/super_sidebar/components/menu_section_spec.js b/spec/frontend/super_sidebar/components/menu_section_spec.js
index 751c4da5a3d..556e07a2e31 100644
--- a/spec/frontend/super_sidebar/components/menu_section_spec.js
+++ b/spec/frontend/super_sidebar/components/menu_section_spec.js
@@ -7,6 +7,7 @@ import { stubComponent } from 'helpers/stub_component';
describe('MenuSection component', () => {
let wrapper;
+ const findButton = () => wrapper.find('button');
const findCollapse = () => wrapper.getComponent(GlCollapse);
const findNavItems = () => wrapper.findAllComponents(NavItem);
const createWrapper = (item, otherProps) => {
@@ -22,7 +23,7 @@ describe('MenuSection component', () => {
it('renders its title', () => {
createWrapper({ title: 'Asdf' });
- expect(wrapper.find('button').text()).toBe('Asdf');
+ expect(findButton().text()).toBe('Asdf');
});
it('renders all its subitems', () => {
@@ -36,6 +37,12 @@ describe('MenuSection component', () => {
expect(findNavItems().length).toBe(2);
});
+ it('associates button with list with aria-controls', () => {
+ createWrapper({ title: 'Asdf' });
+ expect(findButton().attributes('aria-controls')).toBe('asdf');
+ expect(findCollapse().attributes('id')).toBe('asdf');
+ });
+
describe('collapse behavior', () => {
describe('when active', () => {
it('is expanded', () => {
@@ -47,6 +54,7 @@ describe('MenuSection component', () => {
describe('when set to expanded', () => {
it('is expanded', () => {
createWrapper({ title: 'Asdf' }, { expanded: true });
+ expect(findButton().attributes('aria-expanded')).toBe('true');
expect(findCollapse().props('visible')).toBe(true);
});
});
@@ -54,6 +62,7 @@ describe('MenuSection component', () => {
describe('when not active nor set to expanded', () => {
it('is not expanded', () => {
createWrapper({ title: 'Asdf' });
+ expect(findButton().attributes('aria-expanded')).toBe('false');
expect(findCollapse().props('visible')).toBe(false);
});
});
@@ -77,9 +86,9 @@ describe('MenuSection component', () => {
describe('`tag` prop', () => {
describe('by default', () => {
- it('renders as <section> tag', () => {
+ it('renders as <div> tag', () => {
createWrapper({ title: 'Asdf' });
- expect(wrapper.element.tagName).toBe('SECTION');
+ expect(wrapper.element.tagName).toBe('DIV');
});
});
diff --git a/spec/graphql/mutations/design_management/delete_spec.rb b/spec/graphql/mutations/design_management/delete_spec.rb
index 79196d4965d..a76943b9ff8 100644
--- a/spec/graphql/mutations/design_management/delete_spec.rb
+++ b/spec/graphql/mutations/design_management/delete_spec.rb
@@ -86,7 +86,7 @@ RSpec.describe Mutations::DesignManagement::Delete do
end
end
- it 'runs no more than 30 queries' do
+ it 'runs no more than 31 queries' do
allow(Gitlab::Tracking).to receive(:event) # rubocop:disable RSpec/ExpectGitlabTracking
filenames.each(&:present?) # ignore setup
@@ -107,22 +107,23 @@ RSpec.describe Mutations::DesignManagement::Delete do
# 14. project.authorizations for user (same query as 5)
# 15. current designs by filename and issue
# 16, 17 project.authorizations for user (same query as 5)
- # 18. find route by id and source_type
- # 19. find plan for standard context
+ # 18. find design_management_repository for project
+ # 19. find route by id and source_type
+ # 20. find plan for standard context
# ------------- our queries are below:
- # 20. start transaction 1
- # 21. start transaction 2
- # 22. find version by sha and issue
- # 23. exists version with sha and issue?
- # 24. leave transaction 2
- # 25. create version with sha and issue
- # 26. create design-version links
- # 27. validate version.actions.present?
- # 28. validate version.issue.present?
- # 29. validate version.sha is unique
- # 30. leave transaction 1
+ # 21. start transaction 1
+ # 22. start transaction 2
+ # 23. find version by sha and issue
+ # 24. exists version with sha and issue?
+ # 25. leave transaction 2
+ # 26. create version with sha and issue
+ # 27. create design-version links
+ # 28. validate version.actions.present?
+ # 29. validate version.issue.present?
+ # 30. validate version.sha is unique
+ # 31. leave transaction 1
#
- expect { run_mutation }.not_to exceed_query_limit(30)
+ expect { run_mutation }.not_to exceed_query_limit(31)
end
end
diff --git a/spec/helpers/environment_helper_spec.rb b/spec/helpers/environment_helper_spec.rb
index c8d67d6dac2..c2d17832e8c 100644
--- a/spec/helpers/environment_helper_spec.rb
+++ b/spec/helpers/environment_helper_spec.rb
@@ -65,7 +65,8 @@ RSpec.describe EnvironmentHelper do
environment_terminal_path: terminal_project_environment_path(project, environment),
has_terminals: false,
is_environment_available: true,
- auto_stop_at: auto_stop_at
+ auto_stop_at: auto_stop_at,
+ graphql_etag_key: environment.etag_cache_key
}.to_json)
end
end
diff --git a/spec/lib/backup/repositories_spec.rb b/spec/lib/backup/repositories_spec.rb
index c75f6c2ac89..b11538b93b7 100644
--- a/spec/lib/backup/repositories_spec.rb
+++ b/spec/lib/backup/repositories_spec.rb
@@ -159,6 +159,7 @@ RSpec.describe Backup::Repositories, feature_category: :backup_restore do
describe '#restore' do
let_it_be(:project) { create(:project, :repository) }
+
let_it_be(:personal_snippet) { create(:personal_snippet, :repository, author: project.first_owner) }
let_it_be(:project_snippet) { create(:project_snippet, :repository, project: project, author: project.first_owner) }
diff --git a/spec/lib/gitlab/ci/reports/codequality_mr_diff_spec.rb b/spec/lib/gitlab/ci/reports/codequality_mr_diff_spec.rb
index 8b094c64b54..79fa1c3ec75 100644
--- a/spec/lib/gitlab/ci/reports/codequality_mr_diff_spec.rb
+++ b/spec/lib/gitlab/ci/reports/codequality_mr_diff_spec.rb
@@ -18,24 +18,8 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityMrDiff, feature_category: :code_q
it 'generates quality report for mr diff' do
expect(report.files).to match(
"file_a.rb" => [
- { line: 10,
- description: "Avoid parameter lists longer than 5 parameters. [12/5]",
- severity: "major",
- engine_name: "structure",
- categories: ["Complexity"],
- content: { "body" => "" },
- location: { "lines" => { "begin" => 10, "end" => 10 }, "path" => "file_a.rb" },
- other_locations: [],
- type: "issue" },
- { line: 10,
- description: "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.",
- severity: "major",
- engine_name: "structure",
- categories: ["Complexity"],
- content: { "body" => "" },
- location: { "lines" => { "begin" => 10, "end" => 10 }, "path" => "file_a.rb" },
- other_locations: [],
- type: "issue" }
+ { line: 10, description: "Avoid parameter lists longer than 5 parameters. [12/5]", severity: "major" },
+ { line: 10, description: "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.", severity: "major" }
]
)
end
@@ -44,14 +28,16 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityMrDiff, feature_category: :code_q
context 'with several degradations on several files' do
let(:new_degradations) { [degradation_1, degradation_2, degradation_3] }
- it 'returns quality report including the files' do
- expect(report.files.keys).to match_array(["file_a.rb", "file_b.rb"])
- end
-
- it 'converts the content body to html' do
- body = report.files["file_b.rb"].first[:content]["body"]
-
- expect(body).to eq('<p data-sourcepos="1:1-3:66" dir="auto">This cop checks for methods with too many parameters.&#x000A;The maximum number of parameters is configurable.&#x000A;Keyword arguments can optionally be excluded from the total count.</p>')
+ it 'returns quality report for mr diff' do
+ expect(report.files).to match(
+ "file_a.rb" => [
+ { line: 10, description: "Avoid parameter lists longer than 5 parameters. [12/5]", severity: "major" },
+ { line: 10, description: "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.", severity: "major" }
+ ],
+ "file_b.rb" => [
+ { line: 10, description: "Avoid parameter lists longer than 5 parameters. [12/5]", severity: "minor" }
+ ]
+ )
end
end
end
diff --git a/spec/lib/gitlab/gitaly_client/with_feature_flag_actors_spec.rb b/spec/lib/gitlab/gitaly_client/with_feature_flag_actors_spec.rb
index 61945cc06b8..42153a9a3d8 100644
--- a/spec/lib/gitlab/gitaly_client/with_feature_flag_actors_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/with_feature_flag_actors_spec.rb
@@ -131,23 +131,23 @@ RSpec.describe Gitlab::GitalyClient::WithFeatureFlagActors do
end
context 'when project design' do
- let_it_be(:project) { create(:project, group: create(:group)) }
- let(:issue) { create(:issue, project: project) }
- let(:design) { create(:design, issue: issue) }
+ let_it_be(:design_repo) do
+ create(:design_management_repository, project: create(:project, group: create(:group)))
+ end
- let(:expected_project) { project }
- let(:expected_group) { project.group }
+ let(:expected_project) { design_repo.project }
+ let(:expected_group) { design_repo.project.group }
it_behaves_like 'Gitaly feature flag actors are inferred from repository' do
- let(:repository) { design.repository }
+ let(:repository) { design_repo.repository }
end
it_behaves_like 'Gitaly feature flag actors are inferred from repository' do
- let(:repository) { design.repository.raw }
+ let(:repository) { design_repo.repository.raw }
end
it_behaves_like 'Gitaly feature flag actors are inferred from repository' do
- let(:repository) { raw_repo_without_container(design.repository) }
+ let(:repository) { raw_repo_without_container(design_repo.repository) }
end
end
end
diff --git a/spec/lib/gitlab/gl_repository/identifier_spec.rb b/spec/lib/gitlab/gl_repository/identifier_spec.rb
index 0a8559dd800..dbdcafea6d6 100644
--- a/spec/lib/gitlab/gl_repository/identifier_spec.rb
+++ b/spec/lib/gitlab/gl_repository/identifier_spec.rb
@@ -68,10 +68,12 @@ RSpec.describe Gitlab::GlRepository::Identifier do
end
describe 'design' do
+ let(:design_repository_container) { project.design_repository.container }
+
it_behaves_like 'parsing gl_repository identifier' do
let(:record_id) { project.id }
- let(:identifier) { "design-#{project.id}" }
- let(:expected_container) { project }
+ let(:identifier) { "design-#{design_repository_container.id}" }
+ let(:expected_container) { design_repository_container }
let(:expected_type) { Gitlab::GlRepository::DESIGN }
end
end
diff --git a/spec/lib/gitlab/gl_repository/repo_type_spec.rb b/spec/lib/gitlab/gl_repository/repo_type_spec.rb
index 40dcbe16688..2ac2fc1fd4b 100644
--- a/spec/lib/gitlab/gl_repository/repo_type_spec.rb
+++ b/spec/lib/gitlab/gl_repository/repo_type_spec.rb
@@ -12,6 +12,8 @@ RSpec.describe Gitlab::GlRepository::RepoType do
let(:personal_snippet_path) { "snippets/#{personal_snippet.id}" }
let(:project_snippet_path) { "#{project.full_path}/snippets/#{project_snippet.id}" }
+ let(:expected_repository_resolver) { expected_container }
+
describe Gitlab::GlRepository::PROJECT do
it_behaves_like 'a repo type' do
let(:expected_id) { project.id }
@@ -133,11 +135,12 @@ RSpec.describe Gitlab::GlRepository::RepoType do
describe Gitlab::GlRepository::DESIGN do
it_behaves_like 'a repo type' do
- let(:expected_identifier) { "design-#{project.id}" }
- let(:expected_id) { project.id }
+ let(:expected_repository) { project.design_repository }
+ let(:expected_container) { project.design_management_repository }
+ let(:expected_id) { expected_container.id }
+ let(:expected_identifier) { "design-#{expected_id}" }
let(:expected_suffix) { '.design' }
- let(:expected_repository) { project.design_management_repository }
- let(:expected_container) { project }
+ let(:expected_repository_resolver) { project }
end
it 'uses the design access checker' do
@@ -162,5 +165,17 @@ RSpec.describe Gitlab::GlRepository::RepoType do
expect(described_class.valid?(project_snippet_path)).to be_falsey
end
end
+
+ describe '.project_for' do
+ it 'returns a project' do
+ expect(described_class.project_for(project.design_repository.container)).to be_instance_of(Project)
+ end
+ end
+
+ describe '.repository_for' do
+ it 'returns a DesignManagement::GitRepository when a project is passed' do
+ expect(described_class.repository_for(project)).to be_instance_of(DesignManagement::GitRepository)
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/gl_repository_spec.rb b/spec/lib/gitlab/gl_repository_spec.rb
index 05914f92c01..7be01507a82 100644
--- a/spec/lib/gitlab/gl_repository_spec.rb
+++ b/spec/lib/gitlab/gl_repository_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe ::Gitlab::GlRepository do
describe '.parse' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:snippet) { create(:personal_snippet) }
+ let(:design_repository_container) { project.design_repository.container }
it 'parses a project gl_repository' do
expect(described_class.parse("project-#{project.id}")).to eq([project, project, Gitlab::GlRepository::PROJECT])
@@ -20,7 +21,13 @@ RSpec.describe ::Gitlab::GlRepository do
end
it 'parses a design gl_repository' do
- expect(described_class.parse("design-#{project.id}")).to eq([project, project, Gitlab::GlRepository::DESIGN])
+ expect(described_class.parse("design-#{design_repository_container.id}")).to eq(
+ [
+ design_repository_container,
+ project,
+ Gitlab::GlRepository::DESIGN
+ ]
+ )
end
it 'throws an argument error on an invalid gl_repository type' do
diff --git a/spec/lib/gitlab/patch/draw_route_spec.rb b/spec/lib/gitlab/patch/draw_route_spec.rb
index 4d1c7bf9fcf..d983f6f15bb 100644
--- a/spec/lib/gitlab/patch/draw_route_spec.rb
+++ b/spec/lib/gitlab/patch/draw_route_spec.rb
@@ -20,8 +20,10 @@ RSpec.describe Gitlab::Patch::DrawRoute do
it 'evaluates CE only route' do
subject.draw(:help)
+ route_file_path = subject.route_path('config/routes/help.rb')
+
expect(subject).to have_received(:instance_eval)
- .with(File.read(subject.route_path('config/routes/help.rb')))
+ .with(File.read(route_file_path), route_file_path)
.once
expect(subject).to have_received(:instance_eval)
diff --git a/spec/lib/gitlab/subscription_portal_spec.rb b/spec/lib/gitlab/subscription_portal_spec.rb
index 52c7a68921b..96d3e855843 100644
--- a/spec/lib/gitlab/subscription_portal_spec.rb
+++ b/spec/lib/gitlab/subscription_portal_spec.rb
@@ -12,62 +12,10 @@ RSpec.describe ::Gitlab::SubscriptionPortal do
stub_env('CUSTOMER_PORTAL_URL', env_value)
end
- describe '.default_subscriptions_url' do
- where(:test, :development, :result) do
- false | false | prod_customers_url
- false | true | staging_customers_url
- true | false | staging_customers_url
- end
-
- before do
- allow(Rails).to receive_message_chain(:env, :test?).and_return(test)
- allow(Rails).to receive_message_chain(:env, :development?).and_return(development)
- end
-
- with_them do
- subject { described_class.default_subscriptions_url }
-
- it { is_expected.to eq(result) }
- end
- end
-
- describe '.subscriptions_url' do
- subject { described_class.subscriptions_url }
-
- context 'when CUSTOMER_PORTAL_URL ENV is unset' do
- it { is_expected.to eq(staging_customers_url) }
- end
-
- context 'when CUSTOMER_PORTAL_URL ENV is set' do
- let(:env_value) { 'https://customers.example.com' }
-
- it { is_expected.to eq(env_value) }
- end
- end
-
- describe '.subscriptions_comparison_url' do
- subject { described_class.subscriptions_comparison_url }
-
- link_match = %r{\Ahttps://about\.gitlab\.((cn/pricing/saas)|(com/pricing/gitlab-com))/feature-comparison\z}
-
- it { is_expected.to match(link_match) }
- end
-
describe 'class methods' do
where(:method_name, :result) do
- :default_subscriptions_url | staging_customers_url
- :payment_form_url | "#{staging_customers_url}/payment_forms/cc_validation"
:payment_validation_form_id | 'payment_method_validation'
- :registration_validation_form_url | "#{staging_customers_url}/payment_forms/cc_registration_validation"
:registration_validation_form_id | 'cc_registration_validation'
- :subscriptions_graphql_url | "#{staging_customers_url}/graphql"
- :subscriptions_more_minutes_url | "#{staging_customers_url}/buy_pipeline_minutes"
- :subscriptions_more_storage_url | "#{staging_customers_url}/buy_storage"
- :subscriptions_manage_url | "#{staging_customers_url}/subscriptions"
- :subscriptions_legacy_sign_in_url | "#{staging_customers_url}/customers/sign_in?legacy=true"
- :subscriptions_instance_review_url | "#{staging_customers_url}/instance_review"
- :subscriptions_gitlab_plans_url | "#{staging_customers_url}/gitlab_plans"
- :edit_account_url | "#{staging_customers_url}/customers/edit"
end
with_them do
@@ -77,40 +25,6 @@ RSpec.describe ::Gitlab::SubscriptionPortal do
end
end
- describe '.add_extra_seats_url' do
- subject { described_class.add_extra_seats_url(group_id) }
-
- let(:group_id) { 153 }
-
- it do
- url = "#{staging_customers_url}/gitlab/namespaces/#{group_id}/extra_seats"
- is_expected.to eq(url)
- end
- end
-
- describe '.upgrade_subscription_url' do
- subject { described_class.upgrade_subscription_url(group_id, plan_id) }
-
- let(:group_id) { 153 }
- let(:plan_id) { 5 }
-
- it do
- url = "#{staging_customers_url}/gitlab/namespaces/#{group_id}/upgrade/#{plan_id}"
- is_expected.to eq(url)
- end
- end
-
- describe '.renew_subscription_url' do
- subject { described_class.renew_subscription_url(group_id) }
-
- let(:group_id) { 153 }
-
- it do
- url = "#{staging_customers_url}/gitlab/namespaces/#{group_id}/renew"
- is_expected.to eq(url)
- end
- end
-
describe 'constants' do
where(:constant_name, :result) do
'REGISTRATION_VALIDATION_FORM_ID' | 'cc_registration_validation'
diff --git a/spec/models/design_management/design_collection_spec.rb b/spec/models/design_management/design_collection_spec.rb
index bc8330c7dd3..06596d37fde 100644
--- a/spec/models/design_management/design_collection_spec.rb
+++ b/spec/models/design_management/design_collection_spec.rb
@@ -128,7 +128,7 @@ RSpec.describe DesignManagement::DesignCollection do
describe "#repository" do
it "builds a design repository" do
- expect(collection.repository).to be_a(DesignManagement::Repository)
+ expect(collection.repository).to be_a(DesignManagement::GitRepository)
end
end
diff --git a/spec/models/design_management/design_spec.rb b/spec/models/design_management/design_spec.rb
index 52bb23588e3..72c0d1d1a64 100644
--- a/spec/models/design_management/design_spec.rb
+++ b/spec/models/design_management/design_spec.rb
@@ -463,7 +463,7 @@ RSpec.describe DesignManagement::Design, feature_category: :design_management do
it 'is a design repository' do
design = build(:design, issue: issue)
- expect(design.repository).to be_a(DesignManagement::Repository)
+ expect(design.repository).to be_a(DesignManagement::GitRepository)
end
end
diff --git a/spec/models/design_management/git_repository_spec.rb b/spec/models/design_management/git_repository_spec.rb
index 1b07e337cde..736f2ad45cf 100644
--- a/spec/models/design_management/git_repository_spec.rb
+++ b/spec/models/design_management/git_repository_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
RSpec.describe DesignManagement::GitRepository, feature_category: :design_management do
- let_it_be(:project) { create(:project) }
- let(:git_repository) { described_class.new(project) }
+ let_it_be(:container_repo) { DesignManagement::Repository.new(project: create(:project)) }
+ let(:git_repository) { container_repo.repository }
shared_examples 'returns parsed git attributes that enable LFS for all file types' do
it do
@@ -16,6 +16,12 @@ RSpec.describe DesignManagement::GitRepository, feature_category: :design_manage
end
end
+ describe '.container' do
+ it 'is of class DesignManagement::Repository' do
+ expect(git_repository.container).to be_a_kind_of(DesignManagement::Repository)
+ end
+ end
+
describe "#info_attributes" do
subject { git_repository.info_attributes }
diff --git a/spec/models/design_management/repository_spec.rb b/spec/models/design_management/repository_spec.rb
index 67cdba40f82..74f393306dc 100644
--- a/spec/models/design_management/repository_spec.rb
+++ b/spec/models/design_management/repository_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe DesignManagement::Repository, feature_category: :design_management do
let_it_be(:project) { create(:project) }
- let(:subject) { ::DesignManagement::Repository.new({ project: project }) }
+ let(:subject) { described_class.new({ project: project }) }
describe 'associations' do
it { is_expected.to belong_to(:project).inverse_of(:design_management_repository) }
@@ -14,4 +14,12 @@ RSpec.describe DesignManagement::Repository, feature_category: :design_managemen
it { is_expected.to validate_presence_of(:project) }
it { is_expected.to validate_uniqueness_of(:project) }
end
+
+ it "returns the project's full path" do
+ expect(subject.full_path).to eq(project.full_path + Gitlab::GlRepository::DESIGN.path_suffix)
+ end
+
+ it "returns the project's disk path" do
+ expect(subject.disk_path).to eq(project.disk_path + Gitlab::GlRepository::DESIGN.path_suffix)
+ end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index a54ba0ba40a..0e432edebb0 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -5784,6 +5784,34 @@ RSpec.describe User, feature_category: :user_profile do
expect(user).not_to be_blocked
end
+
+ context 'when target user is the same as deleted_by' do
+ let(:deleted_by) { user }
+
+ it 'blocks the user and schedules the record for deletion with the correct delay' do
+ freeze_time do
+ expect(DeleteUserWorker).to receive(:perform_in).with(7.days, user.id, user.id, {})
+
+ user.delete_async(deleted_by: deleted_by)
+
+ expect(user).to be_blocked
+ end
+ end
+
+ context 'when delay_delete_own_user feature flag is disabled' do
+ before do
+ stub_feature_flags(delay_delete_own_user: false)
+ end
+
+ it 'schedules user for deletion without blocking them' do
+ expect(DeleteUserWorker).to receive(:perform_async).with(user.id, user.id, {})
+
+ user.delete_async(deleted_by: deleted_by)
+
+ expect(user).not_to be_blocked
+ end
+ end
+ end
end
describe '#max_member_access_for_project_ids' do
diff --git a/spec/presenters/ci/pipeline_artifacts/code_quality_mr_diff_presenter_spec.rb b/spec/presenters/ci/pipeline_artifacts/code_quality_mr_diff_presenter_spec.rb
index 3f40b5469ad..99c82795210 100644
--- a/spec/presenters/ci/pipeline_artifacts/code_quality_mr_diff_presenter_spec.rb
+++ b/spec/presenters/ci/pipeline_artifacts/code_quality_mr_diff_presenter_spec.rb
@@ -36,24 +36,8 @@ RSpec.describe Ci::PipelineArtifacts::CodeQualityMrDiffPresenter, feature_catego
expect(quality_data).to match(
files: {
"file_a.rb" => [
- { line: 10,
- description: "Avoid parameter lists longer than 5 parameters. [12/5]",
- severity: "major",
- engine_name: "structure",
- categories: ["Complexity"],
- content: { "body" => "" },
- location: { "lines" => { "begin" => 10, "end" => 10 }, "path" => "file_a.rb" },
- other_locations: [],
- type: "issue" },
- { line: 10,
- description: "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.",
- severity: "minor",
- engine_name: "structure",
- categories: ["Complexity"],
- content: { "body" => "" },
- location: { "lines" => { "begin" => 10, "end" => 10 }, "path" => "file_a.rb" },
- other_locations: [],
- type: "issue" }
+ { line: 10, description: "Avoid parameter lists longer than 5 parameters. [12/5]", severity: "major" },
+ { line: 10, description: "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.", severity: "minor" }
]
}
)
@@ -67,34 +51,11 @@ RSpec.describe Ci::PipelineArtifacts::CodeQualityMrDiffPresenter, feature_catego
expect(quality_data).to match(
files: {
"file_a.rb" => [
- { line: 10,
- description: "Avoid parameter lists longer than 5 parameters. [12/5]",
- severity: "major",
- engine_name: "structure",
- categories: ["Complexity"],
- content: { "body" => "" },
- location: { "lines" => { "begin" => 10, "end" => 10 }, "path" => "file_a.rb" },
- other_locations: [],
- type: "issue" },
- { line: 10,
- description: "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.",
- severity: "minor",
- engine_name: "structure",
- categories: ["Complexity"],
- content: { "body" => "" },
- location: { "lines" => { "begin" => 10, "end" => 10 }, "path" => "file_a.rb" },
- other_locations: [],
- type: "issue" }
+ { line: 10, description: "Avoid parameter lists longer than 5 parameters. [12/5]", severity: "major" },
+ { line: 10, description: "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.", severity: "minor" }
],
"file_b.rb" => [
- { line: 10,
- description: "This cop checks for methods with too many parameters.\nThe maximum number of parameters is configurable.\nKeyword arguments can optionally be excluded from the total count.",
- severity: "minor",
- engine_name: "rubocop",
- categories: ["Complexity"],
- content: { "body" => "" },
- location: { "positions" => { "begin" => { "column" => 14, "line" => 10 }, "end" => { "column" => 39, "line" => 10 } }, "path" => "file_b.rb" },
- type: "Issue" }
+ { line: 10, description: "This cop checks for methods with too many parameters.\nThe maximum number of parameters is configurable.\nKeyword arguments can optionally be excluded from the total count.", severity: "minor" }
]
}
)
diff --git a/spec/routing/directs/subscription_portal_spec.rb b/spec/routing/directs/subscription_portal_spec.rb
new file mode 100644
index 00000000000..768990fae62
--- /dev/null
+++ b/spec/routing/directs/subscription_portal_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Custom URLs', 'Subscription Portal', feature_category: :subscription_management do
+ using RSpec::Parameterized::TableSyntax
+ include SubscriptionPortalHelper
+
+ let(:env_value) { nil }
+ let(:staging_env_value) { nil }
+
+ before do
+ stub_env('CUSTOMER_PORTAL_URL', env_value)
+ stub_env('STAGING_CUSTOMER_PORTAL_URL', staging_env_value)
+ end
+
+ describe 'subscription_portal_staging_url' do
+ subject { subscription_portal_staging_url }
+
+ context 'when STAGING_CUSTOMER_PORTAL_URL is unset' do
+ it { is_expected.to eq(staging_customers_url) }
+ end
+
+ context 'when STAGING_CUSTOMER_PORTAL_URL is set' do
+ let(:staging_env_value) { 'https://customers.staging.example.com' }
+
+ it { is_expected.to eq(staging_env_value) }
+ end
+ end
+
+ describe 'subscription_portal_url' do
+ subject { subscription_portal_url }
+
+ context 'when CUSTOMER_PORTAL_URL ENV is unset' do
+ where(:test, :development, :expected_url) do
+ false | false | prod_customers_url
+ false | true | subscription_portal_staging_url
+ true | false | subscription_portal_staging_url
+ end
+
+ before do
+ allow(Rails).to receive_message_chain(:env, :test?).and_return(test)
+ allow(Rails).to receive_message_chain(:env, :development?).and_return(development)
+ end
+
+ with_them do
+ it { is_expected.to eq(expected_url) }
+ end
+ end
+
+ context 'when CUSTOMER_PORTAL_URL ENV is set' do
+ let(:env_value) { 'https://customers.example.com' }
+
+ it { is_expected.to eq(env_value) }
+ end
+ end
+
+ describe 'subscription_portal_instance_review_url' do
+ subject { subscription_portal_instance_review_url }
+
+ it { is_expected.to eq("#{staging_customers_url}/instance_review") }
+ end
+end
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index c2a3afc0e57..c2458d3485f 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -128,6 +128,18 @@ RSpec.describe 'project routing' do
it 'to #archive with "/" in route' do
expect(get('/gitlab/gitlabhq/-/archive/improve/awesome/gitlabhq-improve-awesome.tar.gz')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq', format: 'tar.gz', id: 'improve/awesome/gitlabhq-improve-awesome')
end
+
+ it 'to #archive format:html' do
+ expect(get('/gitlab/gitlabhq/-/archive/master.html')).to route_to_route_not_found
+ end
+
+ it 'to #archive format:yaml' do
+ expect(get('/gitlab/gitlabhq/-/archive/master.yaml')).to route_to_route_not_found
+ end
+
+ it 'to #archive format:yml' do
+ expect(get('/gitlab/gitlabhq/-/archive/master.yml')).to route_to_route_not_found
+ end
end
describe Projects::BranchesController, 'routing' do
diff --git a/spec/serializers/ci/codequality_mr_diff_entity_spec.rb b/spec/serializers/ci/codequality_mr_diff_entity_spec.rb
index a6e29a3914d..19b872b68db 100644
--- a/spec/serializers/ci/codequality_mr_diff_entity_spec.rb
+++ b/spec/serializers/ci/codequality_mr_diff_entity_spec.rb
@@ -19,17 +19,8 @@ RSpec.describe Ci::CodequalityMrDiffEntity, feature_category: :code_quality do
end
it 'contains correct codequality mr diff report', :aggregate_failures do
- expect(report[:files].keys).to match_array(["file_a.rb"])
- expect(report[:files]["file_a.rb"].first).to include(
- :line,
- :description,
- :severity,
- :engine_name,
- :categories,
- :content,
- :location,
- :other_locations,
- :type)
+ expect(report[:files].keys).to eq(["file_a.rb"])
+ expect(report[:files]["file_a.rb"].first).to include(:line, :description, :severity)
end
end
end
diff --git a/spec/services/design_management/save_designs_service_spec.rb b/spec/services/design_management/save_designs_service_spec.rb
index a87494d87f7..ea53fcc3b12 100644
--- a/spec/services/design_management/save_designs_service_spec.rb
+++ b/spec/services/design_management/save_designs_service_spec.rb
@@ -11,7 +11,10 @@ RSpec.describe DesignManagement::SaveDesignsService, feature_category: :design_m
let(:project) { issue.project }
let(:user) { developer }
let(:files) { [rails_sample] }
- let(:design_repository) { ::Gitlab::GlRepository::DESIGN.repository_resolver.call(project) }
+ let(:design_repository) do
+ ::Gitlab::GlRepository::DESIGN.repository_resolver.call(project)
+ end
+
let(:rails_sample_name) { 'rails_sample.jpg' }
let(:rails_sample) { sample_image(rails_sample_name) }
let(:dk_png) { sample_image('dk.png') }
diff --git a/spec/services/merge_requests/after_create_service_spec.rb b/spec/services/merge_requests/after_create_service_spec.rb
index 9361ec44e30..50a3d49d4a3 100644
--- a/spec/services/merge_requests/after_create_service_spec.rb
+++ b/spec/services/merge_requests/after_create_service_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe MergeRequests::AfterCreateService, feature_category: :code_review_workflow do
let_it_be(:merge_request) { create(:merge_request) }
+ let(:project) { merge_request.project }
subject(:after_create_service) do
described_class.new(project: merge_request.target_project, current_user: merge_request.author)
@@ -68,6 +69,12 @@ RSpec.describe MergeRequests::AfterCreateService, feature_category: :code_review
execute_service
end
+ it 'executes hooks with default action' do
+ expect(project).to receive(:execute_hooks)
+
+ execute_service
+ end
+
it_behaves_like 'records an onboarding progress action', :merge_request_created do
let(:namespace) { merge_request.target_project.namespace }
end
diff --git a/spec/services/merge_requests/base_service_spec.rb b/spec/services/merge_requests/base_service_spec.rb
index deabb1c9804..1ca4bfe622c 100644
--- a/spec/services/merge_requests/base_service_spec.rb
+++ b/spec/services/merge_requests/base_service_spec.rb
@@ -15,6 +15,7 @@ RSpec.describe MergeRequests::BaseService, feature_category: :code_review_workfl
let_it_be(:project) { create(:project, :repository) }
+ let(:user) { project.first_owner }
let(:title) { 'Awesome merge_request' }
let(:params) do
{
@@ -25,14 +26,14 @@ RSpec.describe MergeRequests::BaseService, feature_category: :code_review_workfl
}
end
- subject { MergeRequests::CreateService.new(project: project, current_user: project.first_owner, params: params) }
-
describe '#execute_hooks' do
+ subject { MergeRequests::CreateService.new(project: project, current_user: user, params: params).execute }
+
shared_examples 'enqueues Jira sync worker' do
specify :aggregate_failures do
expect(JiraConnect::SyncMergeRequestWorker).to receive(:perform_async).with(kind_of(Numeric), kind_of(Numeric)).and_call_original
Sidekiq::Testing.fake! do
- expect { subject.execute }.to change(JiraConnect::SyncMergeRequestWorker.jobs, :size).by(1)
+ expect { subject }.to change(JiraConnect::SyncMergeRequestWorker.jobs, :size).by(1)
end
end
end
@@ -40,7 +41,7 @@ RSpec.describe MergeRequests::BaseService, feature_category: :code_review_workfl
shared_examples 'does not enqueue Jira sync worker' do
it do
Sidekiq::Testing.fake! do
- expect { subject.execute }.not_to change(JiraConnect::SyncMergeRequestWorker.jobs, :size)
+ expect { subject }.not_to change(JiraConnect::SyncMergeRequestWorker.jobs, :size)
end
end
end
@@ -53,7 +54,20 @@ RSpec.describe MergeRequests::BaseService, feature_category: :code_review_workfl
context 'MR contains Jira issue key' do
let(:title) { 'Awesome merge_request with issue JIRA-123' }
- it_behaves_like 'enqueues Jira sync worker'
+ it_behaves_like 'does not enqueue Jira sync worker'
+
+ context 'for UpdateService' do
+ subject { MergeRequests::UpdateService.new(project: project, current_user: user, params: params).execute(merge_request) }
+
+ let(:merge_request) do
+ create(:merge_request, :simple, title: 'Old title',
+ assignee_ids: [user.id],
+ source_project: project,
+ author: user)
+ end
+
+ it_behaves_like 'enqueues Jira sync worker'
+ end
end
context 'MR does not contain Jira issue key' do
@@ -69,13 +83,13 @@ RSpec.describe MergeRequests::BaseService, feature_category: :code_review_workfl
describe `#create_pipeline_for` do
let_it_be(:merge_request) { create(:merge_request) }
- subject { MergeRequests::ExampleService.new(project: project, current_user: project.first_owner, params: params) }
+ subject { MergeRequests::ExampleService.new(project: project, current_user: user, params: params) }
context 'async: false' do
it 'creates a pipeline directly' do
expect(MergeRequests::CreatePipelineService)
.to receive(:new)
- .with(hash_including(project: project, current_user: project.first_owner, params: { allow_duplicate: false }))
+ .with(hash_including(project: project, current_user: user, params: { allow_duplicate: false }))
.and_call_original
expect(MergeRequests::CreatePipelineWorker).not_to receive(:perform_async)
@@ -86,7 +100,7 @@ RSpec.describe MergeRequests::BaseService, feature_category: :code_review_workfl
it 'passes :allow_duplicate as true' do
expect(MergeRequests::CreatePipelineService)
.to receive(:new)
- .with(hash_including(project: project, current_user: project.first_owner, params: { allow_duplicate: true }))
+ .with(hash_including(project: project, current_user: user, params: { allow_duplicate: true }))
.and_call_original
expect(MergeRequests::CreatePipelineWorker).not_to receive(:perform_async)
@@ -100,7 +114,7 @@ RSpec.describe MergeRequests::BaseService, feature_category: :code_review_workfl
expect(MergeRequests::CreatePipelineService).not_to receive(:new)
expect(MergeRequests::CreatePipelineWorker)
.to receive(:perform_async)
- .with(project.id, project.first_owner.id, merge_request.id, { "allow_duplicate" => false })
+ .with(project.id, user.id, merge_request.id, { "allow_duplicate" => false })
.and_call_original
Sidekiq::Testing.fake! do
@@ -113,7 +127,7 @@ RSpec.describe MergeRequests::BaseService, feature_category: :code_review_workfl
expect(MergeRequests::CreatePipelineService).not_to receive(:new)
expect(MergeRequests::CreatePipelineWorker)
.to receive(:perform_async)
- .with(project.id, project.first_owner.id, merge_request.id, { "allow_duplicate" => true })
+ .with(project.id, user.id, merge_request.id, { "allow_duplicate" => true })
.and_call_original
Sidekiq::Testing.fake! do
diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb
index c4de56f39dd..7705278f30d 100644
--- a/spec/services/merge_requests/create_service_spec.rb
+++ b/spec/services/merge_requests/create_service_spec.rb
@@ -28,7 +28,6 @@ RSpec.describe MergeRequests::CreateService, :clean_gitlab_redis_shared_state, f
before do
project.add_maintainer(user)
project.add_developer(user2)
- allow(service).to receive(:execute_hooks)
end
it 'creates an MR' do
@@ -39,8 +38,10 @@ RSpec.describe MergeRequests::CreateService, :clean_gitlab_redis_shared_state, f
expect(merge_request.merge_params['force_remove_source_branch']).to eq('1')
end
- it 'executes hooks with default action' do
- expect(service).to have_received(:execute_hooks).with(merge_request)
+ it 'does not execute hooks' do
+ expect(project).not_to receive(:execute_hooks)
+
+ service.execute
end
it 'refreshes the number of open merge requests', :use_clean_rails_memory_store_caching do
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index 2704458ca4d..48d5935f22f 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -715,10 +715,15 @@ RSpec.describe Projects::TransferService, feature_category: :projects do
project.design_repository
end
+ def clear_design_repo_memoization
+ project.design_management_repository.clear_memoization(:repository)
+ project.clear_memoization(:design_repository)
+ end
+
it 'does not create a design repository' do
expect(subject.execute(group)).to be true
- project.clear_memoization(:design_repository)
+ clear_design_repo_memoization
expect(design_repository.exists?).to be false
end
@@ -734,7 +739,7 @@ RSpec.describe Projects::TransferService, feature_category: :projects do
it 'moves the repository' do
expect(subject.execute(group)).to be true
- project.clear_memoization(:design_repository)
+ clear_design_repo_memoization
expect(design_repository).to have_attributes(
disk_path: new_full_path,
@@ -746,7 +751,7 @@ RSpec.describe Projects::TransferService, feature_category: :projects do
allow(subject).to receive(:execute_system_hooks).and_raise('foo')
expect { subject.execute(group) }.to raise_error('foo')
- project.clear_memoization(:design_repository)
+ clear_design_repo_memoization
expect(design_repository).to have_attributes(
disk_path: old_full_path,
@@ -763,7 +768,7 @@ RSpec.describe Projects::TransferService, feature_category: :projects do
expect(subject.execute(group)).to be true
- project.clear_memoization(:design_repository)
+ clear_design_repo_memoization
expect(design_repository).to have_attributes(
disk_path: old_disk_path,
@@ -777,7 +782,7 @@ RSpec.describe Projects::TransferService, feature_category: :projects do
allow(subject).to receive(:execute_system_hooks).and_raise('foo')
expect { subject.execute(group) }.to raise_error('foo')
- project.clear_memoization(:design_repository)
+ clear_design_repo_memoization
expect(design_repository).to have_attributes(
disk_path: old_disk_path,
diff --git a/spec/support/shared_examples/lib/gitlab/repo_type_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/repo_type_shared_examples.rb
index 025f0d5c7ea..c2898513424 100644
--- a/spec/support/shared_examples/lib/gitlab/repo_type_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/repo_type_shared_examples.rb
@@ -15,7 +15,7 @@ RSpec.shared_examples 'a repo type' do
describe '#repository_for' do
it 'finds the repository for the repo type' do
- expect(described_class.repository_for(expected_container)).to eq(expected_repository)
+ expect(described_class.repository_for(expected_repository_resolver)).to eq(expected_repository)
end
it 'returns nil when container is nil' do
diff --git a/spec/workers/delete_user_worker_spec.rb b/spec/workers/delete_user_worker_spec.rb
index 05c2b0e7930..8a99f69c079 100644
--- a/spec/workers/delete_user_worker_spec.rb
+++ b/spec/workers/delete_user_worker_spec.rb
@@ -21,4 +21,54 @@ RSpec.describe DeleteUserWorker, feature_category: :user_management do
described_class.new.perform(current_user.id, user.id, { "test" => "test" })
end
+
+ shared_examples 'does nothing' do
+ it "does not instantiate a DeleteUserWorker" do
+ expect(Users::DestroyService).not_to receive(:new)
+
+ perform
+ end
+ end
+
+ context 'when user is banned' do
+ subject(:perform) { described_class.new.perform(current_user.id, user.id) }
+
+ before do
+ user.ban
+ end
+
+ it_behaves_like 'does nothing'
+
+ context 'when delay_delete_own_user feature flag is disabled' do
+ before do
+ stub_feature_flags(delay_delete_own_user: false)
+ end
+
+ it "proceeds with deletion" do
+ expect_next_instance_of(Users::DestroyService) do |service|
+ expect(service).to receive(:execute).with(user, {})
+ end
+
+ perform
+ end
+ end
+ end
+
+ context 'when user to delete does not exist' do
+ subject(:perform) { described_class.new.perform(current_user.id, non_existing_record_id) }
+
+ it_behaves_like 'does nothing'
+ end
+
+ context 'when current user does not exist' do
+ subject(:perform) { described_class.new.perform(non_existing_record_id, user.id) }
+
+ it_behaves_like 'does nothing'
+ end
+
+ context 'when user to delete and current user do not exist' do
+ subject(:perform) { described_class.new.perform(non_existing_record_id, non_existing_record_id) }
+
+ it_behaves_like 'does nothing'
+ end
end
diff --git a/yarn.lock b/yarn.lock
index c46ffb26473..589f4fe050b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -12878,10 +12878,10 @@ webpack-dev-middleware@^5.3.1:
range-parser "^1.2.1"
schema-utils "^4.0.0"
-webpack-dev-server@4.13.2:
- version "4.13.2"
- resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.13.2.tgz#d97445481d78691efe6d9a3b230833d802fc31f9"
- integrity sha512-5i6TrGBRxG4vnfDpB6qSQGfnB6skGBXNL5/542w2uRGLimX6qeE5BQMLrzIC3JYV/xlGOv+s+hTleI9AZKUQNw==
+webpack-dev-server@4.13.3:
+ version "4.13.3"
+ resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.13.3.tgz#9feb740b8b56b886260bae1360286818a221bae8"
+ integrity sha512-KqqzrzMRSRy5ePz10VhjyL27K2dxqwXQLP5rAKwRJBPUahe7Z2bBWzHw37jeb8GCPKxZRO79ZdQUAPesMh/Nug==
dependencies:
"@types/bonjour" "^3.5.9"
"@types/connect-history-api-fallback" "^1.3.5"