Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Gemfile8
-rw-r--r--Gemfile.checksum6
-rw-r--r--Gemfile.lock16
-rw-r--r--app/assets/javascripts/commons/gitlab_ui.js2
-rw-r--r--app/assets/javascripts/import_entities/components/import_status.vue57
-rw-r--r--app/assets/javascripts/import_entities/constants.js53
-rw-r--r--app/assets/javascripts/import_entities/import_groups/components/import_status.vue83
-rw-r--r--app/assets/javascripts/observability/client.js19
-rw-r--r--app/assets/javascripts/pages/import/bulk_imports/history/components/bulk_imports_history_app.vue32
-rw-r--r--app/assets/javascripts/pages/import/bulk_imports/history/index.js3
-rw-r--r--app/assets/javascripts/vue_shared/components/form/input_copy_toggle_visibility.vue16
-rw-r--r--app/controllers/import/bulk_imports_controller.rb4
-rw-r--r--app/services/bulk_imports/process_service.rb4
-rw-r--r--app/services/members/create_service.rb6
-rw-r--r--app/views/import/bulk_imports/history.html.haml2
-rw-r--r--app/workers/bulk_import_worker.rb14
-rw-r--r--app/workers/bulk_imports/entity_worker.rb23
-rw-r--r--config/feature_flags/development/activity_filter_has_mr.yml2
-rw-r--r--config/sidekiq_queues.yml4
-rw-r--r--doc/api/bulk_imports.md2
-rw-r--r--doc/api/dependency_list_export.md2
-rw-r--r--doc/api/settings.md6
-rw-r--r--doc/development/database/multiple_databases.md6
-rw-r--r--doc/update/versions/gitlab_16_changes.md54
-rw-r--r--doc/user/custom_roles.md81
-rw-r--r--doc/user/group/manage.md2
-rw-r--r--doc/user/product_analytics/index.md19
-rw-r--r--doc/user/project/members/index.md1
-rw-r--r--doc/user/project/merge_requests/reviews/index.md3
-rw-r--r--doc/user/workspace/index.md8
-rw-r--r--lib/gitlab/ci/templates/Kaniko.gitlab-ci.yml23
-rw-r--r--locale/gitlab.pot66
-rw-r--r--package.json4
-rw-r--r--qa/qa/flow/user_onboarding.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/10_govern/login/register_spec.rb8
-rw-r--r--spec/frontend/__mocks__/@gitlab/ui.js2
-rw-r--r--spec/frontend/ci/runner/components/registration/registration_dropdown_spec.js2
-rw-r--r--spec/frontend/ci/runner/components/registration/registration_token_spec.js2
-rw-r--r--spec/frontend/groups/components/overview_tabs_spec.js4
-rw-r--r--spec/frontend/import_entities/import_groups/components/import_status_spec.js99
-rw-r--r--spec/frontend/observability/client_spec.js40
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/pypi_installation_spec.js.snap2
-rw-r--r--spec/frontend/vue_shared/components/form/input_copy_toggle_visibility_spec.js4
-rw-r--r--spec/lib/gitlab/instrumentation_helper_spec.rb1
-rw-r--r--spec/services/bulk_imports/process_service_spec.rb17
-rw-r--r--spec/support/shared_examples/services/protected_branches_shared_examples.rb4
-rw-r--r--spec/workers/bulk_import_worker_spec.rb12
-rw-r--r--spec/workers/bulk_imports/entity_worker_spec.rb44
-rw-r--r--spec/workers/every_sidekiq_worker_spec.rb6
-rw-r--r--yarn.lock16
50 files changed, 632 insertions, 268 deletions
diff --git a/Gemfile b/Gemfile
index 9c1b3f0d081..363aec7b029 100644
--- a/Gemfile
+++ b/Gemfile
@@ -344,10 +344,10 @@ gem 'gitlab-license', '~> 2.3' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'rack-attack', '~> 6.7.0' # rubocop:todo Gemfile/MissingFeatureCategory
# Sentry integration
-gem 'sentry-raven', '~> 3.1' # rubocop:todo Gemfile/MissingFeatureCategory
-gem 'sentry-ruby', '~> 5.8.0' # rubocop:todo Gemfile/MissingFeatureCategory
-gem 'sentry-rails', '~> 5.8.0' # rubocop:todo Gemfile/MissingFeatureCategory
-gem 'sentry-sidekiq', '~> 5.8.0' # rubocop:todo Gemfile/MissingFeatureCategory
+gem 'sentry-raven', '~> 3.1', feature_category: :error_tracking
+gem 'sentry-ruby', '~> 5.12', feature_category: :error_tracking
+gem 'sentry-rails', '~> 5.12', feature_category: :error_tracking
+gem 'sentry-sidekiq', '~> 5.12', feature_category: :error_tracking
# PostgreSQL query parsing
#
diff --git a/Gemfile.checksum b/Gemfile.checksum
index 1585a49311b..44e1da6987e 100644
--- a/Gemfile.checksum
+++ b/Gemfile.checksum
@@ -575,10 +575,10 @@
{"name":"seed-fu","version":"2.3.7","platform":"ruby","checksum":"f19673443e9af799b730e3d4eca6a89b39e5a36825015dffd00d02ea3365cf74"},
{"name":"selenium-webdriver","version":"4.14.0","platform":"ruby","checksum":"55726f81021d3f085ed9fcd318486b7ba90155598bb9e1fde7e4deeefe139d24"},
{"name":"semver_dialects","version":"1.2.1","platform":"ruby","checksum":"60a1f67659f79c51a667e8858ec9b089c1e4ce4f6d2a0f0b4ac101916946eb23"},
-{"name":"sentry-rails","version":"5.8.0","platform":"ruby","checksum":"c11b2d909de2c2bfda793c45f64180fd784d54c46886338b683ee3f8efa7731b"},
+{"name":"sentry-rails","version":"5.12.0","platform":"ruby","checksum":"7a1743c93aea9399646cd88c7ae5034fdbc9bac77106dda9b9840ae6dcbc0cc7"},
{"name":"sentry-raven","version":"3.1.2","platform":"ruby","checksum":"103d3b122958810d34898ce2e705bcf549ddb9d855a70ce9a3970ee2484f364a"},
-{"name":"sentry-ruby","version":"5.8.0","platform":"ruby","checksum":"caeb121433be379fb94e991a45265a287b13a9a9083e7264f539752369d37110"},
-{"name":"sentry-sidekiq","version":"5.8.0","platform":"ruby","checksum":"90d1123d16a9fc5fd99dbad190b766dd189eaf9e2baddad641f1334e1877c779"},
+{"name":"sentry-ruby","version":"5.12.0","platform":"ruby","checksum":"2a8c161a9e5af6e8af251a778b5692fa3bfaf355a9cf83857eeef9f84e0e649a"},
+{"name":"sentry-sidekiq","version":"5.12.0","platform":"ruby","checksum":"c2e5b13cf67ae5baef8c246fba5d944ac90d6fcf9721594fb75a586124d082a4"},
{"name":"set","version":"1.0.2","platform":"ruby","checksum":"02ffa4de1f2621495e05b72326040dd014d7abbcb02fea698bc600a389992c02"},
{"name":"sexp_processor","version":"4.17.0","platform":"ruby","checksum":"4daa4874ce1838cd801c65e66ed5d4f140024404a3de7482c36d4ef2604dff6f"},
{"name":"shellany","version":"0.0.1","platform":"ruby","checksum":"0e127a9132698766d7e752e82cdac8250b6adbd09e6c0a7fbbb6f61964fedee7"},
diff --git a/Gemfile.lock b/Gemfile.lock
index 5bad1617d72..f33f819954a 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1480,15 +1480,15 @@ GEM
pastel (~> 0.8.0)
thor (~> 1.2.0)
tty-command (~> 0.10.1)
- sentry-rails (5.8.0)
+ sentry-rails (5.12.0)
railties (>= 5.0)
- sentry-ruby (~> 5.8.0)
+ sentry-ruby (~> 5.12.0)
sentry-raven (3.1.2)
faraday (>= 1.0)
- sentry-ruby (5.8.0)
+ sentry-ruby (5.12.0)
concurrent-ruby (~> 1.0, >= 1.0.2)
- sentry-sidekiq (5.8.0)
- sentry-ruby (~> 5.8.0)
+ sentry-sidekiq (5.12.0)
+ sentry-ruby (~> 5.12.0)
sidekiq (>= 3.0)
set (1.0.2)
sexp_processor (4.17.0)
@@ -2008,10 +2008,10 @@ DEPENDENCIES
seed-fu (~> 2.3.7)
selenium-webdriver (~> 4.14)
semver_dialects (~> 1.2.1)
- sentry-rails (~> 5.8.0)
+ sentry-rails (~> 5.12)
sentry-raven (~> 3.1)
- sentry-ruby (~> 5.8.0)
- sentry-sidekiq (~> 5.8.0)
+ sentry-ruby (~> 5.12)
+ sentry-sidekiq (~> 5.12)
shoulda-matchers (~> 5.1.0)
sidekiq (~> 6.5.10)
sidekiq-cron (~> 1.8.0)
diff --git a/app/assets/javascripts/commons/gitlab_ui.js b/app/assets/javascripts/commons/gitlab_ui.js
index 4a9f79460da..fe6142ae145 100644
--- a/app/assets/javascripts/commons/gitlab_ui.js
+++ b/app/assets/javascripts/commons/gitlab_ui.js
@@ -5,6 +5,8 @@ applyGitLabUIConfig({
translations: {
'GlSearchBoxByType.input.placeholder': __('Search'),
'GlSearchBoxByType.clearButtonTitle': __('Clear'),
+ 'GlSorting.sortAscending': __('Sort direction: Ascending'),
+ 'GlSorting.sortDescending': __('Sort direction: Descending'),
'ClearIconButton.title': __('Clear'),
},
});
diff --git a/app/assets/javascripts/import_entities/components/import_status.vue b/app/assets/javascripts/import_entities/components/import_status.vue
index 91436457b03..3cde3a8df3c 100644
--- a/app/assets/javascripts/import_entities/components/import_status.vue
+++ b/app/assets/javascripts/import_entities/components/import_status.vue
@@ -1,46 +1,9 @@
<script>
import { GlAccordion, GlAccordionItem, GlBadge, GlIcon, GlLink } from '@gitlab/ui';
-import { __, s__ } from '~/locale';
+import { s__ } from '~/locale';
import { STATISTIC_ITEMS } from '~/import/constants';
-import { STATUSES } from '../constants';
-
-const SCHEDULED_STATUS = {
- icon: 'status-scheduled',
- text: __('Pending'),
- variant: 'muted',
-};
-
-const STATUS_MAP = {
- [STATUSES.NONE]: {
- icon: 'status-waiting',
- text: __('Not started'),
- variant: 'muted',
- },
- [STATUSES.SCHEDULING]: SCHEDULED_STATUS,
- [STATUSES.SCHEDULED]: SCHEDULED_STATUS,
- [STATUSES.CREATED]: SCHEDULED_STATUS,
- [STATUSES.STARTED]: {
- icon: 'status-running',
- text: __('Importing...'),
- variant: 'info',
- },
- [STATUSES.FAILED]: {
- icon: 'status-failed',
- text: __('Failed'),
- variant: 'danger',
- },
- [STATUSES.TIMEOUT]: {
- icon: 'status-failed',
- text: __('Timeout'),
- variant: 'danger',
- },
- [STATUSES.CANCELED]: {
- icon: 'status-stopped',
- text: __('Cancelled'),
- variant: 'neutral',
- },
-};
+import { STATUSES, STATUS_ICON_MAP } from '../constants';
function isIncompleteImport(stats) {
return Object.keys(stats?.fetched ?? []).some(
@@ -96,21 +59,11 @@ export default {
},
mappedStatus() {
- if (this.status === STATUSES.FINISHED) {
- return this.isIncomplete
- ? {
- icon: 'status-alert',
- text: s__('Import|Partially completed'),
- variant: 'warning',
- }
- : {
- icon: 'status-success',
- text: __('Complete'),
- variant: 'success',
- };
+ if (this.isIncomplete) {
+ return STATUS_ICON_MAP[STATUSES.PARTIAL];
}
- return STATUS_MAP[this.status];
+ return STATUS_ICON_MAP[this.status];
},
showDetails() {
diff --git a/app/assets/javascripts/import_entities/constants.js b/app/assets/javascripts/import_entities/constants.js
index 48b7febca4b..23604c7fb44 100644
--- a/app/assets/javascripts/import_entities/constants.js
+++ b/app/assets/javascripts/import_entities/constants.js
@@ -1,18 +1,65 @@
-// The `scheduling` status is only present on the client-side,
-// it is used as the status when we are requesting to start an import.
+import { __, s__ } from '~/locale';
export const STATUSES = {
FINISHED: 'finished',
FAILED: 'failed',
SCHEDULED: 'scheduled',
+ SCHEDULING: 'scheduling', // only present client-side, used when user is requesting to start an import
CREATED: 'created',
STARTED: 'started',
NONE: 'none',
- SCHEDULING: 'scheduling',
CANCELED: 'canceled',
TIMEOUT: 'timeout',
+ PARTIAL: 'partial', // only present client-side, finished but with failures
};
export const PROVIDERS = {
GITHUB: 'github',
};
+
+const SCHEDULED_STATUS_ICON = {
+ icon: 'status-scheduled',
+ text: __('Pending'),
+ variant: 'muted',
+};
+
+export const STATUS_ICON_MAP = {
+ [STATUSES.NONE]: {
+ icon: 'status-waiting',
+ text: __('Not started'),
+ variant: 'muted',
+ },
+ [STATUSES.SCHEDULING]: SCHEDULED_STATUS_ICON,
+ [STATUSES.SCHEDULED]: SCHEDULED_STATUS_ICON,
+ [STATUSES.CREATED]: SCHEDULED_STATUS_ICON,
+ [STATUSES.STARTED]: {
+ icon: 'status-running',
+ text: __('Importing...'),
+ variant: 'info',
+ },
+ [STATUSES.FAILED]: {
+ icon: 'status-failed',
+ text: __('Failed'),
+ variant: 'danger',
+ },
+ [STATUSES.TIMEOUT]: {
+ icon: 'status-failed',
+ text: __('Timeout'),
+ variant: 'danger',
+ },
+ [STATUSES.CANCELED]: {
+ icon: 'status-stopped',
+ text: __('Cancelled'),
+ variant: 'neutral',
+ },
+ [STATUSES.FINISHED]: {
+ icon: 'status-success',
+ text: __('Complete'),
+ variant: 'success',
+ },
+ [STATUSES.PARTIAL]: {
+ icon: 'status-alert',
+ text: s__('Import|Partially completed'),
+ variant: 'warning',
+ },
+};
diff --git a/app/assets/javascripts/import_entities/import_groups/components/import_status.vue b/app/assets/javascripts/import_entities/import_groups/components/import_status.vue
new file mode 100644
index 00000000000..cdb38cdf7f1
--- /dev/null
+++ b/app/assets/javascripts/import_entities/import_groups/components/import_status.vue
@@ -0,0 +1,83 @@
+<script>
+import { GlBadge, GlLink } from '@gitlab/ui';
+import { mergeUrlParams } from '~/lib/utils/url_utility';
+import { STATUSES, STATUS_ICON_MAP } from '~/import_entities/constants';
+
+export default {
+ components: {
+ GlBadge,
+ GlLink,
+ },
+
+ inject: {
+ detailsPath: {
+ default: undefined,
+ },
+ },
+
+ props: {
+ id: {
+ type: Number,
+ required: false,
+ default: null,
+ },
+ entityId: {
+ type: Number,
+ required: false,
+ default: null,
+ },
+ hasFailures: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ showDetailsLink: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ status: {
+ type: String,
+ required: true,
+ },
+ },
+
+ computed: {
+ isPartial() {
+ return this.status === STATUSES.FINISHED && this.hasFailures;
+ },
+
+ mappedStatus() {
+ if (this.isPartial) {
+ return STATUS_ICON_MAP[STATUSES.PARTIAL];
+ }
+
+ return STATUS_ICON_MAP[this.status];
+ },
+
+ showDetails() {
+ return this.showDetailsLink && Boolean(this.detailsPathWithId) && this.hasFailures;
+ },
+
+ detailsPathWithId() {
+ if (!this.id || !this.entityId || !this.detailsPath) {
+ return null;
+ }
+
+ return mergeUrlParams({ id: this.id, entity_id: this.entityId }, this.detailsPath);
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <gl-badge :icon="mappedStatus.icon" :variant="mappedStatus.variant" size="md" icon-size="sm">
+ {{ mappedStatus.text }}
+ </gl-badge>
+
+ <div v-if="showDetails" class="gl-mt-2">
+ <gl-link :href="detailsPathWithId">{{ s__('Import|See failures') }}</gl-link>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/observability/client.js b/app/assets/javascripts/observability/client.js
index fa75ead2f92..da8837c21da 100644
--- a/app/assets/javascripts/observability/client.js
+++ b/app/assets/javascripts/observability/client.js
@@ -44,18 +44,11 @@ async function fetchTrace(tracingUrl, traceId) {
throw new Error('traceId is required.');
}
- const { data } = await axios.get(tracingUrl, {
+ const { data } = await axios.get(`${tracingUrl}/${traceId}`, {
withCredentials: true,
- params: {
- trace_id: traceId,
- },
});
- if (!Array.isArray(data.traces) || data.traces.length === 0) {
- throw new Error('traces are missing/invalid in the response'); // eslint-disable-line @gitlab/require-i18n-strings
- }
-
- return data.traces[0];
+ return data;
} catch (e) {
return reportErrorAndThrow(e);
}
@@ -67,7 +60,7 @@ async function fetchTrace(tracingUrl, traceId) {
const SUPPORTED_FILTERS = {
durationMs: ['>', '<'],
operation: ['=', '!='],
- serviceName: ['=', '!='],
+ service: ['=', '!='],
period: ['='],
traceId: ['=', '!='],
attribute: ['='],
@@ -80,7 +73,7 @@ const SUPPORTED_FILTERS = {
const FILTER_TO_QUERY_PARAM = {
durationMs: 'duration_nano',
operation: 'operation',
- serviceName: 'service_name',
+ service: 'service_name',
period: 'period',
traceId: 'trace_id',
attribute: 'attribute',
@@ -141,7 +134,7 @@ function handleAttributeFilter(filterValue, filterOperator, searchParams) {
* filterObj = {
* durationMs: [{operator: '>', value: '100'}, {operator: '<', value: '1000' }],
* operation: [{operator: '=', value: 'someOp' }],
- * serviceName: [{operator: '!=', value: 'foo' }]
+ * service: [{operator: '!=', value: 'foo' }]
* }
*
* It handles converting the filter to the proper supported query params
@@ -186,7 +179,7 @@ function filterObjToQueryParams(filterObj) {
* {
* durationMs: [ {operator: '>', value: '100'}, {operator: '<', value: '1000'}],
* operation: [ {operator: '=', value: 'someOp}],
- * serviceName: [ {operator: '!=', value: 'foo}]
+ * service: [ {operator: '!=', value: 'foo}]
* }
*
* @returns Array<Trace> : A list of traces
diff --git a/app/assets/javascripts/pages/import/bulk_imports/history/components/bulk_imports_history_app.vue b/app/assets/javascripts/pages/import/bulk_imports/history/components/bulk_imports_history_app.vue
index 0b9c2cf4772..e912bfa4f92 100644
--- a/app/assets/javascripts/pages/import/bulk_imports/history/components/bulk_imports_history_app.vue
+++ b/app/assets/javascripts/pages/import/bulk_imports/history/components/bulk_imports_history_app.vue
@@ -14,13 +14,14 @@ import { createAlert } from '~/alert';
import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils';
import { joinPaths } from '~/lib/utils/url_utility';
import { getBulkImportsHistory } from '~/rest_api';
-import ImportStatus from '~/import_entities/components/import_status.vue';
+import ImportStatus from '~/import_entities/import_groups/components/import_status.vue';
import { StatusPoller } from '~/import_entities/import_groups/services/status_poller';
import { WORKSPACE_GROUP, WORKSPACE_PROJECT } from '~/issues/constants';
import PaginationBar from '~/vue_shared/components/pagination_bar/pagination_bar.vue';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { isImporting } from '../utils';
import { DEFAULT_ERROR } from '../utils/error_messages';
@@ -57,6 +58,8 @@ export default {
GlTooltip,
},
+ mixins: [glFeatureFlagMixin()],
+
inject: ['realtimeChangesPath'],
data() {
@@ -103,6 +106,10 @@ export default {
.filter((item) => isImporting(item.status))
.map((item) => item.bulk_import_id);
},
+
+ showDetailsLink() {
+ return this.glFeatures.bulkImportDetailsPage;
+ },
},
watch: {
@@ -247,14 +254,23 @@ export default {
<time-ago :time="value" />
</template>
<template #cell(status)="{ value, item, toggleDetails, detailsShowing }">
- <import-status :status="value" class="gl-display-inline-block gl-w-13" />
- <gl-button
- v-if="item.failures.length"
- class="gl-ml-3"
- :selected="detailsShowing"
- @click="toggleDetails"
- >{{ __('Details') }}</gl-button
+ <div
+ class="gl-display-flex gl-flex-direction-column gl-lg-flex-direction-row gl-align-items-flex-start gl-justify-content-space-between gl-gap-3"
>
+ <import-status
+ :id="item.bulk_import_id"
+ :entity-id="item.id"
+ :has-failures="item.has_failures"
+ :show-details-link="showDetailsLink"
+ :status="value"
+ />
+ <gl-button
+ v-if="!showDetailsLink && item.failures.length"
+ :selected="detailsShowing"
+ @click="toggleDetails"
+ >{{ __('Details') }}</gl-button
+ >
+ </div>
</template>
<template #row-details="{ item }">
<pre><code>{{ item.failures }}</code></pre>
diff --git a/app/assets/javascripts/pages/import/bulk_imports/history/index.js b/app/assets/javascripts/pages/import/bulk_imports/history/index.js
index cc12723572d..ac975db3667 100644
--- a/app/assets/javascripts/pages/import/bulk_imports/history/index.js
+++ b/app/assets/javascripts/pages/import/bulk_imports/history/index.js
@@ -4,13 +4,14 @@ import BulkImportHistoryApp from './components/bulk_imports_history_app.vue';
function mountImportHistoryApp(mountElement) {
if (!mountElement) return undefined;
- const { realtimeChangesPath } = mountElement.dataset;
+ const { realtimeChangesPath, detailsPath } = mountElement.dataset;
return new Vue({
el: mountElement,
name: 'BulkImportHistoryRoot',
provide: {
realtimeChangesPath,
+ detailsPath,
},
render(createElement) {
return createElement(BulkImportHistoryApp);
diff --git a/app/assets/javascripts/vue_shared/components/form/input_copy_toggle_visibility.vue b/app/assets/javascripts/vue_shared/components/form/input_copy_toggle_visibility.vue
index d97f1ae6135..0455685627d 100644
--- a/app/assets/javascripts/vue_shared/components/form/input_copy_toggle_visibility.vue
+++ b/app/assets/javascripts/vue_shared/components/form/input_copy_toggle_visibility.vue
@@ -94,8 +94,12 @@ export default {
computedValueIsVisible() {
return !this.showToggleVisibilityButton || this.valueIsVisible;
},
- inputType() {
- return this.computedValueIsVisible ? 'text' : 'password';
+ formInputClass() {
+ return [
+ 'gl-font-monospace! gl-cursor-default!',
+ { 'input-copy-show-disc': !this.computedValueIsVisible },
+ this.formInputGroupProps.class,
+ ];
},
},
mounted() {
@@ -157,10 +161,9 @@ export default {
ref="input"
:readonly="readonly"
:width="size"
- class="gl-font-monospace! gl-cursor-default!"
+ :class="formInputClass"
v-bind="formInputGroupProps"
:value="value"
- :type="inputType"
@input="handleInput"
@click="handleClick"
/>
@@ -194,3 +197,8 @@ export default {
</template>
</gl-form-group>
</template>
+<style>
+.input-copy-show-disc {
+ -webkit-text-security: disc;
+}
+</style>
diff --git a/app/controllers/import/bulk_imports_controller.rb b/app/controllers/import/bulk_imports_controller.rb
index 443eb37e1d7..bc425323d6f 100644
--- a/app/controllers/import/bulk_imports_controller.rb
+++ b/app/controllers/import/bulk_imports_controller.rb
@@ -6,6 +6,10 @@ class Import::BulkImportsController < ApplicationController
before_action :ensure_bulk_import_enabled
before_action :verify_blocked_uri, only: :status
+ before_action only: [:history] do
+ push_frontend_feature_flag(:bulk_import_details_page)
+ end
+
feature_category :importers
urgency :low
diff --git a/app/services/bulk_imports/process_service.rb b/app/services/bulk_imports/process_service.rb
index 40882ba13a1..ebf075f0669 100644
--- a/app/services/bulk_imports/process_service.rb
+++ b/app/services/bulk_imports/process_service.rb
@@ -20,10 +20,6 @@ module BulkImports
process_bulk_import
re_enqueue
- rescue StandardError => e
- Gitlab::ErrorTracking.track_exception(e, bulk_import_id: bulk_import.id)
-
- bulk_import.fail_op
end
private
diff --git a/app/services/members/create_service.rb b/app/services/members/create_service.rb
index 1686c46e90d..79bc07103fd 100644
--- a/app/services/members/create_service.rb
+++ b/app/services/members/create_service.rb
@@ -29,6 +29,8 @@ module Members
validate_invitable!
add_members
+ after_add_hooks
+
enqueue_onboarding_progress_action
publish_event!
@@ -132,6 +134,10 @@ module Members
existing_errors.concat(member.errors.full_messages).uniq
end
+ def after_add_hooks
+ # overridden in subclasses/ee
+ end
+
def after_execute(member:)
super
diff --git a/app/views/import/bulk_imports/history.html.haml b/app/views/import/bulk_imports/history.html.haml
index 38196f97030..57e3e60a702 100644
--- a/app/views/import/bulk_imports/history.html.haml
+++ b/app/views/import/bulk_imports/history.html.haml
@@ -3,4 +3,4 @@
- add_page_specific_style 'page_bundles/import'
- page_title _('Import history')
-#import-history-mount-element{ data: { realtime_changes_path: realtime_changes_import_bulk_imports_path(format: :json) } }
+#import-history-mount-element{ data: { details_path: details_import_bulk_imports_path, realtime_changes_path: realtime_changes_import_bulk_imports_path(format: :json) } }
diff --git a/app/workers/bulk_import_worker.rb b/app/workers/bulk_import_worker.rb
index 588e936e5e3..70e7d82741f 100644
--- a/app/workers/bulk_import_worker.rb
+++ b/app/workers/bulk_import_worker.rb
@@ -5,13 +5,25 @@ class BulkImportWorker
data_consistency :always
feature_category :importers
- sidekiq_options retry: false, dead: false
+ sidekiq_options retry: 3, dead: false
idempotent!
+ sidekiq_retries_exhausted do |msg, exception|
+ new.perform_failure(exception, msg['args'].first)
+ end
+
def perform(bulk_import_id)
bulk_import = BulkImport.find_by_id(bulk_import_id)
return unless bulk_import
BulkImports::ProcessService.new(bulk_import).execute
end
+
+ def perform_failure(exception, bulk_import_id)
+ bulk_import = BulkImport.find_by_id(bulk_import_id)
+
+ Gitlab::ErrorTracking.track_exception(exception, bulk_import_id: bulk_import.id)
+
+ bulk_import.fail_op
+ end
end
diff --git a/app/workers/bulk_imports/entity_worker.rb b/app/workers/bulk_imports/entity_worker.rb
index 1c555d95a2e..02eee3094ad 100644
--- a/app/workers/bulk_imports/entity_worker.rb
+++ b/app/workers/bulk_imports/entity_worker.rb
@@ -8,9 +8,13 @@ module BulkImports
deduplicate :until_executed, if_deduplicated: :reschedule_once
data_consistency :always
feature_category :importers
- sidekiq_options retry: false, dead: false
+ sidekiq_options retry: 3, dead: false
worker_has_external_dependencies!
+ sidekiq_retries_exhausted do |msg, exception|
+ new.perform_failure(exception, msg['args'].first)
+ end
+
PERFORM_DELAY = 30.seconds
# Keep `_current_stage` parameter for backwards compatibility.
@@ -27,10 +31,12 @@ module BulkImports
end
re_enqueue
- rescue StandardError => e
- Gitlab::ErrorTracking.track_exception(e, log_params(message: 'Entity failed'))
+ end
+
+ def perform_failure(exception, entity_id)
+ @entity = ::BulkImports::Entity.find(entity_id)
- @entity.fail_op!
+ log_and_fail(exception)
end
private
@@ -93,5 +99,14 @@ module BulkImports
defaults.merge(extra)
end
+
+ def log_and_fail(exception)
+ Gitlab::ErrorTracking.track_exception(
+ exception,
+ log_params(message: "Request to export #{entity.source_type} failed")
+ )
+
+ entity.fail_op!
+ end
end
end
diff --git a/config/feature_flags/development/activity_filter_has_mr.yml b/config/feature_flags/development/activity_filter_has_mr.yml
index 235e8b559b5..b276f6b9b49 100644
--- a/config/feature_flags/development/activity_filter_has_mr.yml
+++ b/config/feature_flags/development/activity_filter_has_mr.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/426104
milestone: '16.5'
type: development
group: group::threat insights
-default_enabled: false
+default_enabled: true
diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml
index 6f1b5c544a9..b9bd6e6c740 100644
--- a/config/sidekiq_queues.yml
+++ b/config/sidekiq_queues.yml
@@ -413,8 +413,6 @@
- 1
- - llm_namespace_access_cache_reset
- 1
-- - llm_tanuki_bot_update
- - 1
- - llm_vertex_ai_access_token_refresh
- 1
- - mail_scheduler
@@ -473,6 +471,8 @@
- 1
- - ml_experiment_tracking_associate_ml_candidate_to_package
- 1
+- - namespaces_free_user_cap_group_over_limit_notification
+ - 1
- - namespaces_process_sync_events
- 1
- - namespaces_storage_usage_export
diff --git a/doc/api/bulk_imports.md b/doc/api/bulk_imports.md
index 612dae504d5..0f9df4eba31 100644
--- a/doc/api/bulk_imports.md
+++ b/doc/api/bulk_imports.md
@@ -260,6 +260,8 @@ curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab
## Get list of failed import records for group or project migration entity
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/428016) in GitLab 16.6.
+
```plaintext
GET /bulk_imports/:id/entities/:entity_id/failures
```
diff --git a/doc/api/dependency_list_export.md b/doc/api/dependency_list_export.md
index 744309a402e..077f9e8dc1e 100644
--- a/doc/api/dependency_list_export.md
+++ b/doc/api/dependency_list_export.md
@@ -23,7 +23,7 @@ and subject to change without notice.
Create a new CycloneDX JSON export for all the project dependencies detected in a pipeline.
-If an authenticated user doesn't have permission to [read_dependency](../user/custom_roles.md#custom-role-requirements),
+If an authenticated user does not have permission to [read_dependency](../user/custom_roles.md#available-permissions),
this request returns a `403 Forbidden` status code.
SBOM exports can be only accessed by the export's author.
diff --git a/doc/api/settings.md b/doc/api/settings.md
index 3c64e295b27..670be23336c 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -434,6 +434,7 @@ listed in the descriptions of the relevant settings.
| `gitaly_timeout_fast` | integer | no | Gitaly fast operation timeout, in seconds. Some Gitaly operations are expected to be fast. If they exceed this threshold, there may be a problem with a storage shard and 'failing fast' can help maintain the stability of the GitLab instance. Set to `0` to disable timeouts. |
| `gitaly_timeout_medium` | integer | no | Medium Gitaly timeout, in seconds. This should be a value between the Fast and the Default timeout. Set to `0` to disable timeouts. |
| `gitlab_dedicated_instance` | boolean | no | Indicates whether the instance was provisioned for GitLab Dedicated. |
+| `gitlab_shell_operation_limit` | integer | no | Maximum number of Git operations per minute a user can perform. Default: `600`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/412088) in GitLab 16.2. |
| `grafana_enabled` | boolean | no | Enable Grafana. |
| `grafana_url` | string | no | Grafana URL. |
| `gravatar_enabled` | boolean | no | Enable Gravatar. |
@@ -455,6 +456,8 @@ listed in the descriptions of the relevant settings.
| `import_sources` | array of strings | no | Sources to allow project import from, possible values: `github`, `bitbucket`, `bitbucket_server`, `fogbugz`, `git`, `gitlab_project`, `gitea`, and `manifest`. |
| `invisible_captcha_enabled` | boolean | no | Enable Invisible CAPTCHA spam detection during sign-up. Disabled by default. |
| `issues_create_limit` | integer | no | Max number of issue creation requests per minute per user. Disabled by default.|
+| `jira_connect_application_key` | String | no | Application ID of the OAuth application that should be used to authenticate with the GitLab for Jira Cloud app |
+| `jira_connect_proxy_url` | String | no | URL of the GitLab instance that should be used as a proxy for the GitLab for Jira Cloud app |
| `keep_latest_artifact` | boolean | no | Prevent the deletion of the artifacts from the most recent successful jobs, regardless of the expiry time. Enabled by default. |
| `local_markdown_version` | integer | no | Increase this value when any cached Markdown should be invalidated. |
| `mailgun_signing_key` | string | no | The Mailgun HTTP webhook signing key for receiving events from webhook. |
@@ -613,9 +616,6 @@ listed in the descriptions of the relevant settings.
| `valid_runner_registrars` | array of strings | no | List of types which are allowed to register a GitLab Runner. Can be `[]`, `['group']`, `['project']` or `['group', 'project']`. |
| `whats_new_variant` | string | no | What's new variant, possible values: `all_tiers`, `current_tier`, and `disabled`. |
| `wiki_page_max_content_bytes` | integer | no | Maximum wiki page content size in **bytes**. Default: 52428800 Bytes (50 MB). The minimum value is 1024 bytes. |
-| `jira_connect_application_key` | String | no | Application ID of the OAuth application that should be used to authenticate with the GitLab for Jira Cloud app |
-| `jira_connect_proxy_url` | String | no | URL of the GitLab instance that should be used as a proxy for the GitLab for Jira Cloud app |
-| `gitlab_shell_operation_limit` | integer | no | Maximum number of Git operations per minute a user can perform. Default: `600`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/412088) in GitLab 16.2. |
### Configure inactive project deletion
diff --git a/doc/development/database/multiple_databases.md b/doc/development/database/multiple_databases.md
index c8c696c4e88..5bfaf7c4a71 100644
--- a/doc/development/database/multiple_databases.md
+++ b/doc/development/database/multiple_databases.md
@@ -56,6 +56,12 @@ When you choose the appropriate schema for tables, consider the following guidel
- Default to `gitlab_main_cell`: We expect most tables to be assigned to the `gitlab_main_cell` schema by default. Choose this schema if the data in the table is related to `projects` or `namespaces`.
- Consult with the Tenant Scale group: If you believe that the `gitlab_main_clusterwide` schema is more suitable for a table, seek approval from the Tenant Scale group This is crucial because it has scaling implications and may require reconsideration of the schema choice.
+After a schema has been assigned, the merge request pipeline might fail due to one or more of the following reasons, which can be rectified by following the linked guidelines:
+
+- [Cross-database joins](#suggestions-for-removing-cross-database-joins)
+- [Cross-database transactions](#fixing-cross-database-transactions)
+- [Cross-database foreign keys](#foreign-keys-that-cross-databases)
+
### The impact of `gitlab_schema`
The usage of `gitlab_schema` has a significant impact on the application.
diff --git a/doc/update/versions/gitlab_16_changes.md b/doc/update/versions/gitlab_16_changes.md
index 148cab3e023..85e415f2e2d 100644
--- a/doc/update/versions/gitlab_16_changes.md
+++ b/doc/update/versions/gitlab_16_changes.md
@@ -58,6 +58,24 @@ Specific information applies to installations using Geo:
For more information, see [issue 429617](https://gitlab.com/gitlab-org/gitlab/-/issues/429617).
+- [Object storage verification](https://about.gitlab.com/releases/2023/09/22/gitlab-16-4-released/#geo-verifies-object-storage) was added in GitLab 16.4. Due to an [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/429242) some Geo installations are reporting high memory usage which can lead to the GitLab application on the primary becoming unresponsive.
+
+ Your installation may be impacted if you have configured it to use [object storage](../../administration/object_storage.md) and have enabled [GitLab-managed object storage replication](../../administration/geo/replication/object_storage.md#enabling-gitlab-managed-object-storage-replication)
+
+ Until this is fixed, the workaround is to disable object storage verification.
+ Run the following command on one of the Rails nodes on the primary site:
+
+ ```shell
+ sudo gitlab-rails runner 'Feature.disable(:geo_object_storage_verification)'
+ ```
+
+ **Affected releases**:
+
+ | Affected minor releases | Affected patch releases | Fixed in |
+ | ------ | ------ | ------ |
+ | 16.4 | All | None |
+ | 16.5 | All | None |
+
## 16.4.0
- Updating a group path [received a bug fix](https://gitlab.com/gitlab-org/gitlab/-/issues/419289) that uses a database index introduced in 16.3.
@@ -134,6 +152,33 @@ Specific information applies to installations using Geo:
For more information, see [issue 429617](https://gitlab.com/gitlab-org/gitlab/-/issues/429617).
+- [Object storage verification](https://about.gitlab.com/releases/2023/09/22/gitlab-16-4-released/#geo-verifies-object-storage) was added in GitLab 16.4. Due to an [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/429242) some Geo installations are reporting high memory usage which can lead to the GitLab application on the primary becoming unresponsive.
+
+ Your installation may be impacted if you have configured it to use [object storage](../../administration/object_storage.md) and have enabled [GitLab-managed object storage replication](../../administration/geo/replication/object_storage.md#enabling-gitlab-managed-object-storage-replication)
+
+ Until this is fixed, the workaround is to disable object storage verification.
+ Run the following command on one of the Rails nodes on the primary site:
+
+ ```shell
+ sudo gitlab-rails runner 'Feature.disable(:geo_object_storage_verification)'
+ ```
+
+ **Affected releases**:
+
+ | Affected minor releases | Affected patch releases | Fixed in |
+ | ------ | ------ | ------ |
+ | 16.4 | All | None |
+ | 16.5 | All | None |
+
+- An [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/419370) with sync states getting stuck in pending state results in replication being stuck indefinitely for impacted items leading to risk of data loss in the event of a failover. This mostly impact repository syncs but can also can also affect container registry syncs. You are advised to upgrade to a fixed version to avoid risk of data loss.
+
+ **Affected releases**:
+
+ | Affected minor releases | Affected patch releases | Fixed in |
+ | ------ | ------ | ------ |
+ | 16.3 | 16.3.0 - 16.3.5 | 16.3.6 |
+ | 16.4 | 16.4.0 - 16.4.1 | 16.4.2 |
+
## 16.3.0
- **Update to GitLab 16.3.5 or later**. This avoids [issue 425971](https://gitlab.com/gitlab-org/gitlab/-/issues/425971) that causes an excessive use of database disk space for GitLab 16.3.3 and 16.3.4.
@@ -221,6 +266,15 @@ Specific information applies to installations using Geo:
For more information, see [issue 429617](https://gitlab.com/gitlab-org/gitlab/-/issues/429617).
+- An [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/419370) with sync states getting stuck in pending state results in replication being stuck indefinitely for impacted items leading to risk of data loss in the event of a failover. This mostly impact repository syncs but can also can also affect container registry syncs. You are advised to upgrade to a fixed version to avoid risk of data loss.
+
+ **Affected releases**:
+
+ | Affected minor releases | Affected patch releases | Fixed in |
+ | ------ | ------ | ------ |
+ | 16.3 | 16.3.0 - 16.3.5 | 16.3.6 |
+ | 16.4 | 16.4.0 - 16.4.1 | 16.4.2 |
+
## 16.2.0
- Legacy LDAP configuration settings may cause
diff --git a/doc/user/custom_roles.md b/doc/user/custom_roles.md
index f9978d5065c..1b827d82792 100644
--- a/doc/user/custom_roles.md
+++ b/doc/user/custom_roles.md
@@ -13,35 +13,17 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - Ability to view a vulnerability report [enabled by default](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/123835) in GitLab 16.1.
> - [Feature flag `custom_roles_vulnerability` removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124049) in GitLab 16.2.
> - Ability to create and remove a custom role with the UI [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/393235) in GitLab 16.4.
-> - Ability to manage group members [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/17364) in GitLab 16.5 under `admin_group_member` Feature flag.
-> - Ability to manage project access tokens [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/421778) in GitLab 16.5 under `manage_project_access_tokens` Feature flag.
+> - Ability to manage group members [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/17364) in GitLab 16.5.
+> - Ability to manage project access tokens [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/421778) in GitLab 16.5 [with a flag](../administration/feature_flags.md) named `manage_project_access_tokens`.
-Custom roles allow group members who are assigned the Owner role to create roles
+Custom roles allow group Owners or instance administrators to create roles
specific to the needs of their organization.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For a demo of the custom roles feature, see [[Demo] Ultimate Guest can view code on private repositories via custom role](https://www.youtube.com/watch?v=46cp_-Rtxps).
-The following granular permissions are available. You can add these permissions to any base role, and add them in combination with each other to create a customized role:
-
-- The Guest+1 role, which allows users with the Guest role to view code.
-- In GitLab 16.1 and later, you can create a custom role that can view vulnerability reports and change the status of the vulnerabilities.
-- In GitLab 16.3 and later, you can create a custom role that can view the dependency list.
-- In GitLab 16.4 and later, you can create a custom role that can approve merge requests.
-- In GitLab 16.5 and later, you can create a custom role that can manage group members.
-
You can discuss individual custom role and permission requests in [issue 391760](https://gitlab.com/gitlab-org/gitlab/-/issues/391760).
-When you enable a custom role for a user with the Guest role, that user has
-access to elevated permissions, and therefore:
-
-- Is considered a [billable user](../subscriptions/self_managed/index.md#billable-users) on self-managed GitLab.
-- [Uses a seat](../subscriptions/gitlab_com/index.md#how-seat-usage-is-determined) on GitLab.com.
-
-This does not apply to Guest+1, a Guest custom role that only enables the `read_code`
-permission. Users with that specific custom role are not considered billable users
-and do not use a seat.
-
## Create a custom role
Prerequisites:
@@ -51,9 +33,19 @@ Prerequisites:
- The group must be in the Ultimate tier.
- You must have:
- At least one private project so that you can see the effect of giving a
- user with the Guest role a custom role. The project can be in the group itself
+ user a custom role. The project can be in the group itself
or one of that group's subgroups.
- - A [personal access token with the API scope](profile/personal_access_tokens.md#create-a-personal-access-token).
+ - If you are using the API to create the custom role, a [personal access token with the API scope](profile/personal_access_tokens.md#create-a-personal-access-token).
+
+You create a custom role by selecting [permissions](#available-permissions) to add
+to a base role.
+
+You can select any number of permissions. For example, you can create a custom role
+with the ability to:
+
+- View vulnerability reports.
+- Change the status of vulnerabilities.
+- Approve merge requests.
### GitLab SaaS
@@ -64,7 +56,7 @@ Prerequisite:
1. On the left sidebar, select **Search or go to** and find your group.
1. Select **Settings > Roles and Permissions**.
1. Select **Add new role**.
-1. In **Base role to use as template**, select **Guest**.
+1. In **Base role to use as template**, select an existing non-custom role.
1. In **Role name**, enter the custom role's title.
1. Select the **Permissions** for the new custom role.
1. Select **Create new role**.
@@ -80,30 +72,43 @@ Prerequisite:
1. Select **Settings > Roles and Permissions**.
1. From the top dropdown list, select the group you want to create a custom role in.
1. Select **Add new role**.
-1. In **Base role to use as template**, select **Guest**.
+1. In **Base role to use as template**, select an existing non-custom role.
1. In **Role name**, enter the custom role's title.
1. Select the **Permissions** for the new custom role.
1. Select **Create new role**.
To create a custom role, you can also [use the API](../api/member_roles.md#add-a-member-role-to-a-group).
-### Custom role requirements
+### Available permissions
+
+The following permissions are available. You can add these permissions in any combination
+to a base role to create a custom role.
+
+Some permissions require having other permissions enabled first. For example, administration of vulnerabilities (`admin_vulnerability`) can only be enabled if reading vulnerabilities (`read_vulnerability`) is also enabled.
+
+These requirements are documented in the `Required permission` column in the following table.
-For every ability, a minimal access level is defined. To be able to create a custom role which enables a certain ability, the `member_roles` table record has to have the associated minimal access level. For all abilities, the minimal access level is Guest. Only users who have at least the Guest role can be assigned to a custom role.
+| Permission | Version | Required permission | Description |
+| ------------------------------- | -----------------------| -------------------- | ----------- |
+| `read_code` | GitLab 15.7 and later | Not applicable | View project code. Does not include the ability to pull code. |
+| `read_vulnerability` | GitLab 16.1 and later | Not applicable | View [vulnerability reports](application_security/vulnerability_report/index.md). |
+| `admin_vulnerability` | GitLab 16.1 and later | `read_vulnerability` | Change the [status of vulnerabilities](application_security/vulnerabilities/index.md#vulnerability-status-values). |
+| `read_dependency` | GitLab 16.3 and later | Not applicable | View [project dependencies](application_security/dependency_list/index.md). |
+| `admin_merge_request` | GitLab 16.4 and later | Not applicable | View and approve [merge requests](project/merge_requests/index.md), and view the associated merge request code. <br> Does not allow users to view or change merge request approval rules. |
+| `manage_project_access_tokens` | GitLab 16.5 and later | Not applicable | Create, delete, and list [project access tokens](project/settings/project_access_tokens.md). |
+| `admin_group_member` | GitLab 16.5 and later | Not applicable | Add or remove [group members](group/manage.md). |
-Some roles and abilities require having other abilities enabled. For example, a custom role can only have administration of vulnerabilities (`admin_vulnerability`) enabled if reading vulnerabilities (`read_vulnerability`) is also enabled.
+## Billing and seat usage
-You can see the abilities requirements in the following table.
+When you enable a custom role for a user with the Guest role, that user has
+access to elevated permissions over the base role, and therefore:
-| Ability | Required ability |
-| -- | -- |
-| `read_code` | - |
-| `read_dependency` | - |
-| `read_vulnerability` | - |
-| `admin_merge_request` | - |
-| `admin_vulnerability` | `read_vulnerability` |
-| `admin_group_member` | - |
-| `manage_project_access_tokens` | - |
+- Is considered a [billable user](../subscriptions/self_managed/index.md#billable-users) on self-managed GitLab.
+- [Uses a seat](../subscriptions/gitlab_com/index.md#how-seat-usage-is-determined) on GitLab.com.
+
+This does not apply when the user's custom role only has the `read_code` permission
+enabled. Guest users with that specific permission only are not considered billable users
+and do not use a seat.
## Associate a custom role with an existing group member
diff --git a/doc/user/group/manage.md b/doc/user/group/manage.md
index b2ca9c35d37..5a2f6d32154 100644
--- a/doc/user/group/manage.md
+++ b/doc/user/group/manage.md
@@ -131,7 +131,7 @@ After sharing the `Frontend` group with the `Engineering` group:
- The **Groups** tab lists the `Engineering` group.
- The **Groups** tab lists a group regardless of whether it is a public or private group.
- From [GitLab 16.6](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/134623),
- the invited group's name and membership source will be hidden unless:
+ the invited group's name and membership source will be masked unless:
- the invited group is public, or
- the current user is a member of the invited group, or
- the current user is a member of the current group.
diff --git a/doc/user/product_analytics/index.md b/doc/user/product_analytics/index.md
index dec16fcab76..6f5f4ed4db8 100644
--- a/doc/user/product_analytics/index.md
+++ b/doc/user/product_analytics/index.md
@@ -273,6 +273,25 @@ POST /api/v4/projects/PROJECT_ID/product_analytics/request/load?queryType=multi
If the request is successful, the returned JSON includes an array of rows of results.
+## View product analytics usage quota
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/424153) in GitLab 16.6 with a [flag](../../administration/feature_flags.md) named `product_analytics_usage_quota`. Disabled by default.
+
+FLAG:
+On self-managed GitLab, by default this feature is not available. To make it available per project or for your entire instance, an administrator can [enable the feature flag](../../administration/feature_flags.md) named `product_analytics_usage_quota`.
+On GitLab.com, this feature is not available.
+This feature is not ready for production use.
+
+Product analytics usage quota is calculated from the number of events received from instrumented applications.
+The tab displays the monthly totals for the group, and a breakdown of usage per project. Current month shows events counted to date.
+
+To view product analytics usage quota:
+
+1. On the left sidebar, select **Search or go to** and find your group.
+1. Select **Settings > Usage quota** and select the **Product analytics** tab.
+
+The usage quota excludes projects that are not onboarded with product analytics.
+
## Onboarding GitLab internal projects
GitLab team members can enable Product Analytics on their own internal projects on GitLab.com during the experiment phase.
diff --git a/doc/user/project/members/index.md b/doc/user/project/members/index.md
index 901a8fe9850..1fe6e3523b3 100644
--- a/doc/user/project/members/index.md
+++ b/doc/user/project/members/index.md
@@ -190,6 +190,7 @@ To add a group to a project:
1. Select **Invite**.
The members of the group are not displayed on the **Members** tab.
+Private groups are masked from unauthenticated users.
The **Members** tab shows:
- Members who are directly assigned to the project.
diff --git a/doc/user/project/merge_requests/reviews/index.md b/doc/user/project/merge_requests/reviews/index.md
index da7c82b6558..d3124b716da 100644
--- a/doc/user/project/merge_requests/reviews/index.md
+++ b/doc/user/project/merge_requests/reviews/index.md
@@ -30,6 +30,9 @@ For an overview, see [Merge request review](https://www.youtube.com/watch?v=2May
GitLab uses machine learning to suggest reviewers for your merge request.
+<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
+For an overview, see [GitLab Duo Suggested Reviewers](https://www.youtube.com/embed/ivwZQgh4Rxw).
+
To suggest reviewers, GitLab uses:
- The changes in the merge request
diff --git a/doc/user/workspace/index.md b/doc/user/workspace/index.md
index 1284067a391..e946a14e9a1 100644
--- a/doc/user/workspace/index.md
+++ b/doc/user/workspace/index.md
@@ -95,18 +95,24 @@ Only these properties are relevant to the GitLab implementation of the `containe
| `endpoints` | Port mappings to expose from the container. |
| `volumeMounts` | Storage volume to mount in the container. |
+### Using variables in a Devfile
+
+You can also define variables to be used in your devfile. The `variables` object is a map of name-value pairs used for string replacement in the devfile. The [Devfile 2.2.0 documentation](https://devfile.io/docs/2.2.0/defining-variables) lists some limits on how and where you can use variables. In addition, you cannot define any variables whose names are prefixed with `gl-`/`gl_`/`GL-`/`GL_`.
+
### Example configurations
The following is an example devfile configuration:
```yaml
schemaVersion: 2.2.0
+variables:
+ registry-root: registry.gitlab.com
components:
- name: tooling-container
attributes:
gl/inject-editor: true
container:
- image: registry.gitlab.com/gitlab-org/remote-development/gitlab-remote-development-docs/debian-bullseye-ruby-3.2-node-18.12:rubygems-3.4-git-2.33-lfs-2.9-yarn-1.22-graphicsmagick-1.3.36-gitlab-workspaces
+ image: "{{registry-root}}/gitlab-org/remote-development/gitlab-remote-development-docs/debian-bullseye-ruby-3.2-node-18.12:rubygems-3.4-git-2.33-lfs-2.9-yarn-1.22-graphicsmagick-1.3.36-gitlab-workspaces"
env:
- name: KEY
value: VALUE
diff --git a/lib/gitlab/ci/templates/Kaniko.gitlab-ci.yml b/lib/gitlab/ci/templates/Kaniko.gitlab-ci.yml
index d7a6104082d..4c89497fa97 100644
--- a/lib/gitlab/ci/templates/Kaniko.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Kaniko.gitlab-ci.yml
@@ -46,13 +46,30 @@ kaniko-build:
# Write credentials to access Gitlab Container Registry within the runner/ci
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(echo -n ${CI_REGISTRY_USER}:${CI_REGISTRY_PASSWORD} | base64 | tr -d '\n')\"}}}" > /kaniko/.docker/config.json
# Build and push the container. To disable push add --no-push
- - DOCKERFILE_PATH=${DOCKERFILE_PATH:-"$KANIKO_BUILD_CONTEXT/Dockerfile"}
+ # Both Dockerfile and Containerfile are supported. For retrocompatibility, if both files are present, Dockerfile will be used.
+ - |
+ if [ -z "$DOCKERFILE_PATH" ]; then
+ if [ -f "$KANIKO_BUILD_CONTEXT/Dockerfile" ]; then
+ DOCKERFILE_PATH="$KANIKO_BUILD_CONTEXT/Dockerfile"
+ elif [ -n "$CONTAINERFILE_PATH" ]; then
+ DOCKERFILE_PATH="$CONTAINERFILE_PATH"
+ elif [ -f "$KANIKO_BUILD_CONTEXT/Containerfile" ]; then
+ DOCKERFILE_PATH="$KANIKO_BUILD_CONTEXT/Containerfile"
+ else \
+ echo "No suitable configuration for the build context have been found. Please check your configuration."
+ exit 1
+ fi
+ fi
+ - echo $DOCKERFILE_PATH
- /kaniko/executor --context $KANIKO_BUILD_CONTEXT --dockerfile $DOCKERFILE_PATH --destination $IMAGE_TAG $KANIKO_ARGS
- # Run this job in a branch/tag where a Dockerfile exists
+ # Run this job in a branch/tag where a Containerfile/Dockerfile exists
rules:
- exists:
+ - Containerfile
- Dockerfile
- # custom Dockerfile path
+ # custom Containerfile/Dockerfile path
+ # If both variables are set, DOCKERFILE_PATH will be used
- if: $DOCKERFILE_PATH
+ - if: $CONTAINERFILE_PATH
# custom build context without an explicit Dockerfile path
- if: $KANIKO_BUILD_CONTEXT != $CI_PROJECT_DIR
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 521e52a8592..a2808bffd78 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -5476,6 +5476,9 @@ msgstr ""
msgid "Analytics|Analytics dashboards"
msgstr ""
+msgid "Analytics|Analytics events by month"
+msgstr ""
+
msgid "Analytics|Analytics settings for '%{project_name}' were successfully updated."
msgstr ""
@@ -5593,9 +5596,6 @@ msgstr ""
msgid "Analytics|Event Props"
msgstr ""
-msgid "Analytics|Event counts update hourly"
-msgstr ""
-
msgid "Analytics|Events"
msgstr ""
@@ -5617,6 +5617,9 @@ msgstr ""
msgid "Analytics|Link clicks"
msgstr ""
+msgid "Analytics|Month"
+msgstr ""
+
msgid "Analytics|New dashboard"
msgstr ""
@@ -5647,9 +5650,15 @@ msgstr ""
msgid "Analytics|Previous month"
msgstr ""
+msgid "Analytics|Product analytics usage is calculated based on the total number of events received from projects within the group. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "Analytics|Projects"
msgstr ""
+msgid "Analytics|Projects (%{maxProjects} of %{totalProjects} shown)"
+msgstr ""
+
msgid "Analytics|Referer"
msgstr ""
@@ -5722,6 +5731,9 @@ msgstr ""
msgid "Analytics|Updating visualization %{visualizationName}"
msgstr ""
+msgid "Analytics|Usage by month"
+msgstr ""
+
msgid "Analytics|Usage by project"
msgstr ""
@@ -21043,6 +21055,36 @@ msgstr ""
msgid "Free trial will expire in %{days}"
msgstr ""
+msgid "FreeUserCap|Action required: %{namespace_name} group has been placed into a read-only state"
+msgstr ""
+
+msgid "FreeUserCap|Explore paid plans"
+msgstr ""
+
+msgid "FreeUserCap|Explore paid plans:"
+msgstr ""
+
+msgid "FreeUserCap|Manage members"
+msgstr ""
+
+msgid "FreeUserCap|Manage members:"
+msgstr ""
+
+msgid "FreeUserCap|Start a trial:"
+msgstr ""
+
+msgid "FreeUserCap|To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your top-level group to %{free_user_limit} users or less. You can also %{upgrade_start}upgrade%{upgrade_end} to a paid tier, which do not have user limits. If you need additional time, you can %{trial_start}start a free 30-day trial%{trial_end} which includes unlimited users."
+msgstr ""
+
+msgid "FreeUserCap|Upgrade:"
+msgstr ""
+
+msgid "FreeUserCap|You have exceeded your limit of %{free_user_limit} users for %{namespace_name} group because users were added to a group inherited by a group or project in the %{namespace_name} group."
+msgstr ""
+
+msgid "FreeUserCap|You've exceeded your user limit"
+msgstr ""
+
msgid "Freeze end"
msgstr ""
@@ -50334,6 +50376,9 @@ msgstr ""
msgid "Tracing|Attribute"
msgstr ""
+msgid "Tracing|Attributes"
+msgstr ""
+
msgid "Tracing|Check again"
msgstr ""
@@ -50385,12 +50430,18 @@ msgstr ""
msgid "Tracing|Last 7 days"
msgstr ""
+msgid "Tracing|Metadata"
+msgstr ""
+
msgid "Tracing|No traces to display."
msgstr ""
msgid "Tracing|Operation"
msgstr ""
+msgid "Tracing|Resource attributes"
+msgstr ""
+
msgid "Tracing|Select a service to load suggestions"
msgstr ""
@@ -50403,15 +50454,6 @@ msgstr ""
msgid "Tracing|Something went wrong while fetching the services"
msgstr ""
-msgid "Tracing|Span ID"
-msgstr ""
-
-msgid "Tracing|Span details"
-msgstr ""
-
-msgid "Tracing|Status code"
-msgstr ""
-
msgid "Tracing|Time range"
msgstr ""
diff --git a/package.json b/package.json
index 976c58591df..799755386db 100644
--- a/package.json
+++ b/package.json
@@ -59,7 +59,7 @@
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/fonts": "^1.3.0",
"@gitlab/svgs": "3.69.0",
- "@gitlab/ui": "67.3.3",
+ "@gitlab/ui": "67.5.1",
"@gitlab/visual-review-tools": "1.7.3",
"@gitlab/web-ide": "0.0.1-dev-20231004090414",
"@mattiasbuelens/web-streams-adapter": "^0.1.0",
@@ -124,7 +124,7 @@
"clipboard": "^2.0.8",
"compression-webpack-plugin": "^5.0.2",
"copy-webpack-plugin": "^6.4.1",
- "core-js": "^3.32.2",
+ "core-js": "^3.33.2",
"cron-validator": "^1.1.1",
"cronstrue": "^1.122.0",
"cropper": "^2.3.0",
diff --git a/qa/qa/flow/user_onboarding.rb b/qa/qa/flow/user_onboarding.rb
index ee595cae338..3753dc7c8d7 100644
--- a/qa/qa/flow/user_onboarding.rb
+++ b/qa/qa/flow/user_onboarding.rb
@@ -5,7 +5,11 @@ module QA
module UserOnboarding
extend self
- def onboard_user(wait: Capybara.default_max_wait_time)
+ def onboard_user
+ # Implemented in EE only
+ end
+
+ def create_initial_project
# Implemented in EE only
end
end
diff --git a/qa/qa/specs/features/browser_ui/10_govern/login/register_spec.rb b/qa/qa/specs/features/browser_ui/10_govern/login/register_spec.rb
index 1fca9f490f7..e200c7d3f87 100644
--- a/qa/qa/specs/features/browser_ui/10_govern/login/register_spec.rb
+++ b/qa/qa/specs/features/browser_ui/10_govern/login/register_spec.rb
@@ -157,11 +157,11 @@ module QA
Flow::Login.sign_in(as: user, skip_page_validation: true)
Flow::UserOnboarding.onboard_user
-
- # In development env and .com the user is asked to create a group and a project which can be skipped for
- # the purpose of this test
+ # In development env and .com the user is asked to create a group and a project
+ Flow::UserOnboarding.create_initial_project if page.has_text?("Create or import your first project", wait: 0)
Runtime::Browser.visit(:gitlab, Page::Dashboard::Welcome)
- Page::Main::Menu.perform(&:has_personal_area?)
+
+ expect(Page::Main::Menu.perform(&:has_personal_area?)).to be_truthy
end
end
end
diff --git a/spec/frontend/__mocks__/@gitlab/ui.js b/spec/frontend/__mocks__/@gitlab/ui.js
index c51f37db384..04b3215a88a 100644
--- a/spec/frontend/__mocks__/@gitlab/ui.js
+++ b/spec/frontend/__mocks__/@gitlab/ui.js
@@ -1,3 +1,5 @@
+import '~/commons/gitlab_ui';
+
export * from '@gitlab/ui';
/**
diff --git a/spec/frontend/ci/runner/components/registration/registration_dropdown_spec.js b/spec/frontend/ci/runner/components/registration/registration_dropdown_spec.js
index 3fb845b186a..f8ef0bdad51 100644
--- a/spec/frontend/ci/runner/components/registration/registration_dropdown_spec.js
+++ b/spec/frontend/ci/runner/components/registration/registration_dropdown_spec.js
@@ -178,7 +178,7 @@ describe('RegistrationDropdown', () => {
mountExtended,
);
- expect(findRegistrationTokenInput().element.type).toBe('password');
+ expect(findRegistrationTokenInput().classes()).toContain('input-copy-show-disc');
});
});
diff --git a/spec/frontend/ci/runner/components/registration/registration_token_spec.js b/spec/frontend/ci/runner/components/registration/registration_token_spec.js
index eccfe43b47f..af9dbe89eb6 100644
--- a/spec/frontend/ci/runner/components/registration/registration_token_spec.js
+++ b/spec/frontend/ci/runner/components/registration/registration_token_spec.js
@@ -55,7 +55,7 @@ describe('RegistrationToken', () => {
mountFn: mountExtended,
});
- expect(wrapper.find('input').element.type).toBe('password');
+ expect(wrapper.find('input').classes()).toContain('input-copy-show-disc');
});
describe('When the copy to clipboard button is clicked', () => {
diff --git a/spec/frontend/groups/components/overview_tabs_spec.js b/spec/frontend/groups/components/overview_tabs_spec.js
index 8db69295ac4..6bed744685f 100644
--- a/spec/frontend/groups/components/overview_tabs_spec.js
+++ b/spec/frontend/groups/components/overview_tabs_spec.js
@@ -362,9 +362,7 @@ describe('OverviewTabs', () => {
describe('when sort direction is changed', () => {
beforeEach(async () => {
await setup();
- await wrapper
- .findByRole('button', { name: 'Sorting Direction: Ascending' })
- .trigger('click');
+ await wrapper.findByRole('button', { name: 'Sort direction: Ascending' }).trigger('click');
});
it('updates query string with `sort` key', () => {
diff --git a/spec/frontend/import_entities/import_groups/components/import_status_spec.js b/spec/frontend/import_entities/import_groups/components/import_status_spec.js
new file mode 100644
index 00000000000..8d055d45dd8
--- /dev/null
+++ b/spec/frontend/import_entities/import_groups/components/import_status_spec.js
@@ -0,0 +1,99 @@
+import { GlBadge, GlLink } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+
+import ImportStatus from '~/import_entities/import_groups/components/import_status.vue';
+import { STATUSES, STATUS_ICON_MAP } from '~/import_entities/constants';
+
+describe('Group import status component', () => {
+ let wrapper;
+
+ const defaultProps = {
+ status: STATUSES.FINISHED,
+ };
+
+ const mockDetailsPath = '/details';
+
+ const createComponent = ({ props } = {}) => {
+ wrapper = shallowMount(ImportStatus, {
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
+ provide: {
+ detailsPath: mockDetailsPath,
+ },
+ });
+ };
+
+ const findGlBadge = () => wrapper.findComponent(GlBadge);
+ const findGlLink = () => wrapper.findComponent(GlLink);
+
+ describe('status badge text', () => {
+ describe('when import is partial', () => {
+ beforeEach(() => {
+ createComponent({
+ props: {
+ status: STATUSES.FINISHED,
+ hasFailures: true,
+ },
+ });
+ });
+
+ it('renders warning badge with text', () => {
+ expect(findGlBadge().props()).toMatchObject({
+ icon: 'status-alert',
+ variant: 'warning',
+ });
+ expect(findGlBadge().text()).toBe('Partially completed');
+ });
+ });
+
+ describe.each([
+ STATUSES.CREATED,
+ STATUSES.FAILED,
+ STATUSES.FINISHED,
+ STATUSES.STARTED,
+ STATUSES.TIMEOUT,
+ ])(`when import is %s`, (status) => {
+ beforeEach(() => {
+ createComponent({
+ props: {
+ status,
+ },
+ });
+ });
+
+ it('renders badge with text', () => {
+ const expectedStatus = STATUS_ICON_MAP[status];
+
+ expect(findGlBadge().props()).toMatchObject({
+ icon: expectedStatus.icon,
+ variant: expectedStatus.variant,
+ });
+ expect(findGlBadge().text()).toBe(expectedStatus.text);
+ });
+ });
+ });
+
+ describe('details link', () => {
+ it('does not render by default', () => {
+ createComponent();
+
+ expect(findGlLink().exists()).toBe(false);
+ });
+
+ it('renders with correct link when import is partial', () => {
+ createComponent({
+ props: {
+ id: 2,
+ entityId: 11,
+ hasFailures: true,
+ showDetailsLink: true,
+ status: STATUSES.FINISHED,
+ },
+ });
+
+ expect(findGlLink().attributes('href')).toBe('/details?id=2&entity_id=11');
+ });
+ });
+});
diff --git a/spec/frontend/observability/client_spec.js b/spec/frontend/observability/client_spec.js
index c7bb83fb4f2..31998053742 100644
--- a/spec/frontend/observability/client_spec.js
+++ b/spec/frontend/observability/client_spec.js
@@ -132,45 +132,25 @@ describe('buildClient', () => {
describe('fetchTrace', () => {
it('fetches the trace from the tracing URL', async () => {
- const mockTraces = [
- {
- trace_id: 'trace-1',
- duration_nano: 3000,
- spans: [{ duration_nano: 1000 }, { duration_nano: 2000 }],
- },
- ];
-
- axiosMock.onGet(tracingUrl).reply(200, {
- traces: mockTraces,
- });
+ const mockTrace = {
+ trace_id: 'trace-1',
+ duration_nano: 3000,
+ spans: [{ duration_nano: 1000 }, { duration_nano: 2000 }],
+ };
+ axiosMock.onGet(`${tracingUrl}/trace-1`).reply(200, mockTrace);
const result = await client.fetchTrace('trace-1');
expect(axios.get).toHaveBeenCalledTimes(1);
- expect(axios.get).toHaveBeenCalledWith(tracingUrl, {
+ expect(axios.get).toHaveBeenCalledWith(`${tracingUrl}/trace-1`, {
withCredentials: true,
- params: { trace_id: 'trace-1' },
});
- expect(result).toEqual(mockTraces[0]);
+ expect(result).toEqual(mockTrace);
});
it('rejects if trace id is missing', () => {
return expect(client.fetchTrace()).rejects.toThrow('traceId is required.');
});
-
- it('rejects if traces are empty', async () => {
- axiosMock.onGet(tracingUrl).reply(200, { traces: [] });
-
- await expect(client.fetchTrace('trace-1')).rejects.toThrow(FETCHING_TRACES_ERROR);
- expectErrorToBeReported(new Error(FETCHING_TRACES_ERROR));
- });
-
- it('rejects if traces are invalid', async () => {
- axiosMock.onGet(tracingUrl).reply(200, { traces: 'invalid' });
-
- await expect(client.fetchTraces()).rejects.toThrow(FETCHING_TRACES_ERROR);
- expectErrorToBeReported(new Error(FETCHING_TRACES_ERROR));
- });
});
describe('fetchTraces', () => {
@@ -250,7 +230,7 @@ describe('buildClient', () => {
{ operator: '=', value: 'op' },
{ operator: '!=', value: 'not-op' },
],
- serviceName: [
+ service: [
{ operator: '=', value: 'service' },
{ operator: '!=', value: 'not-service' },
],
@@ -317,7 +297,7 @@ describe('buildClient', () => {
{ operator: '>', value: 'foo' },
{ operator: '<', value: 'foo' },
],
- serviceName: [
+ service: [
{ operator: '>', value: 'foo' },
{ operator: '<', value: 'foo' },
],
diff --git a/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/pypi_installation_spec.js.snap b/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/pypi_installation_spec.js.snap
index 17acf7381c0..d6c3d98efa3 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/pypi_installation_spec.js.snap
+++ b/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/pypi_installation_spec.js.snap
@@ -177,7 +177,7 @@ exports[`PypiInstallation renders all the messages 1`] = `
</div>
</div>
<small
- class="form-text text-muted"
+ class="form-text text-gl-muted"
id="reference-8"
tabindex="-1"
>
diff --git a/spec/frontend/vue_shared/components/form/input_copy_toggle_visibility_spec.js b/spec/frontend/vue_shared/components/form/input_copy_toggle_visibility_spec.js
index 72a0eb98a07..b57643a1359 100644
--- a/spec/frontend/vue_shared/components/form/input_copy_toggle_visibility_spec.js
+++ b/spec/frontend/vue_shared/components/form/input_copy_toggle_visibility_spec.js
@@ -44,11 +44,11 @@ describe('InputCopyToggleVisibility', () => {
};
function expectInputToBeMasked() {
- expect(findFormInput().element.type).toBe('password');
+ expect(findFormInput().classes()).toContain('input-copy-show-disc');
}
function expectInputToBeRevealed() {
- expect(findFormInput().element.type).toBe('text');
+ expect(findFormInput().classes()).not.toContain('input-copy-show-disc');
expect(findFormInput().element.value).toBe(valueProp);
}
diff --git a/spec/lib/gitlab/instrumentation_helper_spec.rb b/spec/lib/gitlab/instrumentation_helper_spec.rb
index 072a9c5f008..9e69566df36 100644
--- a/spec/lib/gitlab/instrumentation_helper_spec.rb
+++ b/spec/lib/gitlab/instrumentation_helper_spec.rb
@@ -44,6 +44,7 @@ RSpec.describe Gitlab::InstrumentationHelper, :clean_gitlab_redis_repository_cac
before do # init redis connection with `test` env details
redis_store_class.with(&:ping)
+ Gitlab::Redis::Queues.with(&:ping)
RequestStore.clear!
end
diff --git a/spec/services/bulk_imports/process_service_spec.rb b/spec/services/bulk_imports/process_service_spec.rb
index 7dbd3153357..9d519bc3b05 100644
--- a/spec/services/bulk_imports/process_service_spec.rb
+++ b/spec/services/bulk_imports/process_service_spec.rb
@@ -133,23 +133,6 @@ RSpec.describe BulkImports::ProcessService, feature_category: :importers do
end
end
end
-
- context 'when exception occurs' do
- it 'tracks the exception & marks import as failed' do
- create(:bulk_import_entity, :created, bulk_import: bulk_import)
-
- allow(BulkImports::ExportRequestWorker).to receive(:perform_async).and_raise(StandardError)
-
- expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
- kind_of(StandardError),
- bulk_import_id: bulk_import.id
- )
-
- subject.execute
-
- expect(bulk_import.reload.failed?).to eq(true)
- end
- end
end
context 'when importing a group' do
diff --git a/spec/support/shared_examples/services/protected_branches_shared_examples.rb b/spec/support/shared_examples/services/protected_branches_shared_examples.rb
index 51653a7460a..80e2f09ed44 100644
--- a/spec/support/shared_examples/services/protected_branches_shared_examples.rb
+++ b/spec/support/shared_examples/services/protected_branches_shared_examples.rb
@@ -29,11 +29,11 @@ RSpec.shared_context 'with scan result policy preventing force pushing' do
let(:policy_path) { Security::OrchestrationPolicyConfiguration::POLICY_PATH }
let(:default_branch) { policy_project.default_branch }
- let(:prevent_force_pushing) { true }
+ let(:prevent_pushing_and_force_pushing) { true }
let(:scan_result_policy) do
build(:scan_result_policy, branches: [branch_name],
- approval_settings: { prevent_force_pushing: prevent_force_pushing })
+ approval_settings: { prevent_pushing_and_force_pushing: prevent_pushing_and_force_pushing })
end
let(:policy_yaml) do
diff --git a/spec/workers/bulk_import_worker_spec.rb b/spec/workers/bulk_import_worker_spec.rb
index b76bd714a70..2983902dff7 100644
--- a/spec/workers/bulk_import_worker_spec.rb
+++ b/spec/workers/bulk_import_worker_spec.rb
@@ -31,4 +31,16 @@ RSpec.describe BulkImportWorker, feature_category: :importers do
it_behaves_like 'an idempotent worker'
end
+
+ describe '#sidekiq_retries_exhausted' do
+ it 'logs export failure and marks entity as failed' do
+ exception = StandardError.new('Exhausted error!')
+
+ expect(Gitlab::ErrorTracking).to receive(:track_exception).with(exception, bulk_import_id: bulk_import.id)
+
+ described_class.sidekiq_retries_exhausted_block.call({ 'args' => job_args }, exception)
+
+ expect(bulk_import.reload.failed?).to eq(true)
+ end
+ end
end
diff --git a/spec/workers/bulk_imports/entity_worker_spec.rb b/spec/workers/bulk_imports/entity_worker_spec.rb
index 5902ca87a17..507e6773bdb 100644
--- a/spec/workers/bulk_imports/entity_worker_spec.rb
+++ b/spec/workers/bulk_imports/entity_worker_spec.rb
@@ -118,29 +118,25 @@ RSpec.describe BulkImports::EntityWorker, feature_category: :importers do
end
end
- it 'logs and tracks the raised exceptions' do
- exception = StandardError.new('Error!')
-
- expect(BulkImports::PipelineWorker)
- .to receive(:perform_async)
- .and_raise(exception)
-
- expect(Gitlab::ErrorTracking)
- .to receive(:track_exception)
- .with(
- exception,
- hash_including(
- bulk_import_entity_id: entity.id,
- bulk_import_id: entity.bulk_import_id,
- bulk_import_entity_type: entity.source_type,
- source_full_path: entity.source_full_path,
- source_version: entity.bulk_import.source_version_info.to_s,
- importer: 'gitlab_migration'
- )
- )
-
- worker.perform(entity.id)
-
- expect(entity.reload.failed?).to eq(true)
+ describe '#sidekiq_retries_exhausted' do
+ it 'logs export failure and marks entity as failed' do
+ exception = StandardError.new('Exhausted error!')
+
+ expect(Gitlab::ErrorTracking)
+ .to receive(:track_exception)
+ .with(exception, a_hash_including(
+ message: "Request to export #{entity.source_type} failed",
+ bulk_import_entity_id: entity.id,
+ bulk_import_id: entity.bulk_import_id,
+ bulk_import_entity_type: entity.source_type,
+ source_full_path: entity.source_full_path,
+ source_version: entity.bulk_import.source_version_info.to_s,
+ importer: 'gitlab_migration'
+ ))
+
+ described_class.sidekiq_retries_exhausted_block.call({ 'args' => [entity.id] }, exception)
+
+ expect(entity.reload.failed?).to eq(true)
+ end
end
end
diff --git a/spec/workers/every_sidekiq_worker_spec.rb b/spec/workers/every_sidekiq_worker_spec.rb
index 6e1da58549c..37c878aa16b 100644
--- a/spec/workers/every_sidekiq_worker_spec.rb
+++ b/spec/workers/every_sidekiq_worker_spec.rb
@@ -137,9 +137,9 @@ RSpec.describe 'Every Sidekiq worker', feature_category: :shared do
'BuildHooksWorker' => 3,
'BuildQueueWorker' => 3,
'BuildSuccessWorker' => 3,
- 'BulkImportWorker' => false,
+ 'BulkImportWorker' => 3,
'BulkImports::ExportRequestWorker' => 5,
- 'BulkImports::EntityWorker' => false,
+ 'BulkImports::EntityWorker' => 3,
'BulkImports::PipelineWorker' => 3,
'BulkImports::PipelineBatchWorker' => 3,
'BulkImports::FinishProjectImportWorker' => 5,
@@ -351,8 +351,6 @@ RSpec.describe 'Every Sidekiq worker', feature_category: :shared do
'Llm::Embedding::GitlabDocumentation::SetEmbeddingsOnTheRecordWorker' => 5,
'Llm::Embedding::GitlabDocumentation::CreateEmptyEmbeddingsRecordsWorker' => 3,
'Llm::Embedding::GitlabDocumentation::CreateDbEmbeddingsPerDocFileWorker' => 5,
- 'Llm::TanukiBot::UpdateWorker' => 1,
- 'Llm::TanukiBot::RecreateRecordsWorker' => 3,
'MailScheduler::IssueDueWorker' => 3,
'MailScheduler::NotificationServiceWorker' => 3,
'MembersDestroyer::UnassignIssuablesWorker' => 3,
diff --git a/yarn.lock b/yarn.lock
index f293cdcead8..ef4fd43d18a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1274,10 +1274,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.69.0.tgz#bf76b8ffbe72a783807761a38abe8aaedcfe8c12"
integrity sha512-Zu8Fcjhi3Bk26jZOptcD5F4SHWC7/KuAe00NULViCeswKdoda1k19B+9oCSbsbxY7vMoFuD20kiCJdBCpxb3HA==
-"@gitlab/ui@67.3.3":
- version "67.3.3"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-67.3.3.tgz#11ce618a41288fd64ad0a5768dd517ac830fadc8"
- integrity sha512-Gc+HYwB0MdEO0qDboNnDTROpItlWB1rFFqFqe7h/IuqJUHyr2NmUAvhrPINE6f56qa5GcMTDlFazmUUY4kcxCg==
+"@gitlab/ui@67.5.1":
+ version "67.5.1"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-67.5.1.tgz#e331b34fa920f8f26ab0635dc84d15cbd4ac29e3"
+ integrity sha512-nsIlkZlU9Sig/KyVMkiHUp2Mcg/H2h4U5wN7ROnTv5heLhCP5oCvRpzGuazzhR/oemPqnKv/53ySAosDokhlmA==
dependencies:
"@floating-ui/dom" "1.2.9"
bootstrap-vue "2.23.1"
@@ -4513,10 +4513,10 @@ core-js-pure@^3.0.0:
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813"
integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==
-core-js@^3.29.1, core-js@^3.32.2, core-js@^3.6.5:
- version "3.32.2"
- resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.32.2.tgz#172fb5949ef468f93b4be7841af6ab1f21992db7"
- integrity sha512-pxXSw1mYZPDGvTQqEc5vgIb83jGQKFGYWY76z4a7weZXUolw3G+OvpZqSRcfYOoOVUQJYEPsWeQK8pKEnUtWxQ==
+core-js@^3.29.1, core-js@^3.33.2, core-js@^3.6.5:
+ version "3.33.2"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.33.2.tgz#312bbf6996a3a517c04c99b9909cdd27138d1ceb"
+ integrity sha512-XeBzWI6QL3nJQiHmdzbAOiMYqjrb7hwU7A39Qhvd/POSa/t9E1AeZyEZx3fNvp/vtM8zXwhoL0FsiS0hD0pruQ==
core-util-is@~1.0.0:
version "1.0.3"