summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-03-23 15:11:27 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2023-03-23 15:11:27 +0000
commit003d7f2a09668af85f94e48ed49d60862b96d8f8 (patch)
tree10f9baf4674416a5a7ca376bcc651973a56917b5
parente10ea43772b9a6be150a074be7e26bfd6fa0380e (diff)
downloadgitlab-ce-003d7f2a09668af85f94e48ed49d60862b96d8f8.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.rubocop_todo/rspec/missing_feature_category.yml1
-rw-r--r--.rubocop_todo/rspec/misspelled_aggregate_failures.yml4
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--app/assets/javascripts/graphql_shared/subscriptions/work_item_dates.subscription.graphql (renamed from app/assets/javascripts/work_items/graphql/work_item_dates.subscription.graphql)4
-rw-r--r--app/assets/javascripts/header_search/components/app.vue17
-rw-r--r--app/assets/javascripts/header_search/constants.js2
-rw-r--r--app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue26
-rw-r--r--app/assets/javascripts/sidebar/constants.js2
-rw-r--r--app/assets/javascripts/super_sidebar/components/nav_item.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/source_viewer/components/chunk_line.vue4
-rw-r--r--app/assets/javascripts/work_items/components/notes/work_item_add_note.vue16
-rw-r--r--app/assets/javascripts/work_items/components/notes/work_item_comment_form.vue20
-rw-r--r--app/assets/javascripts/work_items/components/notes/work_item_discussion.vue17
-rw-r--r--app/assets/javascripts/work_items/components/notes/work_item_note.vue11
-rw-r--r--app/assets/javascripts/work_items/components/work_item_detail.vue3
-rw-r--r--app/assets/javascripts/work_items/components/work_item_detail_modal.vue2
-rw-r--r--app/assets/javascripts/work_items/components/work_item_notes.vue20
-rw-r--r--app/assets/stylesheets/framework/header.scss10
-rw-r--r--app/assets/stylesheets/framework/icons.scss1
-rw-r--r--app/assets/stylesheets/framework/variables.scss1
-rw-r--r--app/assets/stylesheets/utilities.scss192
-rw-r--r--app/controllers/projects/blob_controller.rb1
-rw-r--r--app/controllers/projects/issues_controller.rb1
-rw-r--r--app/controllers/projects/tree_controller.rb1
-rw-r--r--app/controllers/projects_controller.rb1
-rw-r--r--app/models/broadcast_message.rb5
-rw-r--r--app/presenters/packages/npm/package_presenter.rb85
-rw-r--r--app/services/bulk_imports/create_service.rb43
-rw-r--r--app/services/packages/npm/generate_metadata_service.rb111
-rw-r--r--app/views/shared/_file_highlight.html.haml10
-rw-r--r--config/feature_flags/development/real_time_issue_due_date.yml (renamed from config/feature_flags/development/file_line_blame.yml)12
-rw-r--r--db/docs/broadcast_messages.yml2
-rw-r--r--db/post_migrate/20230321095759_remove_namespaces_broadcast_messages_namespace_id_fk.rb20
-rw-r--r--db/schema_migrations/202303210957591
-rw-r--r--db/structure.sql3
-rw-r--r--doc/administration/reference_architectures/10k_users.md29
-rw-r--r--doc/administration/reference_architectures/25k_users.md29
-rw-r--r--doc/administration/reference_architectures/2k_users.md16
-rw-r--r--doc/administration/reference_architectures/3k_users.md30
-rw-r--r--doc/administration/reference_architectures/50k_users.md29
-rw-r--r--doc/administration/reference_architectures/5k_users.md30
-rw-r--r--doc/administration/reference_architectures/index.md191
-rw-r--r--doc/administration/sidekiq/index.md15
-rw-r--r--doc/user/analytics/code_review_analytics.md4
-rw-r--r--doc/user/analytics/dora_metrics.md1
-rw-r--r--doc/user/project/merge_requests/reviews/index.md6
-rw-r--r--doc/user/project/quick_actions.md5
-rw-r--r--doc/user/project/settings/index.md1
-rw-r--r--lib/api/concerns/packages/npm_endpoints.rb9
-rw-r--r--lib/bulk_imports/clients/http.rb2
-rw-r--r--lib/bulk_imports/error.rb9
-rw-r--r--locale/gitlab.pot6
-rw-r--r--package.json8
-rw-r--r--rubocop/cop/rspec/misspelled_aggregate_failures.rb35
-rwxr-xr-xscripts/decomposition/generate-loose-foreign-key5
-rw-r--r--spec/db/schema_spec.rb1
-rw-r--r--spec/features/projects/work_items/work_item_spec.rb2
-rw-r--r--spec/frontend/header_search/components/app_spec.js31
-rw-r--r--spec/frontend/sidebar/components/date/sidebar_date_widget_spec.js39
-rw-r--r--spec/frontend/sidebar/mock_data.js11
-rw-r--r--spec/frontend/vue_shared/components/source_viewer/components/chunk_line_spec.js6
-rw-r--r--spec/frontend/vue_shared/components/source_viewer/components/chunk_spec.js1
-rw-r--r--spec/frontend/work_items/components/notes/work_item_add_note_spec.js2
-rw-r--r--spec/frontend/work_items/components/notes/work_item_comment_form_spec.js6
-rw-r--r--spec/frontend/work_items/components/notes/work_item_discussion_spec.js2
-rw-r--r--spec/frontend/work_items/components/notes/work_item_note_spec.js2
-rw-r--r--spec/frontend/work_items/components/work_item_detail_spec.js2
-rw-r--r--spec/frontend/work_items/components/work_item_notes_spec.js12
-rw-r--r--spec/frontend/work_items/mock_data.js6
-rw-r--r--spec/frontend/work_items/router_spec.js2
-rw-r--r--spec/lib/api/github/entities_spec.rb2
-rw-r--r--spec/lib/bulk_imports/clients/graphql_spec.rb6
-rw-r--r--spec/lib/gitlab/redis/multi_store_spec.rb32
-rw-r--r--spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb4
-rw-r--r--spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb2
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb2
-rw-r--r--spec/lib/gitlab/utils/measuring_spec.rb2
-rw-r--r--spec/migrations/schedule_recalculate_vulnerability_finding_signatures_for_findings_spec.rb2
-rw-r--r--spec/presenters/packages/npm/package_presenter_spec.rb161
-rw-r--r--spec/requests/api/bulk_imports_spec.rb36
-rw-r--r--spec/requests/api/graphql/jobs_query_spec.rb8
-rw-r--r--spec/requests/api/graphql/mutations/ci/job/play_spec.rb2
-rw-r--r--spec/requests/api/projects_spec.rb2
-rw-r--r--spec/requests/api/search_spec.rb2
-rw-r--r--spec/requests/api/tags_spec.rb2
-rw-r--r--spec/rubocop/cop/rspec/misspelled_aggregate_failures_spec.rb136
-rw-r--r--spec/services/bulk_imports/create_service_spec.rb144
-rw-r--r--spec/services/ci/delete_objects_service_spec.rb2
-rw-r--r--spec/services/packages/npm/generate_metadata_service_spec.rb173
-rw-r--r--spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb2
-rw-r--r--spec/support/shared_examples/features/work_items_shared_examples.rb56
-rw-r--r--spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb43
-rw-r--r--spec/support_specs/matchers/event_store_spec.rb2
-rw-r--r--spec/workers/concerns/cronjob_queue_spec.rb2
-rw-r--r--yarn.lock54
96 files changed, 1138 insertions, 983 deletions
diff --git a/.rubocop_todo/rspec/missing_feature_category.yml b/.rubocop_todo/rspec/missing_feature_category.yml
index 5cdd4d2adb1..8ceb7c4f3a7 100644
--- a/.rubocop_todo/rspec/missing_feature_category.yml
+++ b/.rubocop_todo/rspec/missing_feature_category.yml
@@ -5939,7 +5939,6 @@ RSpec/MissingFeatureCategory:
- 'spec/support_specs/helpers/stub_feature_flags_spec.rb'
- 'spec/support_specs/helpers/stub_method_calls_spec.rb'
- 'spec/support_specs/matchers/be_sorted_spec.rb'
- - 'spec/support_specs/matchers/event_store_spec.rb'
- 'spec/support_specs/matchers/exceed_query_limit_helpers_spec.rb'
- 'spec/support_specs/time_travel_spec.rb'
- 'spec/tasks/admin_mode_spec.rb'
diff --git a/.rubocop_todo/rspec/misspelled_aggregate_failures.yml b/.rubocop_todo/rspec/misspelled_aggregate_failures.yml
new file mode 100644
index 00000000000..75465411b63
--- /dev/null
+++ b/.rubocop_todo/rspec/misspelled_aggregate_failures.yml
@@ -0,0 +1,4 @@
+---
+# Cop supports --autocorrect.
+RSpec/MisspelledAggregateFailures:
+ Details: grace period
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 579cb4cffaf..9638906b90e 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-f4946fa24a5572704afdfc0050db44f099911c69
+4726cac92d47e773b875c6e64ebd4a3a73e0e69d
diff --git a/app/assets/javascripts/work_items/graphql/work_item_dates.subscription.graphql b/app/assets/javascripts/graphql_shared/subscriptions/work_item_dates.subscription.graphql
index d8760f147e1..28405d9dc9b 100644
--- a/app/assets/javascripts/work_items/graphql/work_item_dates.subscription.graphql
+++ b/app/assets/javascripts/graphql_shared/subscriptions/work_item_dates.subscription.graphql
@@ -10,5 +10,9 @@ subscription issuableDatesUpdated($issuableId: IssuableID!) {
}
}
}
+ ... on Issue {
+ id
+ dueDate
+ }
}
}
diff --git a/app/assets/javascripts/header_search/components/app.vue b/app/assets/javascripts/header_search/components/app.vue
index 7dc1740b729..306183ebcca 100644
--- a/app/assets/javascripts/header_search/components/app.vue
+++ b/app/assets/javascripts/header_search/components/app.vue
@@ -35,6 +35,7 @@ import {
IS_SEARCHING,
IS_FOCUSED,
IS_NOT_FOCUSED,
+ DROPDOWN_CLOSE_TIMEOUT,
} from '../constants';
import HeaderSearchAutocompleteItems from './header_search_autocomplete_items.vue';
import HeaderSearchDefaultItems from './header_search_default_items.vue';
@@ -166,13 +167,17 @@ export default {
});
},
collapseAndCloseSearchBar() {
- this.isFocused = false;
- this.$emit('collapseSearchBar');
+ // without timeout dropdown closes
+ // before click event is dispatched
+ setTimeout(() => {
+ this.isFocused = false;
+ this.$emit('collapseSearchBar');
- Tracking.event(undefined, 'blur_input', {
- label: 'global_search',
- property: 'navigation_top',
- });
+ Tracking.event(undefined, 'blur_input', {
+ label: 'global_search',
+ property: 'navigation_top',
+ });
+ }, DROPDOWN_CLOSE_TIMEOUT);
},
submitSearch() {
if (this.search?.length <= SEARCH_SHORTCUTS_MIN_CHARACTERS && this.currentFocusIndex < 0) {
diff --git a/app/assets/javascripts/header_search/constants.js b/app/assets/javascripts/header_search/constants.js
index b9bb4e573fd..47aeb2f9caa 100644
--- a/app/assets/javascripts/header_search/constants.js
+++ b/app/assets/javascripts/header_search/constants.js
@@ -31,3 +31,5 @@ export const IS_NOT_FOCUSED = 'is-not-focused';
export const FETCH_TYPES = ['generic', 'search'];
export const SEARCH_INPUT_FIELD_MAX_WIDTH = '640px';
+
+export const DROPDOWN_CLOSE_TIMEOUT = 200;
diff --git a/app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue b/app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue
index 190b8c1de62..b654c6e4903 100644
--- a/app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue
+++ b/app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue
@@ -4,6 +4,7 @@ import { createAlert } from '~/alert';
import { TYPE_ISSUE } from '~/issues/constants';
import { dateInWords, formatDate, parsePikadayDate } from '~/lib/utils/datetime_utility';
import { __, sprintf } from '~/locale';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { dateFields, dateTypes, dueDateQueries, startDateQueries, Tracking } from '../../constants';
import SidebarEditableItem from '../sidebar_editable_item.vue';
import SidebarFormattedDate from './sidebar_formatted_date.vue';
@@ -30,6 +31,7 @@ export default {
SidebarFormattedDate,
SidebarInheritDate,
},
+ mixins: [glFeatureFlagsMixin()],
inject: ['canUpdate'],
props: {
iid: {
@@ -106,6 +108,19 @@ export default {
),
});
},
+ subscribeToMore: {
+ document() {
+ return this.dateQueries[this.issuableType].subscription;
+ },
+ variables() {
+ return {
+ issuableId: this.issuableId,
+ };
+ },
+ skip() {
+ return this.skipIssueDueDateSubscription;
+ },
+ },
},
},
computed: {
@@ -163,6 +178,17 @@ export default {
dataTestId() {
return this.dateType === dateTypes.start ? 'sidebar-start-date' : 'sidebar-due-date';
},
+ issuableId() {
+ return this.issuable.id;
+ },
+ skipIssueDueDateSubscription() {
+ return (
+ this.issuableType !== TYPE_ISSUE ||
+ !this.issuableId ||
+ this.isLoading ||
+ !this.glFeatures?.realTimeIssueDueDate
+ );
+ },
},
methods: {
epicDatePopoverEl() {
diff --git a/app/assets/javascripts/sidebar/constants.js b/app/assets/javascripts/sidebar/constants.js
index 7bca83c4142..0f82182c6e2 100644
--- a/app/assets/javascripts/sidebar/constants.js
+++ b/app/assets/javascripts/sidebar/constants.js
@@ -13,6 +13,7 @@ import {
WORKSPACE_PROJECT,
} from '~/issues/constants';
import updateAlertAssigneesMutation from '~/vue_shared/alert_details/graphql/mutations/alert_set_assignees.mutation.graphql';
+import issuableDatesUpdatedSubscription from '../graphql_shared/subscriptions/work_item_dates.subscription.graphql';
import updateTestCaseLabelsMutation from './components/labels/labels_select_widget/graphql/update_test_case_labels.mutation.graphql';
import epicLabelsQuery from './components/labels/labels_select_widget/graphql/epic_labels.query.graphql';
import updateEpicLabelsMutation from './components/labels/labels_select_widget/graphql/epic_update_labels.mutation.graphql';
@@ -218,6 +219,7 @@ export const dueDateQueries = {
[TYPE_ISSUE]: {
query: issueDueDateQuery,
mutation: updateIssueDueDateMutation,
+ subscription: issuableDatesUpdatedSubscription,
},
[TYPE_EPIC]: {
query: epicDueDateQuery,
diff --git a/app/assets/javascripts/super_sidebar/components/nav_item.vue b/app/assets/javascripts/super_sidebar/components/nav_item.vue
index b101cbce22a..14cbeda84c7 100644
--- a/app/assets/javascripts/super_sidebar/components/nav_item.vue
+++ b/app/assets/javascripts/super_sidebar/components/nav_item.vue
@@ -70,7 +70,7 @@ export default {
return {
// Reset user agent styles on <button>
'gl-appearance-none gl-border-0 gl-bg-transparent gl-text-left': this.isSection,
- 'gl-w-full gl-focus': this.isSection,
+ 'gl-w-full gl-focus--focus': this.isSection,
'gl-bg-t-gray-a-08': this.isActive,
...this.linkClasses,
};
diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue b/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue
index 092e8ba6c15..d77061d4b31 100644
--- a/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue
+++ b/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue
@@ -1,6 +1,5 @@
<script>
import { GlIntersectionObserver } from '@gitlab/ui';
-import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import SafeHtml from '~/vue_shared/directives/safe_html';
import { getPageParamValue, getPageSearchString } from '~/blob/utils';
@@ -21,7 +20,6 @@ export default {
directives: {
SafeHtml,
},
- mixins: [glFeatureFlagMixin()],
props: {
isHighlighted: {
type: Boolean,
@@ -69,7 +67,6 @@ export default {
return this.content.split('\n');
},
pageSearchString() {
- if (!this.glFeatures.fileLineBlame) return '';
const page = getPageParamValue(this.number);
return getPageSearchString(this.blamePath, page);
},
@@ -106,7 +103,6 @@ export default {
class="gl-p-0! gl-z-index-3 diff-line-num gl-border-r gl-display-flex line-links line-numbers"
>
<a
- v-if="glFeatures.fileLineBlame"
class="gl-user-select-none gl-shadow-none! file-line-blame"
:href="`${blamePath}${pageSearchString}#L${calculateLineNumber(index)}`"
></a>
diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk_line.vue b/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk_line.vue
index ce6741f33b1..1690e666d3c 100644
--- a/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk_line.vue
+++ b/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk_line.vue
@@ -1,13 +1,11 @@
<script>
import SafeHtml from '~/vue_shared/directives/safe_html';
-import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { getPageParamValue, getPageSearchString } from '~/blob/utils';
export default {
directives: {
SafeHtml,
},
- mixins: [glFeatureFlagMixin()],
props: {
number: {
type: Number,
@@ -28,7 +26,6 @@ export default {
},
computed: {
pageSearchString() {
- if (!this.glFeatures.fileLineBlame) return '';
const page = getPageParamValue(this.number);
return getPageSearchString(this.blamePath, page);
},
@@ -41,7 +38,6 @@ export default {
class="gl-p-0! gl-absolute gl-z-index-3 diff-line-num gl-border-r gl-display-flex line-links line-numbers"
>
<a
- v-if="glFeatures.fileLineBlame"
class="gl-user-select-none gl-shadow-none! file-line-blame"
:href="`${blamePath}${pageSearchString}#L${number}`"
></a>
diff --git a/app/assets/javascripts/work_items/components/notes/work_item_add_note.vue b/app/assets/javascripts/work_items/components/notes/work_item_add_note.vue
index a282f7e3ac3..85fab772b9a 100644
--- a/app/assets/javascripts/work_items/components/notes/work_item_add_note.vue
+++ b/app/assets/javascripts/work_items/components/notes/work_item_add_note.vue
@@ -66,6 +66,15 @@ export default {
required: false,
default: ASC,
},
+ markdownPreviewPath: {
+ type: String,
+ required: true,
+ },
+ autocompleteDataSources: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
},
data() {
return {
@@ -109,11 +118,6 @@ export default {
property: `type_${this.workItemType}`,
};
},
- markdownPreviewPath() {
- return `${gon.relative_url_root || ''}/${this.fullPath}/preview_markdown?target_type=${
- this.workItemType
- }`;
- },
isLockedOutOrSignedOut() {
return !this.signedIn || !this.canUpdate;
},
@@ -241,6 +245,8 @@ export default {
:aria-label="__('Add a reply')"
:is-submitting="isSubmitting"
:autosave-key="autosaveKey"
+ :autocomplete-data-sources="autocompleteDataSources"
+ :markdown-preview-path="markdownPreviewPath"
@submitForm="updateWorkItem"
@cancelEditing="cancelEditing"
/>
diff --git a/app/assets/javascripts/work_items/components/notes/work_item_comment_form.vue b/app/assets/javascripts/work_items/components/notes/work_item_comment_form.vue
index a3ebd51f76d..c3564f1cac5 100644
--- a/app/assets/javascripts/work_items/components/notes/work_item_comment_form.vue
+++ b/app/assets/javascripts/work_items/components/notes/work_item_comment_form.vue
@@ -2,7 +2,6 @@
import { GlButton } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import { s__, __ } from '~/locale';
-import { joinPaths } from '~/lib/utils/url_utility';
import { getDraft, clearDraft, updateDraft } from '~/lib/utils/autosave';
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
import MarkdownEditor from '~/vue_shared/components/markdown/markdown_editor.vue';
@@ -44,6 +43,15 @@ export default {
required: false,
default: __('Comment'),
},
+ markdownPreviewPath: {
+ type: String,
+ required: true,
+ },
+ autocompleteDataSources: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
},
data() {
return {
@@ -51,14 +59,6 @@ export default {
};
},
computed: {
- markdownPreviewPath() {
- return joinPaths(
- '/',
- gon.relative_url_root || '',
- this.fullPath,
- `/preview_markdown?target_type=${this.workItemType}`,
- );
- },
formFieldProps() {
return {
'aria-label': this.ariaLabel,
@@ -103,11 +103,13 @@ export default {
:value="commentText"
:render-markdown-path="markdownPreviewPath"
:markdown-docs-path="$options.constantOptions.markdownDocsPath"
+ :autocomplete-data-sources="autocompleteDataSources"
:form-field-props="formFieldProps"
data-testid="work-item-add-comment"
class="gl-mb-3"
autofocus
use-bottom-toolbar
+ supports-quick-actions
@input="setCommentText"
@keydown.meta.enter="$emit('submitForm', commentText)"
@keydown.ctrl.enter="$emit('submitForm', commentText)"
diff --git a/app/assets/javascripts/work_items/components/notes/work_item_discussion.vue b/app/assets/javascripts/work_items/components/notes/work_item_discussion.vue
index 1e08fecaf3d..017f9a69444 100644
--- a/app/assets/javascripts/work_items/components/notes/work_item_discussion.vue
+++ b/app/assets/javascripts/work_items/components/notes/work_item_discussion.vue
@@ -54,6 +54,15 @@ export default {
required: false,
default: false,
},
+ markdownPreviewPath: {
+ type: String,
+ required: true,
+ },
+ autocompleteDataSources: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
},
data() {
return {
@@ -142,6 +151,8 @@ export default {
:has-replies="hasReplies"
:work-item-type="workItemType"
:is-modal="isModal"
+ :autocomplete-data-sources="autocompleteDataSources"
+ :markdown-preview-path="markdownPreviewPath"
:class="{ 'gl-mb-4': hasReplies }"
@startReplying="showReplyForm"
@deleteNote="$emit('deleteNote', note)"
@@ -167,6 +178,8 @@ export default {
:work-item-type="workItemType"
:is-modal="isModal"
:class="{ 'gl-mb-4': hasReplies }"
+ :autocomplete-data-sources="autocompleteDataSources"
+ :markdown-preview-path="markdownPreviewPath"
@startReplying="showReplyForm"
@deleteNote="$emit('deleteNote', note)"
@error="$emit('error', $event)"
@@ -186,6 +199,8 @@ export default {
:note="reply"
:work-item-type="workItemType"
:is-modal="isModal"
+ :autocomplete-data-sources="autocompleteDataSources"
+ :markdown-preview-path="markdownPreviewPath"
@startReplying="showReplyForm"
@deleteNote="$emit('deleteNote', reply)"
@error="$emit('error', $event)"
@@ -204,6 +219,8 @@ export default {
:work-item-type="workItemType"
:sort-order="sortOrder"
:add-padding="true"
+ :autocomplete-data-sources="autocompleteDataSources"
+ :markdown-preview-path="markdownPreviewPath"
@cancelEditing="hideReplyForm"
@replied="onReplied"
@replying="onReplying"
diff --git a/app/assets/javascripts/work_items/components/notes/work_item_note.vue b/app/assets/javascripts/work_items/components/notes/work_item_note.vue
index dcb6557600e..fa200de1358 100644
--- a/app/assets/javascripts/work_items/components/notes/work_item_note.vue
+++ b/app/assets/javascripts/work_items/components/notes/work_item_note.vue
@@ -61,6 +61,15 @@ export default {
required: false,
default: false,
},
+ markdownPreviewPath: {
+ type: String,
+ required: true,
+ },
+ autocompleteDataSources: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
},
data() {
return {
@@ -179,6 +188,8 @@ export default {
:autosave-key="autosaveKey"
:initial-value="note.body"
:comment-button-text="__('Save comment')"
+ :autocomplete-data-sources="autocompleteDataSources"
+ :markdown-preview-path="markdownPreviewPath"
@cancelEditing="isEditing = false"
@submitForm="updateNote"
/>
diff --git a/app/assets/javascripts/work_items/components/work_item_detail.vue b/app/assets/javascripts/work_items/components/work_item_detail.vue
index ad7a54aaf16..bc7df3f3621 100644
--- a/app/assets/javascripts/work_items/components/work_item_detail.vue
+++ b/app/assets/javascripts/work_items/components/work_item_detail.vue
@@ -39,7 +39,7 @@ import {
WIDGET_TYPE_NOTES,
} from '../constants';
-import workItemDatesSubscription from '../graphql/work_item_dates.subscription.graphql';
+import workItemDatesSubscription from '../../graphql_shared/subscriptions/work_item_dates.subscription.graphql';
import workItemTitleSubscription from '../graphql/work_item_title.subscription.graphql';
import workItemAssigneesSubscription from '../graphql/work_item_assignees.subscription.graphql';
import workItemMilestoneSubscription from '../graphql/work_item_milestone.subscription.graphql';
@@ -705,6 +705,7 @@ export default {
<work-item-notes
v-if="workItemNotes"
:work-item-id="workItem.id"
+ :work-item-iid="workItem.iid"
:query-variables="queryVariables"
:full-path="fullPath"
:fetch-by-iid="fetchByIid"
diff --git a/app/assets/javascripts/work_items/components/work_item_detail_modal.vue b/app/assets/javascripts/work_items/components/work_item_detail_modal.vue
index 730bdb4e7c7..51b957bb852 100644
--- a/app/assets/javascripts/work_items/components/work_item_detail_modal.vue
+++ b/app/assets/javascripts/work_items/components/work_item_detail_modal.vue
@@ -188,7 +188,7 @@ export default {
:work-item-parent-id="issueGid"
:work-item-id="displayedWorkItemId"
:work-item-iid="displayedWorkItemIid"
- class="gl-p-5 gl-mt-n3 gl-reset-bg gl-isolate"
+ class="gl-p-5 gl-mt-n3 gl-reset-bg gl-isolation-isolate"
@close="hide"
@deleteWorkItem="deleteWorkItem"
@update-modal="updateModal"
diff --git a/app/assets/javascripts/work_items/components/work_item_notes.vue b/app/assets/javascripts/work_items/components/work_item_notes.vue
index 4ca8054fa5f..2a45cc968d8 100644
--- a/app/assets/javascripts/work_items/components/work_item_notes.vue
+++ b/app/assets/javascripts/work_items/components/work_item_notes.vue
@@ -14,7 +14,11 @@ import {
WORK_ITEM_NOTES_FILTER_ONLY_HISTORY,
} from '~/work_items/constants';
import { ASC, DESC } from '~/notes/constants';
-import { getWorkItemNotesQuery } from '~/work_items/utils';
+import {
+ getWorkItemNotesQuery,
+ autocompleteDataSources,
+ markdownPreviewPath,
+} from '~/work_items/utils';
import {
updateCacheAfterCreatingNote,
updateCacheAfterDeletingNote,
@@ -48,6 +52,10 @@ export default {
type: String,
required: true,
},
+ workItemIid: {
+ type: String,
+ required: true,
+ },
queryVariables: {
type: Object,
required: true,
@@ -102,6 +110,12 @@ export default {
formAtTop() {
return this.sortOrder === DESC;
},
+ markdownPreviewPath() {
+ return markdownPreviewPath(this.fullPath, this.workItemIid);
+ },
+ autocompleteDataSources() {
+ return autocompleteDataSources(this.fullPath, this.workItemIid);
+ },
workItemCommentFormProps() {
return {
queryVariables: this.queryVariables,
@@ -110,6 +124,8 @@ export default {
fetchByIid: this.fetchByIid,
workItemType: this.workItemType,
sortOrder: this.sortOrder,
+ markdownPreviewPath: this.markdownPreviewPath,
+ autocompleteDataSources: this.autocompleteDataSources,
};
},
notesArray() {
@@ -357,6 +373,8 @@ export default {
:fetch-by-iid="fetchByIid"
:work-item-type="workItemType"
:is-modal="isModal"
+ :autocomplete-data-sources="autocompleteDataSources"
+ :markdown-preview-path="markdownPreviewPath"
@deleteNote="showDeleteNoteModal($event, discussion)"
@error="$emit('error', $event)"
/>
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index e45341075b6..4eb5a686d43 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -349,16 +349,6 @@ $search-input-field-x-min-width: 200px;
top: 1px;
}
}
-
- .dropdown-menu li a .identicon {
- width: 17px;
- height: 17px;
- font-size: $gl-font-size-xs;
- vertical-align: middle;
- text-indent: 0;
- line-height: $gl-font-size-xs + 2px;
- display: inline-block;
- }
}
.breadcrumbs-list {
diff --git a/app/assets/stylesheets/framework/icons.scss b/app/assets/stylesheets/framework/icons.scss
index f27a36d1966..37a2264122d 100644
--- a/app/assets/stylesheets/framework/icons.scss
+++ b/app/assets/stylesheets/framework/icons.scss
@@ -74,7 +74,6 @@
}
.user-avatar-link {
- display: inline-block;
text-decoration: none;
}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index ca369c2ebc7..509ef5feae8 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -390,7 +390,6 @@ $nav-active-bg: $t-gray-a-08;
* Text
*/
$gl-font-size: 14px;
-$gl-font-size-xs: 11px;
$gl-font-size-small: 12px;
$gl-font-size-large: 16px;
$gl-font-weight-normal: 400;
diff --git a/app/assets/stylesheets/utilities.scss b/app/assets/stylesheets/utilities.scss
index 11f73b592fc..fc5be72f7cf 100644
--- a/app/assets/stylesheets/utilities.scss
+++ b/app/assets/stylesheets/utilities.scss
@@ -71,58 +71,11 @@
// https://gitlab.com/groups/gitlab-org/-/epics/2882
.gl-h-200\! { height: px-to-rem($grid-size * 25) !important; }
-.gl-bg-purple-light { background-color: $purple-light; }
-
-// move this to GitLab UI once onboarding experiment is considered a success
-.gl-py-8 {
- padding-top: $gl-spacing-scale-8;
- padding-bottom: $gl-spacing-scale-8;
-}
-
-.gl-transition-property-stroke-opacity {
- transition-property: stroke-opacity;
-}
-
-.gl-transition-property-stroke {
- transition-property: stroke;
-}
-
-.gl-top-66vh {
- top: 66vh;
-}
-
-.gl-shadow-x0-y0-b3-s1-blue-500 {
- box-shadow: inset 0 0 3px $gl-border-size-1 $blue-500;
-}
-
// This utility is used to force the z-index to match that of dropdown menu's
.gl-z-dropdown-menu\! {
z-index: $zindex-dropdown-menu !important;
}
-.gl-flex-basis-quarter {
- flex-basis: 25%;
-}
-
-// Will be moved to @gitlab/ui (without the !important) in https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1462
-// We only need the bang (!) version until the non-bang version is added to
-// @gitlab/ui utitlities.scss. Once there, it will get loaded in the correct
-// order to properly override `.gl-mt-6` which is used for narrower screen
-// widths (currently that style gets added to the application.css stylesheet
-// after this one, so it takes precedence).
-.gl-md-mt-11\! {
- @media (min-width: $breakpoint-md) {
- margin-top: $gl-spacing-scale-11 !important;
- }
-}
-
-// Same as above (also without the !important) but for overriding `.gl-pt-6`
-.gl-md-pt-11\! {
- @media (min-width: $breakpoint-md) {
- padding-top: $gl-spacing-scale-11 !important;
- }
-}
-
// This is used to help prevent issues with margin collapsing.
// See https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Mastering_margin_collapsing.
.gl-force-block-formatting-context::after {
@@ -130,34 +83,6 @@
display: flex;
}
-.gl-sm-mr-3 {
- @include media-breakpoint-up(sm) {
- margin-right: $gl-spacing-scale-3;
- }
-}
-
-.gl-xl-ml-3 {
- @include media-breakpoint-up(lg) {
- margin-left: $gl-spacing-scale-3;
- }
-}
-
-.gl-mr-n2 {
- margin-right: -$gl-spacing-scale-2;
-}
-
-.gl-w-grid-size-30 {
- width: $grid-size * 30;
-}
-
-.gl-w-grid-size-40 {
- width: $grid-size * 40;
-}
-
-.gl-max-w-50p {
- max-width: 50%;
-}
-
/**
Note: ::-webkit-scrollbar is a non-standard rule only
supported by webkit browsers.
@@ -181,59 +106,6 @@
@include gl-focus($gl-border-size-1, $gray-900, true);
}
-/*
-All of the following (up until the "End gitlab-ui#1709" comment) will be moved
-to @gitlab/ui by https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1709
-*/
-.gl-md-grid-template-columns-3 {
- @include media-breakpoint-up(md) {
- grid-template-columns: repeat(3, 1fr);
- }
-}
-
-.gl-lg-grid-template-columns-4 {
- @include media-breakpoint-up(lg) {
- grid-template-columns: repeat(4, 1fr);
- }
-}
-
-.gl-max-w-48 {
- max-width: $gl-spacing-scale-48;
-}
-
-.gl-max-w-75 {
- max-width: $gl-spacing-scale-75;
-}
-
-.gl-md-pt-11 {
- @include media-breakpoint-up(md) {
- padding-top: $gl-spacing-scale-11 !important; // only need !important for now so that it overrides styles from @gitlab/ui which currently take precedence
- }
-}
-
-.gl-md-mb-6 {
- @include media-breakpoint-up(md) {
- margin-bottom: $gl-spacing-scale-6 !important; // only need !important for now so that it overrides styles from @gitlab/ui which currently take precedence
- }
-}
-
-.gl-md-mb-12 {
- @include media-breakpoint-up(md) {
- margin-bottom: $gl-spacing-scale-12 !important; // only need !important for now so that it overrides styles from @gitlab/ui which currently take precedence
- }
-}
-
-.gl-mt-n5 {
- margin-top: -$gl-spacing-scale-5;
-}
-
-// Utils below are very specific so cannot be part of GitLab UI
-.gl-md-mt-5 {
- @include gl-media-breakpoint-up(md) {
- margin-top: $gl-spacing-scale-5;
- }
-}
-
.gl-sm-mr-0\! {
@include gl-media-breakpoint-down(md) {
margin-right: 0 !important;
@@ -245,67 +117,3 @@ to @gitlab/ui by https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1709
margin-bottom: $gl-spacing-scale-5;
}
}
-
-.gl-md-mb-3\! {
- @include gl-media-breakpoint-up(md) {
- margin-bottom: $gl-spacing-scale-3 !important;
- }
-}
-
-.gl-font-xs {
- font-size: px-to-rem(10px);
-}
-
-.gl-line-height-12 {
- line-height: px-to-rem(12px);
-}
-
-.gl-letter-spacing-06em {
- letter-spacing: 0.06em;
-}
-
-.gl-flex-flow-row-wrap {
- flex-flow: row wrap;
-}
-
-.gl-isolate {
- isolation: isolate;
-}
-
-.gl-text-transform-uppercase {
- text-transform: uppercase;
-}
-
-/*
- * The below style will be moved to @gitlab/ui by
- * https://gitlab.com/gitlab-org/gitlab-ui/-/issues/2177
- */
-.gl-gap-2 {
- gap: $gl-spacing-scale-2;
-}
-
-.gl-bg-t-gray-a-08 {
- background-color: $t-gray-a-08;
-}
-
-.gl-hover-bg-t-gray-a-08:hover {
- background-color: $t-gray-a-08;
-}
-
-.gl-inset-border-1-gray-a-08 {
- box-shadow: inset 0 0 0 $gl-border-size-1 $t-gray-a-08;
-}
-
-.gl-line-height-1 {
- line-height: 1;
-}
-
-.gl-focus:focus {
- @include gl-focus;
-}
-
-.gl-md-justify-content-space-between {
- @include gl-media-breakpoint-up(md) {
- justify-content: space-between;
- }
-}
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 3413aeb6f8a..e108150e432 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -48,7 +48,6 @@ class Projects::BlobController < Projects::ApplicationController
before_action do
push_frontend_feature_flag(:highlight_js, @project)
- push_frontend_feature_flag(:file_line_blame, @project)
push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks)
end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 6e38de8b0ea..f4eeee96813 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -67,6 +67,7 @@ class Projects::IssuesController < Projects::ApplicationController
push_force_frontend_feature_flag(:work_items_mvc_2, project&.work_items_mvc_2_feature_flag_enabled?)
push_frontend_feature_flag(:epic_widget_edit_confirmation, project)
push_frontend_feature_flag(:incident_event_tags, project)
+ push_frontend_feature_flag(:real_time_issue_due_date, project)
end
around_action :allow_gitaly_ref_name_caching, only: [:discussions]
diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb
index ba18a2e0dce..6e7630fa1b2 100644
--- a/app/controllers/projects/tree_controller.rb
+++ b/app/controllers/projects/tree_controller.rb
@@ -18,7 +18,6 @@ class Projects::TreeController < Projects::ApplicationController
before_action do
push_frontend_feature_flag(:highlight_js, @project)
- push_frontend_feature_flag(:file_line_blame, @project)
push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks)
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index f18055f80b7..9cbefe101b9 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -38,7 +38,6 @@ class ProjectsController < Projects::ApplicationController
before_action do
push_frontend_feature_flag(:highlight_js, @project)
- push_frontend_feature_flag(:file_line_blame, @project)
push_frontend_feature_flag(:synchronize_fork, @project)
push_licensed_feature(:file_locks) if @project.present? && @project.licensed_feature_available?(:file_locks)
push_licensed_feature(:security_orchestration_policies) if @project.present? && @project.licensed_feature_available?(:security_orchestration_policies)
diff --git a/app/models/broadcast_message.rb b/app/models/broadcast_message.rb
index c5a234ffa69..14aecbc9420 100644
--- a/app/models/broadcast_message.rb
+++ b/app/models/broadcast_message.rb
@@ -1,8 +1,9 @@
# frozen_string_literal: true
-class BroadcastMessage < ApplicationRecord
+class BroadcastMessage < MainClusterwide::ApplicationRecord
include CacheMarkdownField
include Sortable
+ include IgnorableColumns
ALLOWED_TARGET_ACCESS_LEVELS = [
Gitlab::Access::GUEST,
@@ -12,6 +13,8 @@ class BroadcastMessage < ApplicationRecord
Gitlab::Access::OWNER
].freeze
+ ignore_column :namespace_id, remove_with: '16.0', remove_after: '2022-06-22'
+
cache_markdown_field :message, pipeline: :broadcast_message, whitelisted: true
validates :message, presence: true
diff --git a/app/presenters/packages/npm/package_presenter.rb b/app/presenters/packages/npm/package_presenter.rb
index 57bdd373309..42f61182ab8 100644
--- a/app/presenters/packages/npm/package_presenter.rb
+++ b/app/presenters/packages/npm/package_presenter.rb
@@ -3,94 +3,25 @@
module Packages
module Npm
class PackagePresenter
- include API::Helpers::RelatedResourcesHelpers
-
- # Allowed fields are those defined in the abbreviated form
- # defined here: https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#abbreviated-version-object
- # except: name, version, dist, dependencies and xDependencies. Those are generated by this presenter.
- PACKAGE_JSON_ALLOWED_FIELDS = %w[deprecated bin directories dist engines _hasShrinkwrap].freeze
-
- attr_reader :name, :packages
+ def initialize(metadata)
+ @metadata = metadata
+ end
- def initialize(name, packages)
- @name = name
- @packages = packages
+ def name
+ metadata[:name]
end
def versions
- package_versions = {}
-
- packages.each_batch do |relation|
- batched_packages = relation.including_dependency_links
- .preload_files
- .preload_npm_metadatum
-
- batched_packages.each do |package|
- package_file = package.installable_package_files.last
-
- next unless package_file
-
- package_versions[package.version] = build_package_version(package, package_file)
- end
- end
-
- package_versions
+ metadata[:versions]
end
def dist_tags
- build_package_tags.tap { |t| t["latest"] ||= sorted_versions.last }
+ metadata[:dist_tags]
end
private
- def build_package_tags
- package_tags.to_h { |tag| [tag.name, tag.package.version] }
- end
-
- def build_package_version(package, package_file)
- abbreviated_package_json(package).merge(
- name: package.name,
- version: package.version,
- dist: {
- shasum: package_file.file_sha1,
- tarball: tarball_url(package, package_file)
- }
- ).tap do |package_version|
- package_version.merge!(build_package_dependencies(package))
- end
- end
-
- def tarball_url(package, package_file)
- expose_url "#{api_v4_projects_path(id: package.project_id)}" \
- "/packages/npm/#{package.name}" \
- "/-/#{package_file.file_name}"
- end
-
- def build_package_dependencies(package)
- dependencies = Hash.new { |h, key| h[key] = {} }
-
- package.dependency_links.each do |dependency_link|
- dependency = dependency_link.dependency
- dependencies[dependency_link.dependency_type][dependency.name] = dependency.version_pattern
- end
-
- dependencies
- end
-
- def sorted_versions
- versions = packages.pluck_versions.compact
- VersionSorter.sort(versions)
- end
-
- def package_tags
- Packages::Tag.for_package_ids(packages.last_of_each_version_ids)
- .preload_package
- end
-
- def abbreviated_package_json(package)
- json = package.npm_metadatum&.package_json || {}
- json.slice(*PACKAGE_JSON_ALLOWED_FIELDS)
- end
+ attr_reader :metadata
end
end
end
diff --git a/app/services/bulk_imports/create_service.rb b/app/services/bulk_imports/create_service.rb
index 1db978bf1ec..ac019d9ec5b 100644
--- a/app/services/bulk_imports/create_service.rb
+++ b/app/services/bulk_imports/create_service.rb
@@ -27,11 +27,6 @@
#
module BulkImports
class CreateService
- ENTITY_TYPES_MAPPING = {
- 'group_entity' => 'groups',
- 'project_entity' => 'projects'
- }.freeze
-
attr_reader :current_user, :params, :credentials
def initialize(current_user, params, credentials)
@@ -62,7 +57,6 @@ module BulkImports
def validate!
client.validate_instance_version!
- validate_setting_enabled!
client.validate_import_scopes!
end
@@ -94,28 +88,6 @@ module BulkImports
end
end
- def validate_setting_enabled!
- source_full_path, source_type = params[0].values_at(:source_full_path, :source_type)
- entity_type = ENTITY_TYPES_MAPPING.fetch(source_type)
- if source_full_path =~ /^[0-9]+$/
- query = query_type(entity_type)
- response = graphql_client.execute(
- graphql_client.parse(query.to_s),
- { full_path: source_full_path }
- ).original_hash
-
- source_entity_identifier = ::GlobalID.parse(response.dig(*query.data_path, 'id')).model_id
- else
- source_entity_identifier = ERB::Util.url_encode(source_full_path)
- end
-
- client.get("/#{entity_type}/#{source_entity_identifier}/export_relations/status")
- # the source instance will return a 404 if the feature is disabled as the endpoint won't be available
- rescue Gitlab::HTTP::BlockedUrlError
- rescue BulkImports::NetworkError
- raise ::BulkImports::Error.setting_not_enabled
- end
-
def track_access_level(entity_params)
Gitlab::Tracking.event(
self.class.name,
@@ -168,20 +140,5 @@ module BulkImports
token: @credentials[:access_token]
)
end
-
- def graphql_client
- @graphql_client ||= BulkImports::Clients::Graphql.new(
- url: @credentials[:url],
- token: @credentials[:access_token]
- )
- end
-
- def query_type(entity_type)
- if entity_type == 'groups'
- BulkImports::Groups::Graphql::GetGroupQuery.new(context: nil)
- else
- BulkImports::Projects::Graphql::GetProjectQuery.new(context: nil)
- end
- end
end
end
diff --git a/app/services/packages/npm/generate_metadata_service.rb b/app/services/packages/npm/generate_metadata_service.rb
new file mode 100644
index 00000000000..800c3ce19b4
--- /dev/null
+++ b/app/services/packages/npm/generate_metadata_service.rb
@@ -0,0 +1,111 @@
+# frozen_string_literal: true
+
+module Packages
+ module Npm
+ class GenerateMetadataService
+ include API::Helpers::RelatedResourcesHelpers
+
+ # Allowed fields are those defined in the abbreviated form
+ # defined here: https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#abbreviated-version-object
+ # except: name, version, dist, dependencies and xDependencies. Those are generated by this service.
+ PACKAGE_JSON_ALLOWED_FIELDS = %w[deprecated bin directories dist engines _hasShrinkwrap].freeze
+
+ def initialize(name, packages)
+ @name = name
+ @packages = packages
+ end
+
+ def execute(only_dist_tags: false)
+ ServiceResponse.success(payload: metadata(only_dist_tags))
+ end
+
+ private
+
+ attr_reader :name, :packages
+
+ def metadata(only_dist_tags)
+ result = { dist_tags: dist_tags }
+
+ unless only_dist_tags
+ result[:name] = name
+ result[:versions] = versions
+ end
+
+ result
+ end
+
+ def versions
+ package_versions = {}
+
+ packages.each_batch do |relation|
+ batched_packages = relation.including_dependency_links
+ .preload_files
+ .preload_npm_metadatum
+
+ batched_packages.each do |package|
+ package_file = package.installable_package_files.last
+
+ next unless package_file
+
+ package_versions[package.version] = build_package_version(package, package_file)
+ end
+ end
+
+ package_versions
+ end
+
+ def dist_tags
+ build_package_tags.tap { |t| t['latest'] ||= sorted_versions.last }
+ end
+
+ def build_package_tags
+ package_tags.to_h { |tag| [tag.name, tag.package.version] }
+ end
+
+ def build_package_version(package, package_file)
+ abbreviated_package_json(package).merge(
+ name: package.name,
+ version: package.version,
+ dist: {
+ shasum: package_file.file_sha1,
+ tarball: tarball_url(package, package_file)
+ }
+ ).tap do |package_version|
+ package_version.merge!(build_package_dependencies(package))
+ end
+ end
+
+ def tarball_url(package, package_file)
+ expose_url api_v4_projects_packages_npm_package_name___file_name_path(
+ { id: package.project_id, package_name: package.name, file_name: package_file.file_name }, true
+ )
+ end
+
+ def build_package_dependencies(package)
+ dependencies = Hash.new { |h, key| h[key] = {} }
+
+ package.dependency_links.each do |dependency_link|
+ dependency = dependency_link.dependency
+ dependencies[dependency_link.dependency_type][dependency.name] = dependency.version_pattern
+ end
+
+ dependencies
+ end
+
+ def sorted_versions
+ versions = packages.pluck_versions.compact
+ VersionSorter.sort(versions)
+ end
+
+ def package_tags
+ Packages::Tag.for_package_ids(packages.last_of_each_version_ids)
+ .preload_package
+ end
+
+ def abbreviated_package_json(package)
+ json = package.npm_metadatum&.package_json || {}
+ json.slice(*PACKAGE_JSON_ALLOWED_FIELDS)
+ end
+ end
+ end
+end
diff --git a/app/views/shared/_file_highlight.html.haml b/app/views/shared/_file_highlight.html.haml
index a749d1037a1..9dfbad20726 100644
--- a/app/views/shared/_file_highlight.html.haml
+++ b/app/views/shared/_file_highlight.html.haml
@@ -2,14 +2,8 @@
- offset = defined?(first_line_number) ? first_line_number : 1
- highlight = defined?(highlight_line) && highlight_line ? highlight_line - offset : nil
-- file_line_blame = Feature.enabled?(:file_line_blame)
-
-- if file_line_blame
- - line_class = "js-line-links"
- - blame_path = project_blame_path(@project, tree_join(@ref, blob.path))
-- else
- - line_class = nil
- - blame_path = nil
+- line_class = "js-line-links"
+- blame_path = project_blame_path(@project, tree_join(@ref, blob.path))
- highlighted_blob = blob.present.highlight
diff --git a/config/feature_flags/development/file_line_blame.yml b/config/feature_flags/development/real_time_issue_due_date.yml
index b0762b18fed..efe67398be1 100644
--- a/config/feature_flags/development/file_line_blame.yml
+++ b/config/feature_flags/development/real_time_issue_due_date.yml
@@ -1,8 +1,8 @@
---
-name: file_line_blame
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92538
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/370818
-milestone: '15.3'
+name: real_time_issue_due_date
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/114927
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/397720
+milestone: '15.11'
type: development
-group: group::source code
-default_enabled: true
+group: group::application performance
+default_enabled: false
diff --git a/db/docs/broadcast_messages.yml b/db/docs/broadcast_messages.yml
index d6d6a93ad2e..bceb79b7f9c 100644
--- a/db/docs/broadcast_messages.yml
+++ b/db/docs/broadcast_messages.yml
@@ -7,4 +7,4 @@ feature_categories:
description: GitLab can display broadcast messages to users of a GitLab instance
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/f1ecf53c1e55fbbc66cb2d7d12fb411cbfc2ace8
milestone: '6.3'
-gitlab_schema: gitlab_main
+gitlab_schema: gitlab_main_clusterwide
diff --git a/db/post_migrate/20230321095759_remove_namespaces_broadcast_messages_namespace_id_fk.rb b/db/post_migrate/20230321095759_remove_namespaces_broadcast_messages_namespace_id_fk.rb
new file mode 100644
index 00000000000..b22fb1d547d
--- /dev/null
+++ b/db/post_migrate/20230321095759_remove_namespaces_broadcast_messages_namespace_id_fk.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class RemoveNamespacesBroadcastMessagesNamespaceIdFk < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ def up
+ return unless foreign_key_exists?(:broadcast_messages, :namespaces, name: "fk_7bf2ec43da")
+
+ with_lock_retries do
+ remove_foreign_key_if_exists(:broadcast_messages, :namespaces,
+ name: "fk_7bf2ec43da", reverse_lock_order: true)
+ end
+ end
+
+ def down
+ add_concurrent_foreign_key(:broadcast_messages, :namespaces,
+ name: "fk_7bf2ec43da", column: :namespace_id,
+ target_column: :id, on_delete: :cascade)
+ end
+end
diff --git a/db/schema_migrations/20230321095759 b/db/schema_migrations/20230321095759
new file mode 100644
index 00000000000..0f51a5001b8
--- /dev/null
+++ b/db/schema_migrations/20230321095759
@@ -0,0 +1 @@
+f2b7e2856672f294a7686a61d3b953b8341b21b31a60a2f62419e878c3244d64 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 85d72df2101..3ded4c79003 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -34602,9 +34602,6 @@ ALTER TABLE ONLY vulnerabilities
ALTER TABLE ONLY issue_customer_relations_contacts
ADD CONSTRAINT fk_7b92f835bb FOREIGN KEY (contact_id) REFERENCES customer_relations_contacts(id) ON DELETE CASCADE;
-ALTER TABLE ONLY broadcast_messages
- ADD CONSTRAINT fk_7bf2ec43da FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
-
ALTER TABLE ONLY vulnerabilities
ADD CONSTRAINT fk_7c5bb22a22 FOREIGN KEY (due_date_sourcing_milestone_id) REFERENCES milestones(id) ON DELETE SET NULL;
diff --git a/doc/administration/reference_architectures/10k_users.md b/doc/administration/reference_architectures/10k_users.md
index 1934ca6bff0..09ba9838cf8 100644
--- a/doc/administration/reference_architectures/10k_users.md
+++ b/doc/administration/reference_architectures/10k_users.md
@@ -31,8 +31,8 @@ full list of reference architectures, see
| Gitaly<sup>5 6</sup> | 3 | 16 vCPU, 60 GB memory | `n1-standard-16` | `m5.4xlarge` |
| Praefect<sup>5</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
| Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
-| Sidekiq | 4 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` |
-| GitLab Rails | 3 | 32 vCPU, 28.8 GB memory | `n1-highcpu-32` | `c5.9xlarge` |
+| Sidekiq<sup>7</sup> | 4 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` |
+| GitLab Rails<sup>7</sup> | 3 | 32 vCPU, 28.8 GB memory | `n1-highcpu-32` | `c5.9xlarge` |
| Monitoring node | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` |
| Object storage<sup>4</sup> | - | - | - | - |
@@ -53,6 +53,8 @@ full list of reference architectures, see
6. Gitaly has been designed and tested with repositories of varying sizes that follow best practices. However, large
repositories or monorepos that don't follow these practices can significantly impact Gitaly requirements. Refer to
[Large repositories](index.md#large-repositories) for more information.
+7. Can be placed in Auto Scaling Groups (ASGs) as the component doesn't store any [stateful data](index.md#autoscaling-of-stateful-nodes).
+ However, for GitLab Rails certain processes like [migrations](#gitlab-rails-post-configuration) and [Mailroom](../incoming_email.md) should be run on only one node.
<!-- markdownlint-enable MD029 -->
NOTE:
@@ -417,6 +419,11 @@ spread connections equally in practice.
## Configure Consul
+Next, we set up the Consul servers.
+
+NOTE:
+Consul must be deployed in an odd number of 3 nodes or higher. This is to ensure the nodes can take votes as part of a quorum.
+
The following IPs will be used as an example:
- `10.6.0.11`: Consul 1
@@ -801,6 +808,9 @@ Using [Redis](https://redis.io/) in scalable environment is possible using a **P
topology with a [Redis Sentinel](https://redis.io/docs/manual/sentinel/) service to watch and automatically
start the failover procedure.
+NOTE:
+Redis clusters must each be deployed in an odd number of 3 nodes or higher. This is to ensure Redis Sentinel can take votes as part of a quorum. This does not apply when configuring Redis externally, such as a cloud provider service.
+
Redis requires authentication if used with Sentinel. See
[Redis Security](https://redis.io/docs/manual/security/) documentation for more
information. We recommend using a combination of a Redis password and tight
@@ -1336,6 +1346,9 @@ This is how this would work with a Omnibus GitLab PostgreSQL setup:
Praefect is the router and transaction manager for Gitaly Cluster and all connections to Gitaly go through
it. This section details how to configure it.
+NOTE:
+Consul must be deployed in an odd number of 3 nodes or higher. This is to ensure the nodes can take votes as part of a quorum.
+
Praefect requires several secret tokens to secure communications across the Cluster:
- `<praefect_external_token>`: Used for repositories hosted on your Gitaly cluster and can only be accessed by Gitaly clients that carry this token.
@@ -1510,9 +1523,7 @@ Due to Gitaly having notable input and output requirements, we strongly
recommend that all Gitaly nodes use solid-state drives (SSDs). These SSDs
should have a throughput of at least 8,000
input/output operations per second (IOPS) for read operations and 2,000 IOPS for
-write operations. These IOPS values are initial recommendations, and may be
-adjusted to greater or lesser values depending on the scale of your
-environment's workload. If you're running the environment on a Cloud provider,
+write operations. If you're running the environment on a Cloud provider,
refer to their documentation about how to configure IOPS correctly.
Gitaly servers must not be exposed to the public internet, as Gitaly's network
@@ -2089,11 +2100,9 @@ the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html).
sudo gitlab-rake gitlab:db:configure
```
- If you encounter a `rake aborted!` error message stating that PgBouncer is
- failing to connect to PostgreSQL, it may be that your PgBouncer node's IP
- address is missing from PostgreSQL's `trust_auth_cidr_addresses` in `gitlab.rb`
- on your database nodes. Before proceeding, see
- [PgBouncer error `ERROR: pgbouncer cannot connect to server`](../postgresql/replication_and_failover.md#pgbouncer-error-error-pgbouncer-cannot-connect-to-server).
+ Note that this requires the Rails node to be configured to connect to the primary database
+ directly, [bypassing PgBouncer](../postgresql/pgbouncer.md#procedure-for-bypassing-pgbouncer).
+ After migrations have completed, you must configure the node to pass through PgBouncer again.
1. [Configure fast lookup of authorized SSH keys in the database](../operations/fast_ssh_key_lookup.md).
diff --git a/doc/administration/reference_architectures/25k_users.md b/doc/administration/reference_architectures/25k_users.md
index 08cb6e2cdff..f55740bdf9d 100644
--- a/doc/administration/reference_architectures/25k_users.md
+++ b/doc/administration/reference_architectures/25k_users.md
@@ -31,8 +31,8 @@ full list of reference architectures, see
| Gitaly<sup>5 6</sup> | 3 | 32 vCPU, 120 GB memory | `n1-standard-32` | `m5.8xlarge` |
| Praefect<sup>5</sup> | 3 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` |
| Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
-| Sidekiq | 4 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` |
-| GitLab Rails | 5 | 32 vCPU, 28.8 GB memory | `n1-highcpu-32` | `c5.9xlarge` |
+| Sidekiq<sup>7</sup> | 4 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` |
+| GitLab Rails<sup>7</sup> | 5 | 32 vCPU, 28.8 GB memory | `n1-highcpu-32` | `c5.9xlarge` |
| Monitoring node | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` |
| Object storage<sup>4</sup> | - | - | - | - |
@@ -53,6 +53,8 @@ full list of reference architectures, see
6. Gitaly has been designed and tested with repositories of varying sizes that follow best practices. However, large
repositories or monorepos that don't follow these practices can significantly impact Gitaly requirements. Refer to
[Large repositories](index.md#large-repositories) for more information.
+7. Can be placed in Auto Scaling Groups (ASGs) as the component doesn't store any [stateful data](index.md#autoscaling-of-stateful-nodes).
+ However, for GitLab Rails certain processes like [migrations](#gitlab-rails-post-configuration) and [Mailroom](../incoming_email.md) should be run on only one node.
<!-- markdownlint-enable MD029 -->
NOTE:
@@ -434,6 +436,11 @@ spread connections equally in practice.
## Configure Consul
+Next, we set up the Consul servers.
+
+NOTE:
+Consul must be deployed in an odd number of 3 nodes or higher. This is to ensure the nodes can take votes as part of a quorum.
+
The following IPs will be used as an example:
- `10.6.0.11`: Consul 1
@@ -818,6 +825,9 @@ Using [Redis](https://redis.io/) in scalable environment is possible using a **P
topology with a [Redis Sentinel](https://redis.io/docs/manual/sentinel/) service to watch and automatically
start the failover procedure.
+NOTE:
+Redis clusters must each be deployed in an odd number of 3 nodes or higher. This is to ensure Redis Sentinel can take votes as part of a quorum. This does not apply when configuring Redis externally, such as a cloud provider service.
+
Redis requires authentication if used with Sentinel. See
[Redis Security](https://redis.io/docs/manual/security/) documentation for more
information. We recommend using a combination of a Redis password and tight
@@ -1353,6 +1363,9 @@ This is how this would work with a Omnibus GitLab PostgreSQL setup:
Praefect is the router and transaction manager for Gitaly Cluster and all connections to Gitaly go through
it. This section details how to configure it.
+NOTE:
+Praefect must be deployed in an odd number of 3 nodes or higher. This is to ensure the nodes can take votes as part of a quorum.
+
Praefect requires several secret tokens to secure communications across the Cluster:
- `<praefect_external_token>`: Used for repositories hosted on your Gitaly cluster and can only be accessed by Gitaly clients that carry this token.
@@ -1527,9 +1540,7 @@ Due to Gitaly having notable input and output requirements, we strongly
recommend that all Gitaly nodes use solid-state drives (SSDs). These SSDs
should have a throughput of at least 8,000
input/output operations per second (IOPS) for read operations and 2,000 IOPS for
-write operations. These IOPS values are initial recommendations, and may be
-adjusted to greater or lesser values depending on the scale of your
-environment's workload. If you're running the environment on a Cloud provider,
+write operations. If you're running the environment on a Cloud provider,
refer to their documentation about how to configure IOPS correctly.
Gitaly servers must not be exposed to the public internet, as Gitaly's network
@@ -2108,11 +2119,9 @@ the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html).
sudo gitlab-rake gitlab:db:configure
```
- If you encounter a `rake aborted!` error message stating that PgBouncer is
- failing to connect to PostgreSQL, it may be that your PgBouncer node's IP
- address is missing from PostgreSQL's `trust_auth_cidr_addresses` in `gitlab.rb`
- on your database nodes. Before proceeding, see
- [PgBouncer error `ERROR: pgbouncer cannot connect to server`](../postgresql/replication_and_failover.md#pgbouncer-error-error-pgbouncer-cannot-connect-to-server).
+ Note that this requires the Rails node to be configured to connect to the primary database
+ directly, [bypassing PgBouncer](../postgresql/pgbouncer.md#procedure-for-bypassing-pgbouncer).
+ After migrations have completed, you must configure the node to pass through PgBouncer again.
1. [Configure fast lookup of authorized SSH keys in the database](../operations/fast_ssh_key_lookup.md).
diff --git a/doc/administration/reference_architectures/2k_users.md b/doc/administration/reference_architectures/2k_users.md
index d87e8270dcb..893d9eff21c 100644
--- a/doc/administration/reference_architectures/2k_users.md
+++ b/doc/administration/reference_architectures/2k_users.md
@@ -26,7 +26,7 @@ For a full list of reference architectures, see
| PostgreSQL<sup>1</sup> | 1 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` | `D2s v3` |
| Redis<sup>2</sup> | 1 | 1 vCPU, 3.75 GB memory | `n1-standard-1` | `m5.large` | `D2s v3` |
| Gitaly<sup>5</sup> | 1 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
-| GitLab Rails | 2 | 8 vCPU, 7.2 GB memory | `n1-highcpu-8` | `c5.2xlarge` | `F8s v2` |
+| GitLab Rails<sup>6</sup> | 2 | 8 vCPU, 7.2 GB memory | `n1-highcpu-8` | `c5.2xlarge` | `F8s v2` |
| Monitoring node | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
| Object storage<sup>4</sup> | - | - | - | - | - |
@@ -45,6 +45,8 @@ For a full list of reference architectures, see
5. Gitaly has been designed and tested with repositories of varying sizes that follow best practices. However, large
repositories or monorepos that don't follow these practices can significantly impact Gitaly requirements. Refer to
[Large repositories](index.md#large-repositories) for more information.
+6. Can be placed in Auto Scaling Groups (ASGs) as the component doesn't store any [stateful data](index.md#autoscaling-of-stateful-nodes).
+ However, for GitLab Rails certain processes like [migrations](#gitlab-rails-post-configuration) and [Mailroom](../incoming_email.md) should be run on only one node.
<!-- markdownlint-enable MD029 -->
NOTE:
@@ -423,9 +425,7 @@ Due to Gitaly having notable input and output requirements, we strongly
recommend that all Gitaly nodes use solid-state drives (SSDs). These SSDs
should have a throughput of at least 8,000
input/output operations per second (IOPS) for read operations and 2,000 IOPS
-for write operations. These IOPS values are initial recommendations, and may be
-adjusted to greater or lesser values depending on the scale of your
-environment's workload. If you're running the environment on a Cloud provider,
+for write operations. If you're running the environment on a Cloud provider,
refer to their documentation about how to configure IOPS correctly.
Be sure to note the following items:
@@ -774,11 +774,9 @@ the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html).
sudo gitlab-rake gitlab:db:configure
```
- If you encounter a `rake aborted!` error message stating that PgBouncer is
- failing to connect to PostgreSQL, it may be that your PgBouncer node's IP
- address is missing from PostgreSQL's `trust_auth_cidr_addresses` in `gitlab.rb`
- on your database nodes. Before proceeding, see
- [PgBouncer error `ERROR: pgbouncer cannot connect to server`](../postgresql/replication_and_failover.md#pgbouncer-error-error-pgbouncer-cannot-connect-to-server).
+ Note that this requires the Rails node to be configured to connect to the primary database
+ directly, [bypassing PgBouncer](../postgresql/pgbouncer.md#procedure-for-bypassing-pgbouncer).
+ After migrations have completed, you must configure the node to pass through PgBouncer again.
1. [Configure fast lookup of authorized SSH keys in the database](../operations/fast_ssh_key_lookup.md).
diff --git a/doc/administration/reference_architectures/3k_users.md b/doc/administration/reference_architectures/3k_users.md
index c28799ed459..47d49ffb437 100644
--- a/doc/administration/reference_architectures/3k_users.md
+++ b/doc/administration/reference_architectures/3k_users.md
@@ -40,8 +40,8 @@ For a full list of reference architectures, see
| Gitaly<sup>5 6</sup> | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` |
| Praefect<sup>5</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
| Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
-| Sidekiq | 4 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` |
-| GitLab Rails | 3 | 8 vCPU, 7.2 GB memory | `n1-highcpu-8` | `c5.2xlarge` |
+| Sidekiq<sup>7</sup> | 4 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` |
+| GitLab Rails<sup>7</sup> | 3 | 8 vCPU, 7.2 GB memory | `n1-highcpu-8` | `c5.2xlarge` |
| Monitoring node | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
| Object storage<sup>4</sup> | - | - | - | - |
@@ -62,6 +62,8 @@ For a full list of reference architectures, see
6. Gitaly has been designed and tested with repositories of varying sizes that follow best practices. However, large
repositories or monorepos that don't follow these practices can significantly impact Gitaly requirements. Refer to
[Large repositories](index.md#large-repositories) for more information.
+7. Can be placed in Auto Scaling Groups (ASGs) as the component doesn't store any [stateful data](index.md#autoscaling-of-stateful-nodes).
+ However, for GitLab Rails certain processes like [migrations](#gitlab-rails-post-configuration) and [Mailroom](../incoming_email.md) should be run on only one node.
<!-- markdownlint-enable MD029 -->
NOTE:
@@ -640,8 +642,13 @@ are supported and can be added if needed.
## Configure Consul and Sentinel
-Now that the Redis servers are all set up, let's configure the Sentinel
-servers. The following IPs will be used as an example:
+Now that the Redis servers are all set up, let's configure the Consul + Sentinel
+servers.
+
+NOTE:
+Consul and Redis Sentinel must be deployed in an odd number of 3 nodes or higher. This is to ensure the nodes can take votes as part of a quorum.
+
+The following IPs will be used as an example:
- `10.6.0.11`: Consul/Sentinel 1
- `10.6.0.12`: Consul/Sentinel 2
@@ -1288,6 +1295,9 @@ This is how this would work with a Omnibus GitLab PostgreSQL setup:
Praefect is the router and transaction manager for Gitaly Cluster and all connections to Gitaly go through
it. This section details how to configure it.
+NOTE:
+Praefect must be deployed in an odd number of 3 nodes or higher. This is to ensure the nodes can take votes as part of a quorum.
+
Praefect requires several secret tokens to secure communications across the Cluster:
- `<praefect_external_token>`: Used for repositories hosted on your Gitaly cluster and can only be accessed by Gitaly clients that carry this token.
@@ -1462,9 +1472,7 @@ Due to Gitaly having notable input and output requirements, we strongly
recommend that all Gitaly nodes use solid-state drives (SSDs). These SSDs
should have a throughput of at least 8,000
input/output operations per second (IOPS) for read operations and 2,000 IOPS for
-write operations. These IOPS values are initial recommendations, and may be
-adjusted to greater or lesser values depending on the scale of your
-environment's workload. If you're running the environment on a Cloud provider,
+write operations. If you're running the environment on a Cloud provider,
refer to their documentation about how to configure IOPS correctly.
Gitaly servers must not be exposed to the public internet, as Gitaly's network
@@ -2073,11 +2081,9 @@ the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html).
gitlab-rake gitlab:db:configure
```
- If you encounter a `rake aborted!` error message stating that PgBouncer is
- failing to connect to PostgreSQL, it may be that your PgBouncer node's IP
- address is missing from PostgreSQL's `trust_auth_cidr_addresses` in `gitlab.rb`
- on your database nodes. Before proceeding, see
- [PgBouncer error `ERROR: pgbouncer cannot connect to server`](../postgresql/replication_and_failover.md#pgbouncer-error-error-pgbouncer-cannot-connect-to-server).
+ Note that this requires the Rails node to be configured to connect to the primary database
+ directly, [bypassing PgBouncer](../postgresql/pgbouncer.md#procedure-for-bypassing-pgbouncer).
+ After migrations have completed, you must configure the node to pass through PgBouncer again.
1. [Configure fast lookup of authorized SSH keys in the database](../operations/fast_ssh_key_lookup.md).
diff --git a/doc/administration/reference_architectures/50k_users.md b/doc/administration/reference_architectures/50k_users.md
index 3f2771dda29..b89effb730a 100644
--- a/doc/administration/reference_architectures/50k_users.md
+++ b/doc/administration/reference_architectures/50k_users.md
@@ -31,8 +31,8 @@ full list of reference architectures, see
| Gitaly<sup>5 6</sup> | 3 | 64 vCPU, 240 GB memory | `n1-standard-64` | `m5.16xlarge` |
| Praefect<sup>5</sup> | 3 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` |
| Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
-| Sidekiq | 4 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` |
-| GitLab Rails | 12 | 32 vCPU, 28.8 GB memory | `n1-highcpu-32` | `c5.9xlarge` |
+| Sidekiq<sup>7</sup> | 4 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` |
+| GitLab Rails<sup>7</sup> | 12 | 32 vCPU, 28.8 GB memory | `n1-highcpu-32` | `c5.9xlarge` |
| Monitoring node | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` |
| Object storage<sup>4</sup> | - | - | - | - |
@@ -53,6 +53,8 @@ full list of reference architectures, see
6. Gitaly has been designed and tested with repositories of varying sizes that follow best practices. However, large
repositories or monorepos that don't follow these practices can significantly impact Gitaly requirements. Refer to
[Large repositories](index.md#large-repositories) for more information.
+7. Can be placed in Auto Scaling Groups (ASGs) as the component doesn't store any [stateful data](index.md#autoscaling-of-stateful-nodes).
+ However, for GitLab Rails certain processes like [migrations](#gitlab-rails-post-configuration) and [Mailroom](../incoming_email.md) should be run on only one node.
<!-- markdownlint-enable MD029 -->
NOTE:
@@ -426,6 +428,11 @@ spread connections equally in practice.
## Configure Consul
+Next, we set up the Consul servers.
+
+NOTE:
+Consul must be deployed in an odd number of 3 nodes or higher. This is to ensure the nodes can take votes as part of a quorum.
+
The following IPs will be used as an example:
- `10.6.0.11`: Consul 1
@@ -811,6 +818,9 @@ Using [Redis](https://redis.io/) in scalable environment is possible using a **P
topology with a [Redis Sentinel](https://redis.io/docs/manual/sentinel/) service to watch and automatically
start the failover procedure.
+NOTE:
+Redis clusters must each be deployed in an odd number of 3 nodes or higher. This is to ensure Redis Sentinel can take votes as part of a quorum. This does not apply when configuring Redis externally, such as a cloud provider service.
+
Redis requires authentication if used with Sentinel. See
[Redis Security](https://redis.io/docs/manual/security/) documentation for more
information. We recommend using a combination of a Redis password and tight
@@ -1349,6 +1359,9 @@ This is how this would work with a Omnibus GitLab PostgreSQL setup:
Praefect is the router and transaction manager for Gitaly Cluster and all connections to Gitaly go through
it. This section details how to configure it.
+NOTE:
+Praefect must be deployed in an odd number of 3 nodes or higher. This is to ensure the nodes can take votes as part of a quorum.
+
Praefect requires several secret tokens to secure communications across the Cluster:
- `<praefect_external_token>`: Used for repositories hosted on your Gitaly cluster and can only be accessed by Gitaly clients that carry this token.
@@ -1523,9 +1536,7 @@ Due to Gitaly having notable input and output requirements, we strongly
recommend that all Gitaly nodes use solid-state drives (SSDs). These SSDs
should have a throughput of at least 8,000
input/output operations per second (IOPS) for read operations and 2,000 IOPS for
-write operations. These IOPS values are initial recommendations, and may be
-adjusted to greater or lesser values depending on the scale of your
-environment's workload. If you're running the environment on a Cloud provider,
+write operations. If you're running the environment on a Cloud provider,
refer to their documentation about how to configure IOPS correctly.
Gitaly servers must not be exposed to the public internet, as Gitaly's network
@@ -2107,11 +2118,9 @@ the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html).
sudo gitlab-rake gitlab:db:configure
```
- If you encounter a `rake aborted!` error message stating that PgBouncer is
- failing to connect to PostgreSQL, it may be that your PgBouncer node's IP
- address is missing from PostgreSQL's `trust_auth_cidr_addresses` in `gitlab.rb`
- on your database nodes. Before proceeding, see
- [PgBouncer error `ERROR: pgbouncer cannot connect to server`](../postgresql/replication_and_failover.md#pgbouncer-error-error-pgbouncer-cannot-connect-to-server).
+ Note that this requires the Rails node to be configured to connect to the primary database
+ directly, [bypassing PgBouncer](../postgresql/pgbouncer.md#procedure-for-bypassing-pgbouncer).
+ After migrations have completed, you must configure the node to pass through PgBouncer again.
1. [Configure fast lookup of authorized SSH keys in the database](../operations/fast_ssh_key_lookup.md).
diff --git a/doc/administration/reference_architectures/5k_users.md b/doc/administration/reference_architectures/5k_users.md
index 43d63d4f08e..5d6c650fe50 100644
--- a/doc/administration/reference_architectures/5k_users.md
+++ b/doc/administration/reference_architectures/5k_users.md
@@ -37,8 +37,8 @@ costly-to-operate environment by using the
| Gitaly<sup>5 6</sup> | 3 | 8 vCPU, 30 GB memory | `n1-standard-8` | `m5.2xlarge` |
| Praefect<sup>5</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
| Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
-| Sidekiq | 4 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` |
-| GitLab Rails | 3 | 16 vCPU, 14.4 GB memory | `n1-highcpu-16` | `c5.4xlarge` |
+| Sidekiq<sup>7</sup> | 4 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` |
+| GitLab Rails<sup>7</sup> | 3 | 16 vCPU, 14.4 GB memory | `n1-highcpu-16` | `c5.4xlarge` |
| Monitoring node | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
| Object storage<sup>4</sup> | - | - | - | - |
@@ -59,6 +59,8 @@ costly-to-operate environment by using the
6. Gitaly has been designed and tested with repositories of varying sizes that follow best practices. However, large
repositories or monorepos that don't follow these practices can significantly impact Gitaly requirements. Refer to
[Large repositories](index.md#large-repositories) for more information.
+7. Can be placed in Auto Scaling Groups (ASGs) as the component doesn't store any [stateful data](index.md#autoscaling-of-stateful-nodes).
+ However, for GitLab Rails certain processes like [migrations](#gitlab-rails-post-configuration) and [Mailroom](../incoming_email.md) should be run on only one node.
<!-- markdownlint-enable MD029 -->
NOTE:
@@ -637,8 +639,13 @@ are supported and can be added if needed.
## Configure Consul and Sentinel
-Now that the Redis servers are all set up, let's configure the Sentinel
-servers. The following IPs are used as an example:
+Now that the Redis servers are all set up, let's configure the Consul + Sentinel
+servers.
+
+NOTE:
+Consul and Redis Sentinel must be deployed in an odd number of 3 nodes or higher. This is to ensure the nodes can take votes as part of a quorum.
+
+The following IPs will be used as an example:
- `10.6.0.11`: Consul/Sentinel 1
- `10.6.0.12`: Consul/Sentinel 2
@@ -1285,6 +1292,9 @@ This is how this would work with a Omnibus GitLab PostgreSQL setup:
Praefect is the router and transaction manager for Gitaly Cluster and all connections to Gitaly go through
it. This section details how to configure it.
+NOTE:
+Praefect must be deployed in an odd number of 3 nodes or higher. This is to ensure the nodes can take votes as part of a quorum.
+
Praefect requires several secret tokens to secure communications across the Cluster:
- `<praefect_external_token>`: Used for repositories hosted on your Gitaly cluster and can only be accessed by Gitaly clients that carry this token.
@@ -1459,9 +1469,7 @@ Due to Gitaly having notable input and output requirements, we strongly
recommend that all Gitaly nodes use solid-state drives (SSDs). These SSDs
should have a throughput of at least 8,000
input/output operations per second (IOPS) for read operations and 2,000 IOPS for
-write operations. These IOPS values are initial recommendations, and may be
-adjusted to greater or lesser values depending on the scale of your
-environment's workload. If you're running the environment on a Cloud provider,
+write operations. If you're running the environment on a Cloud provider,
refer to their documentation about how to configure IOPS correctly.
Gitaly servers must not be exposed to the public internet, as Gitaly's network
@@ -2068,11 +2076,9 @@ the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html).
gitlab-rake gitlab:db:configure
```
- If you encounter a `rake aborted!` error message stating that PgBouncer is
- failing to connect to PostgreSQL, it may be that your PgBouncer node's IP
- address is missing from PostgreSQL's `trust_auth_cidr_addresses` in `gitlab.rb`
- on your database nodes. Before proceeding, see
- [PgBouncer error `ERROR: pgbouncer cannot connect to server`](../postgresql/replication_and_failover.md#pgbouncer-error-error-pgbouncer-cannot-connect-to-server).
+ Note that this requires the Rails node to be configured to connect to the primary database
+ directly, [bypassing PgBouncer](../postgresql/pgbouncer.md#procedure-for-bypassing-pgbouncer).
+ After migrations have completed, you must configure the node to pass through PgBouncer again.
1. [Configure fast lookup of authorized SSH keys in the database](../operations/fast_ssh_key_lookup.md).
diff --git a/doc/administration/reference_architectures/index.md b/doc/administration/reference_architectures/index.md
index e4d07558306..b73740cd1d2 100644
--- a/doc/administration/reference_architectures/index.md
+++ b/doc/administration/reference_architectures/index.md
@@ -47,9 +47,9 @@ The following Cloud Native Hybrid reference architectures, where select recommen
The first choice to consider is whether a Self Managed approach is correct for you and your requirements.
-Running any application in production is complex, and the same applies for GitLab. While we aim to make this as smooth as possible, there are still the general complexities. This depends on the design chosen, but typically you'll need to manage all aspects such as hardware, operating systems, networking, storage, security, GitLab itself, and more.
+Running any application in production is complex, and the same applies for GitLab. While we aim to make this as smooth as possible, there are still the general complexities. This depends on the design chosen, but typically you'll need to manage all aspects such as hardware, operating systems, networking, storage, security, GitLab itself, and more. This includes both the initial setup of the environment and the longer term maintenance.
-As such, it's recommended that you have a working knowledge of running applications in production when deciding on going down this route. For users who want a more managed solution it's recommended to instead explore our other offerings such as [GitLab SaaS](../../subscriptions/gitlab_com/index.md) or [GitLab Dedicated](../../subscriptions/gitlab_dedicated/index.md).
+As such, it's recommended that you have a working knowledge of running and maintaining applications in production when deciding on going down this route. If you aren't in this position, our [Professional Services](https://about.gitlab.com/services/#implementation-services) team offers implementation services, but for those who want a more managed solution long term, it's recommended to instead explore our other offerings such as [GitLab SaaS](../../subscriptions/gitlab_com/index.md) or [GitLab Dedicated](../../subscriptions/gitlab_dedicated/index.md).
## Deciding which architecture to use
@@ -76,8 +76,6 @@ High Availability ensures every component in the GitLab setup can handle failure
For environments serving 3,000 or more users we generally recommend that a HA strategy is used as at this level outages have a bigger impact against more users. All the architectures in this range have HA built in by design for this reason.
-For users who still need to have HA for a lower number of users this can also be achieved with an adjusted [3K architecture](3k_users.md#supported-modifications-for-lower-user-counts-ha).
-
#### Do you need High Availability (HA)?
As mentioned above, achieving HA does come at a cost. The environment's required are sizable as each component needs to be multiplied, which comes with additional actual and maintenance costs.
@@ -89,6 +87,10 @@ In general then, we'd only recommend you employ HA in the following scenarios:
- When you have 3,000 or more users.
- When GitLab being down would critically impact your workflow.
+#### Scaled-down High Availability (HA) approaches
+
+If you still need to have HA for a lower number of users, this can be achieved with an adjusted [3K architecture](3k_users.md#supported-modifications-for-lower-user-counts-ha).
+
#### Zero Downtime Upgrades
[Zero Downtime Upgrades](../../update/zero_downtime.md) are available for standard Reference Architecture environments with HA (Cloud Native Hybrid is not supported at this time). This allows for an environment to stay up during an upgrade, but the process is more complex as a result and has some limitations as detailed in the documentation.
@@ -163,7 +165,7 @@ Before implementing a reference architecture, refer to the following requirement
These reference architectures were built and tested on Google Cloud Platform (GCP) using the
[Intel Xeon E5 v3 (Haswell)](https://cloud.google.com/compute/docs/cpu-platforms)
-CPU platform as a baseline ([Sysbench benchmark](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Reference-Architectures/GCP-CPU-Benchmarks)).
+CPU platform as a lowest common denominator baseline ([Sysbench benchmark](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Reference-Architectures/GCP-CPU-Benchmarks)).
Newer, similarly-sized CPUs are supported and may have improved performance as a result. For Omnibus GitLab environments,
ARM-based equivalents are also supported.
@@ -324,10 +326,12 @@ When selecting a database service, it should run a standard, performant, and [su
- Read Replicas for [Database Load Balancing](../postgresql/database_load_balancing.md).
- Cross Region replication for [GitLab Geo](../geo/index.md).
-Several cloud provider services are known not to support the above or have been found to have other issues and aren't recommended:
+#### Unsupported database services
+
+Several database cloud provider services are known not to support the above or have been found to have other issues and aren't recommended:
- [Amazon Aurora](https://aws.amazon.com/rds/aurora/) is incompatible and not supported. See [14.4.0](../../update/index.md#1440) for more details.
-- [Azure Database for PostgreSQL Single Server](https://azure.microsoft.com/en-gb/products/postgresql/#overview) (Single / Flexible) is **strongly not recommended** for use due to notable performance / stability issues or missing functionality. See [Recommendation Notes for Azure](#recommendation-notes-for-azure) for more details.
+- [Azure Database for PostgreSQL Single Server](https://azure.microsoft.com/en-gb/products/postgresql/#overview) (Single / Flexible) is not supported for use due to notable performance / stability issues or missing functionality. See [Recommendation Notes for Azure](#recommendation-notes-for-azure) for more details.
- [Google AlloyDB](https://cloud.google.com/alloydb) and [Amazon RDS Multi-AZ DB cluster](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/multi-az-db-clusters-concepts.html) have not been tested and are not recommended. Both solutions are specifically not expected to work with GitLab Geo.
- [Amazon RDS Multi-AZ DB instance](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZSingleStandby.html) is a separate product and is supported.
@@ -337,10 +341,55 @@ Due to performance issues that we found with several key Azure services, we only
In addition to the above, you should be aware of the additional specific guidance for Azure:
-- **We outright strongly do not recommend [Azure Database for PostgreSQL Single Server](https://learn.microsoft.com/en-us/azure/postgresql/single-server/overview-single-server)** specifically due to significant performance and stability issues found. **For GitLab 14.0 and higher the service is not supported** due to it only supporting up to PostgreSQL 11.
- - A new service, [Azure Database for PostgreSQL Flexible Server](https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/) has been released. [Internal testing](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/91) has shown that it does look to perform as expected, but this hasn't been validated in production, so generally isn't recommended at this time. Additionally, as it's a new service, you may find that it's missing some functionality depending on your requirements.
+- [Azure Database for PostgreSQL Single Server](https://azure.microsoft.com/en-gb/products/postgresql/#overview) (Single / Flexible) is not supported for use due to notable performance / stability issues or missing functionality.
+- A new service, [Azure Database for PostgreSQL Flexible Server](https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/) has been released. [Internal testing](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/91) has shown that it does look to perform as expected, but this hasn't been validated in production, so generally isn't recommended at this time. Additionally, as it's a new service, you may find that it's missing some functionality depending on your requirements.
- [Azure Blob Storage](https://azure.microsoft.com/en-gb/products/storage/blobs/) has been found to have [performance limits that can impact production use at certain times](https://gitlab.com/gitlab-org/gitlab/-/issues/344861). However, this has only been seen in our largest architectures (25k+) so far.
+## Deviating from the suggested reference architectures
+
+As a general guideline, the further away you move from the reference architectures,
+the harder it is to get support for it. With any deviation, you're introducing
+a layer of complexity that adds challenges to finding out where potential
+issues might lie.
+
+The reference architectures use the official GitLab Linux packages (Omnibus
+GitLab) or [Helm Charts](https://docs.gitlab.com/charts/) to install and configure the various components. The components are
+installed on separate machines (virtualized or bare metal), with machine hardware
+requirements listed in the "Configuration" column and equivalent VM standard sizes listed
+in GCP/AWS/Azure columns of each [available reference architecture](#available-reference-architectures).
+
+Running components on Docker (including Docker Compose) with the same specs should be fine, as Docker is well known in terms of support.
+However, it is still an additional layer and may still add some support complexities, such as not being able to run `strace` easily in containers.
+
+### Unsupported designs
+
+While we endeavour to try and have a good range of support for GitLab environment designs, there are certain approaches we know definitively not to work, and as a result are not supported. Those approaches are detailed in the following sections.
+
+#### Stateful components in Kubernetes
+
+[Running stateful components in Kubernetes, such as Gitaly Cluster, is not supported](https://docs.gitlab.com/charts/installation/#configure-the-helm-chart-to-use-external-stateful-data).
+
+Gitaly Cluster is only supported to be run on VMs as Git itself doesn't match well with the Kubernetes design and attempting to run it can lead to significant and complex issues.
+[Refer to epic 6127 for more information](https://gitlab.com/groups/gitlab-org/-/epics/6127).
+
+This also applies to other third-party stateful components such as Postgres and Redis, but you can explore other third-party solutions for those components if desired such as supported Cloud Provider services unless called out specifically as unsupported.
+
+#### Autoscaling of stateful nodes
+
+As a general guidance, only _stateless_ components of GitLab can be run in Autoscaling groups, namely GitLab Rails
+and Sidekiq.
+
+Other components that have state, such as Gitaly, are not supported in this fashion (for more information, see [issue 2997](https://gitlab.com/gitlab-org/gitaly/-/issues/2997)).
+
+This also applies to other third-party stateful components such as Postgres and Redis, but you can explore other third-party solutions for those components if desired such as supported Cloud Provider services unless called out specifically as unsupported.
+
+#### Spreading one environment over multiple data centers
+
+Deploying one GitLab environment over multiple data centers is not supported due to potential split brain edge cases
+if a data center were to go down. For example, several components of the GitLab setup, namely Consul, Redis Sentinel and Praefect require an odd number quorum to function correctly and splitting over multiple data centers can impact this notably.
+
+For deploying GitLab over multiple data centers or regions we offer [GitLab Geo](https://about.gitlab.com/solutions/geo/) as a comprehensive solution.
+
## Validation and test results
The [Quality Engineering team](https://about.gitlab.com/handbook/engineering/quality/quality-engineering/)
@@ -427,34 +476,34 @@ table.test-coverage th {
<tr>
<th scope="row">1k</th>
<td><a href="https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/1k">Weekly</a></td>
- <td></td>
- <td></td>
- <td></td>
- <td></td>
+ <td style="background-color:lightgrey"></td>
+ <td style="background-color:lightgrey"></td>
+ <td style="background-color:lightgrey"></td>
+ <td style="background-color:lightgrey"></td>
</tr>
<tr>
<th scope="row">2k</th>
<td><a href="https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/2k">Weekly</a></td>
- <td></td>
- <td></td>
- <td></td>
- <td></td>
+ <td style="background-color:lightgrey"></td>
+ <td style="background-color:lightgrey"></td>
+ <td style="background-color:lightgrey"></td>
+ <td><i>Planned</i></td>
</tr>
<tr>
<th scope="row">3k</th>
<td><a href="https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/3k">Weekly</a></td>
- <td></td>
- <td></td>
+ <td style="background-color:lightgrey"></td>
+ <td style="background-color:lightgrey"></td>
<td><a href="https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/3k_hybrid_aws_services">Weekly</a></td>
- <td></td>
+ <td style="background-color:lightgrey"></td>
</tr>
<tr>
<th scope="row">5k</th>
<td><a href="https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/5k">Weekly</a></td>
- <td></td>
- <td></td>
- <td></td>
- <td></td>
+ <td style="background-color:lightgrey"></td>
+ <td style="background-color:lightgrey"></td>
+ <td style="background-color:lightgrey"></td>
+ <td style="background-color:lightgrey"></td>
</tr>
<tr>
<th scope="row">10k</th>
@@ -462,29 +511,33 @@ table.test-coverage th {
<td><a href="https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/10k_hybrid">Weekly</a></td>
<td><a href="https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/10k_aws">Weekly</a></td>
<td><a href="https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/10k_hybrid_aws_services">Weekly</a></td>
- <td><a href="https://gitlab.com/gitlab-org/quality/performance/-/wikis/Past-Results/10k">Ad-Hoc</a></td>
+ <td style="background-color:lightgrey"></td>
</tr>
<tr>
<th scope="row">25k</th>
<td><a href="https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/25k">Weekly</a></td>
- <td></td>
- <td></td>
- <td></td>
- <td><a href="https://gitlab.com/gitlab-org/quality/performance/-/wikis/Past-Results/25k">Ad-Hoc</a></td>
+ <td style="background-color:lightgrey"></td>
+ <td style="background-color:lightgrey"></td>
+ <td style="background-color:lightgrey"></td>
+ <td style="background-color:lightgrey"></td>
</tr>
<tr>
<th scope="row">50k</th>
<td><a href="https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/50k">Weekly</a></td>
- <td></td>
- <td><a href="https://gitlab.com/gitlab-org/quality/performance/-/wikis/Past-Results/50k">Ad-Hoc (inc Cloud Services)</a></td>
- <td></td>
- <td></td>
+ <td style="background-color:lightgrey"></td>
+ <td style="background-color:lightgrey"></td>
+ <td style="background-color:lightgrey"></td>
+ <td style="background-color:lightgrey"></td>
</tr>
</table>
## Cost to run
-The following table details the cost to run the different reference architectures across GCP, AWS, and Azure. Bare-metal costs are not included here as it varies widely depending on each customer configuration.
+As a starting point, the following table details initial costs for the different reference architectures across GCP, AWS, and Azure via Omnibus.
+
+NOTE:
+Due to the nature of Cloud Native Hybrid, it's not possible to give a static cost calculation.
+Bare-metal costs are also not included here as it varies widely depending on each configuration.
<table class="test-coverage">
<col>
@@ -492,91 +545,93 @@ The following table details the cost to run the different reference architecture
<colgroup span="2"></colgroup>
<tr>
<th rowspan="2">Reference<br/>Architecture</th>
- <th style="text-align: center" colspan="2" scope="colgroup">GCP</th>
- <th style="text-align: center" colspan="2" scope="colgroup">AWS</th>
- <th style="text-align: center" colspan="2" scope="colgroup">Azure</th>
+ <th style="text-align: center" scope="colgroup">GCP</th>
+ <th style="text-align: center" scope="colgroup">AWS</th>
+ <th style="text-align: center" scope="colgroup">Azure</th>
</tr>
<tr>
<th scope="col">Omnibus</th>
- <th scope="col">Cloud Native Hybrid</th>
<th scope="col">Omnibus</th>
- <th scope="col">Cloud Native Hybrid</th>
<th scope="col">Omnibus</th>
</tr>
<tr>
<th scope="row">1k</th>
<td><a href="https://cloud.google.com/products/calculator#id=a6d6a94a-c7dc-4c22-85c4-7c5747f272ed">Calculated cost</a></td>
- <td></td>
<td><a href="https://calculator.aws/#/estimate?id=b51f178f4403b69a63f6eb33ea425f82de3bf249">Calculated cost</a></td>
- <td></td>
<td><a href="https://azure.microsoft.com/en-us/pricing/calculator/?shared-estimate=1adf30bef7e34ceba9efa97c4470417b">Calculated cost</a></td>
</tr>
<tr>
<th scope="row">2k</th>
<td><a href="https://cloud.google.com/products/calculator#id=0d3aff1f-ea3d-43f9-aa59-df49d27c35ca">Calculated cost</a></td>
- <td></td>
<td><a href="https://calculator.aws/#/estimate?id=3b3e3b81953737132789591d3a5179521943f1c0">Calculated cost</a></td>
- <td></td>
<td><a href="https://azure.microsoft.com/en-us/pricing/calculator/?shared-estimate=25f66c35ba454bb98fb4034a8a50bb8c">Calculated cost</a></td>
</tr>
<tr>
<th scope="row">3k</th>
<td><a href="https://cloud.google.com/products/calculator/#id=15fc2bd9-5b1c-479d-bc46-d5ce096b8107">Calculated cost</a></td>
- <td></td>
<td><a href="https://calculator.aws/#/estimate?id=7e94eb8712f6845fdeb05e61f459598a91dac3cb">Calculated cost</a></td>
- <td></td>
<td><a href="https://azure.microsoft.com/en-us/pricing/calculator/?shared-estimate=24ac11fd947a4985ae9c9a5142649ad3">Calculated cost</a></td>
</tr>
<tr>
<th scope="row">5k</th>
<td><a href="https://cloud.google.com/products/calculator/#id=9a798136-53f2-4c35-be43-8e1e975a6663">Calculated cost</a></td>
- <td></td>
<td><a href="https://calculator.aws/#/estimate?id=ad4c9db623a214c92d780cd9dff33f444d62cf02">Calculated cost</a></td>
- <td></td>
<td><a href="https://azure.microsoft.com/en-us/pricing/calculator/?shared-estimate=bcf23017ddfd40649fdc885cacd08d0c">Calculated cost</a></td>
</tr>
<tr>
<th scope="row">10k</th>
<td><a href="https://cloud.google.com/products/calculator#id=cbe61840-31a1-487f-88fa-631251c2fde5">Calculated cost</a></td>
- <td></td>
<td><a href="https://calculator.aws/#/estimate?id=3e2970f919915a6337acea76a9f07655a1ecda4a">Calculated cost</a></td>
- <td></td>
<td><a href="https://azure.microsoft.com/en-us/pricing/calculator/?shared-estimate=5748068be4864af6a34efb1cde685fa1">Calculated cost</a></td>
</tr>
<tr>
<th scope="row">25k</th>
<td><a href="https://cloud.google.com/products/calculator#id=b4b8b587-508a-4433-adc8-dc506bbe924f">Calculated cost</a></td>
- <td></td>
<td><a href="https://calculator.aws/#/estimate?id=32acaeaa93366110cd5fbf98a66a8a141db7adcb">Calculated cost</a></td>
- <td></td>
<td><a href="https://azure.microsoft.com/en-us/pricing/calculator/?shared-estimate=24f878f20ee64b5cb64de459d34c8128">Calculate cost</a></td>
</tr>
<tr>
<th scope="row">50k</th>
<td><a href="https://cloud.google.com/products/calculator/#id=48b4d817-d6cd-44b8-b069-0ba9a5d123ea">Calculated cost</a></td>
- <td></td>
<td><a href="https://calculator.aws/#/estimate?id=5a0bba1338e3577d627ec97833dbc80ac9615562">Calculated cost</a></td>
- <td></td>
<td><a href="https://azure.microsoft.com/en-us/pricing/calculator/?shared-estimate=4dd065eea2194d70b44d6d897e81f460">Calculated cost</a></td>
</tr>
</table>
-## Deviating from the suggested reference architectures
+## Maintaining a Reference Architecture environment
-As a general guideline, the further away you move from the Reference Architectures,
-the harder it is to get support for it. With any deviation, you're introducing
-a layer of complexity that adds challenges to finding out where potential
-issues might lie.
+Maintaining a Reference Architecture environment is generally the same as any other GitLab environment is generally covered in other sections of this documentation.
-The reference architectures use the official GitLab Linux packages (Omnibus
-GitLab) or [Helm Charts](https://docs.gitlab.com/charts/) to install and configure the various components. The components are
-installed on separate machines (virtualized or bare metal), with machine hardware
-requirements listed in the "Configuration" column and equivalent VM standard sizes listed
-in GCP/AWS/Azure columns of each [available reference architecture](#available-reference-architectures).
+In this section you'll find links to documentation for relevant areas as well as any specific Reference Architecture notes.
-Running components on Docker (including Compose) with the same specs should be fine, as Docker is well known in terms of support.
-However, it is still an additional layer and may still add some support complexities, such as not being able to run `strace` easily in containers.
+### Upgrades
+
+Upgrades for a Reference Architecture environment is the same as any other GitLab environment.
+The main [Upgrade GitLab](../../update/index.md) section has detailed steps on how to approach this.
+
+[Zero-downtime upgrades](#zero-downtime-upgrades) are also available.
+
+NOTE:
+You should upgrade a Reference Architecture in the same order as you created it.
+
+### Scaling an environment
+
+Scaling a GitLab environment is designed to be as seamless as possible.
+
+In terms of the Reference Architectures, you would look to the next size and adjust accordingly.
+Most setups would only need vertical scaling, but there are some specific areas that can be adjusted depending on the setup:
+
+- If you're scaling from a non-HA environment to an HA environment, various components are recommended to be deployed in their HA forms:
+ - Redis to multi-node Redis w/ Redis Sentinel
+ - Postgres to multi-node Postgres w/ Consul + PgBouncer
+ - Gitaly to Gitaly Cluster w/ Praefect
+- From 10k users and higher, Redis is recommended to be split into multiple HA servers as it's single threaded.
+
+Conversely, if you have robust metrics in place that show the environment is over-provisioned you can apply the same process for
+scaling downloads. It's recommended to take an iterative approach when scaling downwards however to ensure there are no issues.
+
+### How to monitor your environment
-Other technologies, like [Docker swarm](https://docs.docker.com/engine/swarm/)
-are not officially supported, but can be implemented at your own risk. In that
-case, GitLab Support is not able to help you.
+To monitor your GitLab environment, you can use the tools
+[bundled with GitLab](../monitoring/index.md), but it's also possible to use third-party
+options if desired.
diff --git a/doc/administration/sidekiq/index.md b/doc/administration/sidekiq/index.md
index 8645df55a62..6fb12aa6ef9 100644
--- a/doc/administration/sidekiq/index.md
+++ b/doc/administration/sidekiq/index.md
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
You can configure an external Sidekiq instance by using the Sidekiq that's bundled in the GitLab package. Sidekiq requires connection to the Redis,
PostgreSQL, and Gitaly instances.
-## Configure TCP access for PostgreSQL, Gitaly, and Redis
+## Configure TCP access for PostgreSQL, Gitaly, and Redis on the GitLab instance
By default, GitLab uses UNIX sockets and is not set up to communicate via TCP. To change this:
@@ -56,7 +56,6 @@ By default, GitLab uses UNIX sockets and is not set up to communicate via TCP. T
redis['password'] = 'redis-password-goes-here'
gitlab_rails['redis_password'] = 'redis-password-goes-here'
- gitlab_rails['auto_migrate'] = false
```
1. Run `reconfigure`:
@@ -71,18 +70,6 @@ By default, GitLab uses UNIX sockets and is not set up to communicate via TCP. T
sudo gitlab-ctl restart postgresql
```
-1. After the restart, set `auto_migrate` to `true` or comment to use the default settings:
-
- ```ruby
- gitlab_rails['auto_migrate'] = true
- ```
-
-1. Run `reconfigure` again:
-
- ```shell
- sudo gitlab-ctl reconfigure
- ```
-
## Set up Sidekiq instance
1. SSH into the Sidekiq server.
diff --git a/doc/user/analytics/code_review_analytics.md b/doc/user/analytics/code_review_analytics.md
index fae9545003f..2e76252f7d3 100644
--- a/doc/user/analytics/code_review_analytics.md
+++ b/doc/user/analytics/code_review_analytics.md
@@ -5,7 +5,6 @@ group: Optimize
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-
# Code review analytics **(PREMIUM)**
> Moved to GitLab Premium in 13.9.
@@ -24,6 +23,9 @@ and improve your code review process.
- Opportunities to accelerate your development cycle.
- Few comments and approvers may indicate a lack of available team members.
+<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
+For a video explanation, see [Code review analytics: Faster code review](https://www.youtube.com/watch?v=OkLaWhYkASM).
+
## View code review analytics
Prerequisite:
diff --git a/doc/user/analytics/dora_metrics.md b/doc/user/analytics/dora_metrics.md
index bdbfe5072d2..e7c5fba3a44 100644
--- a/doc/user/analytics/dora_metrics.md
+++ b/doc/user/analytics/dora_metrics.md
@@ -20,6 +20,7 @@ DORA includes four key metrics, divided into two core areas of DevOps:
For software leaders, tracking velocity alongside quality metrics ensures they're not sacrificing quality for speed.
+<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For a video explanation, see [DORA metrics: User analytics](https://www.youtube.com/watch?v=lM_FbVYuN8s) and [GitLab speed run: DORA metrics](https://www.youtube.com/watch?v=1BrcMV6rCDw).
## DORA metrics dashboard in Value Stream Analytics
diff --git a/doc/user/project/merge_requests/reviews/index.md b/doc/user/project/merge_requests/reviews/index.md
index 4bd77c7ebdd..9696bb39d83 100644
--- a/doc/user/project/merge_requests/reviews/index.md
+++ b/doc/user/project/merge_requests/reviews/index.md
@@ -23,14 +23,14 @@ review merge requests in Visual Studio Code.
## Suggested reviewers **(ULTIMATE SAAS)**
-> [Introduced](https://gitlab.com/groups/gitlab-org/modelops/applied-ml/review-recommender/-/epics/3) in GitLab 15.4.
+> - [Introduced](https://gitlab.com/groups/gitlab-org/modelops/applied-ml/review-recommender/-/epics/3) in GitLab 15.4 as an [Open Beta](../../../../policy/alpha-beta-support.md#beta-features) feature [with a flag](../../../../administration/feature_flags.md) named `suggested_reviewers_control`. Disabled by default.
+> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/368356) in GitLab 15.6.
+> - Beta designation [removed from the UI](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113058) in GitLab 15.10.
GitLab can suggest reviewers. Using the changes in a merge request and a project's contribution graph, machine learning suggestions appear in the reviewer section of the right sidebar.
![Suggested Reviewers](img/suggested_reviewers_v15_9.png)
-This feature is currently in [Open Beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#open-beta) behind a [feature flag](https://gitlab.com/gitlab-org/gitlab/-/issues/368356).
-
For more information, see [Data usage in Suggested Reviewers](data_usage.md).
### Enable suggested reviewers
diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md
index b54a64e6b7b..38b6bb44b48 100644
--- a/doc/user/project/quick_actions.md
+++ b/doc/user/project/quick_actions.md
@@ -132,7 +132,10 @@ threads. Some quick actions might not be available to all subscription tiers.
## Work items
-The following quick actions can be applied through the description field when editing work items.
+> Executing quick actions from comments [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/391282) in GitLab 15.10.
+
+Work items in GitLab include [tasks](../tasks.md) and [OKRs](../okrs.md).
+The following quick actions can be applied through the description field when editing or commenting on work items.
| Command | Task | Objective | Key Result | Action
|:-------------------------------------------------------------------------------------------------|:-----------------------|:-----------------------|:-----------------------|:-------|
diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md
index 7250406144f..42387535100 100644
--- a/doc/user/project/settings/index.md
+++ b/doc/user/project/settings/index.md
@@ -162,6 +162,7 @@ Configure your project's merge request settings:
- Configure [suggested changes commit messages](../merge_requests/reviews/suggestions.md#configure-the-commit-message-for-applied-suggestions).
- Configure [merge and squash commit message templates](../merge_requests/commit_templates.md).
- Configure [the default target project](../merge_requests/creating_merge_requests.md#set-the-default-target-project) for merge requests coming from forks.
+- Enable [Suggested Reviewers](../merge_requests/reviews/index.md#suggested-reviewers).
## Service Desk
diff --git a/lib/api/concerns/packages/npm_endpoints.rb b/lib/api/concerns/packages/npm_endpoints.rb
index 703a655ddce..3e7d5ac9d0c 100644
--- a/lib/api/concerns/packages/npm_endpoints.rb
+++ b/lib/api/concerns/packages/npm_endpoints.rb
@@ -42,6 +42,10 @@ module API
present []
end
end
+
+ def generate_metadata_service(packages)
+ ::Packages::Npm::GenerateMetadataService.new(params[:package_name], packages)
+ end
end
params do
@@ -73,7 +77,8 @@ module API
not_found! if packages.empty?
- present ::Packages::Npm::PackagePresenter.new(package_name, packages),
+ metadata = generate_metadata_service(packages).execute(only_dist_tags: true)
+ present ::Packages::Npm::PackagePresenter.new(metadata),
with: ::API::Entities::NpmPackageTag
end
@@ -186,7 +191,7 @@ module API
not_found!('Packages') if packages.empty?
- present ::Packages::Npm::PackagePresenter.new(package_name, packages),
+ present ::Packages::Npm::PackagePresenter.new(generate_metadata_service(packages).execute),
with: ::API::Entities::NpmPackage
end
end
diff --git a/lib/bulk_imports/clients/http.rb b/lib/bulk_imports/clients/http.rb
index 616ab8754b4..6efee83a0dd 100644
--- a/lib/bulk_imports/clients/http.rb
+++ b/lib/bulk_imports/clients/http.rb
@@ -165,8 +165,6 @@ module BulkImports
raise ::BulkImports::NetworkError.new("Unsuccessful response #{response.code} from #{response.request.path.path}. Body: #{response.parsed_response}", response: response)
- rescue Gitlab::HTTP::BlockedUrlError => e
- raise e
rescue *Gitlab::HTTP::HTTP_ERRORS => e
raise ::BulkImports::NetworkError, e
end
diff --git a/lib/bulk_imports/error.rb b/lib/bulk_imports/error.rb
index fb72cb61de0..009fa02a72a 100644
--- a/lib/bulk_imports/error.rb
+++ b/lib/bulk_imports/error.rb
@@ -3,7 +3,8 @@
module BulkImports
class Error < StandardError
def self.unsupported_gitlab_version
- self.new("Unsupported GitLab version. Minimum supported version is #{BulkImport::MIN_MAJOR_VERSION}.")
+ self.new("Unsupported GitLab version. Source instance must run GitLab version #{BulkImport::MIN_MAJOR_VERSION} " \
+ "or later.")
end
def self.scope_validation_failure
@@ -18,11 +19,5 @@ module BulkImports
def self.destination_full_path_validation_failure(full_path)
self.new("Import aborted as '#{full_path}' already exists. Change the destination and try again.")
end
-
- def self.setting_not_enabled
- self.new("Group import disabled on source or destination instance. " \
- "Ask an administrator to enable it on both instances and try again."
- )
- end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 23475c83bb3..08a57b9f3b2 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -39050,6 +39050,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose approver type"
msgstr ""
+msgid "SecurityOrchestration|Choose specific role"
+msgstr ""
+
msgid "SecurityOrchestration|Clear all"
msgstr ""
@@ -39206,6 +39209,9 @@ msgstr ""
msgid "SecurityOrchestration|Require %{approvals} %{plural} from %{approvers} if any of the following occur:"
msgstr ""
+msgid "SecurityOrchestration|Roles"
+msgstr ""
+
msgid "SecurityOrchestration|Rule mode"
msgstr ""
diff --git a/package.json b/package.json
index 74ba2fd6ee2..4d6fa5fe6bb 100644
--- a/package.json
+++ b/package.json
@@ -50,13 +50,13 @@
"@apollo/client": "^3.5.10",
"@babel/core": "^7.18.5",
"@babel/preset-env": "^7.18.2",
- "@cubejs-client/core": "^0.32.0",
- "@cubejs-client/vue": "^0.32.0",
+ "@cubejs-client/core": "^0.32.12",
+ "@cubejs-client/vue": "^0.32.12",
"@gitlab/at.js": "1.5.7",
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/fonts": "^1.2.0",
- "@gitlab/svgs": "3.28.0",
- "@gitlab/ui": "58.2.1",
+ "@gitlab/svgs": "3.30.0",
+ "@gitlab/ui": "58.4.0",
"@gitlab/visual-review-tools": "1.7.3",
"@gitlab/web-ide": "0.0.1-dev-20230223005157",
"@mattiasbuelens/web-streams-adapter": "^0.1.0",
diff --git a/rubocop/cop/rspec/misspelled_aggregate_failures.rb b/rubocop/cop/rspec/misspelled_aggregate_failures.rb
new file mode 100644
index 00000000000..04eec515318
--- /dev/null
+++ b/rubocop/cop/rspec/misspelled_aggregate_failures.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'rubocop/cop/rspec/base'
+
+module RuboCop
+ module Cop
+ module RSpec
+ class MisspelledAggregateFailures < RuboCop::Cop::RSpec::Base
+ extend RuboCop::Cop::AutoCorrector
+
+ CORRECT_SPELLING = :aggregate_failures
+ MSG = "Use `#{CORRECT_SPELLING.inspect}` to aggregate failures.".freeze
+
+ # @!method aggregate_tag(node)
+ def_node_matcher :aggregate_tag, <<~PATTERN
+ (block
+ (send #rspec? {#ExampleGroups.all #Examples.all}
+ <$(sym /^aggregate[a-z]*_[a-z]+$/) ...>
+ )
+ ...
+ )
+ PATTERN
+
+ def on_block(node)
+ tag_node = aggregate_tag(node)
+ return if tag_node.nil? || tag_node.value == CORRECT_SPELLING
+
+ add_offense(tag_node) do |corrector|
+ corrector.replace(tag_node, CORRECT_SPELLING.inspect)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/scripts/decomposition/generate-loose-foreign-key b/scripts/decomposition/generate-loose-foreign-key
index 1ea1728732b..fbffebb6086 100755
--- a/scripts/decomposition/generate-loose-foreign-key
+++ b/scripts/decomposition/generate-loose-foreign-key
@@ -168,9 +168,8 @@ def generate_migration(definition)
return unless foreign_key_exists?(:#{definition.from_table}, :#{definition.to_table}, name: "#{definition.name}")
with_lock_retries do
- execute('LOCK #{definition.to_table}, #{definition.from_table} IN ACCESS EXCLUSIVE MODE') if transaction_open?
-
- remove_foreign_key_if_exists(:#{definition.from_table}, :#{definition.to_table}, name: "#{definition.name}")
+ remove_foreign_key_if_exists(:#{definition.from_table}, :#{definition.to_table},
+ name: "#{definition.name}", reverse_lock_order: true)
end
end
diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb
index 561f7b37199..45a914439a9 100644
--- a/spec/db/schema_spec.rb
+++ b/spec/db/schema_spec.rb
@@ -31,6 +31,7 @@ RSpec.describe 'Database schema', feature_category: :database do
award_emoji: %w[awardable_id user_id],
aws_roles: %w[role_external_id],
boards: %w[milestone_id iteration_id],
+ broadcast_messages: %w[namespace_id],
chat_names: %w[chat_id team_id user_id integration_id],
chat_teams: %w[team_id],
ci_build_needs: %w[partition_id build_id],
diff --git a/spec/features/projects/work_items/work_item_spec.rb b/spec/features/projects/work_items/work_item_spec.rb
index 807a4db5bf5..e88d514e967 100644
--- a/spec/features/projects/work_items/work_item_spec.rb
+++ b/spec/features/projects/work_items/work_item_spec.rb
@@ -29,7 +29,7 @@ RSpec.describe 'Work item', :js, feature_category: :team_planning do
it_behaves_like 'work items status'
it_behaves_like 'work items assignees'
it_behaves_like 'work items labels'
- it_behaves_like 'work items comments'
+ it_behaves_like 'work items comments', :issue
it_behaves_like 'work items description'
it_behaves_like 'work items milestone'
end
diff --git a/spec/frontend/header_search/components/app_spec.js b/spec/frontend/header_search/components/app_spec.js
index d789579204a..ad56b2dde24 100644
--- a/spec/frontend/header_search/components/app_spec.js
+++ b/spec/frontend/header_search/components/app_spec.js
@@ -20,6 +20,7 @@ import {
IS_NOT_FOCUSED,
IS_FOCUSED,
SEARCH_SHORTCUTS_MIN_CHARACTERS,
+ DROPDOWN_CLOSE_TIMEOUT,
} from '~/header_search/constants';
import DropdownKeyboardNavigation from '~/vue_shared/components/dropdown_keyboard_navigation.vue';
import { ENTER_KEY } from '~/lib/utils/keys';
@@ -43,6 +44,9 @@ jest.mock('~/lib/utils/url_utility', () => ({
describe('HeaderSearchApp', () => {
let wrapper;
+ jest.useFakeTimers();
+ jest.spyOn(global, 'setTimeout');
+
const actionSpies = {
setSearch: jest.fn(),
fetchAutocompleteOptions: jest.fn(),
@@ -174,6 +178,7 @@ describe('HeaderSearchApp', () => {
it(`should close the dropdown when press escape key`, async () => {
findHeaderSearchInput().vm.$emit('keydown', new KeyboardEvent({ key: 27 }));
+ jest.runAllTimers();
await nextTick();
expect(findHeaderSearchDropdown().exists()).toBe(false);
expect(wrapper.emitted().expandSearchBar.length).toBe(1);
@@ -349,12 +354,12 @@ describe('HeaderSearchApp', () => {
});
describe('events', () => {
- beforeEach(() => {
- window.gon.current_username = MOCK_USERNAME;
- createComponent();
- });
-
describe('Header Search Input', () => {
+ beforeEach(() => {
+ window.gon.current_username = MOCK_USERNAME;
+ createComponent();
+ });
+
describe('when dropdown is closed', () => {
let trackingSpy;
@@ -379,6 +384,7 @@ describe('HeaderSearchApp', () => {
expect(findHeaderSearchDropdown().exists()).toBe(false);
findHeaderSearchInput().vm.$emit('focusout');
+ jest.runAllTimers();
await nextTick();
expect(findHeaderSearchDropdown().exists()).toBe(false);
@@ -427,6 +433,21 @@ describe('HeaderSearchApp', () => {
});
});
});
+
+ describe('onFocusout dropdown', () => {
+ beforeEach(() => {
+ window.gon.current_username = MOCK_USERNAME;
+ createComponent({ search: 'tes' }, {});
+ findHeaderSearchInput().vm.$emit('focusin');
+ });
+
+ it('closes with timeout so click event gets emited', () => {
+ findHeaderSearchInput().vm.$emit('focusout');
+
+ expect(setTimeout).toHaveBeenCalledTimes(1);
+ expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), DROPDOWN_CLOSE_TIMEOUT);
+ });
+ });
});
describe('computed', () => {
diff --git a/spec/frontend/sidebar/components/date/sidebar_date_widget_spec.js b/spec/frontend/sidebar/components/date/sidebar_date_widget_spec.js
index b9c8655d5d8..be3fee148da 100644
--- a/spec/frontend/sidebar/components/date/sidebar_date_widget_spec.js
+++ b/spec/frontend/sidebar/components/date/sidebar_date_widget_spec.js
@@ -11,7 +11,12 @@ import SidebarInheritDate from '~/sidebar/components/date/sidebar_inherit_date.v
import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
import epicStartDateQuery from '~/sidebar/queries/epic_start_date.query.graphql';
import issueDueDateQuery from '~/sidebar/queries/issue_due_date.query.graphql';
-import { issuableDueDateResponse, issuableStartDateResponse } from '../../mock_data';
+import issueDueDateSubscription from '~/graphql_shared/subscriptions/work_item_dates.subscription.graphql';
+import {
+ issuableDueDateResponse,
+ issuableStartDateResponse,
+ issueDueDateSubscriptionResponse,
+} from '../../mock_data';
jest.mock('~/alert');
@@ -29,19 +34,25 @@ describe('Sidebar date Widget', () => {
const createComponent = ({
dueDateQueryHandler = jest.fn().mockResolvedValue(issuableDueDateResponse()),
startDateQueryHandler = jest.fn().mockResolvedValue(issuableStartDateResponse()),
+ dueDateSubscriptionHandler = jest.fn().mockResolvedValue(issueDueDateSubscriptionResponse()),
canInherit = false,
dateType = undefined,
issuableType = 'issue',
+ realTimeIssueDueDate = true,
} = {}) => {
fakeApollo = createMockApollo([
[issueDueDateQuery, dueDateQueryHandler],
[epicStartDateQuery, startDateQueryHandler],
+ [issueDueDateSubscription, dueDateSubscriptionHandler],
]);
wrapper = shallowMount(SidebarDateWidget, {
apolloProvider: fakeApollo,
provide: {
canUpdate: true,
+ glFeatures: {
+ realTimeIssueDueDate,
+ },
},
propsData: {
fullPath: 'group/project',
@@ -136,6 +147,32 @@ describe('Sidebar date Widget', () => {
});
});
+ describe('real time issue due date feature', () => {
+ describe('when :real_time_issue_due_date feature is enabled', () => {
+ it('should call the subscription', async () => {
+ const dueDateSubscriptionHandler = jest
+ .fn()
+ .mockResolvedValue(issueDueDateSubscriptionResponse());
+ createComponent({ realTimeIssueDueDate: true, dueDateSubscriptionHandler });
+ await waitForPromises();
+
+ expect(dueDateSubscriptionHandler).toHaveBeenCalled();
+ });
+ });
+
+ describe('when :real_time_issue_due_date feature is disabled', () => {
+ it('should not call the subscription', async () => {
+ const dueDateSubscriptionHandler = jest
+ .fn()
+ .mockResolvedValue(issueDueDateSubscriptionResponse());
+ createComponent({ realTimeIssueDueDate: false, dueDateSubscriptionHandler });
+ await waitForPromises();
+
+ expect(dueDateSubscriptionHandler).not.toHaveBeenCalled();
+ });
+ });
+ });
+
it.each`
canInherit | component | componentName | expected
${true} | ${SidebarFormattedDate} | ${'SidebarFormattedDate'} | ${false}
diff --git a/spec/frontend/sidebar/mock_data.js b/spec/frontend/sidebar/mock_data.js
index 844320efc1c..05a7f504fd4 100644
--- a/spec/frontend/sidebar/mock_data.js
+++ b/spec/frontend/sidebar/mock_data.js
@@ -249,6 +249,17 @@ export const issuableDueDateResponse = (dueDate = null) => ({
},
});
+export const issueDueDateSubscriptionResponse = () => ({
+ data: {
+ issuableDatesUpdated: {
+ issue: {
+ id: 'gid://gitlab/Issue/4',
+ dueDate: '2022-12-31',
+ },
+ },
+ },
+});
+
export const issuableStartDateResponse = (startDate = null) => ({
data: {
workspace: {
diff --git a/spec/frontend/vue_shared/components/source_viewer/components/chunk_line_spec.js b/spec/frontend/vue_shared/components/source_viewer/components/chunk_line_spec.js
index 6c8fc244fa0..9a38a96663d 100644
--- a/spec/frontend/vue_shared/components/source_viewer/components/chunk_line_spec.js
+++ b/spec/frontend/vue_shared/components/source_viewer/components/chunk_line_spec.js
@@ -10,16 +10,10 @@ const DEFAULT_PROPS = {
describe('Chunk Line component', () => {
let wrapper;
- const fileLineBlame = true;
const createComponent = (props = {}) => {
wrapper = shallowMountExtended(ChunkLine, {
propsData: { ...DEFAULT_PROPS, ...props },
- provide: {
- glFeatures: {
- fileLineBlame,
- },
- },
});
};
diff --git a/spec/frontend/vue_shared/components/source_viewer/components/chunk_spec.js b/spec/frontend/vue_shared/components/source_viewer/components/chunk_spec.js
index 59880496d74..ff50326917f 100644
--- a/spec/frontend/vue_shared/components/source_viewer/components/chunk_spec.js
+++ b/spec/frontend/vue_shared/components/source_viewer/components/chunk_spec.js
@@ -11,7 +11,6 @@ describe('Chunk component', () => {
const createComponent = (props = {}) => {
wrapper = shallowMountExtended(Chunk, {
propsData: { ...CHUNK_1, ...props },
- provide: { glFeatures: { fileLineBlame: true } },
});
};
diff --git a/spec/frontend/work_items/components/notes/work_item_add_note_spec.js b/spec/frontend/work_items/components/notes/work_item_add_note_spec.js
index 19893d6c0a4..908c6510bb2 100644
--- a/spec/frontend/work_items/components/notes/work_item_add_note_spec.js
+++ b/spec/frontend/work_items/components/notes/work_item_add_note_spec.js
@@ -84,6 +84,8 @@ describe('WorkItemCommentForm', () => {
queryVariables,
fetchByIid,
workItemType,
+ markdownPreviewPath: '/group/project/preview_markdown?target_type=WorkItem',
+ autocompleteDataSources: {},
},
stubs: {
WorkItemCommentLocked,
diff --git a/spec/frontend/work_items/components/notes/work_item_comment_form_spec.js b/spec/frontend/work_items/components/notes/work_item_comment_form_spec.js
index 23a9f285804..bf36e3999d4 100644
--- a/spec/frontend/work_items/components/notes/work_item_comment_form_spec.js
+++ b/spec/frontend/work_items/components/notes/work_item_comment_form_spec.js
@@ -35,6 +35,8 @@ describe('Work item comment form component', () => {
autosaveKey: mockAutosaveKey,
isSubmitting,
initialValue,
+ markdownPreviewPath: '/group/project/preview_markdown?target_type=WorkItem',
+ autocompleteDataSources: {},
},
provide: {
fullPath: 'test-project-path',
@@ -42,11 +44,11 @@ describe('Work item comment form component', () => {
});
};
- it('passes correct markdown preview path to markdown editor', () => {
+ it('passes markdown preview path to markdown editor', () => {
createComponent();
expect(findMarkdownEditor().props('renderMarkdownPath')).toBe(
- '/test-project-path/preview_markdown?target_type=Issue',
+ '/group/project/preview_markdown?target_type=WorkItem',
);
});
diff --git a/spec/frontend/work_items/components/notes/work_item_discussion_spec.js b/spec/frontend/work_items/components/notes/work_item_discussion_spec.js
index 6b95da0910b..1255e3b549a 100644
--- a/spec/frontend/work_items/components/notes/work_item_discussion_spec.js
+++ b/spec/frontend/work_items/components/notes/work_item_discussion_spec.js
@@ -41,6 +41,8 @@ describe('Work Item Discussion', () => {
fetchByIid,
fullPath,
workItemType,
+ markdownPreviewPath: '/group/project/preview_markdown?target_type=WorkItem',
+ autocompleteDataSources: {},
},
});
};
diff --git a/spec/frontend/work_items/components/notes/work_item_note_spec.js b/spec/frontend/work_items/components/notes/work_item_note_spec.js
index 2d6d12c69f9..715b6bde286 100644
--- a/spec/frontend/work_items/components/notes/work_item_note_spec.js
+++ b/spec/frontend/work_items/components/notes/work_item_note_spec.js
@@ -59,6 +59,8 @@ describe('Work Item Note', () => {
note,
isFirstNote,
workItemType: 'Task',
+ markdownPreviewPath: '/group/project/preview_markdown?target_type=WorkItem',
+ autocompleteDataSources: {},
},
apolloProvider: mockApollo([[updateWorkItemNoteMutation, updateNoteMutationHandler]]),
});
diff --git a/spec/frontend/work_items/components/work_item_detail_spec.js b/spec/frontend/work_items/components/work_item_detail_spec.js
index 4d13fbb3250..a4bf2386ed4 100644
--- a/spec/frontend/work_items/components/work_item_detail_spec.js
+++ b/spec/frontend/work_items/components/work_item_detail_spec.js
@@ -29,7 +29,7 @@ import WorkItemDetailModal from '~/work_items/components/work_item_detail_modal.
import { i18n } from '~/work_items/constants';
import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql';
-import workItemDatesSubscription from '~/work_items/graphql/work_item_dates.subscription.graphql';
+import workItemDatesSubscription from '~/graphql_shared/subscriptions/work_item_dates.subscription.graphql';
import workItemTitleSubscription from '~/work_items/graphql/work_item_title.subscription.graphql';
import workItemAssigneesSubscription from '~/work_items/graphql/work_item_assignees.subscription.graphql';
import workItemMilestoneSubscription from '~/work_items/graphql/work_item_milestone.subscription.graphql';
diff --git a/spec/frontend/work_items/components/work_item_notes_spec.js b/spec/frontend/work_items/components/work_item_notes_spec.js
index a067923b9fc..68ba8fb43a1 100644
--- a/spec/frontend/work_items/components/work_item_notes_spec.js
+++ b/spec/frontend/work_items/components/work_item_notes_spec.js
@@ -18,6 +18,7 @@ import workItemNoteUpdatedSubscription from '~/work_items/graphql/notes/work_ite
import workItemNoteDeletedSubscription from '~/work_items/graphql/notes/work_item_note_deleted.subscription.graphql';
import { DEFAULT_PAGE_SIZE_NOTES, WIDGET_TYPE_NOTES } from '~/work_items/constants';
import { ASC, DESC } from '~/notes/constants';
+import { autocompleteDataSources, markdownPreviewPath } from '~/work_items/utils';
import {
mockWorkItemNotesResponse,
workItemQueryResponse,
@@ -30,6 +31,7 @@ import {
} from '../mock_data';
const mockWorkItemId = workItemQueryResponse.data.workItem.id;
+const mockWorkItemIid = workItemQueryResponse.data.workItem.iid;
const mockNotesWidgetResponse = mockWorkItemNotesResponse.data.workItem.widgets.find(
(widget) => widget.type === WIDGET_TYPE_NOTES,
);
@@ -92,6 +94,7 @@ describe('WorkItemNotes component', () => {
const createComponent = ({
workItemId = mockWorkItemId,
fetchByIid = false,
+ workItemIid = mockWorkItemIid,
defaultWorkItemNotesQueryHandler = workItemNotesQueryHandler,
deleteWINoteMutationHandler = deleteWorkItemNoteMutationSuccessHandler,
} = {}) => {
@@ -106,6 +109,7 @@ describe('WorkItemNotes component', () => {
]),
propsData: {
workItemId,
+ workItemIid,
queryVariables: {
id: workItemId,
},
@@ -258,9 +262,11 @@ describe('WorkItemNotes component', () => {
const commentIndex = 0;
const firstCommentNote = findWorkItemCommentNoteAtIndex(commentIndex);
- expect(firstCommentNote.props('discussion')).toEqual(
- mockDiscussions[commentIndex].notes.nodes,
- );
+ expect(firstCommentNote.props()).toMatchObject({
+ discussion: mockDiscussions[commentIndex].notes.nodes,
+ autocompleteDataSources: autocompleteDataSources('test-path', mockWorkItemIid),
+ markdownPreviewPath: markdownPreviewPath('test-path', mockWorkItemIid),
+ });
});
});
diff --git a/spec/frontend/work_items/mock_data.js b/spec/frontend/work_items/mock_data.js
index fecf98b2651..ddad9470eee 100644
--- a/spec/frontend/work_items/mock_data.js
+++ b/spec/frontend/work_items/mock_data.js
@@ -313,7 +313,7 @@ export const workItemResponseFactory = ({
workItem: {
__typename: 'WorkItem',
id: 'gid://gitlab/WorkItem/1',
- iid: 1,
+ iid: '1',
title: 'Updated title',
state: 'OPEN',
description: 'description',
@@ -834,7 +834,7 @@ export const workItemHierarchyEmptyResponse = {
data: {
workItem: {
id: 'gid://gitlab/WorkItem/1',
- iid: 1,
+ iid: '1',
state: 'OPEN',
workItemType: {
id: 'gid://gitlab/WorkItems::Type/1',
@@ -881,7 +881,7 @@ export const workItemHierarchyNoUpdatePermissionResponse = {
data: {
workItem: {
id: 'gid://gitlab/WorkItem/1',
- iid: 1,
+ iid: '1',
state: 'OPEN',
workItemType: {
id: 'gid://gitlab/WorkItems::Type/6',
diff --git a/spec/frontend/work_items/router_spec.js b/spec/frontend/work_items/router_spec.js
index 5dad7f7c43f..bd75c5be6f1 100644
--- a/spec/frontend/work_items/router_spec.js
+++ b/spec/frontend/work_items/router_spec.js
@@ -13,7 +13,7 @@ import {
} from 'jest/work_items/mock_data';
import App from '~/work_items/components/app.vue';
import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
-import workItemDatesSubscription from '~/work_items/graphql/work_item_dates.subscription.graphql';
+import workItemDatesSubscription from '~/graphql_shared/subscriptions/work_item_dates.subscription.graphql';
import workItemTitleSubscription from '~/work_items/graphql/work_item_title.subscription.graphql';
import workItemAssigneesSubscription from '~/work_items/graphql/work_item_assignees.subscription.graphql';
import workItemLabelsSubscription from 'ee_else_ce/work_items/graphql/work_item_labels.subscription.graphql';
diff --git a/spec/lib/api/github/entities_spec.rb b/spec/lib/api/github/entities_spec.rb
index 00ea60c5d65..63c54b259a2 100644
--- a/spec/lib/api/github/entities_spec.rb
+++ b/spec/lib/api/github/entities_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe API::Github::Entities do
subject { entity.as_json }
- specify :aggregate_failure do
+ specify :aggregate_failures do
expect(subject[:id]).to eq user.id
expect(subject[:login]).to eq 'name_of_user'
expect(subject[:url]).to eq expected_user_url
diff --git a/spec/lib/bulk_imports/clients/graphql_spec.rb b/spec/lib/bulk_imports/clients/graphql_spec.rb
index 5ded54a3448..58e6992698c 100644
--- a/spec/lib/bulk_imports/clients/graphql_spec.rb
+++ b/spec/lib/bulk_imports/clients/graphql_spec.rb
@@ -13,6 +13,10 @@ RSpec.describe BulkImports::Clients::Graphql, feature_category: :importers do
let(:response_double) { double }
let(:version) { '14.0.0' }
+ before do
+ stub_const('BulkImports::MINIMUM_COMPATIBLE_MAJOR_VERSION', version)
+ end
+
describe 'source instance validation' do
before do
allow(graphql_client_double).to receive(:execute)
@@ -33,7 +37,7 @@ RSpec.describe BulkImports::Clients::Graphql, feature_category: :importers do
let(:version) { '13.0.0' }
it 'raises an error' do
- expect { subject.execute('test') }.to raise_error(::BulkImports::Error, "Unsupported GitLab version. Minimum supported version is 14.")
+ expect { subject.execute('test') }.to raise_error(::BulkImports::Error, "Unsupported GitLab version. Source instance must run GitLab version #{BulkImport::MIN_MAJOR_VERSION} or later.")
end
end
end
diff --git a/spec/lib/gitlab/redis/multi_store_spec.rb b/spec/lib/gitlab/redis/multi_store_spec.rb
index baf2546fc5c..e45c29a9dd2 100644
--- a/spec/lib/gitlab/redis/multi_store_spec.rb
+++ b/spec/lib/gitlab/redis/multi_store_spec.rb
@@ -333,7 +333,7 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
stub_feature_flags(use_primary_store_as_default_for_test_store: false)
end
- it 'executes only on secondary redis store', :aggregate_errors do
+ it 'executes only on secondary redis store', :aggregate_failures do
expect(secondary_store).to receive(name).with(*expected_args).and_call_original
expect(primary_store).not_to receive(name).with(*expected_args).and_call_original
@@ -342,7 +342,7 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
end
context 'when using primary store as default' do
- it 'executes only on primary redis store', :aggregate_errors do
+ it 'executes only on primary redis store', :aggregate_failures do
expect(primary_store).to receive(name).with(*expected_args).and_call_original
expect(secondary_store).not_to receive(name).with(*expected_args).and_call_original
@@ -437,7 +437,7 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
end
RSpec.shared_examples_for 'verify that store contains values' do |store|
- it "#{store} redis store contains correct values", :aggregate_errors do
+ it "#{store} redis store contains correct values", :aggregate_failures do
subject
redis_store = multi_store.send(store)
@@ -530,7 +530,7 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
end
context 'when executing on primary instance is successful' do
- it 'executes on both primary and secondary redis store', :aggregate_errors do
+ it 'executes on both primary and secondary redis store', :aggregate_failures do
expect(primary_store).to receive(name).with(*expected_args).and_call_original
expect(secondary_store).to receive(name).with(*expected_args).and_call_original
@@ -551,7 +551,7 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
stub_feature_flags(use_primary_store_as_default_for_test_store: false)
end
- it 'executes only on secondary redis store', :aggregate_errors do
+ it 'executes only on secondary redis store', :aggregate_failures do
expect(secondary_store).to receive(name).with(*expected_args).and_call_original
expect(primary_store).not_to receive(name).with(*expected_args).and_call_original
@@ -560,7 +560,7 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
end
context 'when using primary store as default' do
- it 'executes only on primary redis store', :aggregate_errors do
+ it 'executes only on primary redis store', :aggregate_failures do
expect(primary_store).to receive(name).with(*expected_args).and_call_original
expect(secondary_store).not_to receive(name).with(*expected_args).and_call_original
@@ -575,7 +575,7 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
allow(Gitlab::ErrorTracking).to receive(:log_exception)
end
- it 'logs the exception and execute on secondary instance', :aggregate_errors do
+ it 'logs the exception and execute on secondary instance', :aggregate_failures do
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(an_instance_of(StandardError),
hash_including(:multi_store_error_message, command_name: name, instance_name: instance_name))
expect(secondary_store).to receive(name).with(*expected_args).and_call_original
@@ -593,7 +593,7 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
end
end
- it 'is executed only 1 time on each instance', :aggregate_errors do
+ it 'is executed only 1 time on each instance', :aggregate_failures do
expect(primary_store).to receive(:pipelined).and_call_original
expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
expect(pipeline).to receive(name).with(*expected_args).once.and_call_original
@@ -645,7 +645,7 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
end
context 'when executing on primary instance is successful' do
- it 'executes on both primary and secondary redis store', :aggregate_errors do
+ it 'executes on both primary and secondary redis store', :aggregate_failures do
expect(primary_store).to receive(name).and_call_original
expect(secondary_store).to receive(name).and_call_original
@@ -662,7 +662,7 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
allow(Gitlab::ErrorTracking).to receive(:log_exception)
end
- it 'logs the exception and execute on secondary instance', :aggregate_errors do
+ it 'logs the exception and execute on secondary instance', :aggregate_failures do
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(an_instance_of(StandardError),
hash_including(:multi_store_error_message, command_name: name))
expect(secondary_store).to receive(name).and_call_original
@@ -760,7 +760,7 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
stub_feature_flags(use_primary_store_as_default_for_test_store: false)
end
- it 'executes on secondary store', :aggregate_errors do
+ it 'executes on secondary store', :aggregate_failures do
expect(primary_store).not_to receive(:send).and_call_original
expect(secondary_store).to receive(:send).and_call_original
@@ -769,7 +769,7 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
end
context 'when using primary store as default' do
- it 'executes on primary store', :aggregate_errors do
+ it 'executes on primary store', :aggregate_failures do
expect(secondary_store).not_to receive(:send).and_call_original
expect(primary_store).to receive(:send).and_call_original
@@ -930,7 +930,7 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
subject
end
- it 'fallback and executes only on the secondary store', :aggregate_errors do
+ it 'fallback and executes only on the secondary store', :aggregate_failures do
expect(primary_store).to receive(:command).and_call_original
expect(secondary_store).not_to receive(:command)
@@ -955,7 +955,7 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
end
context 'with feature flag :use_primary_store_as_default_for_test_store is enabled' do
- it 'fallback and executes only on the secondary store', :aggregate_errors do
+ it 'fallback and executes only on the secondary store', :aggregate_failures do
expect(primary_store).to receive(:command).and_call_original
expect(secondary_store).not_to receive(:command)
@@ -968,7 +968,7 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
stub_feature_flags(use_primary_store_as_default_for_test_store: false)
end
- it 'fallback and executes only on the secondary store', :aggregate_errors do
+ it 'fallback and executes only on the secondary store', :aggregate_failures do
expect(secondary_store).to receive(:command).and_call_original
expect(primary_store).not_to receive(:command)
@@ -981,7 +981,7 @@ RSpec.describe Gitlab::Redis::MultiStore, feature_category: :redis do
multi_store.pipelined(&:command)
end
- it 'is executed only 1 time on each instance', :aggregate_errors do
+ it 'is executed only 1 time on each instance', :aggregate_failures do
expect(primary_store).to receive(:pipelined).once.and_call_original
expect(secondary_store).to receive(:pipelined).once.and_call_original
diff --git a/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb b/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb
index e3d9549a3c0..4b589dc43af 100644
--- a/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb
+++ b/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb
@@ -309,7 +309,7 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
end
shared_examples 'performs database queries' do
- it 'logs the database time', :aggregate_errors do
+ it 'logs the database time', :aggregate_failures do
expect(logger).to receive(:info).with(expected_start_payload).ordered
expect(logger).to receive(:info).with(expected_end_payload_with_db).ordered
@@ -318,7 +318,7 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
end
end
- it 'prevents database time from leaking to the next job', :aggregate_errors do
+ it 'prevents database time from leaking to the next job', :aggregate_failures do
expect(logger).to receive(:info).with(expected_start_payload).ordered
expect(logger).to receive(:info).with(expected_end_payload_with_db).ordered
expect(logger).to receive(:info).with(expected_start_payload).ordered
diff --git a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
index 8c497970555..aadd398e5fd 100644
--- a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
@@ -428,7 +428,7 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
described_class.track_event('event4', values: entity2, time: 2.days.ago)
end
- it 'calculates union of given events', :aggregate_failure do
+ it 'calculates union of given events', :aggregate_failures do
expect(described_class.calculate_events_union(**time_range.merge(event_names: %w[event4]))).to eq 2
expect(described_class.calculate_events_union(**time_range.merge(event_names: %w[event1_slot event2_slot event3_slot]))).to eq 3
end
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index d529319e6e9..1e455c82a15 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -1067,7 +1067,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures, feature_category: :servic
expect(result.duration).to be_an(Float)
end
- it 'records error and returns nil', :aggregated_errors do
+ it 'records error and returns nil', :aggregate_failures do
allow(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception)
result = described_class.with_metadata { raise }
diff --git a/spec/lib/gitlab/utils/measuring_spec.rb b/spec/lib/gitlab/utils/measuring_spec.rb
index 5dad79b1c5f..4d2791f771f 100644
--- a/spec/lib/gitlab/utils/measuring_spec.rb
+++ b/spec/lib/gitlab/utils/measuring_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe Gitlab::Utils::Measuring do
measurement.with_measuring { result }
end
- it 'measures and logs data', :aggregate_failure do
+ it 'measures and logs data', :aggregate_failures do
expect(measurement).to receive(:with_measure_time).and_call_original
expect(measurement).to receive(:with_count_queries).and_call_original
expect(measurement).to receive(:with_gc_stats).and_call_original
diff --git a/spec/migrations/schedule_recalculate_vulnerability_finding_signatures_for_findings_spec.rb b/spec/migrations/schedule_recalculate_vulnerability_finding_signatures_for_findings_spec.rb
index 8903a32285e..a238ab70c30 100644
--- a/spec/migrations/schedule_recalculate_vulnerability_finding_signatures_for_findings_spec.rb
+++ b/spec/migrations/schedule_recalculate_vulnerability_finding_signatures_for_findings_spec.rb
@@ -53,7 +53,7 @@ feature_category: :vulnerability_management do
let!(:signature3) { vulnerability_finding_signatures.create!(finding_id: finding3.id, algorithm_type: 0, signature_sha: ::Digest::SHA1.digest(SecureRandom.hex(50))) }
# this migration is now a no-op
- it 'does not schedule the background jobs', :aggregate_failure do
+ it 'does not schedule the background jobs', :aggregate_failures do
Sidekiq::Testing.fake! do
freeze_time do
migrate!
diff --git a/spec/presenters/packages/npm/package_presenter_spec.rb b/spec/presenters/packages/npm/package_presenter_spec.rb
index 4fa469c7cd2..fe4773a9cad 100644
--- a/spec/presenters/packages/npm/package_presenter_spec.rb
+++ b/spec/presenters/packages/npm/package_presenter_spec.rb
@@ -2,157 +2,32 @@
require 'spec_helper'
-RSpec.describe ::Packages::Npm::PackagePresenter do
- using RSpec::Parameterized::TableSyntax
-
- let_it_be(:project) { create(:project) }
- let_it_be(:package_name) { "@#{project.root_namespace.path}/test" }
- let_it_be(:package1) { create(:npm_package, version: '2.0.4', project: project, name: package_name) }
- let_it_be(:package2) { create(:npm_package, version: '2.0.6', project: project, name: package_name) }
- let_it_be(:latest_package) { create(:npm_package, version: '2.0.11', project: project, name: package_name) }
-
- let(:packages) { project.packages.npm.with_name(package_name).last_of_each_version }
- let(:presenter) { described_class.new(package_name, packages) }
-
- describe '#versions' do
- let_it_be('package_json') do
- {
- 'name': package_name,
- 'version': '2.0.4',
- 'deprecated': 'warning!',
- 'bin': './cli.js',
- 'directories': ['lib'],
- 'engines': { 'npm': '^7.5.6' },
- '_hasShrinkwrap': false,
- 'dist': {
- 'tarball': 'http://localhost/tarball.tgz',
- 'shasum': '1234567890'
- },
- 'custom_field': 'foo_bar'
- }
- end
-
- let(:presenter) { described_class.new(package_name, packages) }
-
- subject { presenter.versions }
-
- where(:has_dependencies, :has_metadatum) do
- true | true
- false | true
- true | false
- false | false
- end
-
- with_them do
- if params[:has_dependencies]
- ::Packages::DependencyLink.dependency_types.keys.each do |dependency_type|
- let_it_be("package_dependency_link_for_#{dependency_type}") { create(:packages_dependency_link, package: package1, dependency_type: dependency_type) }
- end
- end
-
- if params[:has_metadatum]
- let_it_be('package_metadatadum') { create(:npm_metadatum, package: package1, package_json: package_json) }
- end
-
- it { is_expected.to be_a(Hash) }
- it { expect(subject[package1.version].with_indifferent_access).to match_schema('public_api/v4/packages/npm_package_version') }
- it { expect(subject[package2.version].with_indifferent_access).to match_schema('public_api/v4/packages/npm_package_version') }
- it { expect(subject[package1.version]['custom_field']).to be_blank }
-
- context 'dependencies' do
- ::Packages::DependencyLink.dependency_types.keys.each do |dependency_type|
- if params[:has_dependencies]
- it { expect(subject.dig(package1.version, dependency_type.to_s)).to be_any }
- else
- it { expect(subject.dig(package1.version, dependency_type)).to be nil }
- end
-
- it { expect(subject.dig(package2.version, dependency_type)).to be nil }
- end
- end
-
- context 'metadatum' do
- ::Packages::Npm::PackagePresenter::PACKAGE_JSON_ALLOWED_FIELDS.each do |metadata_field|
- if params[:has_metadatum]
- it { expect(subject.dig(package1.version, metadata_field)).not_to be nil }
- else
- it { expect(subject.dig(package1.version, metadata_field)).to be nil }
- end
-
- it { expect(subject.dig(package2.version, metadata_field)).to be nil }
- end
- end
+RSpec.describe Packages::Npm::PackagePresenter, feature_category: :package_registry do
+ let_it_be(:metadata) do
+ {
+ name: 'foo',
+ versions: { '1.0.0' => { 'dist' => { 'tarball' => 'http://localhost/tarball.tgz' } } },
+ dist_tags: { 'latest' => '1.0.0' }
+ }
+ end
- it 'avoids N+1 database queries' do
- check_n_plus_one(:versions) do
- create_list(:npm_package, 5, project: project, name: package_name).each do |npm_package|
- next unless has_dependencies
+ subject { described_class.new(metadata) }
- ::Packages::DependencyLink.dependency_types.keys.each do |dependency_type|
- create(:packages_dependency_link, package: npm_package, dependency_type: dependency_type)
- end
- end
- end
- end
+ describe '#name' do
+ it 'returns the name' do
+ expect(subject.name).to eq('foo')
end
+ end
- context 'with package files pending destruction' do
- let_it_be(:package_file_pending_destruction) { create(:package_file, :pending_destruction, package: package2, file_sha1: 'pending_destruction_sha1') }
-
- let(:shasums) { subject.values.map { |v| v.dig(:dist, :shasum) } }
-
- it 'does not return them' do
- expect(shasums).not_to include(package_file_pending_destruction.file_sha1)
- end
+ describe '#versions' do
+ it 'returns the versions' do
+ expect(subject.versions).to eq({ '1.0.0' => { 'dist' => { 'tarball' => 'http://localhost/tarball.tgz' } } })
end
end
describe '#dist_tags' do
- subject { presenter.dist_tags }
-
- context 'for packages without tags' do
- it { is_expected.to be_a(Hash) }
- it { expect(subject["latest"]).to eq(latest_package.version) }
-
- it 'avoids N+1 database queries' do
- check_n_plus_one(:dist_tags) do
- create_list(:npm_package, 5, project: project, name: package_name)
- end
- end
- end
-
- context 'for packages with tags' do
- let_it_be(:package_tag1) { create(:packages_tag, package: package1, name: 'release_a') }
- let_it_be(:package_tag2) { create(:packages_tag, package: package1, name: 'test_release') }
- let_it_be(:package_tag3) { create(:packages_tag, package: package2, name: 'release_b') }
- let_it_be(:package_tag4) { create(:packages_tag, package: latest_package, name: 'release_c') }
- let_it_be(:package_tag5) { create(:packages_tag, package: latest_package, name: 'latest') }
-
- it { is_expected.to be_a(Hash) }
- it { expect(subject[package_tag1.name]).to eq(package1.version) }
- it { expect(subject[package_tag2.name]).to eq(package1.version) }
- it { expect(subject[package_tag3.name]).to eq(package2.version) }
- it { expect(subject[package_tag4.name]).to eq(latest_package.version) }
- it { expect(subject[package_tag5.name]).to eq(latest_package.version) }
-
- it 'avoids N+1 database queries' do
- check_n_plus_one(:dist_tags) do
- create_list(:npm_package, 5, project: project, name: package_name).each_with_index do |npm_package, index|
- create(:packages_tag, package: npm_package, name: "tag_#{index}")
- end
- end
- end
+ it 'returns the dist_tags' do
+ expect(subject.dist_tags).to eq({ 'latest' => '1.0.0' })
end
end
-
- def check_n_plus_one(field)
- pkgs = project.packages.npm.with_name(package_name).last_of_each_version.preload_files
- control = ActiveRecord::QueryRecorder.new { described_class.new(package_name, pkgs).public_send(field) }
-
- yield
-
- pkgs = project.packages.npm.with_name(package_name).last_of_each_version.preload_files
-
- expect { described_class.new(package_name, pkgs).public_send(field) }.not_to exceed_query_limit(control)
- end
end
diff --git a/spec/requests/api/bulk_imports_spec.rb b/spec/requests/api/bulk_imports_spec.rb
index 70c581716ce..23dfe865ba3 100644
--- a/spec/requests/api/bulk_imports_spec.rb
+++ b/spec/requests/api/bulk_imports_spec.rb
@@ -93,9 +93,6 @@ RSpec.describe API::BulkImports, feature_category: :importers do
}
end
- let(:source_entity_type) { BulkImports::CreateService::ENTITY_TYPES_MAPPING.fetch(params[:entities][0][:source_type]) }
- let(:source_entity_identifier) { ERB::Util.url_encode(params[:entities][0][:source_full_path]) }
-
before do
allow_next_instance_of(BulkImports::Clients::HTTP) do |instance|
allow(instance)
@@ -106,8 +103,6 @@ RSpec.describe API::BulkImports, feature_category: :importers do
.to receive(:instance_enterprise)
.and_return(false)
end
- stub_request(:get, "http://gitlab.example/api/v4/#{source_entity_type}/#{source_entity_identifier}/export_relations/status?page=1&per_page=30&private_token=access_token")
- .to_return(status: 200, body: "", headers: {})
end
shared_examples 'starting a new migration' do
@@ -276,41 +271,12 @@ RSpec.describe API::BulkImports, feature_category: :importers do
}
end
- it 'returns blocked url message in the error' do
- request
-
- expect(response).to have_gitlab_http_status(:unprocessable_entity)
-
- expect(json_response['message']).to include("Url is blocked: Only allowed schemes are http, https")
- end
- end
-
- context 'when source instance setting is disabled' do
- let(:params) do
- {
- configuration: {
- url: 'http://gitlab.example',
- access_token: 'access_token'
- },
- entities: [
- source_type: 'group_entity',
- source_full_path: 'full_path',
- destination_slug: 'destination_slug',
- destination_namespace: 'destination_namespace'
- ]
- }
- end
-
it 'returns blocked url error' do
- stub_request(:get, "http://gitlab.example/api/v4/#{source_entity_type}/#{source_entity_identifier}/export_relations/status?page=1&per_page=30&private_token=access_token")
- .to_return(status: 404, body: "", headers: {})
-
request
expect(response).to have_gitlab_http_status(:unprocessable_entity)
- expect(json_response['message']).to include("Group import disabled on source or destination instance. " \
- "Ask an administrator to enable it on both instances and try again.")
+ expect(json_response['message']).to eq('Validation failed: Url is blocked: Only allowed schemes are http, https')
end
end
diff --git a/spec/requests/api/graphql/jobs_query_spec.rb b/spec/requests/api/graphql/jobs_query_spec.rb
index 0aea8e4c253..179c90fc564 100644
--- a/spec/requests/api/graphql/jobs_query_spec.rb
+++ b/spec/requests/api/graphql/jobs_query_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe 'getting job information', feature_category: :continuous_integrat
context 'when user is admin' do
let_it_be(:current_user) { create(:admin) }
- it 'has full access to all jobs', :aggregate_failure do
+ it 'has full access to all jobs', :aggregate_failures do
post_graphql(query, current_user: current_user)
expect(graphql_data_at(:jobs, :count)).to eq(1)
@@ -25,14 +25,14 @@ RSpec.describe 'getting job information', feature_category: :continuous_integrat
let_it_be(:pending_job) { create(:ci_build, :pending) }
let_it_be(:failed_job) { create(:ci_build, :failed) }
- it 'gets pending jobs', :aggregate_failure do
+ it 'gets pending jobs', :aggregate_failures do
post_graphql(graphql_query_for(:jobs, { statuses: :PENDING }), current_user: current_user)
expect(graphql_data_at(:jobs, :count)).to eq(1)
expect(graphql_data_at(:jobs, :nodes)).to contain_exactly(a_graphql_entity_for(pending_job))
end
- it 'gets pending and failed jobs', :aggregate_failure do
+ it 'gets pending and failed jobs', :aggregate_failures do
post_graphql(graphql_query_for(:jobs, { statuses: [:PENDING, :FAILED] }), current_user: current_user)
expect(graphql_data_at(:jobs, :count)).to eq(2)
@@ -45,7 +45,7 @@ RSpec.describe 'getting job information', feature_category: :continuous_integrat
context 'if the user is not an admin' do
let_it_be(:current_user) { create(:user) }
- it 'has no access to the jobs', :aggregate_failure do
+ it 'has no access to the jobs', :aggregate_failures do
post_graphql(query, current_user: current_user)
expect(graphql_data_at(:jobs, :count)).to eq(0)
diff --git a/spec/requests/api/graphql/mutations/ci/job/play_spec.rb b/spec/requests/api/graphql/mutations/ci/job/play_spec.rb
index 8100274ed97..0c700248f85 100644
--- a/spec/requests/api/graphql/mutations/ci/job/play_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/job/play_spec.rb
@@ -63,7 +63,7 @@ RSpec.describe 'JobPlay', feature_category: :continuous_integration do
}
end
- it 'provides those variables to the job', :aggregated_errors do
+ it 'provides those variables to the job', :aggregate_failures do
expect_next_instance_of(Ci::PlayBuildService) do |instance|
expect(instance).to receive(:execute).with(an_instance_of(Ci::Build), variables[:variables]).and_call_original
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index d755a4231da..0f70d844d9f 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -1723,7 +1723,7 @@ RSpec.describe API::Projects, feature_category: :projects do
expect(json_response.map { |project| project['id'] }).to contain_exactly(private_project1.id)
end
- context 'and using an admin to search', :enable_admin_mode, :aggregate_errors do
+ context 'and using an admin to search', :enable_admin_mode, :aggregate_failures do
it 'returns users projects when authenticated as admin' do
private_project1 = create(:project, :private, name: 'private_project1', creator_id: user4.id, namespace: user4.namespace)
diff --git a/spec/requests/api/search_spec.rb b/spec/requests/api/search_spec.rb
index eb0f3b3eaee..b4818f79ec7 100644
--- a/spec/requests/api/search_spec.rb
+++ b/spec/requests/api/search_spec.rb
@@ -141,7 +141,7 @@ RSpec.describe API::Search, feature_category: :global_search do
end
end
- context 'when DB timeouts occur from global searches', :aggregate_errors do
+ context 'when DB timeouts occur from global searches', :aggregate_failures do
%w(
issues
merge_requests
diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb
index ab5e04246e8..604631bbf7f 100644
--- a/spec/requests/api/tags_spec.rb
+++ b/spec/requests/api/tags_spec.rb
@@ -178,7 +178,7 @@ RSpec.describe API::Tags, feature_category: :source_code_management do
end
end
- context 'with keyset pagination option', :aggregate_errors do
+ context 'with keyset pagination option', :aggregate_failures do
let(:base_params) { { pagination: 'keyset' } }
context 'with gitaly pagination params' do
diff --git a/spec/rubocop/cop/rspec/misspelled_aggregate_failures_spec.rb b/spec/rubocop/cop/rspec/misspelled_aggregate_failures_spec.rb
new file mode 100644
index 00000000000..c551c03b896
--- /dev/null
+++ b/spec/rubocop/cop/rspec/misspelled_aggregate_failures_spec.rb
@@ -0,0 +1,136 @@
+# frozen_string_literal: true
+
+require 'rubocop_spec_helper'
+require 'rspec-parameterized'
+
+require_relative '../../../../rubocop/cop/rspec/misspelled_aggregate_failures'
+
+RSpec.describe RuboCop::Cop::RSpec::MisspelledAggregateFailures, feature_category: :shared do
+ shared_examples 'misspelled tag' do |misspelled|
+ it 'flags and auto-corrects misspelled tags in describe' do
+ expect_offense(<<~'RUBY', misspelled: misspelled)
+ RSpec.describe 'a feature', :%{misspelled} do
+ ^^{misspelled} Use `:aggregate_failures` to aggregate failures.
+ describe 'inner', :%{misspelled} do
+ ^^{misspelled} Use `:aggregate_failures` to aggregate failures.
+ end
+ end
+ RUBY
+
+ expect_correction(<<~'RUBY')
+ RSpec.describe 'a feature', :aggregate_failures do
+ describe 'inner', :aggregate_failures do
+ end
+ end
+ RUBY
+ end
+
+ it 'flags and auto-corrects misspelled tags in context' do
+ expect_offense(<<~'RUBY', misspelled: misspelled)
+ context 'a feature', :%{misspelled} do
+ ^^{misspelled} Use `:aggregate_failures` to aggregate failures.
+ end
+ RUBY
+
+ expect_correction(<<~'RUBY')
+ context 'a feature', :aggregate_failures do
+ end
+ RUBY
+ end
+
+ it 'flags and auto-corrects misspelled tags in examples' do
+ expect_offense(<<~'RUBY', misspelled: misspelled)
+ it 'aggregates', :%{misspelled} do
+ ^^{misspelled} Use `:aggregate_failures` to aggregate failures.
+ end
+
+ specify :%{misspelled} do
+ ^^{misspelled} Use `:aggregate_failures` to aggregate failures.
+ end
+
+ it :%{misspelled} do
+ ^^{misspelled} Use `:aggregate_failures` to aggregate failures.
+ end
+ RUBY
+
+ expect_correction(<<~'RUBY')
+ it 'aggregates', :aggregate_failures do
+ end
+
+ specify :aggregate_failures do
+ end
+
+ it :aggregate_failures do
+ end
+ RUBY
+ end
+
+ it 'flags and auto-corrects misspelled tags in any order' do
+ expect_offense(<<~'RUBY', misspelled: misspelled)
+ it 'aggregates', :foo, :%{misspelled} do
+ ^^{misspelled} Use `:aggregate_failures` to aggregate failures.
+ end
+
+ it 'aggregates', :%{misspelled}, :bar do
+ ^^{misspelled} Use `:aggregate_failures` to aggregate failures.
+ end
+ RUBY
+
+ expect_correction(<<~'RUBY')
+ it 'aggregates', :foo, :aggregate_failures do
+ end
+
+ it 'aggregates', :aggregate_failures, :bar do
+ end
+ RUBY
+ end
+ end
+
+ shared_examples 'legit tag' do |legit_tag|
+ it 'does not flag' do
+ expect_no_offenses(<<~RUBY)
+ RSpec.describe 'a feature', :#{legit_tag} do
+ end
+
+ it 'is ok', :#{legit_tag} do
+ end
+ RUBY
+ end
+ end
+
+ context 'with misspelled tags' do
+ where(:tag) do
+ # From https://gitlab.com/gitlab-org/gitlab/-/issues/396356#list
+ %w[
+ aggregate_errors
+ aggregate_failure
+ aggregated_failures
+ aggregate_results
+ aggregated_errors
+ aggregates_failures
+ aggregate_failues
+
+ aggregate_bar
+ aggregate_foo
+ ]
+ end
+
+ with_them do
+ it_behaves_like 'misspelled tag', params[:tag]
+ end
+ end
+
+ context 'with legit tags' do
+ where(:tag) do
+ %w[
+ aggregate
+ aggregations
+ aggregate_two_underscores
+ ]
+ end
+
+ with_them do
+ it_behaves_like 'legit tag', params[:tag]
+ end
+ end
+end
diff --git a/spec/services/bulk_imports/create_service_spec.rb b/spec/services/bulk_imports/create_service_spec.rb
index 35398e4cae0..7f892cfe722 100644
--- a/spec/services/bulk_imports/create_service_spec.rb
+++ b/spec/services/bulk_imports/create_service_spec.rb
@@ -35,9 +35,6 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do
]
end
- let(:source_entity_identifier) { ERB::Util.url_encode(params[0][:source_full_path]) }
- let(:source_entity_type) { BulkImports::CreateService::ENTITY_TYPES_MAPPING.fetch(params[0][:source_type]) }
-
subject { described_class.new(user, params, credentials) }
describe '#execute' do
@@ -62,34 +59,6 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do
end
end
- context 'when direct transfer setting query returns a 404' do
- it 'raises a ServiceResponse::Error' do
- stub_request(:get, 'http://gitlab.example/api/v4/version?private_token=token').to_return(status: 404)
- stub_request(:get, 'http://gitlab.example/api/v4/metadata?private_token=token')
- .to_return(
- status: 200,
- body: source_version.to_json,
- headers: { 'Content-Type' => 'application/json' }
- )
- stub_request(:get, "http://gitlab.example/api/v4/#{source_entity_type}/#{source_entity_identifier}/export_relations/status?page=1&per_page=30&private_token=token")
- .to_return(status: 404)
-
- expect_next_instance_of(BulkImports::Clients::HTTP) do |client|
- expect(client).to receive(:get).and_raise(BulkImports::Error.setting_not_enabled)
- end
-
- result = subject.execute
-
- expect(result).to be_a(ServiceResponse)
- expect(result).to be_error
- expect(result.message)
- .to eq(
- "Group import disabled on source or destination instance. " \
- "Ask an administrator to enable it on both instances and try again."
- )
- end
- end
-
context 'when required scopes are not present' do
it 'returns ServiceResponse with error if token does not have api scope' do
stub_request(:get, 'http://gitlab.example/api/v4/version?private_token=token').to_return(status: 404)
@@ -99,13 +68,9 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do
body: source_version.to_json,
headers: { 'Content-Type' => 'application/json' }
)
- stub_request(:get, "http://gitlab.example/api/v4/#{source_entity_type}/#{source_entity_identifier}/export_relations/status?page=1&per_page=30&private_token=token")
- .to_return(
- status: 200
- )
allow_next_instance_of(BulkImports::Clients::HTTP) do |client|
- allow(client).to receive(:validate_import_scopes!).and_raise(BulkImports::Error.scope_validation_failure)
+ allow(client).to receive(:validate_instance_version!).and_raise(BulkImports::Error.scope_validation_failure)
end
result = subject.execute
@@ -125,10 +90,6 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do
stub_request(:get, 'http://gitlab.example/api/v4/version?private_token=token').to_return(status: 404)
stub_request(:get, 'http://gitlab.example/api/v4/metadata?private_token=token')
.to_return(status: 200, body: source_version.to_json, headers: { 'Content-Type' => 'application/json' })
- stub_request(:get, "http://gitlab.example/api/v4/#{source_entity_type}/#{source_entity_identifier}/export_relations/status?page=1&per_page=30&private_token=token")
- .to_return(
- status: 200
- )
stub_request(:get, 'http://gitlab.example/api/v4/personal_access_tokens/self?private_token=token')
.to_return(
status: 200,
@@ -208,10 +169,6 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do
allow_next_instance_of(BulkImports::Clients::HTTP) do |instance|
allow(instance).to receive(:instance_version).and_return(source_version)
allow(instance).to receive(:instance_enterprise).and_return(false)
- stub_request(:get, "http://gitlab.example/api/v4/#{source_entity_type}/#{source_entity_identifier}/export_relations/status?page=1&per_page=30&private_token=token")
- .to_return(
- status: 200
- )
end
end
@@ -368,105 +325,6 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do
end
end
- describe '.validate_setting_enabled!' do
- let(:entity_source_id) { 'gid://gitlab/Model/12345' }
- let(:graphql_client) { instance_double(BulkImports::Clients::Graphql) }
- let(:http_client) { instance_double(BulkImports::Clients::HTTP) }
- let(:http_response) { double(code: 200, success?: true) } # rubocop:disable RSpec/VerifiedDoubles
-
- before do
- allow(BulkImports::Clients::HTTP).to receive(:new).and_return(http_client)
- allow(BulkImports::Clients::Graphql).to receive(:new).and_return(graphql_client)
-
- allow(http_client).to receive(:instance_version).and_return(status: 200)
- allow(http_client).to receive(:instance_enterprise).and_return(false)
- allow(http_client).to receive(:validate_instance_version!).and_return(source_version)
- allow(http_client).to receive(:validate_import_scopes!).and_return(true)
- end
-
- context 'when the source_type is a group' do
- context 'when the source_full_path contains only integer characters' do
- let(:query_string) { BulkImports::Groups::Graphql::GetGroupQuery.new(context: nil).to_s }
- let(:graphql_response) do
- double(original_hash: { 'data' => { 'group' => { 'id' => entity_source_id } } }) # rubocop:disable RSpec/VerifiedDoubles
- end
-
- let(:params) do
- [
- {
- source_type: 'group_entity',
- source_full_path: '67890',
- destination_slug: 'destination-group-1',
- destination_namespace: 'destination1'
- }
- ]
- end
-
- before do
- allow(graphql_client).to receive(:parse).with(query_string)
- allow(graphql_client).to receive(:execute).and_return(graphql_response)
-
- allow(http_client).to receive(:get)
- .with("/groups/12345/export_relations/status")
- .and_return(http_response)
-
- stub_request(:get, "http://gitlab.example/api/v4/groups/12345/export_relations/status?page=1&per_page=30&private_token=token")
- .to_return(status: 200, body: "", headers: {})
- end
-
- it 'makes a graphql request using the group full path and an http request with the correct id' do
- expect(graphql_client).to receive(:parse).with(query_string)
- expect(graphql_client).to receive(:execute).and_return(graphql_response)
-
- expect(http_client).to receive(:get).with("/groups/12345/export_relations/status")
-
- subject.execute
- end
- end
- end
-
- context 'when the source_type is a project' do
- context 'when the source_full_path contains only integer characters' do
- let(:query_string) { BulkImports::Projects::Graphql::GetProjectQuery.new(context: nil).to_s }
- let(:graphql_response) do
- double(original_hash: { 'data' => { 'project' => { 'id' => entity_source_id } } }) # rubocop:disable RSpec/VerifiedDoubles
- end
-
- let(:params) do
- [
- {
- source_type: 'project_entity',
- source_full_path: '67890',
- destination_slug: 'destination-group-1',
- destination_namespace: 'destination1'
- }
- ]
- end
-
- before do
- allow(graphql_client).to receive(:parse).with(query_string)
- allow(graphql_client).to receive(:execute).and_return(graphql_response)
-
- allow(http_client).to receive(:get)
- .with("/projects/12345/export_relations/status")
- .and_return(http_response)
-
- stub_request(:get, "http://gitlab.example/api/v4/projects/12345/export_relations/status?page=1&per_page=30&private_token=token")
- .to_return(status: 200, body: "", headers: {})
- end
-
- it 'makes a graphql request using the group full path and an http request with the correct id' do
- expect(graphql_client).to receive(:parse).with(query_string)
- expect(graphql_client).to receive(:execute).and_return(graphql_response)
-
- expect(http_client).to receive(:get).with("/projects/12345/export_relations/status")
-
- subject.execute
- end
- end
- end
- end
-
describe '.validate_destination_full_path' do
context 'when the source_type is a group' do
context 'when the provided destination_slug already exists in the destination_namespace' do
diff --git a/spec/services/ci/delete_objects_service_spec.rb b/spec/services/ci/delete_objects_service_spec.rb
index d84ee596721..939b72cef3b 100644
--- a/spec/services/ci/delete_objects_service_spec.rb
+++ b/spec/services/ci/delete_objects_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::DeleteObjectsService, :aggregate_failure, feature_category: :continuous_integration do
+RSpec.describe Ci::DeleteObjectsService, :aggregate_failures, feature_category: :continuous_integration do
let(:service) { described_class.new }
let(:artifact) { create(:ci_job_artifact, :archive) }
let(:data) { [artifact] }
diff --git a/spec/services/packages/npm/generate_metadata_service_spec.rb b/spec/services/packages/npm/generate_metadata_service_spec.rb
new file mode 100644
index 00000000000..c22a9ef1428
--- /dev/null
+++ b/spec/services/packages/npm/generate_metadata_service_spec.rb
@@ -0,0 +1,173 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ::Packages::Npm::GenerateMetadataService, feature_category: :package_registry do
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be(:project) { create(:project) }
+ let_it_be(:package_name) { "@#{project.root_namespace.path}/test" }
+ let_it_be(:package1) { create(:npm_package, version: '2.0.4', project: project, name: package_name) }
+ let_it_be(:package2) { create(:npm_package, version: '2.0.6', project: project, name: package_name) }
+ let_it_be(:latest_package) { create(:npm_package, version: '2.0.11', project: project, name: package_name) }
+
+ let(:packages) { project.packages.npm.with_name(package_name).last_of_each_version }
+ let(:metadata) { described_class.new(package_name, packages).execute }
+
+ describe '#versions' do
+ let_it_be(:version_schema) { 'public_api/v4/packages/npm_package_version' }
+ let_it_be(:package_json) do
+ {
+ name: package_name,
+ version: '2.0.4',
+ deprecated: 'warning!',
+ bin: './cli.js',
+ directories: ['lib'],
+ engines: { npm: '^7.5.6' },
+ _hasShrinkwrap: false,
+ dist: {
+ tarball: 'http://localhost/tarball.tgz',
+ shasum: '1234567890'
+ },
+ custom_field: 'foo_bar'
+ }
+ end
+
+ subject { metadata[:versions] }
+
+ where(:has_dependencies, :has_metadatum) do
+ true | true
+ false | true
+ true | false
+ false | false
+ end
+
+ with_them do
+ if params[:has_dependencies]
+ ::Packages::DependencyLink.dependency_types.each_key do |dependency_type|
+ let_it_be("package_dependency_link_for_#{dependency_type}") do
+ create(:packages_dependency_link, package: package1, dependency_type: dependency_type)
+ end
+ end
+ end
+
+ if params[:has_metadatum]
+ let_it_be(:package_metadatadum) { create(:npm_metadatum, package: package1, package_json: package_json) }
+ end
+
+ it { is_expected.to be_a(Hash) }
+ it { expect(subject[package1.version].with_indifferent_access).to match_schema(version_schema) }
+ it { expect(subject[package2.version].with_indifferent_access).to match_schema(version_schema) }
+ it { expect(subject[package1.version]['custom_field']).to be_blank }
+
+ context 'for dependencies' do
+ ::Packages::DependencyLink.dependency_types.each_key do |dependency_type|
+ if params[:has_dependencies]
+ it { expect(subject.dig(package1.version, dependency_type.to_s)).to be_any }
+ else
+ it { expect(subject.dig(package1.version, dependency_type)).to be nil }
+ end
+
+ it { expect(subject.dig(package2.version, dependency_type)).to be nil }
+ end
+ end
+
+ context 'for metadatum' do
+ ::Packages::Npm::GenerateMetadataService::PACKAGE_JSON_ALLOWED_FIELDS.each do |metadata_field|
+ if params[:has_metadatum]
+ it { expect(subject.dig(package1.version, metadata_field)).not_to be nil }
+ else
+ it { expect(subject.dig(package1.version, metadata_field)).to be nil }
+ end
+
+ it { expect(subject.dig(package2.version, metadata_field)).to be nil }
+ end
+ end
+
+ it 'avoids N+1 database queries' do
+ check_n_plus_one do
+ create_list(:npm_package, 5, project: project, name: package_name).each do |npm_package|
+ next unless has_dependencies
+
+ ::Packages::DependencyLink.dependency_types.each_key do |dependency_type|
+ create(:packages_dependency_link, package: npm_package, dependency_type: dependency_type)
+ end
+ end
+ end
+ end
+ end
+
+ context 'with package files pending destruction' do
+ let_it_be(:package_file_pending_destruction) do
+ create(:package_file, :pending_destruction, package: package2, file_sha1: 'pending_destruction_sha1')
+ end
+
+ let(:shasums) { subject.values.map { |v| v.dig(:dist, :shasum) } }
+
+ it 'does not return them' do
+ expect(shasums).not_to include(package_file_pending_destruction.file_sha1)
+ end
+ end
+ end
+
+ describe '#dist_tags' do
+ subject { metadata[:dist_tags] }
+
+ context 'for packages without tags' do
+ it { is_expected.to be_a(Hash) }
+ it { expect(subject['latest']).to eq(latest_package.version) }
+
+ it 'avoids N+1 database queries' do
+ check_n_plus_one(only_dist_tags: true) do
+ create_list(:npm_package, 5, project: project, name: package_name)
+ end
+ end
+ end
+
+ context 'for packages with tags' do
+ let_it_be(:package_tag1) { create(:packages_tag, package: package1, name: 'release_a') }
+ let_it_be(:package_tag2) { create(:packages_tag, package: package1, name: 'test_release') }
+ let_it_be(:package_tag3) { create(:packages_tag, package: package2, name: 'release_b') }
+ let_it_be(:package_tag4) { create(:packages_tag, package: latest_package, name: 'release_c') }
+ let_it_be(:package_tag5) { create(:packages_tag, package: latest_package, name: 'latest') }
+
+ it { is_expected.to be_a(Hash) }
+ it { expect(subject[package_tag1.name]).to eq(package1.version) }
+ it { expect(subject[package_tag2.name]).to eq(package1.version) }
+ it { expect(subject[package_tag3.name]).to eq(package2.version) }
+ it { expect(subject[package_tag4.name]).to eq(latest_package.version) }
+ it { expect(subject[package_tag5.name]).to eq(latest_package.version) }
+
+ it 'avoids N+1 database queries' do
+ check_n_plus_one(only_dist_tags: true) do
+ create_list(:npm_package, 5, project: project, name: package_name).each_with_index do |npm_package, index|
+ create(:packages_tag, package: npm_package, name: "tag_#{index}")
+ end
+ end
+ end
+ end
+ end
+
+ context 'when passing only_dist_tags: true' do
+ subject { described_class.new(package_name, packages).execute(only_dist_tags: true) }
+
+ it 'returns only dist tags' do
+ expect(subject.payload.keys).to contain_exactly(:dist_tags)
+ end
+ end
+
+ def check_n_plus_one(only_dist_tags: false)
+ pkgs = project.packages.npm.with_name(package_name).last_of_each_version.preload_files
+ control = ActiveRecord::QueryRecorder.new do
+ described_class.new(package_name, pkgs).execute(only_dist_tags: only_dist_tags)
+ end
+
+ yield
+
+ pkgs = project.packages.npm.with_name(package_name).last_of_each_version.preload_files
+
+ expect do
+ described_class.new(package_name, pkgs).execute(only_dist_tags: only_dist_tags)
+ end.not_to exceed_query_limit(control)
+ end
+end
diff --git a/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb b/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb
index 6285b43311d..160a7fc1045 100644
--- a/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb
+++ b/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb
@@ -121,7 +121,7 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService, feature_cate
end.to change(work_item, :work_item_parent).from(parent_work_item).to(nil)
end
- it 'returns success status if parent not present', :aggregate_failure do
+ it 'returns success status if parent not present', :aggregate_failures do
work_item.update!(work_item_parent: nil)
expect(subject[:status]).to eq(:success)
diff --git a/spec/support/shared_examples/features/work_items_shared_examples.rb b/spec/support/shared_examples/features/work_items_shared_examples.rb
index 6bc589fea8f..4f667f13a62 100644
--- a/spec/support/shared_examples/features/work_items_shared_examples.rb
+++ b/spec/support/shared_examples/features/work_items_shared_examples.rb
@@ -30,7 +30,7 @@ RSpec.shared_examples 'work items status' do
end
end
-RSpec.shared_examples 'work items comments' do
+RSpec.shared_examples 'work items comments' do |type|
let(:form_selector) { '[data-testid="work-item-add-comment"]' }
it 'successfully creates and shows comments' do
@@ -43,6 +43,58 @@ RSpec.shared_examples 'work items comments' do
expect(page).to have_content "Test comment"
end
+
+ context 'when using quick actions' do
+ it 'autocompletes quick actions common to all work item types', :aggregate_failures do
+ click_reply_and_enter_slash
+
+ page.within('#at-view-commands') do
+ expect(page).to have_text("/title")
+ expect(page).to have_text("/shrug")
+ expect(page).to have_text("/tableflip")
+ expect(page).to have_text("/close")
+ expect(page).to have_text("/cc")
+ end
+ end
+
+ context 'when a widget is enabled' do
+ before do
+ WorkItems::Type.default_by_type(type).widget_definitions
+ .find_by_widget_type(:assignees).update!(disabled: false)
+ end
+
+ it 'autocompletes quick action for the enabled widget' do
+ click_reply_and_enter_slash
+
+ page.within('#at-view-commands') do
+ expect(page).to have_text("/assign")
+ end
+ end
+ end
+
+ context 'when a widget is disabled' do
+ before do
+ WorkItems::Type.default_by_type(type).widget_definitions
+ .find_by_widget_type(:assignees).update!(disabled: true)
+ end
+
+ it 'does not autocomplete quick action for the disabled widget' do
+ click_reply_and_enter_slash
+
+ page.within('#at-view-commands') do
+ expect(page).not_to have_text("/assign")
+ end
+ end
+ end
+
+ def click_reply_and_enter_slash
+ click_button 'Add a reply'
+
+ find(form_selector).fill_in(with: "/")
+
+ wait_for_all_requests
+ end
+ end
end
RSpec.shared_examples 'work items assignees' do
@@ -98,7 +150,7 @@ RSpec.shared_examples 'work items description' do
wait_for_requests
- page.within('.atwho-container') do
+ page.within('#at-view-commands') do
expect(page).to have_text("title")
expect(page).to have_text("shrug")
expect(page).to have_text("tableflip")
diff --git a/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb
index ace76b5ef84..f53532d00d7 100644
--- a/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb
@@ -725,77 +725,66 @@ RSpec.shared_examples 'handling different package names, visibilities and user r
role = action == :create ? :developer : :maintainer
where(:auth, :package_name_type, :visibility, :user_role, :expected_result, :expected_status) do
- :oauth | :scoped_naming_convention | :public | nil | :reject | :forbidden
+ nil | :scoped_naming_convention | :public | nil | :reject | :unauthorized
+ nil | :scoped_no_naming_convention | :public | nil | :reject | :unauthorized
+ nil | :unscoped | :public | nil | :reject | :unauthorized
+ nil | :non_existing | :public | nil | :reject | :unauthorized
+ nil | :scoped_naming_convention | :private | nil | :reject | :unauthorized
+ nil | :scoped_no_naming_convention | :private | nil | :reject | :unauthorized
+ nil | :unscoped | :private | nil | :reject | :unauthorized
+ nil | :non_existing | :private | nil | :reject | :unauthorized
+ nil | :scoped_naming_convention | :internal | nil | :reject | :unauthorized
+ nil | :scoped_no_naming_convention | :internal | nil | :reject | :unauthorized
+ nil | :unscoped | :internal | nil | :reject | :unauthorized
+ nil | :non_existing | :internal | nil | :reject | :unauthorized
+
:oauth | :scoped_naming_convention | :public | :guest | :reject | :forbidden
:oauth | :scoped_naming_convention | :public | role | :accept | :ok
- :oauth | :scoped_no_naming_convention | :public | nil | :reject | :forbidden
:oauth | :scoped_no_naming_convention | :public | :guest | :reject | :forbidden
:oauth | :scoped_no_naming_convention | :public | role | :accept | :ok
- :oauth | :unscoped | :public | nil | :reject | :forbidden
:oauth | :unscoped | :public | :guest | :reject | :forbidden
:oauth | :unscoped | :public | role | :accept | :ok
- :oauth | :non_existing | :public | nil | :reject | :forbidden
:oauth | :non_existing | :public | :guest | :reject | :forbidden
:oauth | :non_existing | :public | role | :reject | :not_found
- :oauth | :scoped_naming_convention | :private | nil | :reject | :not_found
:oauth | :scoped_naming_convention | :private | :guest | :reject | :forbidden
:oauth | :scoped_naming_convention | :private | role | :accept | :ok
- :oauth | :scoped_no_naming_convention | :private | nil | :reject | :not_found
:oauth | :scoped_no_naming_convention | :private | :guest | :reject | :forbidden
:oauth | :scoped_no_naming_convention | :private | role | :accept | :ok
- :oauth | :unscoped | :private | nil | :reject | :not_found
:oauth | :unscoped | :private | :guest | :reject | :forbidden
:oauth | :unscoped | :private | role | :accept | :ok
- :oauth | :non_existing | :private | nil | :reject | :not_found
:oauth | :non_existing | :private | :guest | :reject | :forbidden
:oauth | :non_existing | :private | role | :reject | :not_found
- :oauth | :scoped_naming_convention | :internal | nil | :reject | :forbidden
:oauth | :scoped_naming_convention | :internal | :guest | :reject | :forbidden
:oauth | :scoped_naming_convention | :internal | role | :accept | :ok
- :oauth | :scoped_no_naming_convention | :internal | nil | :reject | :forbidden
:oauth | :scoped_no_naming_convention | :internal | :guest | :reject | :forbidden
:oauth | :scoped_no_naming_convention | :internal | role | :accept | :ok
- :oauth | :unscoped | :internal | nil | :reject | :forbidden
:oauth | :unscoped | :internal | :guest | :reject | :forbidden
:oauth | :unscoped | :internal | role | :accept | :ok
- :oauth | :non_existing | :internal | nil | :reject | :forbidden
:oauth | :non_existing | :internal | :guest | :reject | :forbidden
:oauth | :non_existing | :internal | role | :reject | :not_found
- :personal_access_token | :scoped_naming_convention | :public | nil | :reject | :forbidden
:personal_access_token | :scoped_naming_convention | :public | :guest | :reject | :forbidden
:personal_access_token | :scoped_naming_convention | :public | role | :accept | :ok
- :personal_access_token | :scoped_no_naming_convention | :public | nil | :reject | :forbidden
:personal_access_token | :scoped_no_naming_convention | :public | :guest | :reject | :forbidden
:personal_access_token | :scoped_no_naming_convention | :public | role | :accept | :ok
- :personal_access_token | :unscoped | :public | nil | :reject | :forbidden
:personal_access_token | :unscoped | :public | :guest | :reject | :forbidden
:personal_access_token | :unscoped | :public | role | :accept | :ok
- :personal_access_token | :non_existing | :public | nil | :reject | :forbidden
:personal_access_token | :non_existing | :public | :guest | :reject | :forbidden
:personal_access_token | :non_existing | :public | role | :reject | :not_found
- :personal_access_token | :scoped_naming_convention | :private | nil | :reject | :not_found
:personal_access_token | :scoped_naming_convention | :private | :guest | :reject | :forbidden
:personal_access_token | :scoped_naming_convention | :private | role | :accept | :ok
- :personal_access_token | :scoped_no_naming_convention | :private | nil | :reject | :not_found
:personal_access_token | :scoped_no_naming_convention | :private | :guest | :reject | :forbidden
:personal_access_token | :scoped_no_naming_convention | :private | role | :accept | :ok
- :personal_access_token | :unscoped | :private | nil | :reject | :not_found
:personal_access_token | :unscoped | :private | :guest | :reject | :forbidden
:personal_access_token | :unscoped | :private | role | :accept | :ok
- :personal_access_token | :non_existing | :private | nil | :reject | :not_found
:personal_access_token | :non_existing | :private | :guest | :reject | :forbidden
:personal_access_token | :non_existing | :private | role | :reject | :not_found
- :personal_access_token | :scoped_naming_convention | :internal | nil | :reject | :forbidden
:personal_access_token | :scoped_naming_convention | :internal | :guest | :reject | :forbidden
:personal_access_token | :scoped_naming_convention | :internal | role | :accept | :ok
- :personal_access_token | :scoped_no_naming_convention | :internal | nil | :reject | :forbidden
:personal_access_token | :scoped_no_naming_convention | :internal | :guest | :reject | :forbidden
:personal_access_token | :scoped_no_naming_convention | :internal | role | :accept | :ok
- :personal_access_token | :unscoped | :internal | nil | :reject | :forbidden
:personal_access_token | :unscoped | :internal | :guest | :reject | :forbidden
:personal_access_token | :unscoped | :internal | role | :accept | :ok
- :personal_access_token | :non_existing | :internal | nil | :reject | :forbidden
:personal_access_token | :non_existing | :internal | :guest | :reject | :forbidden
:personal_access_token | :non_existing | :internal | role | :reject | :not_found
@@ -837,6 +826,8 @@ RSpec.shared_examples 'handling different package names, visibilities and user r
build_token_auth_header(job.token)
when :deploy_token
build_token_auth_header(deploy_token.token)
+ else
+ {}
end
end
@@ -850,7 +841,9 @@ RSpec.shared_examples 'handling different package names, visibilities and user r
if scope == :instance && params[:package_name_type] != :scoped_naming_convention
example_name = "reject #{action} package tag request"
- status = :not_found
+ # Due to #authenticate_non_get, anonymous requests on private resources
+ # are rejected with unauthorized status
+ status = params[:auth].nil? ? :unauthorized : :not_found
end
it_behaves_like example_name, status: status
diff --git a/spec/support_specs/matchers/event_store_spec.rb b/spec/support_specs/matchers/event_store_spec.rb
index 3614d05fde8..bd77f7124c1 100644
--- a/spec/support_specs/matchers/event_store_spec.rb
+++ b/spec/support_specs/matchers/event_store_spec.rb
@@ -5,7 +5,7 @@ require 'json_schemer'
load File.expand_path('../../../spec/support/matchers/event_store.rb', __dir__)
-RSpec.describe 'event store matchers', :aggregate_errors do
+RSpec.describe 'event store matchers', feature_category: :shared do
let(:event_type1) do
Class.new(Gitlab::EventStore::Event) do
def schema
diff --git a/spec/workers/concerns/cronjob_queue_spec.rb b/spec/workers/concerns/cronjob_queue_spec.rb
index 7e00093b686..26680fcc870 100644
--- a/spec/workers/concerns/cronjob_queue_spec.rb
+++ b/spec/workers/concerns/cronjob_queue_spec.rb
@@ -44,7 +44,7 @@ RSpec.describe CronjobQueue, feature_category: :shared do
expect(worker.sidekiq_options['retry']).to eq(false)
end
- it 'automatically clears project, user and namespace from the context', :aggregate_failues do
+ it 'automatically clears project, user and namespace from the context', :aggregate_failures do
worker_context = worker.get_worker_context.to_lazy_hash.transform_values { |v| v.try(:call) }
expect(worker_context[:user]).to be_nil
diff --git a/yarn.lock b/yarn.lock
index ebea05660d5..14bb1b618ea 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -338,7 +338,7 @@
chalk "^2.0.0"
js-tokens "^4.0.0"
-"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.4", "@babel/parser@^7.16.8", "@babel/parser@^7.18.4", "@babel/parser@^7.18.10", "@babel/parser@^7.19.0":
+"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.4", "@babel/parser@^7.16.8", "@babel/parser@^7.18.10", "@babel/parser@^7.18.4", "@babel/parser@^7.19.0":
version "7.21.2"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.2.tgz#dacafadfc6d7654c3051a66d6fe55b6cb2f2a0b3"
integrity sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==
@@ -1021,10 +1021,10 @@
resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.0.1.tgz#b6b8d81780b9a9f6459f4bfe9226ac6aefaefe87"
integrity sha512-aG20vknL4/YjQF9BSV7ts4EWm/yrjagAN7OWBNmlbEOUiu0llj4OGrFoOKK3g2vey4/p2omKCoHrWtPxSwV3HA==
-"@cubejs-client/core@^0.32.0":
- version "0.32.0"
- resolved "https://registry.yarnpkg.com/@cubejs-client/core/-/core-0.32.0.tgz#9018ea2e3658107adacbc188c0c1ebcc54ee9436"
- integrity sha512-Jqpv2S/CQUN0TNgFiKzVR8JehDgQA+z19yQOM/3Z0UrE6llYXu6x5zlI7FldtuXwFaLO1gfRd4yHsjp/Mf39TA==
+"@cubejs-client/core@^0.32.12":
+ version "0.32.12"
+ resolved "https://registry.yarnpkg.com/@cubejs-client/core/-/core-0.32.12.tgz#31c124ba9c5fc5b297b649bf9bcf6c3ed971b6f8"
+ integrity sha512-jjoohWN0FDGuANHE67kV9Z+z9GJ46syvCrohSAkXkacaWooz+fYf64REbgPNxCwgHSn1TqbSWbDoPnIJ47SCXA==
dependencies:
"@babel/runtime" "^7.1.2"
core-js "^3.6.5"
@@ -1034,12 +1034,12 @@
url-search-params-polyfill "^7.0.0"
uuid "^8.3.2"
-"@cubejs-client/vue@^0.32.0":
- version "0.32.0"
- resolved "https://registry.yarnpkg.com/@cubejs-client/vue/-/vue-0.32.0.tgz#06901d4d0fdeaf462e6374d5b973c8d5e4f26b37"
- integrity sha512-i4Mp3zZiL+bdjdgcLj1JIKPq444pQem8ega0MUb8RBG6A4texip1HMLpXFBGcgdZ1XQ/a8MQTW8ytntMS8BE/A==
+"@cubejs-client/vue@^0.32.12":
+ version "0.32.12"
+ resolved "https://registry.yarnpkg.com/@cubejs-client/vue/-/vue-0.32.12.tgz#9fe98228f45d37c54508f461775293a00076c8a7"
+ integrity sha512-e9RULVF/eSa2CRQVz7ugbouCnsEynZjnAlMPp2+vENdhYTc7HeORTPZ8lf2uadBq9pT3evxwCWcoMAaqEQN+jQ==
dependencies:
- "@cubejs-client/core" "^0.32.0"
+ "@cubejs-client/core" "^0.32.12"
core-js "^3.6.5"
ramda "^0.27.2"
@@ -1122,15 +1122,15 @@
stylelint-declaration-strict-value "1.8.0"
stylelint-scss "4.2.0"
-"@gitlab/svgs@3.28.0":
- version "3.28.0"
- resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.28.0.tgz#e7f49c5d2144b3a4e8edbe366731f0a054d01ec5"
- integrity sha512-H4m9jeZEByIp7k2U3aCgM+wNF4I681JEhpUtWsnxaa8vsX2K/maBD8/q7M2hXrTBLAeisGldN12pLfJRRZr/QQ==
+"@gitlab/svgs@3.30.0":
+ version "3.30.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.30.0.tgz#d49d7871ecebf65a71b3cc70afe1f1bd063fdb5d"
+ integrity sha512-XNXUhqpA03XlXWPTddFc+o6PfLDsc/DS7RMSwBW1WyeOwznjMVVVH1810TLbqcuk+ILcONtlUP3js3/gs/oHbQ==
-"@gitlab/ui@58.2.1":
- version "58.2.1"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-58.2.1.tgz#5bd4889c2c6e32ba56a8766083c3d34b441e6288"
- integrity sha512-4OmhjVZhIYL150pCZOK14dT8X9wJKkrVw8L4KlYCJ+iSoodoFt2s1h2eE0zQ4O0zZ1BMN++augb26bWnWo8cqQ==
+"@gitlab/ui@58.4.0":
+ version "58.4.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-58.4.0.tgz#103b2573b564c2c6ed6564de9c061abd5980a8aa"
+ integrity sha512-uBMo4kyda0SS9NaywdaEKFsg+ZfCE06NvP7MIuvMGVzvbNvcgjEE5tTR1flc9v/3qhVwilFJ7CGX/eqe2NEdJA==
dependencies:
"@popperjs/core" "^2.11.2"
bootstrap-vue "2.23.1"
@@ -2461,6 +2461,15 @@
"@vue/compiler-core" "3.2.47"
"@vue/shared" "3.2.47"
+"@vue/compiler-sfc@2.7.14":
+ version "2.7.14"
+ resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-2.7.14.tgz#3446fd2fbb670d709277fc3ffa88efc5e10284fd"
+ integrity sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==
+ dependencies:
+ "@babel/parser" "^7.18.4"
+ postcss "^8.4.14"
+ source-map "^0.6.1"
+
"@vue/compiler-sfc@^3.2.47":
version "3.2.47"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz#1bdc36f6cdc1643f72e2c397eb1a398f5004ad3d"
@@ -2477,15 +2486,6 @@
postcss "^8.1.10"
source-map "^0.6.1"
-"@vue/compiler-sfc@2.7.14":
- version "2.7.14"
- resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-2.7.14.tgz#3446fd2fbb670d709277fc3ffa88efc5e10284fd"
- integrity sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==
- dependencies:
- "@babel/parser" "^7.18.4"
- postcss "^8.4.14"
- source-map "^0.6.1"
-
"@vue/compiler-ssr@3.2.47":
version "3.2.47"
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.47.tgz#35872c01a273aac4d6070ab9d8da918ab13057ee"