diff --git a/.gitlab/ci/static-analysis.gitlab-ci.yml b/.gitlab/ci/static-analysis.gitlab-ci.yml
index 0ccda31a296..98073c7ad7b 100644
--- a/.gitlab/ci/static-analysis.gitlab-ci.yml
+++ b/.gitlab/ci/static-analysis.gitlab-ci.yml
@@ -234,7 +234,7 @@ ping-appsec-for-sast-findings:
- .ping-appsec-for-sast-findings:rules
variables:
# Project Access Token bot ID for /gitlab-com/gl-security/appsec/sast-custom-rules
- BOT_USER_ID: 14406065
+ BOT_USER_ID: 19650678
needs:
- semgrep-appsec-custom-rules
script:
diff --git a/app/assets/javascripts/ci/runner/components/registration/google_cloud_registration_instructions.vue b/app/assets/javascripts/ci/runner/components/registration/google_cloud_registration_instructions.vue
index e5da1b2eb57..95ea8b0c144 100644
--- a/app/assets/javascripts/ci/runner/components/registration/google_cloud_registration_instructions.vue
+++ b/app/assets/javascripts/ci/runner/components/registration/google_cloud_registration_instructions.vue
@@ -46,14 +46,6 @@ export default {
stepTwoDescription: s__(
'Runners|To view the setup instructions, complete the previous form. The instructions help you set up an autoscaling fleet of runners to execute your CI/CD jobs in Google Cloud.',
),
- projectIdLabel: s__('Runners|Google Cloud project ID'),
- projectIdDescription: s__(
- 'Runners|To improve security, use a dedicated project for CI/CD, separate from resources and identity management projects. %{linkStart}Where’s my project ID in Google Cloud?%{linkEnd}',
- ),
- zonesLinkText: s__('Runners|View available zones'),
- machineTypeDescription: s__(
- 'Runners|For most CI/CD jobs, use a %{linkStart}N2D standard machine type%{linkEnd}.',
- ),
runnerSetupBtnText: s__('Runners|Setup instructions'),
copyCommands: __('Copy commands'),
emptyFieldsAlertMessage: s__(
@@ -329,18 +321,20 @@ export default {
ref="cloudProjectId"
v-model="cloudProjectId"
name="cloudProjectId"
- :label="$options.i18n.projectIdLabel"
+ :label="s__('Runners|Google Cloud project ID')"
:invalid-feedback-if-empty="s__('Runners|Project ID is required.')"
- :invalid-feedback-if-malformed="
- s__(
- 'Runners|Project ID must be 6 to 30 lowercase letters, digits, or hyphens. It needs to start with a lowercase letter and end with a letter or number.',
- )
- "
+ :invalid-feedback-if-malformed="s__('Runners|Project ID must have the right format.')"
:regexp="$options.GC_PROJECT_PATTERN"
data-testid="project-id-input"
>
-
+
+
+
+ my-sample-project-191923
+
+
@@ -360,9 +359,7 @@ export default {
v-model="region"
name="region"
:invalid-feedback-if-empty="s__('Runners|Region is required.')"
- :invalid-feedback-if-malformed="
- s__('Runners|Region must have the correct format. Example: us-central1')
- "
+ :invalid-feedback-if-malformed="s__('Runners|Region must have the right format.')"
:regexp="$options.GC_REGION_PATTERN"
data-testid="region-input"
>
@@ -377,6 +374,18 @@ export default {
+
+
+
+
+ <location>-<sublocation><number>
+
+
+ us-central1
+
+
+
+
@@ -406,10 +413,28 @@ export default {
-
- {{ $options.i18n.zonesLinkText }}
-
-
+
+
+
+ {{ content }}
+
+
+
+
+
+ <region>-<zone_letter>
+
+
+ us-central1-a
+
+
+
@@ -418,11 +443,7 @@ export default {
v-model="machineType"
name="machineType"
:invalid-feedback-if-empty="s__('Runners|Machine type is required.')"
- :invalid-feedback-if-malformed="
- s__(
- 'Runners|Machine type must have the format `family-series-size`. Example: n2d-standard-2',
- )
- "
+ :invalid-feedback-if-malformed="s__('Runners|Machine type must have the right format.')"
:regexp="$options.GC_MACHINE_TYPE_PATTERN"
data-testid="machine-type-input"
>
@@ -442,7 +463,13 @@ export default {
-
+
+
+
+ <series>-<type>
+
+
+ n2d-standard-2
+
+
diff --git a/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js b/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js
deleted file mode 100644
index 286fc2568b2..00000000000
--- a/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js
+++ /dev/null
@@ -1,121 +0,0 @@
-import $ from 'jquery';
-
-/**
- * Linked Tabs
- *
- * Handles persisting and restores the current tab selection and content.
- * Reusable component for static content.
- *
- * ### Example Markup
- *
- *
- * -
- *
- * Tab 1
- *
- *
- * -
- *
- * Tab 2
- *
- *
- *
- *
- *
- *
- * Tab 1 Content
- *
- *
- * Tab 2 Content
- *
- *
- *
- *
- * ### How to use
- *
- * new LinkedTabs({
- * action: "#{controller.action_name}",
- * defaultAction: 'tab1',
- * parentEl: '.tab-links'
- * });
- */
-
-export default class LinkedTabs {
- /**
- * Binds the events and activates de default tab.
- *
- * @param {Object} options
- */
- constructor(options = {}) {
- this.options = options;
-
- this.defaultAction = this.options.defaultAction;
- this.action = this.options.action || this.defaultAction;
- this.hashedTabs = this.options.hashedTabs || false;
-
- if (this.action === 'show') {
- this.action = this.defaultAction;
- }
-
- this.currentLocation = window.location;
-
- if (this.hashedTabs) {
- this.action = this.currentLocation.hash || this.action;
- }
-
- const tabSelector = `${this.options.parentEl} a[data-toggle="tab"]`;
-
- // since this is a custom event we need jQuery :(
- $(document)
- .off('shown.bs.tab', tabSelector)
- .on('shown.bs.tab', tabSelector, (e) => this.tabShown(e));
-
- this.activateTab(this.action);
- }
-
- /**
- * Handles the `shown.bs.tab` event to set the currect url action.
- *
- * @param {type} evt
- * @return {Function}
- */
- tabShown(evt) {
- const source = evt.target.getAttribute('href');
-
- return this.setCurrentAction(source);
- }
-
- /**
- * Updates the URL with the path that matched the given action.
- *
- * @param {String} source
- * @return {String}
- */
- setCurrentAction(source) {
- const copySource = source;
-
- copySource.replace(/\/+$/, '');
-
- const newState = this.hashedTabs
- ? copySource
- : `${copySource}${this.currentLocation.search}${this.currentLocation.hash}`;
-
- window.history.replaceState(
- {
- url: newState,
- },
- document.title,
- newState,
- );
- return newState;
- }
-
- /**
- * Given the current action activates the correct tab.
- * http://getbootstrap.com/javascript/#tab-show
- * Note: Will trigger `shown.bs.tab`
- */
- activateTab() {
- return $(`${this.options.parentEl} a[data-action='${this.action}']`).tab('show');
- }
-}
diff --git a/app/assets/javascripts/lib/utils/favicon_ci.js b/app/assets/javascripts/lib/utils/favicon_ci.js
deleted file mode 100644
index 613e2620e02..00000000000
--- a/app/assets/javascripts/lib/utils/favicon_ci.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import axios from './axios_utils';
-import { setFaviconOverlay, resetFavicon } from './favicon';
-
-export const setCiStatusFavicon = (pageUrl) =>
- axios
- .get(pageUrl)
- .then(({ data }) => {
- if (data && data.favicon) {
- return setFaviconOverlay(data.favicon);
- }
- return resetFavicon();
- })
- .catch((error) => {
- resetFavicon();
- throw error;
- });
diff --git a/app/assets/javascripts/observability/constants.js b/app/assets/javascripts/observability/constants.js
index 8a656666f34..749d0054bae 100644
--- a/app/assets/javascripts/observability/constants.js
+++ b/app/assets/javascripts/observability/constants.js
@@ -46,3 +46,8 @@ const OPERATORS_NOT_LIKE = [{ value: OPERERATOR_NOT_LIKE, description: OPERERATO
export const OPERATORS_LIKE_NOT = [...OPERATORS_LIKE, ...OPERATORS_NOT_LIKE];
export const CUSTOM_DATE_RANGE_OPTION = 'custom';
+export const DATE_RANGE_QUERY_KEY = 'date_range';
+export const DATE_RANGE_START_QUERY_KEY = 'date_start';
+export const DATE_RANGE_END_QUERY_KEY = 'date_end';
+
+export const FILTERED_SEARCH_TERM_QUERY_KEY = 'search';
diff --git a/app/assets/javascripts/observability/utils.js b/app/assets/javascripts/observability/utils.js
index bce82c58996..1ef8896639d 100644
--- a/app/assets/javascripts/observability/utils.js
+++ b/app/assets/javascripts/observability/utils.js
@@ -1,3 +1,12 @@
+import { isValidDate } from '~/lib/utils/datetime_utility';
+
+import {
+ CUSTOM_DATE_RANGE_OPTION,
+ DATE_RANGE_QUERY_KEY,
+ DATE_RANGE_START_QUERY_KEY,
+ DATE_RANGE_END_QUERY_KEY,
+} from './constants';
+
/**
* Return the data range for the given time period
* Accepted values are numbers followed by the unit 'm', 'h', 'd', e.g. '5m', '3h', '7d'
@@ -31,3 +40,74 @@ export const periodToDate = (timePeriod) => {
}
return { min: new Date(maxMs - minMs), max: new Date(maxMs) };
};
+
+/**
+ * Validates the date range query parameters and returns an object with the validated date range.
+ *
+ * @param {string} dateRangeValue - The value of the date range query parameter.
+ * @param {string} dateRangeStart - The value of the start date query parameter.
+ * @param {string} dateRangeEnd - The value of the end date query parameter.
+ * @returns {{value: string, startDate?: Date, endDate?: Date}} An object containing the validated date range.
+ */
+
+function validatedDateRangeQuery(dateRangeValue, dateRangeStart, dateRangeEnd) {
+ const DEFAULT_TIME_RANGE = '1h';
+ if (dateRangeValue === CUSTOM_DATE_RANGE_OPTION) {
+ if (isValidDate(new Date(dateRangeStart)) && isValidDate(new Date(dateRangeEnd))) {
+ return {
+ value: dateRangeValue,
+ startDate: new Date(dateRangeStart),
+ endDate: new Date(dateRangeEnd),
+ };
+ }
+ return {
+ value: DEFAULT_TIME_RANGE,
+ };
+ }
+ return {
+ value: dateRangeValue ?? DEFAULT_TIME_RANGE,
+ };
+}
+
+/**
+ * Converts a query object containing date range parameters to a validated date filter object.
+ *
+ * @param {Object} queryObj - The query object containing date range parameters.
+ * @param {string} queryObj[DATE_RANGE_QUERY_KEY] - The value of the date range query parameter.
+ * @param {string} queryObj[DATE_RANGE_START_QUERY_KEY] - The value of the start date query parameter.
+ * @param {string} queryObj[DATE_RANGE_END_QUERY_KEY] - The value of the end date query parameter.
+ * @returns {{value: string, startDate?: Date, endDate?: Date}} An object containing the validated date range.
+ */
+export function queryToDateFilterObj(queryObj) {
+ const {
+ [DATE_RANGE_QUERY_KEY]: dateRangeValue,
+ [DATE_RANGE_START_QUERY_KEY]: dateRangeStart,
+ [DATE_RANGE_END_QUERY_KEY]: dateRangeEnd,
+ } = queryObj;
+
+ return validatedDateRangeQuery(dateRangeValue, dateRangeStart, dateRangeEnd);
+}
+
+/**
+ * Converts a date filter object to a query object with date range parameters.
+ *
+ * @param {Object} dateFilter - The date filter object.
+ * @param {string} dateFilter.value - The value of the date range.
+ * @param {Date} [dateFilter.startDate] - The start date of the date range.
+ * @param {Date} [dateFilter.endDate] - The end date of the date range.
+ * @returns {Object} An object containing the date range query parameters.
+ */
+export function dateFilterObjToQuery(dateFilter = {}) {
+ return {
+ [DATE_RANGE_QUERY_KEY]: dateFilter.value,
+ ...(dateFilter.value === CUSTOM_DATE_RANGE_OPTION
+ ? {
+ [DATE_RANGE_START_QUERY_KEY]: dateFilter.startDate?.toISOString(),
+ [DATE_RANGE_END_QUERY_KEY]: dateFilter.endDate?.toISOString(),
+ }
+ : {
+ [DATE_RANGE_START_QUERY_KEY]: undefined,
+ [DATE_RANGE_END_QUERY_KEY]: undefined,
+ }),
+ };
+}
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/list/package_search.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/list/package_search.vue
index c3dd3bf0fac..aafa2db6710 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/components/list/package_search.vue
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/list/package_search.vue
@@ -8,21 +8,36 @@ import {
TOKEN_TYPE_TYPE,
TOKEN_TITLE_VERSION,
TOKEN_TYPE_VERSION,
+ TOKEN_TITLE_STATUS,
+ TOKEN_TYPE_STATUS,
} from '~/vue_shared/components/filtered_search_bar/constants';
import PersistedSearch from '~/packages_and_registries/shared/components/persisted_search.vue';
-import { LIST_KEY_CREATED_AT } from '~/packages_and_registries/package_registry/constants';
+import {
+ LIST_KEY_CREATED_AT,
+ PACKAGE_STATUS_OPTIONS,
+ PACKAGE_TYPES_OPTIONS,
+} from '~/packages_and_registries/package_registry/constants';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
-import PackageTypeToken from './tokens/package_type_token.vue';
export default {
tokens: [
+ {
+ type: TOKEN_TYPE_STATUS,
+ icon: 'status',
+ title: TOKEN_TITLE_STATUS,
+ unique: true,
+ token: GlFilteredSearchToken,
+ operators: OPERATORS_IS,
+ options: PACKAGE_STATUS_OPTIONS,
+ },
{
type: TOKEN_TYPE_TYPE,
icon: 'package',
title: TOKEN_TITLE_TYPE,
unique: true,
- token: PackageTypeToken,
+ token: GlFilteredSearchToken,
operators: OPERATORS_IS,
+ options: PACKAGE_TYPES_OPTIONS,
},
{
type: TOKEN_TYPE_VERSION,
@@ -70,36 +85,39 @@ export default {
},
methods: {
formatFilters(filters) {
- const parsed = {
- packageName: '',
- packageType: undefined,
- packageVersion: '',
- };
+ return filters
+ .filter((filter) => filter.value?.data)
+ .reduce((acc, filter) => {
+ if (filter.type === TOKEN_TYPE_TYPE) {
+ return {
+ ...acc,
+ packageType: filter.value.data.toUpperCase(),
+ };
+ }
- return filters.reduce((acc, filter) => {
- if (filter.type === TOKEN_TYPE_TYPE && filter.value?.data) {
- return {
- ...acc,
- packageType: filter.value.data.toUpperCase(),
- };
- }
+ if (filter.type === TOKEN_TYPE_VERSION) {
+ return {
+ ...acc,
+ packageVersion: filter.value.data.trim(),
+ };
+ }
- if (filter.type === TOKEN_TYPE_VERSION && filter.value?.data) {
- return {
- ...acc,
- packageVersion: filter.value.data.trim(),
- };
- }
+ if (filter.type === TOKEN_TYPE_STATUS) {
+ return {
+ ...acc,
+ packageStatus: filter.value.data,
+ };
+ }
- if (filter.type === FILTERED_SEARCH_TERM) {
- return {
- ...acc,
- packageName: `${acc.packageName} ${filter.value.data}`.trim(),
- };
- }
+ if (filter.type === FILTERED_SEARCH_TERM) {
+ return {
+ ...acc,
+ packageName: filter.value.data.trim(),
+ };
+ }
- return acc;
- }, parsed);
+ return acc;
+ }, {});
},
updateSorting(newValue) {
this.sorting = { ...this.sorting, ...newValue };
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/list/packages_list.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/list/packages_list.vue
index 8e0e8f11807..e486d1e652b 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/components/list/packages_list.vue
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/list/packages_list.vue
@@ -170,6 +170,7 @@ export default {
-import { GlFilteredSearchToken, GlFilteredSearchSuggestion } from '@gitlab/ui';
-import { PACKAGE_TYPES } from '~/packages_and_registries/package_registry/constants';
-
-export default {
- components: {
- GlFilteredSearchToken,
- GlFilteredSearchSuggestion,
- },
- PACKAGE_TYPES,
-};
-
-
-
-
-
-
- {{ type }}
-
-
-
-
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/constants.js b/app/assets/javascripts/packages_and_registries/package_registry/constants.js
index 364bd430f07..2f000b9a1b8 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/constants.js
+++ b/app/assets/javascripts/packages_and_registries/package_registry/constants.js
@@ -205,17 +205,30 @@ export const SORT_FIELDS = [
},
];
-export const PACKAGE_TYPES = [
- s__('PackageRegistry|Composer'),
- s__('PackageRegistry|Conan'),
- s__('PackageRegistry|Generic'),
- s__('PackageRegistry|Maven'),
- s__('PackageRegistry|npm'),
- s__('PackageRegistry|NuGet'),
- s__('PackageRegistry|PyPI'),
- s__('PackageRegistry|RubyGems'),
- s__('PackageRegistry|Debian'),
- s__('PackageRegistry|Helm'),
+/* eslint-disable @gitlab/require-i18n-strings */
+export const PACKAGE_TYPES_OPTIONS = [
+ { value: 'Composer', title: s__('PackageRegistry|Composer') },
+ { value: 'Conan', title: s__('PackageRegistry|Conan') },
+ { value: 'Generic', title: s__('PackageRegistry|Generic') },
+ { value: 'Maven', title: s__('PackageRegistry|Maven') },
+ { value: 'npm', title: s__('PackageRegistry|npm') },
+ { value: 'NuGet', title: s__('PackageRegistry|NuGet') },
+ { value: 'PyPI', title: s__('PackageRegistry|PyPI') },
+ { value: 'RubyGems', title: s__('PackageRegistry|RubyGems') },
+ { value: 'Debian', title: s__('PackageRegistry|Debian') },
+ { value: 'Helm', title: s__('PackageRegistry|Helm') },
+];
+/* eslint-enable @gitlab/require-i18n-strings */
+
+export const PACKAGE_STATUS_OPTIONS = [
+ {
+ value: PACKAGE_DEFAULT_STATUS,
+ title: s__('PackageRegistry|Default'),
+ },
+ { value: PACKAGE_ERROR_STATUS, title: s__('PackageRegistry|Error') },
+ { value: 'HIDDEN', title: s__('PackageRegistry|Hidden') },
+ { value: 'PENDING_DESTRUCTION', title: s__('PackageRegistry|Pending deletion') },
+ { value: 'PROCESSING', title: s__('PackageRegistry|Processing') },
];
// links
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_packages.query.graphql b/app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_packages.query.graphql
index 26928c9e817..f2c1b700c46 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_packages.query.graphql
+++ b/app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_packages.query.graphql
@@ -10,6 +10,7 @@ query getPackages(
$packageName: String
$packageType: PackageTypeEnum
$packageVersion: String
+ $packageStatus: PackageStatus
$first: Int
$last: Int
$after: String
@@ -22,6 +23,7 @@ query getPackages(
packageName: $packageName
packageType: $packageType
packageVersion: $packageVersion
+ status: $packageStatus
after: $after
before: $before
first: $first
@@ -46,6 +48,7 @@ query getPackages(
packageName: $packageName
packageType: $packageType
packageVersion: $packageVersion
+ status: $packageStatus
after: $after
before: $before
first: $first
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/pages/list.vue b/app/assets/javascripts/packages_and_registries/package_registry/pages/list.vue
index eb33c020f7d..18302580acb 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/pages/list.vue
+++ b/app/assets/javascripts/packages_and_registries/package_registry/pages/list.vue
@@ -80,10 +80,8 @@ export default {
fullPath: this.fullPath,
sort: this.isGroupPage ? undefined : this.sort,
groupSort: this.isGroupPage ? this.sort : undefined,
- packageName: this.filters?.packageName,
- packageType: this.filters?.packageType,
- packageVersion: this.filters?.packageVersion,
first: GRAPHQL_PAGE_SIZE,
+ ...this.packageParams,
...this.pageParams,
};
},
@@ -93,14 +91,32 @@ export default {
pageInfo() {
return this.packages?.pageInfo ?? {};
},
+ packageParams() {
+ return {
+ packageName: this.filters?.packageName,
+ packageType: this.filters?.packageType,
+ packageVersion: this.filters?.packageVersion,
+ packageStatus: this.filters?.packageStatus,
+ };
+ },
packagesCount() {
return this.packages?.count;
},
hasFilters() {
- return this.filters.packageName || this.filters.packageType || this.filters.packageVersion;
+ return (
+ this.filters.packageName ||
+ this.filters.packageType ||
+ this.filters.packageVersion ||
+ this.filters.packageStatus
+ );
},
emptySearch() {
- return !this.filters.packageName && !this.filters.packageType && !this.filters.packageVersion;
+ return (
+ !this.filters.packageName &&
+ !this.filters.packageType &&
+ !this.filters.packageVersion &&
+ !this.filters.packageStatus
+ );
},
emptyStateTitle() {
return this.emptySearch
diff --git a/app/assets/javascripts/packages_and_registries/shared/utils.js b/app/assets/javascripts/packages_and_registries/shared/utils.js
index e7606936e6b..22aaa716720 100644
--- a/app/assets/javascripts/packages_and_registries/shared/utils.js
+++ b/app/assets/javascripts/packages_and_registries/shared/utils.js
@@ -10,7 +10,7 @@ export const searchArrayToFilterTokens = (search) =>
search.map((s) => keyValueToFilterToken(FILTERED_SEARCH_TERM, s));
export const extractFilterAndSorting = (queryObject) => {
- const { type, search, version, sort, orderBy } = queryObject;
+ const { type, search, version, status, sort, orderBy } = queryObject;
const filters = [];
const sorting = {};
@@ -20,6 +20,9 @@ export const extractFilterAndSorting = (queryObject) => {
if (version) {
filters.push(keyValueToFilterToken('version', version));
}
+ if (status) {
+ filters.push(keyValueToFilterToken('status', status));
+ }
if (search) {
filters.push(...searchArrayToFilterTokens(search));
}
diff --git a/app/assets/javascripts/pages/admin/application_settings/general/components/signup_form.vue b/app/assets/javascripts/pages/admin/application_settings/general/components/signup_form.vue
index cd6c9677b5f..47b879062f1 100644
--- a/app/assets/javascripts/pages/admin/application_settings/general/components/signup_form.vue
+++ b/app/assets/javascripts/pages/admin/application_settings/general/components/signup_form.vue
@@ -216,7 +216,7 @@ export default {
),
domainAllowListLabel: s__('ApplicationSettings|Allowed domains for sign-ups'),
domainAllowListDescription: s__(
- 'ApplicationSettings|Only users with e-mail addresses that match these domain(s) can sign up. Wildcards allowed. Use separate lines for multiple entries. Example: domain.com, *.domain.com',
+ 'ApplicationSettings|Only users with e-mail addresses that match these domain(s) can sign up. Wildcards allowed. Enter multiple entries on separate lines. Example: domain.com, *.domain.com',
),
userCapLabel: s__('ApplicationSettings|User cap'),
userCapDescription: s__(
@@ -232,7 +232,7 @@ export default {
),
domainDenyListListLabel: s__('ApplicationSettings|Denied domains for sign-ups'),
domainDenyListListDescription: s__(
- 'ApplicationSettings|Users with e-mail addresses that match these domain(s) cannot sign up. Wildcards allowed. Use separate lines for multiple entries. Example: domain.com, *.domain.com',
+ 'ApplicationSettings|Users with e-mail addresses that match these domain(s) cannot sign up. Wildcards allowed. Enter multiple entries on separate lines. Example: domain.com, *.domain.com',
),
domainPlaceholder: s__('ApplicationSettings|domain.com'),
emailRestrictionsEnabledGroupLabel: s__('ApplicationSettings|Email restrictions'),
diff --git a/app/assets/javascripts/pages/projects/pipelines/init_pipelines.js b/app/assets/javascripts/pages/projects/pipelines/init_pipelines.js
deleted file mode 100644
index 0c29f8817e7..00000000000
--- a/app/assets/javascripts/pages/projects/pipelines/init_pipelines.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import Pipelines from '~/pipelines';
-
-export default () => {
- const mergeRequestListToggle = document.querySelector('.js-toggle-mr-list');
- const truncatedMergeRequestList = document.querySelector('.js-truncated-mr-list');
- const fullMergeRequestList = document.querySelector('.js-full-mr-list');
-
- if (mergeRequestListToggle) {
- mergeRequestListToggle.addEventListener('click', (e) => {
- e.preventDefault();
- truncatedMergeRequestList.classList.toggle('hide');
- fullMergeRequestList.classList.toggle('hide');
- });
- }
-
- const pipelineTabLink = document.querySelector('.js-pipeline-tab-link a');
- const { controllerAction } = document.querySelector('.js-pipeline-container').dataset;
-
- if (pipelineTabLink) {
- const pipelineStatusUrl = `${pipelineTabLink.getAttribute('href')}/status.json`;
-
- // eslint-disable-next-line no-new
- new Pipelines({
- initTabs: true,
- pipelineStatusUrl,
- tabsOptions: {
- action: controllerAction,
- defaultAction: 'pipelines',
- parentEl: '.pipelines-tabs',
- },
- });
- }
-};
diff --git a/app/assets/javascripts/pages/projects/pipelines/show/index.js b/app/assets/javascripts/pages/projects/pipelines/show/index.js
index 225479c5194..e226e5f04e1 100644
--- a/app/assets/javascripts/pages/projects/pipelines/show/index.js
+++ b/app/assets/javascripts/pages/projects/pipelines/show/index.js
@@ -1,5 +1,3 @@
import initPipelineDetails from '~/ci/pipeline_details/pipeline_details_bundle';
-import initPipelines from '../init_pipelines';
-initPipelines();
initPipelineDetails();
diff --git a/app/assets/javascripts/pipelines.js b/app/assets/javascripts/pipelines.js
deleted file mode 100644
index 3b4e8d0e019..00000000000
--- a/app/assets/javascripts/pipelines.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import LinkedTabs from './lib/utils/bootstrap_linked_tabs';
-import { setCiStatusFavicon } from './lib/utils/favicon_ci';
-
-export default class Pipelines {
- constructor(options = {}) {
- if (options.initTabs && options.tabsOptions) {
- // eslint-disable-next-line no-new
- new LinkedTabs(options.tabsOptions);
- }
-
- if (options.pipelineStatusUrl) {
- setCiStatusFavicon(options.pipelineStatusUrl);
- }
- }
-}
diff --git a/app/services/issuable/callbacks/base.rb b/app/services/issuable/callbacks/base.rb
index 5ac7ccf54fc..b697fb51480 100644
--- a/app/services/issuable/callbacks/base.rb
+++ b/app/services/issuable/callbacks/base.rb
@@ -3,6 +3,7 @@
module Issuable
module Callbacks
class Base
+ Error = Class.new(StandardError)
include Gitlab::Allowable
def initialize(issuable:, current_user:, params: {})
@@ -27,6 +28,10 @@ module Issuable
def has_permission?(permission)
can?(current_user, permission, issuable)
end
+
+ def raise_error(message)
+ raise ::Issuable::Callbacks::Base::Error, message
+ end
end
end
end
diff --git a/app/services/work_items/callbacks/time_tracking.rb b/app/services/issuable/callbacks/time_tracking.rb
similarity index 85%
rename from app/services/work_items/callbacks/time_tracking.rb
rename to app/services/issuable/callbacks/time_tracking.rb
index 917fe101d1f..4d2d0d9d8ae 100644
--- a/app/services/work_items/callbacks/time_tracking.rb
+++ b/app/services/issuable/callbacks/time_tracking.rb
@@ -1,8 +1,10 @@
# frozen_string_literal: true
-module WorkItems
+module Issuable
module Callbacks
class TimeTracking < Base
+ ALLOWED_PARAMS = %i[time_estimate spend_time timelog].freeze
+
def after_initialize
if excluded_in_new_type?
params.delete(:time_estimate)
@@ -10,7 +12,7 @@ module WorkItems
params.delete(:timelog)
end
- return unless has_permission?(:admin_work_item)
+ return unless has_permission?(:"admin_#{issuable.to_ability_name}")
# below 2 parse_*_data methods, parse the data coming in from `time_tracking_widget` argument, in
# work item update mutation.
@@ -19,8 +21,8 @@ module WorkItems
# we still need to set the data here, in case when we had no data coming in from the `time_tracking_widget`
# argument, but data was still set through updating the description and using quick actions.
- work_item.time_estimate = params[:time_estimate] if params[:time_estimate].present?
- work_item.spend_time = params[:spend_time] if params[:spend_time].present?
+ issuable.time_estimate = params[:time_estimate] if params.has_key?(:time_estimate)
+ issuable.spend_time = params[:spend_time] if params[:spend_time].present?
end
private
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index 3e6b7b16478..798b28d6d3c 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -5,7 +5,8 @@ class IssuableBaseService < ::BaseContainerService
def available_callbacks
[
- Issuable::Callbacks::Milestone
+ Issuable::Callbacks::Milestone,
+ Issuable::Callbacks::TimeTracking
].freeze
end
diff --git a/app/services/work_items/create_service.rb b/app/services/work_items/create_service.rb
index dd1992066b9..165e66e82f5 100644
--- a/app/services/work_items/create_service.rb
+++ b/app/services/work_items/create_service.rb
@@ -28,7 +28,7 @@ module WorkItems
else
error(work_item.errors.full_messages, :unprocessable_entity, pass_back: payload(work_item))
end
- rescue ::WorkItems::Widgets::BaseService::WidgetError => e
+ rescue ::WorkItems::Widgets::BaseService::WidgetError, ::Issuable::Callbacks::Base::Error => e
error(e.message, :unprocessable_entity)
end
diff --git a/app/services/work_items/update_service.rb b/app/services/work_items/update_service.rb
index b3ad3597839..021e4ebf12e 100644
--- a/app/services/work_items/update_service.rb
+++ b/app/services/work_items/update_service.rb
@@ -22,7 +22,7 @@ module WorkItems
else
error(updated_work_item.errors.full_messages, :unprocessable_entity, pass_back: payload(updated_work_item))
end
- rescue ::WorkItems::Widgets::BaseService::WidgetError => e
+ rescue ::WorkItems::Widgets::BaseService::WidgetError, ::Issuable::Callbacks::Base::Error => e
error(e.message, :unprocessable_entity)
end
diff --git a/doc/administration/backup_restore/backup_gitlab.md b/doc/administration/backup_restore/backup_gitlab.md
index af00831ca54..52f4a855ec4 100644
--- a/doc/administration/backup_restore/backup_gitlab.md
+++ b/doc/administration/backup_restore/backup_gitlab.md
@@ -168,10 +168,10 @@ including:
- CI/CD job output logs
- CI/CD job artifacts
- LFS objects
-- Terraform states ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/331806) in GitLab 14.7)
+- Terraform states
- Container registry images
- GitLab Pages content
-- Packages ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/332006) in GitLab 14.7)
+- Packages
- Snippets
- [Group wikis](../../user/project/wiki/group.md)
- Project-level Secure Files ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121142) in GitLab 16.1)
@@ -563,21 +563,16 @@ sudo -u git -H bundle exec rake gitlab:backup:create REPOSITORIES_SERVER_SIDE=tr
#### Back up Git repositories concurrently
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37158) in GitLab 13.3.
-> - [Concurrent restore introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69330) in GitLab 14.3
-
When using [multiple repository storages](../repository_storage_paths.md),
repositories can be backed up or restored concurrently to help fully use CPU time. The
following variables are available to modify the default behavior of the Rake
task:
- `GITLAB_BACKUP_MAX_CONCURRENCY`: The maximum number of projects to back up at
- the same time. Defaults to the number of logical CPUs (in GitLab 14.1 and
- earlier, defaults to `1`).
+ the same time. Defaults to the number of logical CPUs.
- `GITLAB_BACKUP_MAX_STORAGE_CONCURRENCY`: The maximum number of projects to
back up at the same time on each storage. This allows the repository backups
- to be spread across storages. Defaults to `2` (in GitLab 14.1 and earlier,
- defaults to `1`).
+ to be spread across storages. Defaults to `2`.
For example, with 4 repository storages:
@@ -599,8 +594,6 @@ sudo -u git -H bundle exec rake gitlab:backup:create GITLAB_BACKUP_MAX_CONCURREN
#### Incremental repository backups
-> - Introduced in GitLab 14.9 [with a flag](../feature_flags.md) named `incremental_repository_backup`. Disabled by default.
-> - [Enabled on self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/355945) in GitLab 14.10.
> - `PREVIOUS_BACKUP` option [introduced](https://gitlab.com/gitlab-org/gitaly/-/issues/4184) in GitLab 15.0.
> - Server-side support for creating incremental backups [introduced](https://gitlab.com/gitlab-org/gitaly/-/merge_requests/6475) in GitLab 16.6.
@@ -617,26 +610,17 @@ support incremental backups for all subtasks.
Incremental repository backups can be faster than full repository backups because they only pack changes since the last backup into the backup bundle for each repository.
The incremental backup archives are not linked to each other: each archive is a self-contained backup of the instance. There must be an existing backup
-to create an incremental backup from:
+to create an incremental backup from.
-- In GitLab 14.9 and 14.10, use the `BACKUP=` option to choose the backup to use. The chosen previous backup is overwritten.
-- In GitLab 15.0 and later, use the `PREVIOUS_BACKUP=` option to choose the backup to use. By default, a backup file is created
- as documented in the [Backup ID](index.md#backup-id) section. You can override the `` portion of the filename by setting the
- [`BACKUP` environment variable](#backup-filename).
+Use the `PREVIOUS_BACKUP=` option to choose the backup to use. By default, a backup file is created
+as documented in the [Backup ID](index.md#backup-id) section. You can override the `` portion of the filename by setting the
+[`BACKUP` environment variable](#backup-filename).
To create an incremental backup, run:
-- In GitLab 15.0 or later:
-
- ```shell
- sudo gitlab-backup create INCREMENTAL=yes PREVIOUS_BACKUP=
- ```
-
-- In GitLab 14.9 and 14.10:
-
- ```shell
- sudo gitlab-backup create INCREMENTAL=yes BACKUP=
- ```
+```shell
+sudo gitlab-backup create INCREMENTAL=yes PREVIOUS_BACKUP=
+```
To create an [untarred](#skipping-tar-creation) incremental backup from a tarred backup, use `SKIP=tar`:
@@ -740,8 +724,6 @@ For Linux package (Omnibus):
##### S3 Encrypted Buckets
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64765) in GitLab 14.3.
-
AWS supports these [modes for server side encryption](https://docs.aws.amazon.com/AmazonS3/latest/userguide/serv-side-encryption.html):
- Amazon S3-Managed Keys (SSE-S3)
@@ -982,8 +964,6 @@ For self-compiled installations:
##### Using Azure Blob storage
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/25877) in GitLab 13.4.
-
::Tabs
:::TabTitle Linux package (Omnibus)
@@ -1334,10 +1314,6 @@ for more details on what these parameters do.
#### `gitaly-backup` for repository backup and restore
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/333034) in GitLab 14.2.
-> - [Deployed behind a feature flag](../../user/feature_flags.md), enabled by default.
-> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/333034) in GitLab 14.10. [Feature flag `gitaly_backup`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/83254) removed.
-
The `gitaly-backup` binary is used by the backup Rake task to create and restore repository backups from Gitaly.
`gitaly-backup` replaces the previous backup method that directly calls RPCs on Gitaly from GitLab.
diff --git a/doc/administration/backup_restore/restore_gitlab.md b/doc/administration/backup_restore/restore_gitlab.md
index bd1e87b532c..e17330b183b 100644
--- a/doc/administration/backup_restore/restore_gitlab.md
+++ b/doc/administration/backup_restore/restore_gitlab.md
@@ -137,7 +137,7 @@ sudo gitlab-ctl restart
sudo gitlab-rake gitlab:check SANITIZE=true
```
-In GitLab 13.1 and later, check [database values can be decrypted](../raketasks/check.md#verify-database-values-can-be-decrypted-using-the-current-secrets)
+Verify that the [database values can be decrypted](../raketasks/check.md#verify-database-values-can-be-decrypted-using-the-current-secrets)
especially if `/etc/gitlab/gitlab-secrets.json` was restored, or if a different server is
the target for the restore.
@@ -334,8 +334,6 @@ The `force=yes` environment variable also disables these prompts.
### Excluding tasks on restore
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/19347) in GitLab 14.10.
-
You can exclude specific tasks on restore by adding the environment variable `SKIP`, whose values are a comma-separated list of the following options:
- `db` (database)
diff --git a/doc/administration/backup_restore/troubleshooting_backup_gitlab.md b/doc/administration/backup_restore/troubleshooting_backup_gitlab.md
index 6fc7ee4838f..3add449d9ed 100644
--- a/doc/administration/backup_restore/troubleshooting_backup_gitlab.md
+++ b/doc/administration/backup_restore/troubleshooting_backup_gitlab.md
@@ -63,25 +63,13 @@ after which users must reactivate 2FA.
1. Enter the database console:
- For the Linux package (Omnibus) GitLab 14.1 and earlier:
-
- ```shell
- sudo gitlab-rails dbconsole
- ```
-
- For the Linux package (Omnibus) GitLab 14.2 and later:
+ For the Linux package (Omnibus):
```shell
sudo gitlab-rails dbconsole --database main
```
- For self-compiled installations, GitLab 14.1 and earlier:
-
- ```shell
- sudo -u git -H bundle exec rails dbconsole -e production
- ```
-
- For self-compiled installations, GitLab 14.2 and later:
+ For self-compiled installations:
```shell
sudo -u git -H bundle exec rails dbconsole -e production --database main
@@ -116,25 +104,13 @@ You may need to reconfigure or restart GitLab for the changes to take effect.
1. Enter the database console:
- For the Linux package (Omnibus) GitLab 14.1 and earlier:
-
- ```shell
- sudo gitlab-rails dbconsole
- ```
-
- For the Linux package (Omnibus) GitLab 14.2 and later:
+ For the Linux package (Omnibus):
```shell
sudo gitlab-rails dbconsole --database main
```
- For self-compiled installations, GitLab 14.1 and earlier:
-
- ```shell
- sudo -u git -H bundle exec rails dbconsole -e production
- ```
-
- For self-compiled installations, GitLab 14.2 and later:
+ For self-compiled installations:
```shell
sudo -u git -H bundle exec rails dbconsole -e production --database main
@@ -165,25 +141,13 @@ You may need to reconfigure or restart GitLab for the changes to take effect.
1. Enter the database console:
- For the Linux package (Omnibus) GitLab 14.1 and earlier:
-
- ```shell
- sudo gitlab-rails dbconsole
- ```
-
- For the Linux package (Omnibus) GitLab 14.2 and later:
+ For the Linux package (Omnibus):
```shell
sudo gitlab-rails dbconsole --database main
```
- For self-compiled installations, GitLab 14.1 and earlier:
-
- ```shell
- sudo -u git -H bundle exec rails dbconsole -e production
- ```
-
- For self-compiled installations, GitLab 14.2 and later:
+ For self-compiled installations:
```shell
sudo -u git -H bundle exec rails dbconsole -e production --database main
@@ -220,25 +184,13 @@ You should verify that the secrets are the root cause before deleting any data.
1. Enter the database console:
- For the Linux package (Omnibus) GitLab 14.1 and earlier:
-
- ```shell
- sudo gitlab-rails dbconsole
- ```
-
- For the Linux package (Omnibus) GitLab 14.2 and later:
+ For the Linux package (Omnibus):
```shell
sudo gitlab-rails dbconsole --database main
```
- For self-compiled installations, GitLab 14.1 and earlier:
-
- ```shell
- sudo -u git -H bundle exec rails dbconsole -e production
- ```
-
- For self-compiled installations, GitLab 14.2 and later:
+ For self-compiled installations:
```shell
sudo -u git -H bundle exec rails dbconsole -e production --database main
@@ -355,30 +307,18 @@ Truncate the filenames in the `uploads` table:
1. Enter the database console:
- For the Linux package (Omnibus) GitLab 14.2 and later:
+ For the Linux package (Omnibus):
```shell
sudo gitlab-rails dbconsole --database main
```
- For the Linux package (Omnibus) GitLab 14.1 and earlier:
-
- ```shell
- sudo gitlab-rails dbconsole
- ```
-
- For self-compiled installations, GitLab 14.2 and later:
+ For self-compiled installations:
```shell
sudo -u git -H bundle exec rails dbconsole -e production --database main
```
- For self-compiled installations, GitLab 14.1 and earlier:
-
- ```shell
- sudo -u git -H bundle exec rails dbconsole -e production
- ```
-
1. Search the `uploads` table for filenames longer than 246 characters:
The following query selects the `uploads` records with filenames longer than 246 characters in batches of 0 to 10000. This improves the performance on large GitLab instances with tables having thousand of records.
diff --git a/doc/administration/geo/disaster_recovery/index.md b/doc/administration/geo/disaster_recovery/index.md
index 0d05f48fde8..57360f6a6e2 100644
--- a/doc/administration/geo/disaster_recovery/index.md
+++ b/doc/administration/geo/disaster_recovery/index.md
@@ -295,25 +295,6 @@ changing Git remotes and API URLs.
This command uses the changed `external_url` configuration defined
in `/etc/gitlab/gitlab.rb`.
-1. For GitLab 12.0 through 12.7, you may need to update the **primary**
- site's name in the database. This bug has been fixed in GitLab 12.8.
-
- To determine if you need to do this, search for the
- `gitlab_rails["geo_node_name"]` setting in your `/etc/gitlab/gitlab.rb`
- file. If it is commented out with `#` or not found at all, then you
- need to update the **primary** site's name in the database. You can search for it
- like so:
-
- ```shell
- grep "geo_node_name" /etc/gitlab/gitlab.rb
- ```
-
- To update the **primary** site's name in the database:
-
- ```shell
- gitlab-rails runner 'Gitlab::Geo.primary_node.update!(name: GeoNode.current_node_name)'
- ```
-
1. Verify you can connect to the newly promoted **primary** using its URL.
If you updated the DNS records for the primary domain, these changes may
not have yet propagated depending on the previous DNS records TTL.
diff --git a/doc/administration/geo/disaster_recovery/runbooks/planned_failover_multi_node.md b/doc/administration/geo/disaster_recovery/runbooks/planned_failover_multi_node.md
index 5412a487cc5..1574546e293 100644
--- a/doc/administration/geo/disaster_recovery/runbooks/planned_failover_multi_node.md
+++ b/doc/administration/geo/disaster_recovery/runbooks/planned_failover_multi_node.md
@@ -182,7 +182,7 @@ follow these steps to avoid unnecessary data loss:
- Revoke object storage permissions from the **primary** site.
- Physically disconnect a machine.
-### Promoting the **secondary** site running GitLab 14.5 and later
+### Promoting the **secondary** site
1. SSH to every Sidekiq, PostgreSQL, and Gitaly node in the **secondary** site and run one of the following commands:
@@ -214,73 +214,9 @@ follow these steps to avoid unnecessary data loss:
1. Verify you can connect to the newly promoted **primary** site using the URL used
previously for the **secondary** site.
+
1. If successful, the **secondary** site is now promoted to the **primary** site.
-### Promoting the **secondary** site running GitLab 14.4 and earlier
-
-WARNING:
-The `gitlab-ctl promote-to-primary-node` and `gitlab-ctl promoted-db` commands are
-deprecated in GitLab 14.5 and later, and [removed in GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/345207).
-Use `gitlab-ctl geo promote` instead.
-
-NOTE:
-A new **secondary** should not be added at this time. If you want to add a new
-**secondary**, do this after you have completed the entire process of promoting
-the **secondary** to the **primary**.
-
-WARNING:
-If you encounter an `ActiveRecord::RecordInvalid: Validation failed: Name has already been taken` error during this process, read
-[the troubleshooting advice](../../replication/troubleshooting/failover.md#fixing-errors-during-a-failover-or-when-promoting-a-secondary-to-a-primary-site).
-
-The `gitlab-ctl promote-to-primary-node` command cannot be used in
-conjunction with multiple servers, as it can only
-perform changes on a **secondary** with only a single machine. Instead, you must
-do this manually.
-
-WARNING:
-In GitLab 13.2 and 13.3, promoting a secondary site to a primary while the
-secondary is paused fails. Do not pause replication before promoting a
-secondary. If the site is paused, be sure to resume before promoting. This
-issue has been fixed in GitLab 13.4 and later.
-
-WARNING:
-If the secondary site [has been paused](../../../geo/index.md#pausing-and-resuming-replication), this performs
-a point-in-time recovery to the last known state.
-Data that was created on the primary while the secondary was paused is lost.
-
-1. SSH in to the PostgreSQL node in the **secondary** and promote PostgreSQL separately:
-
- ```shell
- sudo gitlab-ctl promote-db
- ```
-
-1. Edit `/etc/gitlab/gitlab.rb` on every machine in the **secondary** to
- reflect its new status as **primary** by removing any lines that enabled the
- `geo_secondary_role`:
-
- ```ruby
- ## In pre-11.5 documentation, the role was enabled as follows. Remove this line.
- geo_secondary_role['enable'] = true
-
- ## In 11.5+ documentation, the role was enabled as follows. Remove this line.
- roles ['geo_secondary_role']
- ```
-
- After making these changes, [reconfigure GitLab](../../../restart_gitlab.md#reconfigure-a-linux-package-installation) each
- machine so the changes take effect.
-
-1. Promote the **secondary** to **primary**. SSH into a single Rails node
- server and execute:
-
- ```shell
- sudo gitlab-rake geo:set_secondary_as_primary
- ```
-
-1. Verify you can connect to the newly promoted **primary** using the URL used
- previously for the **secondary**.
-
-1. Success! The **secondary** has now been promoted to **primary**.
-
### Next steps
To regain geographic redundancy as quickly as possible, you should
diff --git a/doc/administration/geo/disaster_recovery/runbooks/planned_failover_single_node.md b/doc/administration/geo/disaster_recovery/runbooks/planned_failover_single_node.md
index 4510a410b5a..b93e6013988 100644
--- a/doc/administration/geo/disaster_recovery/runbooks/planned_failover_single_node.md
+++ b/doc/administration/geo/disaster_recovery/runbooks/planned_failover_single_node.md
@@ -222,7 +222,7 @@ Note the following when promoting a secondary:
error during this process, read
[the troubleshooting advice](../../replication/troubleshooting/failover.md#fixing-errors-during-a-failover-or-when-promoting-a-secondary-to-a-primary-site).
-To promote the secondary site running GitLab 14.5 and later:
+To promote the secondary site:
1. SSH in to your **secondary** site and run one of the following commands:
@@ -243,75 +243,6 @@ To promote the secondary site running GitLab 14.5 and later:
If successful, the **secondary** site is now promoted to the **primary** site.
-To promote the secondary site running GitLab 14.4 and earlier:
-
-WARNING:
-The `gitlab-ctl promote-to-primary-node` and `gitlab-ctl promoted-db` commands are
-deprecated in GitLab 14.5 and later, and [removed in GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/345207).
-Use `gitlab-ctl geo promote` instead.
-
-1. SSH in to your **secondary** site and login as root:
-
- ```shell
- sudo -i
- ```
-
-1. Edit `/etc/gitlab/gitlab.rb` to reflect its new status as **primary** by
- removing any lines that enabled the `geo_secondary_role`:
-
- ```ruby
- ## In pre-11.5 documentation, the role was enabled as follows. Remove this line.
- geo_secondary_role['enable'] = true
-
- ## In 11.5+ documentation, the role was enabled as follows. Remove this line.
- roles ['geo_secondary_role']
- ```
-
-1. Run the following command to list out all preflight checks and automatically
- check if replication and verification are complete before scheduling a planned
- failover to ensure the process goes smoothly:
-
- NOTE:
- In GitLab 13.7 and earlier, if you have a data type with zero items to sync,
- this command reports `ERROR - Replication is not up-to-date` even if
- replication is actually up-to-date. This bug was fixed in GitLab 13.8 and
- later.
-
- ```shell
- gitlab-ctl promotion-preflight-checks
- ```
-
-1. Promote the **secondary**:
-
- NOTE:
- In GitLab 13.7 and earlier, if you have a data type with zero items to sync,
- this command reports `ERROR - Replication is not up-to-date` even if
- replication is actually up-to-date. If replication and verification output
- shows that it is complete, you can add `--skip-preflight-checks` to make the
- command complete promotion. This bug was fixed in GitLab 13.8 and later.
-
- ```shell
- gitlab-ctl promote-to-primary-node
- ```
-
- If you have already run the [preflight checks](../planned_failover.md#preflight-checks)
- or don't want to run them, you can skip them:
-
- ```shell
- gitlab-ctl promote-to-primary-node --skip-preflight-check
- ```
-
- You can also promote the secondary site to primary **without any further confirmation**, even when preflight checks fail:
-
- ```shell
- sudo gitlab-ctl promote-to-primary-node --force
- ```
-
-1. Verify you can connect to the newly promoted **primary** site using the URL used
- previously for the **secondary** site.
-
- If successful, the **secondary** site is now promoted to the **primary** site.
-
### Next steps
To regain geographic redundancy as quickly as possible, you should
diff --git a/doc/administration/geo/index.md b/doc/administration/geo/index.md
index 49bf05b7624..0a95b9b1630 100644
--- a/doc/administration/geo/index.md
+++ b/doc/administration/geo/index.md
@@ -25,7 +25,7 @@ to clone and fetch large repositories, speeding up development and increasing th
Geo secondary sites transparently proxy write requests to the primary site. All Geo sites can be configured to respond to a single GitLab URL, to deliver a consistent, seamless, and comprehensive experience whichever site the user lands on.
-To make sure you're using the right version of the documentation, go to [the Geo page on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/administration/geo/index.md) and choose the appropriate release from the **Switch branch/tag** dropdown list. For example, [`v13.7.6-ee`](https://gitlab.com/gitlab-org/gitlab/-/blob/v13.7.6-ee/doc/administration/geo/index.md).
+To make sure you're using the right version of the documentation, go to [the Geo page on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/administration/geo/index.md) and choose the appropriate release from the **Switch branch/tag** dropdown list. For example, [`v15.7.6-ee`](https://gitlab.com/gitlab-org/gitlab/-/blob/v15.7.6-ee/doc/administration/geo/index.md).
Geo uses a set of defined terms that are described in the [Geo Glossary](glossary.md).
Be sure to familiarize yourself with those terms.
@@ -237,8 +237,6 @@ For information on how to update your Geo sites to the latest GitLab version, se
### Pausing and resuming replication
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/35913) in GitLab 13.2.
-
WARNING:
Pausing and resuming of replication is only supported for Geo installations using a
Linux package-managed database. External databases are not supported.
diff --git a/doc/administration/geo/replication/configuration.md b/doc/administration/geo/replication/configuration.md
index 20766fb7319..d55ff142b10 100644
--- a/doc/administration/geo/replication/configuration.md
+++ b/doc/administration/geo/replication/configuration.md
@@ -405,8 +405,6 @@ Selective synchronization:
### Git operations on unreplicated repositories
-> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2562) in GitLab 12.10 for HTTP(S) and in GitLab 13.0 for SSH.
-
Git clone, pull, and push operations over HTTP(S) and SSH are supported for repositories that
exist on the **primary** site but not on **secondary** sites. This situation can occur
when:
diff --git a/doc/administration/geo/replication/location_aware_git_url.md b/doc/administration/geo/replication/location_aware_git_url.md
index 66a195a8b17..4255699af13 100644
--- a/doc/administration/geo/replication/location_aware_git_url.md
+++ b/doc/administration/geo/replication/location_aware_git_url.md
@@ -11,7 +11,6 @@ DETAILS:
**Offering:** Self-managed
NOTE:
-Since GitLab 14.6,
[GitLab Geo supports a location-aware URL including web UI and API traffic.](../secondary_proxy/location_aware_external_url.md)
This configuration is recommended over the location-aware Git remote URL
described in this document.
diff --git a/doc/administration/geo/replication/troubleshooting/common.md b/doc/administration/geo/replication/troubleshooting/common.md
index 33d5a0ce311..74e2cde3f90 100644
--- a/doc/administration/geo/replication/troubleshooting/common.md
+++ b/doc/administration/geo/replication/troubleshooting/common.md
@@ -281,8 +281,6 @@ sudo gitlab-rake gitlab:geo:check
Ensure you have added the secondary site in the Admin Area under **Geo > Sites** on the web interface for the **primary** site.
Also ensure you entered the `gitlab_rails['geo_node_name']`
when adding the secondary site in the Admin Area of the **primary** site.
- In GitLab 12.3 and earlier, edit the secondary site in the Admin Area of the **primary**
- site and ensure that there is a trailing `/` in the `Name` field.
- Check returns `Exception: PG::UndefinedTable: ERROR: relation "geo_nodes" does not exist`.
diff --git a/doc/administration/geo/replication/troubleshooting/failover.md b/doc/administration/geo/replication/troubleshooting/failover.md
index 8e3ff44a16c..2e9c7c6252e 100644
--- a/doc/administration/geo/replication/troubleshooting/failover.md
+++ b/doc/administration/geo/replication/troubleshooting/failover.md
@@ -36,9 +36,7 @@ You successfully promoted this node!
```
If you encounter this message when running `gitlab-rake geo:set_secondary_as_primary`
-or `gitlab-ctl promote-to-primary-node`, either:
-
-- Enter a Rails console and run:
+or `gitlab-ctl promote-to-primary-node`, enter a Rails console and run:
```ruby
Rails.application.load_tasks; nil
@@ -46,10 +44,6 @@ or `gitlab-ctl promote-to-primary-node`, either:
Rake::Task['geo:set_secondary_as_primary'].invoke
```
-- Upgrade to GitLab 12.6.3 or later if it is safe to do so. For example,
- if the failover was just a test. A
- [caching-related bug](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22021) was fixed.
-
### Message: ``NoMethodError: undefined method `secondary?' for nil:NilClass``
When [promoting a **secondary** site](../../disaster_recovery/index.md#step-3-promoting-a-secondary-site),
diff --git a/doc/administration/geo/replication/troubleshooting/replication.md b/doc/administration/geo/replication/troubleshooting/replication.md
index 8a96238b04f..691d8c2e5c7 100644
--- a/doc/administration/geo/replication/troubleshooting/replication.md
+++ b/doc/administration/geo/replication/troubleshooting/replication.md
@@ -187,10 +187,6 @@ to respect the CIDR format (for example, `10.0.0.1/32`).
This happens if data is detected in the `projects` table. When one or more projects are detected, the operation
is aborted to prevent accidental data loss. To bypass this message, pass the `--force` option to the command.
-In GitLab 13.4, a seed project is added when GitLab is first installed. This makes it necessary to pass `--force` even
-on a new Geo secondary site. There is an [issue to account for seed projects](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5618)
-when checking the database.
-
### Message: `FATAL: could not map anonymous shared memory: Cannot allocate memory`
If you see this message, it means that the secondary site's PostgreSQL tries to request memory that is higher than the available memory. There is an [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/381585) that tracks this problem.
@@ -380,8 +376,6 @@ This iterates over all package files on the secondary, looking at the
and then calculate this value on the secondary to check if they match. This
does not change anything in the UI.
-For GitLab 14.4 and later:
-
```ruby
# Run on secondary
status = {}
@@ -402,28 +396,6 @@ status.keys.each {|key| puts "#{key} count: #{status[key].count}"}
status
```
-For GitLab 14.3 and earlier:
-
-```ruby
-# Run on secondary
-status = {}
-
-Packages::PackageFile.find_each do |package_file|
- primary_checksum = package_file.verification_checksum
- secondary_checksum = Packages::PackageFile.hexdigest(package_file.file.path)
- verification_status = (primary_checksum == secondary_checksum)
-
- status[verification_status.to_s] ||= []
- status[verification_status.to_s] << package_file.id
-end
-
-# Count how many of each value we get
-status.keys.each {|key| puts "#{key} count: #{status[key].count}"}
-
-# See the output in its entirety
-status
-```
-
### Failed verification of Uploads on the primary Geo site
If verification of some uploads is failing on the primary Geo site with `verification_checksum = nil` and with the ``verification_failure = Error during verification: undefined method `underscore' for NilClass:Class``, this can be due to orphaned Uploads. The parent record owning the Upload (the upload's model) has somehow been deleted, but the Upload record still exists. These verification failures are false.
diff --git a/doc/administration/geo/replication/troubleshooting/synchronization.md b/doc/administration/geo/replication/troubleshooting/synchronization.md
index 823480c5661..98052d9976e 100644
--- a/doc/administration/geo/replication/troubleshooting/synchronization.md
+++ b/doc/administration/geo/replication/troubleshooting/synchronization.md
@@ -181,89 +181,6 @@ To solve this:
During a [backfill](../../index.md#backfill), failures are scheduled to be retried at the end
of the backfill queue, therefore these failures only clear up **after** the backfill completes.
-## Sync failure message: "Verification failed with: Error during verification: File is not checksummable"
-
-### Missing files on the Geo primary site
-
-In GitLab 14.5 and earlier, certain data types which were missing on the Geo primary site were marked as "synced" on Geo secondary sites. This was because from the perspective of Geo secondary sites, the state matched the primary site and nothing more could be done on secondary sites.
-
-Secondaries would regularly try to sync these files again by using the "verification" feature:
-
-- Verification fails since the file doesn't exist.
-- The file is marked "sync failed".
-- Sync is retried.
-- The file is marked "sync succeeded".
-- The file is marked "needs verification".
-- Repeat until the file is available again on the primary site.
-
-This can be confusing to troubleshoot, since the registry entries are moved through a logical loop by various background jobs. Also, `last_sync_failure` and `verification_failure` are empty after "sync succeeded" but before verification is retried.
-
-If you see sync failures repeatedly and alternately increase, while successes decrease and vice versa, this is likely to be caused by missing files on the primary site. You can confirm this by searching `geo.log` on secondary sites for `File is not checksummable` affecting the same files over and over.
-
-After confirming this is the problem, the files on the primary site need to be fixed. Some possible causes:
-
-- An NFS share became unmounted.
-- A disk died or became corrupted.
-- Someone unintentionally deleted a file or directory.
-- Bugs in GitLab application:
- - A file was moved when it shouldn't have been moved.
- - A file wasn't moved when it should have been moved.
- - A wrong path was generated in the code.
-- A non-atomic backup was restored.
-- Services or servers or network infrastructure was interrupted/restarted during use.
-
-The appropriate action sometimes depends on the cause. For example, you can remount an NFS share. Often, a root cause may not be apparent or not useful to discover. If you have regular backups, it may be expedient to look through them and pull files from there.
-
-In some cases, a file may be determined to be of low value, and so it may be worth deleting the record.
-
-Geo itself is an excellent mitigation for files missing on the primary. If a file disappears on the primary but it was already synced to the secondary, you can grab the secondary's file. In cases like this, the `File is not checksummable` error message does not occur on Geo secondary sites, and only the primary logs this error message.
-
-This problem is more likely to show up in Geo secondary sites which were set up long after the original GitLab site. In this case, Geo is only surfacing an existing problem.
-
-This behavior affects only the following data types through GitLab 14.6:
-
-| Data type | From version |
-| ------------------------ | ------------ |
-| Package registry | 13.10 |
-| CI Pipeline Artifacts | 13.11 |
-| Terraform State Versions | 13.12 |
-| Infrastructure Registry (renamed to Terraform Module Registry in GitLab 15.11) | 14.0 |
-| External MR diffs | 14.6 |
-| LFS Objects | 14.6 |
-| Pages Deployments | 14.6 |
-| Uploads | 14.6 |
-| CI Job Artifacts | 14.6 |
-
-[Since GitLab 14.7, files that are missing on the primary site are now treated as sync failures](https://gitlab.com/gitlab-org/gitlab/-/issues/348745)
-to make Geo visibly surface data loss risks. The sync/verification loop is
-therefore short-circuited. `last_sync_failure` is now set to `The file is missing on the Geo primary site`.
-
-### Failed syncs with GitLab-managed object storage replication
-
-There is [an issue in GitLab 14.2 through 14.7](https://gitlab.com/gitlab-org/gitlab/-/issues/299819#note_822629467)
-that affects Geo when the GitLab-managed object storage replication is used, causing blob object types to fail synchronization.
-
-Since GitLab 14.2, verification failures result in synchronization failures and cause
-a re-synchronization of these objects.
-
-As verification is not implemented for files stored in object storage (see
-[issue 13845](https://gitlab.com/gitlab-org/gitlab/-/issues/13845) for more details), this
-results in a loop that consistently fails for all objects stored in object storage.
-
-You can work around this by marking the objects as synced and succeeded verification, however
-be aware that can also mark objects that may be
-[missing from the primary](#missing-files-on-the-geo-primary-site).
-
-To do that, enter the [Rails console](../../../operations/rails_console.md)
-and run:
-
-```ruby
-Gitlab::Geo.verification_enabled_replicator_classes.each do |klass|
- updated = klass.registry_class.failed.where(last_sync_failure: "Verification failed with: Error during verification: File is not checksummable").update_all(verification_checksum: '0000000000000000000000000000000000000000', verification_state: 2, verification_failure: nil, verification_retry_at: nil, state: 2, last_sync_failure: nil, retry_at: nil, verification_retry_count: 0, retry_count: 0)
- pp "Updated #{updated} #{klass.replicable_name_plural}"
-end
-```
-
## Message: curl 18 transfer closed with outstanding read data remaining & fetch-pack: unexpected disconnect while reading sideband packet
Unstable networking conditions can cause Gitaly to fail when trying to fetch large repository
diff --git a/doc/administration/geo/secondary_proxy/index.md b/doc/administration/geo/secondary_proxy/index.md
index 5dd20db041b..cc4d90cc586 100644
--- a/doc/administration/geo/secondary_proxy/index.md
+++ b/doc/administration/geo/secondary_proxy/index.md
@@ -10,9 +10,6 @@ DETAILS:
**Tier:** Premium, Ultimate
**Offering:** Self-managed
-> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5914) in GitLab 14.4 [with a flag](../../feature_flags.md) named `geo_secondary_proxy`. Disabled by default.
-> - [Enabled by default for unified URLs](https://gitlab.com/gitlab-org/gitlab/-/issues/325732) in GitLab 14.6.
-> - [Disabled by default for different URLs](https://gitlab.com/gitlab-org/gitlab/-/issues/325732) in GitLab 14.6 [with a flag](../../feature_flags.md) named `geo_secondary_proxy_separate_urls`.
> - [Enabled by default for different URLs](https://gitlab.com/gitlab-org/gitlab/-/issues/346112) in GitLab 15.1.
Use Geo proxying to:
diff --git a/doc/administration/geo/setup/database.md b/doc/administration/geo/setup/database.md
index 5c9957d7a72..724970d23e6 100644
--- a/doc/administration/geo/setup/database.md
+++ b/doc/administration/geo/setup/database.md
@@ -616,15 +616,13 @@ On all GitLab Geo **secondary** sites:
## Multi-node database replication
-In GitLab 14.0, Patroni replaced `repmgr` as the supported
-[highly available PostgreSQL solution](../../postgresql/replication_and_failover.md).
-
NOTE:
-If you still haven't [migrated from repmgr to Patroni](#migrating-from-repmgr-to-patroni) you're highly advised to do so.
+Patroni is the supported
+[highly available PostgreSQL solution](../../postgresql/replication_and_failover.md). If you still haven't [migrated from repmgr to Patroni](#migrating-from-repmgr-to-patroni), you're highly advised to do so.
### Migrating from repmgr to Patroni
-1. Before migrating, you should ensure there is no replication lag between the **primary** and **secondary** sites and that replication is paused. In GitLab 13.2 and later, you can pause and resume replication with `gitlab-ctl geo-replication-pause` and `gitlab-ctl geo-replication-resume` on a Geo secondary database node.
+1. Before migrating, you should ensure there is no replication lag between the **primary** and **secondary** sites and that replication is paused. You can pause and resume replication with `gitlab-ctl geo-replication-pause` and `gitlab-ctl geo-replication-resume` on a Geo secondary database node.
1. Follow the [instructions to migrate repmgr to Patroni](../../postgresql/replication_and_failover.md#switching-from-repmgr-to-patroni). When configuring Patroni on each **primary** site database node, add `patroni['replication_slots'] = { '' => 'physical' }`
to `gitlab.rb` where `` is the name of the replication slot for your **secondary** site. This ensures that Patroni recognizes the replication slot as permanent and doesn't drop it upon restarting.
1. If database replication to the **secondary** site was paused before migration, resume replication after Patroni is confirmed as working on the **primary** site.
diff --git a/doc/administration/geo_sites.md b/doc/administration/geo_sites.md
index efdba56d253..e9f0a81557d 100644
--- a/doc/administration/geo_sites.md
+++ b/doc/administration/geo_sites.md
@@ -61,8 +61,6 @@ you can decrease them.
## Set up the internal URLs
-> - Setting up internal URLs in secondary sites was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/77179) in GitLab 14.7.
-
You can set up a different URL for synchronization between the primary and secondary site.
The **primary** site's Internal URL is used by **secondary** sites to contact it
@@ -90,12 +88,6 @@ breaking communication between **primary** and **secondary** sites when using
HTTPS, customize your Internal URL to point to a load balancer with TLS
terminated at the load balancer.
-WARNING:
-Starting with GitLab 13.3 and [until 13.11](https://gitlab.com/gitlab-org/gitlab/-/issues/325522),
-if you use an internal URL that is not accessible to the users, the
-OAuth authorization flow does not work properly, because users are redirected
-to the internal URL instead of the external one.
-
## Multiple secondary sites behind a load balancer
**Secondary** sites can use identical external URLs if
diff --git a/doc/administration/instance_limits.md b/doc/administration/instance_limits.md
index aa799bc11bf..3cb6b6f5794 100644
--- a/doc/administration/instance_limits.md
+++ b/doc/administration/instance_limits.md
@@ -651,7 +651,7 @@ setting is used:
| `ci_max_artifact_size_secret_detection` | 0 |
| `ci_max_artifact_size_terraform` | 5 MB |
| `ci_max_artifact_size_trace` | 0 |
-| `ci_max_artifact_size_cyclonedx` | 1 MB |
+| `ci_max_artifact_size_cyclonedx` | 5 MB |
For example, to set the `ci_max_artifact_size_junit` limit to 10 MB on a self-managed
installation, run the following in the [GitLab Rails console](operations/rails_console.md#starting-a-rails-console-session):
diff --git a/doc/administration/maintenance_mode/index.md b/doc/administration/maintenance_mode/index.md
index 31afe3a6a40..6d95ddcc421 100644
--- a/doc/administration/maintenance_mode/index.md
+++ b/doc/administration/maintenance_mode/index.md
@@ -10,8 +10,6 @@ DETAILS:
**Tier:** Premium, Ultimate
**Offering:** Self-managed
-> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2149) in GitLab 13.9.
-
Maintenance Mode allows administrators to reduce write operations to a minimum while maintenance tasks are performed. The main goal is to block all external actions that change the internal state. The internal state includes the PostgreSQL database, but especially files, Git repositories, and Container repositories.
When Maintenance Mode is enabled, in-progress actions finish relatively quickly because no new actions are coming in, and internal state changes are minimal.
diff --git a/doc/api/settings.md b/doc/api/settings.md
index 8e7a28a5f7a..e6a6c892972 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -410,7 +410,7 @@ listed in the descriptions of the relevant settings.
| `disabled_oauth_sign_in_sources` | array of strings | no | Disabled OAuth sign-in sources. |
| `dns_rebinding_protection_enabled` | boolean | no | Enforce DNS-rebinding attack protection. |
| `domain_denylist_enabled` | boolean | no | (**If enabled, requires:** `domain_denylist`) Allows blocking sign-ups from emails from specific domains. |
-| `domain_denylist` | array of strings | no | Users with email addresses that match these domains **cannot** sign up. Wildcards allowed. Use separate lines for multiple entries. For example: `domain.com`, `*.domain.com`. |
+| `domain_denylist` | array of strings | no | Users with email addresses that match these domains **cannot** sign up. Wildcards allowed. Enter multiple entries on separate lines. For example: `domain.com`, `*.domain.com`. |
| `domain_allowlist` | array of strings | no | Force people to use only corporate emails for sign-up. Default is `null`, meaning there is no restriction. |
| `downstream_pipeline_trigger_limit_per_project_user_sha` | integer | no | [Maximum downstream pipeline trigger rate](../administration/settings/continuous_integration.md#maximum-downstream-pipeline-trigger-rate). Default: `0` (no restriction). [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/144077) in GitLab 16.10. |
| `dsa_key_restriction` | integer | no | The minimum allowed bit length of an uploaded DSA key. Default is `0` (no restriction). `-1` disables DSA keys. |
diff --git a/doc/architecture/blueprints/remote_development/index.md b/doc/architecture/blueprints/remote_development/index.md
index 7bd3ded8247..6517e0381f9 100644
--- a/doc/architecture/blueprints/remote_development/index.md
+++ b/doc/architecture/blueprints/remote_development/index.md
@@ -521,7 +521,7 @@ Once such a feature is available, Personal Access Tokens for each workspace woul
We need to only allow certain users to access workspaces. Currently, we are restricting this to the creator/owner of the workspace. After the workspace is created, it needs to be exposed to the network so that the user can connect to it.
Thus, any traffic incoming to the workspace needs to be authenticated and authorized.
-[`gitlab-workspaces-proxy`](https://gitlab.com/gitlab-org/remote-development/gitlab-workspaces-proxy) handles discovery, authentication and authorization of the workspaces running in a Kubernetes cluster.
+[`gitlab-workspaces-proxy`](../../../user/workspace/set_up_workspaces_proxy.md) handles discovery, authentication and authorization of the workspaces running in a Kubernetes cluster.
It will proxy all HTTP and WebSocket calls to the correct workspace. It will perform the following tasks:
diff --git a/doc/development/geo.md b/doc/development/geo.md
index 81390ffc492..f196e78a417 100644
--- a/doc/development/geo.md
+++ b/doc/development/geo.md
@@ -634,44 +634,6 @@ If a new feature introduces a new kind of data which is not a Git repository, or
As an example, container registry data does not easily fit into the above categories. It is backed by a registry service which owns the data, and GitLab interacts with the registry service's API. So a one off approach is required for Geo support of container registry. Still, we are able to reuse much of the glue code of [the Geo self-service framework](geo/framework.md#repository-replicator-strategy).
-## History of communication channel
-
-The communication channel has changed since first iteration, you can
-check here historic decisions and why we moved to new implementations.
-
-### Custom code (GitLab 8.6 and earlier)
-
-In GitLab versions before 8.6, custom code is used to handle
-notification from **primary** site to **secondary** sites by HTTP
-requests.
-
-### System hooks (GitLab 8.7 to 9.5)
-
-Later, it was decided to move away from custom code and begin using
-system hooks. More people were using them, so
-many would benefit from improvements made to this communication layer.
-
-There is a specific **internal** endpoint in our API code (Grape),
-that receives all requests from this System Hooks:
-`/api/v4/geo/receive_events`.
-
-We switch and filter from each event by the `event_name` field.
-
-### Geo Log Cursor (GitLab 10.0 and up)
-
-In GitLab 10.0 and later, [System Webhooks](#system-hooks-gitlab-87-to-95) are no longer
-used and [Geo Log Cursor](#geo-log-cursor-daemon) is used instead. The Log Cursor traverses the
-`Geo::EventLog` rows to see if there are changes since the last time
-the log was checked and will handle repository updates, deletes,
-changes, and renames.
-
-The table is within the replicated database. This has two advantages over the
-old method:
-
-- Replication is synchronous and we preserve the order of events.
-- Replication of the events happen at the same time as the changes in the
- database.
-
## Self-service framework
If you want to add easy Geo replication of a resource you're working
diff --git a/doc/drawers/advanced_search_syntax.md b/doc/drawers/advanced_search_syntax.md
index 6f308d3c6b6..275dc26edab 100644
--- a/doc/drawers/advanced_search_syntax.md
+++ b/doc/drawers/advanced_search_syntax.md
@@ -5,7 +5,7 @@ info: "To determine the technical writer assigned to the Stage/Group associated
source: /doc/user/search/advanced_search.md
---
-# Search tips
+# Syntax options
diff --git a/doc/drawers/exact_code_search_syntax.md b/doc/drawers/exact_code_search_syntax.md
index ff425080ee2..83935ed29da 100644
--- a/doc/drawers/exact_code_search_syntax.md
+++ b/doc/drawers/exact_code_search_syntax.md
@@ -5,7 +5,7 @@ info: "To determine the technical writer assigned to the Stage/Group associated
source: /doc/user/search/exact_code_search.md
---
-# Search tips
+# Syntax options
| Query | Regular expression mode | Exact match mode |
| -------------------- | ----------------------------------------------------- | ------------------------------ |
diff --git a/doc/update/versions/gitlab_14_changes.md b/doc/update/versions/gitlab_14_changes.md
index ec8d886c5c4..3b00a5c7ee8 100644
--- a/doc/update/versions/gitlab_14_changes.md
+++ b/doc/update/versions/gitlab_14_changes.md
@@ -229,7 +229,7 @@ DETAILS:
results in a loop that consistently fails for all objects stored in object storage.
For information on how to fix this, see
- [Troubleshooting - Failed syncs with GitLab-managed object storage replication](../../administration/geo/replication/troubleshooting/synchronization.md#failed-syncs-with-gitlab-managed-object-storage-replication).
+ [Troubleshooting - Failed syncs with GitLab-managed object storage replication](https://archives.docs.gitlab.com/14.10/ee/administration/geo/replication/troubleshooting#failed-syncs-with-gitlab-managed-object-storage-replication).
## 14.6.0
@@ -255,7 +255,7 @@ DETAILS:
results in a loop that consistently fails for all objects stored in object storage.
For information on how to fix this, see
- [Troubleshooting - Failed syncs with GitLab-managed object storage replication](../../administration/geo/replication/troubleshooting/synchronization.md#failed-syncs-with-gitlab-managed-object-storage-replication).
+ [Troubleshooting - Failed syncs with GitLab-managed object storage replication](https://archives.docs.gitlab.com/14.10/ee/administration/geo/replication/troubleshooting#failed-syncs-with-gitlab-managed-object-storage-replication).
## 14.5.0
@@ -340,7 +340,7 @@ DETAILS:
results in a loop that consistently fails for all objects stored in object storage.
For information on how to fix this, see
- [Troubleshooting - Failed syncs with GitLab-managed object storage replication](../../administration/geo/replication/troubleshooting/synchronization.md#failed-syncs-with-gitlab-managed-object-storage-replication).
+ [Troubleshooting - Failed syncs with GitLab-managed object storage replication](https://archives.docs.gitlab.com/14.10/ee/administration/geo/replication/troubleshooting#failed-syncs-with-gitlab-managed-object-storage-replication).
## 14.4.4
@@ -424,7 +424,7 @@ DETAILS:
results in a loop that consistently fails for all objects stored in object storage.
For information on how to fix this, see
- [Troubleshooting - Failed syncs with GitLab-managed object storage replication](../../administration/geo/replication/troubleshooting/synchronization.md#failed-syncs-with-gitlab-managed-object-storage-replication).
+ [Troubleshooting - Failed syncs with GitLab-managed object storage replication](https://archives.docs.gitlab.com/14.10/ee/administration/geo/replication/troubleshooting#failed-syncs-with-gitlab-managed-object-storage-replication).
- There is [an issue in GitLab 14.4.0 through 14.4.2](#1440) that can affect
Geo and other features that rely on cronjobs. We recommend upgrading to GitLab 14.4.3 or later.
@@ -594,7 +594,7 @@ DETAILS:
results in a loop that consistently fails for all objects stored in object storage.
For information on how to fix this, see
- [Troubleshooting - Failed syncs with GitLab-managed object storage replication](../../administration/geo/replication/troubleshooting/synchronization.md#failed-syncs-with-gitlab-managed-object-storage-replication).
+ [Troubleshooting - Failed syncs with GitLab-managed object storage replication](https://archives.docs.gitlab.com/14.10/ee/administration/geo/replication/troubleshooting#failed-syncs-with-gitlab-managed-object-storage-replication).
- We found an [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/336013) where the container registry replication
wasn't fully working if you used multi-arch images. In case of a multi-arch image, only the primary architecture
@@ -699,7 +699,7 @@ DETAILS:
results in a loop that consistently fails for all objects stored in object storage.
For information on how to fix this, see
- [Troubleshooting - Failed syncs with GitLab-managed object storage replication](../../administration/geo/replication/troubleshooting/synchronization.md#failed-syncs-with-gitlab-managed-object-storage-replication).
+ [Troubleshooting - Failed syncs with GitLab-managed object storage replication](https://archives.docs.gitlab.com/14.10/ee/administration/geo/replication/troubleshooting#failed-syncs-with-gitlab-managed-object-storage-replication).
- We found an [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/336013) where the container registry replication
wasn't fully working if you used multi-arch images. In case of a multi-arch image, only the primary architecture
diff --git a/doc/user/product_analytics/index.md b/doc/user/product_analytics/index.md
index e849357aa19..f7825718ecd 100644
--- a/doc/user/product_analytics/index.md
+++ b/doc/user/product_analytics/index.md
@@ -395,6 +395,14 @@ The current month displays events counted to date.
The usage quota excludes projects that are not onboarded with product analytics.
+## Best practices
+
+- Define key metrics and goals from the start. Decide what questions you want to answer so you know how to use collected data.
+- Use event data from all stages of the user journey. This data provides a comprehensive view of the user experience.
+- Build dashboards aligned with team needs. Different teams need different data insights.
+- Review dashboards regularly. This way, you can verify customer outcomes, identify trends in data, and update visualizations.
+- Export raw data periodically. Dashboards provide only an overview of a subset of data, so you should export the data for a deeper analysis.
+
## Troubleshooting
### No events are collected
diff --git a/doc/user/workspace/configuration.md b/doc/user/workspace/configuration.md
index 646002a5b52..c2d691d7ba0 100644
--- a/doc/user/workspace/configuration.md
+++ b/doc/user/workspace/configuration.md
@@ -35,7 +35,7 @@ which you can customize to meet the specific needs of each project.
- In development environments, add an entry to the `/etc/hosts` file or update your DNS records.
- In production environments, point `*.` and ``
to the load balancer exposed by the Ingress controller.
- - [Install `gitlab-workspaces-proxy`](https://gitlab.com/gitlab-org/remote-development/gitlab-workspaces-proxy#installation-instructions).
+ - [Set up the GitLab workspaces proxy](set_up_workspaces_proxy.md).
- [Install](../clusters/agent/install/index.md) and [configure](gitlab_agent_configuration.md) the GitLab agent.
- You must have at least the Developer role in the root group.
- In each project you want to use this feature for, create a [devfile](index.md#devfile):
@@ -76,7 +76,7 @@ You also have access to the terminal and can install any necessary dependencies.
Prerequisites:
- SSH must be enabled for the workspace.
-- You must have a TCP load balancer that points to [`gitlab-workspaces-proxy`](https://gitlab.com/gitlab-org/remote-development/gitlab-workspaces-proxy).
+- You must have a TCP load balancer that points to the [GitLab workspaces proxy](set_up_workspaces_proxy.md).
To connect to a workspace with an SSH client:
@@ -94,13 +94,13 @@ When you connect to `gitlab-workspaces-proxy` through the TCP load balancer,
- The personal access token
- User access to the workspace
-### Set up `gitlab-workspaces-proxy` for SSH connections
+### Set up the GitLab workspaces proxy for SSH connections
Prerequisites:
- You must have an SSH host key for client verification.
-SSH is now enabled by default in [`gitlab-workspaces-proxy`](https://gitlab.com/gitlab-org/remote-development/gitlab-workspaces-proxy).
+SSH is now enabled by default in the [GitLab workspaces proxy](set_up_workspaces_proxy.md).
To set up `gitlab-workspaces-proxy` with the GitLab Helm chart:
1. Run this command:
diff --git a/doc/user/workspace/gitlab_agent_configuration.md b/doc/user/workspace/gitlab_agent_configuration.md
index 0808c3ab0c4..8c10d073f25 100644
--- a/doc/user/workspace/gitlab_agent_configuration.md
+++ b/doc/user/workspace/gitlab_agent_configuration.md
@@ -28,7 +28,7 @@ provided that the agent is properly configured for remote development.
|-------------------------------------------------------------------------------------------|----------|-----------------------------------------|-------------|
| [`enabled`](#enabled) | Yes | `false` | Indicates whether remote development is enabled for the GitLab agent. |
| [`dns_zone`](#dns_zone) | Yes | None | DNS zone where workspaces are available. |
-| [`gitlab_workspaces_proxy`](#gitlab_workspaces_proxy) | No | `gitlab-workspaces` | Namespace where [`gitlab-workspaces-proxy`](https://gitlab.com/gitlab-org/remote-development/gitlab-workspaces-proxy) is installed. |
+| [`gitlab_workspaces_proxy`](#gitlab_workspaces_proxy) | No | `gitlab-workspaces` | Namespace where [`gitlab-workspaces-proxy`](set_up_workspaces_proxy.md) is installed. |
| [`network_policy`](#network_policy) | No | See [`network_policy`](#network_policy) | Firewall rules for workspaces. |
| [`default_resources_per_workspace_container`](#default_resources_per_workspace_container) | No | `{}` | Default requests and limits for CPU and memory per workspace container. |
| [`max_resources_per_workspace`](#max_resources_per_workspace) | No | `{}` | Maximum requests and limits for CPU and memory per workspace. |
@@ -71,7 +71,7 @@ remote_development:
### `gitlab_workspaces_proxy`
Use this setting to define the namespace where
-[`gitlab-workspaces-proxy`](https://gitlab.com/gitlab-org/remote-development/gitlab-workspaces-proxy) is installed.
+[`gitlab-workspaces-proxy`](set_up_workspaces_proxy.md) is installed.
The default value for `gitlab_workspaces_proxy.namespace` is `gitlab-workspaces`.
**Example configuration:**
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index 4d298b60800..3e5b930bc84 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -68,8 +68,8 @@ module API
optional :disable_feed_token, type: Boolean, desc: 'Disable display of RSS/Atom and Calendar `feed_tokens`'
optional :disabled_oauth_sign_in_sources, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Disable certain OAuth sign-in sources'
optional :domain_denylist_enabled, type: Boolean, desc: 'Enable domain denylist for sign ups'
- optional :domain_denylist, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com'
- optional :domain_allowlist, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'ONLY users with e-mail addresses that match these domain(s) will be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com'
+ optional :domain_denylist, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Enter multiple entries on separate lines. Ex: domain.com, *.domain.com'
+ optional :domain_allowlist, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'ONLY users with e-mail addresses that match these domain(s) will be able to sign-up. Wildcards allowed. Enter multiple entries on separate lines. Ex: domain.com, *.domain.com'
optional :eks_integration_enabled, type: Boolean, desc: 'Enable integration with Amazon EKS'
given eks_integration_enabled: -> (val) { val } do
requires :eks_account_id, type: String, desc: 'Amazon account ID for EKS integration'
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 35cc186c6ab..e4169091af5 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -6307,7 +6307,7 @@ msgstr ""
msgid "ApplicationSettings|Off"
msgstr ""
-msgid "ApplicationSettings|Only users with e-mail addresses that match these domain(s) can sign up. Wildcards allowed. Use separate lines for multiple entries. Example: domain.com, *.domain.com"
+msgid "ApplicationSettings|Only users with e-mail addresses that match these domain(s) can sign up. Wildcards allowed. Enter multiple entries on separate lines. Example: domain.com, *.domain.com"
msgstr ""
msgid "ApplicationSettings|Require admin approval for new sign-ups"
@@ -6361,7 +6361,7 @@ msgstr ""
msgid "ApplicationSettings|User cap"
msgstr ""
-msgid "ApplicationSettings|Users with e-mail addresses that match these domain(s) cannot sign up. Wildcards allowed. Use separate lines for multiple entries. Example: domain.com, *.domain.com"
+msgid "ApplicationSettings|Users with e-mail addresses that match these domain(s) cannot sign up. Wildcards allowed. Enter multiple entries on separate lines. Example: domain.com, *.domain.com"
msgstr ""
msgid "ApplicationSettings|Users with e-mail addresses that match these domain(s) cannot sign up. Wildcards allowed. Use separate lines or commas for multiple entries."
@@ -17926,6 +17926,9 @@ msgstr ""
msgid "Deployment|Approved"
msgstr ""
+msgid "Deployment|Approved as %{role}"
+msgstr ""
+
msgid "Deployment|Branch"
msgstr ""
@@ -17992,6 +17995,9 @@ msgstr ""
msgid "Deployment|Rejected"
msgstr ""
+msgid "Deployment|Rejected as %{role}"
+msgstr ""
+
msgid "Deployment|Related Tags"
msgstr ""
@@ -36415,6 +36421,9 @@ msgstr ""
msgid "PackageRegistry|Debian"
msgstr ""
+msgid "PackageRegistry|Default"
+msgstr ""
+
msgid "PackageRegistry|Delete %{count} assets"
msgstr ""
@@ -36463,6 +36472,9 @@ msgstr ""
msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
msgstr ""
+msgid "PackageRegistry|Error"
+msgstr ""
+
msgid "PackageRegistry|Error publishing"
msgstr ""
@@ -36520,6 +36532,9 @@ msgstr ""
msgid "PackageRegistry|Helm"
msgstr ""
+msgid "PackageRegistry|Hidden"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -36621,6 +36636,9 @@ msgstr ""
msgid "PackageRegistry|Packages deleted successfully"
msgstr ""
+msgid "PackageRegistry|Pending deletion"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -36630,6 +36648,9 @@ msgstr ""
msgid "PackageRegistry|Pip Command"
msgstr ""
+msgid "PackageRegistry|Processing"
+msgstr ""
+
msgid "PackageRegistry|Project-level"
msgstr ""
@@ -43883,6 +43904,9 @@ msgstr ""
msgid "Runners|%{linkStart}Create a new runner%{linkEnd} to get started."
msgstr ""
+msgid "Runners|%{linkStart}View available zones%{linkEnd}. Must have the format %{format}. Example: %{example}."
+msgstr ""
+
msgid "Runners|%{link_start}These runners%{link_end} are available to all groups and projects."
msgstr ""
@@ -44177,7 +44201,7 @@ msgstr ""
msgid "Runners|Fleet dashboard"
msgstr ""
-msgid "Runners|For most CI/CD jobs, use a %{linkStart}N2D standard machine type%{linkEnd}."
+msgid "Runners|For most CI/CD jobs, use a %{linkStart}N2D standard machine type%{linkEnd}. Must have the format %{format}. Example: %{example}."
msgstr ""
msgid "Runners|Get started with runners"
@@ -44279,7 +44303,7 @@ msgstr ""
msgid "Runners|Machine type is required."
msgstr ""
-msgid "Runners|Machine type must have the format `family-series-size`. Example: n2d-standard-2"
+msgid "Runners|Machine type must have the right format."
msgstr ""
msgid "Runners|Machine type with preset amounts of virtual machines processors (vCPUs) and memory"
@@ -44315,6 +44339,9 @@ msgstr ""
msgid "Runners|Most used instance runners"
msgstr ""
+msgid "Runners|Must have the format %{format}. Example: %{example}."
+msgstr ""
+
msgid "Runners|Never contacted"
msgstr ""
@@ -44415,7 +44442,7 @@ msgstr ""
msgid "Runners|Project ID is required."
msgstr ""
-msgid "Runners|Project ID must be 6 to 30 lowercase letters, digits, or hyphens. It needs to start with a lowercase letter and end with a letter or number."
+msgid "Runners|Project ID must have the right format."
msgstr ""
msgid "Runners|Project runners"
@@ -44442,7 +44469,7 @@ msgstr ""
msgid "Runners|Region is required."
msgstr ""
-msgid "Runners|Region must have the correct format. Example: us-central1"
+msgid "Runners|Region must have the right format."
msgstr ""
msgid "Runners|Register"
@@ -44758,7 +44785,7 @@ msgstr ""
msgid "Runners|To follow the setup instructions, %{gcloudLinkStart}install the Google Cloud CLI%{gcloudLinkEnd} and %{terraformLinkStart}install Terraform%{terraformLinkEnd}."
msgstr ""
-msgid "Runners|To improve security, use a dedicated project for CI/CD, separate from resources and identity management projects. %{linkStart}Where’s my project ID in Google Cloud?%{linkEnd}"
+msgid "Runners|To improve security, use a project just for CI/CD. %{linkStart}Where's my project ID?%{linkEnd} Can be 6 to 30 lowercase letters, digits, or hyphens. Must start with a letter and end with a letter or number. Example: %{example}."
msgstr ""
msgid "Runners|To install Runner in Kubernetes follow the instructions described in the GitLab documentation."
@@ -44845,9 +44872,6 @@ msgstr ""
msgid "Runners|Version starts with"
msgstr ""
-msgid "Runners|View available zones"
-msgstr ""
-
msgid "Runners|View installation instructions"
msgstr ""
@@ -44902,7 +44926,7 @@ msgstr ""
msgid "Runners|Zone is required."
msgstr ""
-msgid "Runners|Zone must have the correct format. Example: us-central1-a"
+msgid "Runners|Zone must have the right format."
msgstr ""
msgid "Runners|active"
diff --git a/scripts/process_custom_semgrep_results.rb b/scripts/process_custom_semgrep_results.rb
index 35aba649f7c..8272d663c09 100755
--- a/scripts/process_custom_semgrep_results.rb
+++ b/scripts/process_custom_semgrep_results.rb
@@ -4,6 +4,8 @@ require 'json'
require 'net/http'
require 'uri'
+# rubocop:disable Layout/LineLength -- we need to construct the URLs
+
# Validate CI_PROJECT_DIR and CI_API_V4_URL against the
# allowlist to protect against pipeline attacks
ALLOWED_PROJECT_DIRS = %w[/builds/gitlab-org/gitlab].freeze
@@ -19,6 +21,25 @@ unless ALLOWED_API_URLS.include?(ENV['CI_API_V4_URL'])
exit 1
end
+def ping_appsec(message)
+ uri = URI.parse("#{ENV['CI_API_V4_URL']}/projects/#{ENV['CI_MERGE_REQUEST_PROJECT_ID']}/merge_requests/#{ENV['CI_MERGE_REQUEST_IID']}/discussions?body=#{message}")
+
+ http = Net::HTTP.new(uri.host, uri.port)
+ http.use_ssl = (uri.scheme == 'https')
+
+ request = Net::HTTP::Post.new(uri.request_uri)
+ request['PRIVATE-TOKEN'] = ENV['CUSTOM_SAST_RULES_BOT_PAT']
+
+ response = http.request(request)
+
+ return if response.instance_of?(Net::HTTPCreated)
+
+ puts "Failed to ping AppSec #{response.code}: #{response.body}"
+ # if we cannot even ping appsec, fail the pipeline
+ # change this to `exit 1` when specs are ready
+ exit 0
+end
+
# Load SAST report
artifact_relative_path = "#{ENV['CI_PROJECT_DIR']}/gl-sast-report.json"
raw_data = File.read(artifact_relative_path)
@@ -26,6 +47,11 @@ data = JSON.parse(raw_data)
path_line_message_dict = {}
+if data["results"].empty?
+ puts "No findings."
+ exit 0
+end
+
# Extract findings from SAST report
results = data["results"]
results.each do |result|
@@ -36,9 +62,7 @@ results.each do |result|
end
# Retrieve existing comments on the merge request
-# rubocop:disable Layout/LineLength -- we need to construct the URL
notes_url = URI.parse("#{ENV['CI_API_V4_URL']}/projects/#{ENV['CI_MERGE_REQUEST_PROJECT_ID']}/merge_requests/#{ENV['CI_MERGE_REQUEST_IID']}/notes")
-# rubocop:enable Layout/LineLength
request = Net::HTTP::Get.new(notes_url)
request["PRIVATE-TOKEN"] = ENV['CUSTOM_SAST_RULES_BOT_PAT']
@@ -58,8 +82,10 @@ end
# Identify and remove duplicate findings
existing_comments.each do |comment|
- next unless comment['author']['id'] == ENV['BOT_USER_ID']
+ next unless comment['author']['id'].to_s == ENV['BOT_USER_ID'].to_s
+ next unless comment['type'] == 'DiffNote'
+ puts "existing comment from BOT: #{comment}"
existing_path = comment['position']['new_path']
existing_line = comment['position']['new_line']
existing_message = comment['body']
@@ -67,22 +93,47 @@ existing_comments.each do |comment|
message: existing_message }
end
+# Fetch base_commit_sha, head_commit_sha and
+# start_commit_sha required for creating inline comment
+versions_url = URI.parse("#{ENV['CI_API_V4_URL']}/projects/#{ENV['CI_MERGE_REQUEST_PROJECT_ID']}/merge_requests/#{ENV['CI_MERGE_REQUEST_IID']}/versions")
+
+request = Net::HTTP::Get.new(versions_url)
+request["PRIVATE-TOKEN"] = ENV['CUSTOM_SAST_RULES_BOT_PAT']
+
+response = Net::HTTP.start(versions_url.hostname, versions_url.port, use_ssl: versions_url.scheme == 'https') do |http|
+ http.request(request)
+end
+
+if response.instance_of?(Net::HTTPOK)
+ commits = JSON.parse(response.body)[0]
+else
+ puts "Failed to fetch versions with status code #{response.code}: #{response.body}"
+ ping_appsec "Failed to fetch versions: #{response.body}. /cc @gitlab-com/gl-security/appsec for visibility."
+
+ exit 0
+end
+
+base_sha = commits['base_commit_sha']
+head_sha = commits['head_commit_sha']
+start_sha = commits['start_commit_sha']
+
# Create new comments for remaining findings
path_line_message_dict.each do |path, info|
new_path = old_path = path
- new_line = old_line = info[:line]
+ new_line = info[:line]
message = info[:message]
- # rubocop:disable Layout/LineLength -- we need to construct the URL
uri = URI.parse("#{ENV['CI_API_V4_URL']}/projects/#{ENV['CI_MERGE_REQUEST_PROJECT_ID']}/merge_requests/#{ENV['CI_MERGE_REQUEST_IID']}/discussions")
- # rubocop:enable Layout/LineLength
+
request = Net::HTTP::Post.new(uri)
request["PRIVATE-TOKEN"] = ENV['CUSTOM_SAST_RULES_BOT_PAT']
request.set_form_data(
"position[position_type]" => "text",
+ "position[base_sha]" => base_sha,
+ "position[head_sha]" => head_sha,
+ "position[start_sha]" => start_sha,
"position[new_path]" => new_path,
"position[old_path]" => old_path,
"position[new_line]" => new_line,
- "position[old_line]" => old_line,
"body" => message
)
@@ -98,24 +149,4 @@ path_line_message_dict.each do |path, info|
exit 0
end
-
-def ping_appsec(message)
- # rubocop:disable Layout/LineLength -- we need to construct the URL
- uri = URI.parse("#{ENV['CI_API_V4_URL']}/projects/#{ENV['CI_MERGE_REQUEST_PROJECT_ID']}/merge_requests/#{ENV['CI_MERGE_REQUEST_IID']}/discussions?body=#{message}")
- # rubocop:enable Layout/LineLength
-
- http = Net::HTTP.new(uri.host, uri.port)
- http.use_ssl = (uri.scheme == 'https')
-
- request = Net::HTTP::Post.new(uri.request_uri)
- request['PRIVATE-TOKEN'] = ENV['CUSTOM_SAST_RULES_BOT_PAT']
-
- response = http.request(request)
-
- return if response.instance_of?(Net::HTTPCreated)
-
- puts "Failed to ping AppSec #{response.code}: #{response.body}"
- # if we cannot even ping appsec, fail the pipeline
- # change this to `exit 1` when specs are ready
- exit 0
-end
+# rubocop:enable Layout/LineLength
diff --git a/spec/frontend/bootstrap_linked_tabs_spec.js b/spec/frontend/bootstrap_linked_tabs_spec.js
deleted file mode 100644
index f900cd9da3b..00000000000
--- a/spec/frontend/bootstrap_linked_tabs_spec.js
+++ /dev/null
@@ -1,71 +0,0 @@
-import htmlLinkedTabs from 'test_fixtures_static/linked_tabs.html';
-import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
-import LinkedTabs from '~/lib/utils/bootstrap_linked_tabs';
-
-describe('Linked Tabs', () => {
- beforeEach(() => {
- setHTMLFixture(htmlLinkedTabs);
- });
-
- afterEach(() => {
- resetHTMLFixture();
- });
-
- describe('when is initialized', () => {
- beforeEach(() => {
- jest.spyOn(window.history, 'replaceState').mockImplementation(() => {});
- });
-
- it('should activate the tab correspondent to the given action', () => {
- // eslint-disable-next-line no-new
- new LinkedTabs({
- action: 'tab1',
- defaultAction: 'tab1',
- parentEl: '.linked-tabs',
- });
-
- expect(document.querySelector('#tab1').classList).toContain('active');
- });
-
- it('should active the default tab action when the action is show', () => {
- // eslint-disable-next-line no-new
- new LinkedTabs({
- action: 'show',
- defaultAction: 'tab1',
- parentEl: '.linked-tabs',
- });
-
- expect(document.querySelector('#tab1').classList).toContain('active');
- });
- });
-
- describe('on click', () => {
- it('should change the url according to the clicked tab', () => {
- const historySpy = jest.spyOn(window.history, 'replaceState').mockImplementation(() => {});
-
- const linkedTabs = new LinkedTabs({
- action: 'show',
- defaultAction: 'tab1',
- parentEl: '.linked-tabs',
- });
-
- const secondTab = document.querySelector('.linked-tabs li:nth-child(2) a');
- const newState =
- secondTab.getAttribute('href') +
- linkedTabs.currentLocation.search +
- linkedTabs.currentLocation.hash;
-
- secondTab.click();
-
- if (historySpy) {
- expect(historySpy).toHaveBeenCalledWith(
- {
- url: newState,
- },
- document.title,
- newState,
- );
- }
- });
- });
-});
diff --git a/spec/frontend/ci/runner/components/registration/google_cloud_registration_instructions_spec.js b/spec/frontend/ci/runner/components/registration/google_cloud_registration_instructions_spec.js
index 2de180031c8..1695c18e3d0 100644
--- a/spec/frontend/ci/runner/components/registration/google_cloud_registration_instructions_spec.js
+++ b/spec/frontend/ci/runner/components/registration/google_cloud_registration_instructions_spec.js
@@ -251,13 +251,13 @@ describe('GoogleCloudRegistrationInstructions', () => {
${'correct'} | ${'correct-project-name'} | ${undefined} | ${''}
${'correct'} | ${'correct-project-name-1'} | ${undefined} | ${''}
${'correct'} | ${'project'} | ${undefined} | ${''}
- ${'invalid (too short)'} | ${'short'} | ${'true'} | ${'Project ID must be'}
- ${'invalid (starts with a number)'} | ${'1number'} | ${'true'} | ${'Project ID must be'}
- ${'invalid (starts with uppercase)'} | ${'Project'} | ${'true'} | ${'Project ID must be'}
- ${'invalid (contains uppercase)'} | ${'pRoject'} | ${'true'} | ${'Project ID must be'}
- ${'invalid (contains symbol)'} | ${'pro!ect'} | ${'true'} | ${'Project ID must be'}
- ${'invalid (too long)'} | ${'a-project-name-that-is-too-long'} | ${'true'} | ${'Project ID must be'}
- ${'invalid (ends with hyphen)'} | ${'a-project-'} | ${'true'} | ${'Project ID must be'}
+ ${'invalid (too short)'} | ${'short'} | ${'true'} | ${'Project ID must have'}
+ ${'invalid (starts with a number)'} | ${'1number'} | ${'true'} | ${'Project ID must have'}
+ ${'invalid (starts with uppercase)'} | ${'Project'} | ${'true'} | ${'Project ID must have'}
+ ${'invalid (contains uppercase)'} | ${'pRoject'} | ${'true'} | ${'Project ID must have'}
+ ${'invalid (contains symbol)'} | ${'pro!ect'} | ${'true'} | ${'Project ID must have'}
+ ${'invalid (too long)'} | ${'a-project-name-that-is-too-long'} | ${'true'} | ${'Project ID must have'}
+ ${'invalid (ends with hyphen)'} | ${'a-project-'} | ${'true'} | ${'Project ID must have'}
${'invalid (missing)'} | ${''} | ${'true'} | ${'Project ID is required'}
`('"$input" as $case', async ({ input, ariaInvalid, feedback }) => {
await fillInTextField(findProjectIdInput(), input);
diff --git a/spec/frontend/fixtures/static/linked_tabs.html b/spec/frontend/fixtures/static/linked_tabs.html
deleted file mode 100644
index a362b01c033..00000000000
--- a/spec/frontend/fixtures/static/linked_tabs.html
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-Tab 1 Content
-
-
-Tab 2 Content
-
-
diff --git a/spec/frontend/lib/utils/favicon_ci_spec.js b/spec/frontend/lib/utils/favicon_ci_spec.js
deleted file mode 100644
index be647d98f1a..00000000000
--- a/spec/frontend/lib/utils/favicon_ci_spec.js
+++ /dev/null
@@ -1,51 +0,0 @@
-import MockAdapter from 'axios-mock-adapter';
-import axios from '~/lib/utils/axios_utils';
-import { setFaviconOverlay, resetFavicon } from '~/lib/utils/favicon';
-import { setCiStatusFavicon } from '~/lib/utils/favicon_ci';
-import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status';
-
-jest.mock('~/lib/utils/favicon');
-
-const TEST_URL = '/test/pipelinable/1';
-const TEST_FAVICON = '/favicon.test.ico';
-
-describe('~/lib/utils/favicon_ci', () => {
- let mock;
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- });
-
- afterEach(() => {
- mock.restore();
- mock = null;
- });
-
- describe('setCiStatusFavicon', () => {
- it.each`
- response | setFaviconOverlayCalls | resetFaviconCalls
- ${{}} | ${[]} | ${[[]]}
- ${{ favicon: TEST_FAVICON }} | ${[[TEST_FAVICON]]} | ${[]}
- `(
- 'with response=$response',
- async ({ response, setFaviconOverlayCalls, resetFaviconCalls }) => {
- mock.onGet(TEST_URL).replyOnce(HTTP_STATUS_OK, response);
-
- expect(setFaviconOverlay).not.toHaveBeenCalled();
- expect(resetFavicon).not.toHaveBeenCalled();
-
- await setCiStatusFavicon(TEST_URL);
-
- expect(setFaviconOverlay.mock.calls).toEqual(setFaviconOverlayCalls);
- expect(resetFavicon.mock.calls).toEqual(resetFaviconCalls);
- },
- );
-
- it('with error', async () => {
- mock.onGet(TEST_URL).replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR);
-
- await expect(setCiStatusFavicon(TEST_URL)).rejects.toEqual(expect.any(Error));
- expect(resetFavicon).toHaveBeenCalled();
- });
- });
-});
diff --git a/spec/frontend/observability/utils_spec.js b/spec/frontend/observability/utils_spec.js
index 38c14ed9ca1..1ed9cfb58a5 100644
--- a/spec/frontend/observability/utils_spec.js
+++ b/spec/frontend/observability/utils_spec.js
@@ -1,4 +1,10 @@
-import { periodToDate } from '~/observability/utils';
+import { periodToDate, dateFilterObjToQuery, queryToDateFilterObj } from '~/observability/utils';
+import {
+ CUSTOM_DATE_RANGE_OPTION,
+ DATE_RANGE_QUERY_KEY,
+ DATE_RANGE_START_QUERY_KEY,
+ DATE_RANGE_END_QUERY_KEY,
+} from '~/observability/constants';
describe('periodToDate', () => {
const realDateNow = Date.now;
@@ -33,3 +39,72 @@ describe('periodToDate', () => {
expect(periodToDate('2w')).toEqual({});
});
});
+
+describe('queryToDateFilterObj', () => {
+ it('returns default date range if no query params provided', () => {
+ expect(queryToDateFilterObj({})).toEqual({ value: '1h' });
+ });
+
+ it('returns query params with provided value', () => {
+ expect(
+ queryToDateFilterObj({
+ [DATE_RANGE_QUERY_KEY]: '7d',
+ }),
+ ).toEqual({ value: '7d' });
+ });
+
+ it('returns custom range if custom params provided', () => {
+ const query = {
+ [DATE_RANGE_QUERY_KEY]: CUSTOM_DATE_RANGE_OPTION,
+ [DATE_RANGE_START_QUERY_KEY]: '2020-01-01T00:00:00.000Z',
+ [DATE_RANGE_END_QUERY_KEY]: '2020-01-02T00:00:00.000Z',
+ };
+ expect(queryToDateFilterObj(query)).toEqual({
+ value: CUSTOM_DATE_RANGE_OPTION,
+ startDate: new Date('2020-01-01T00:00:00.000Z'),
+ endDate: new Date('2020-01-02T00:00:00.000Z'),
+ });
+ });
+
+ it('returns default range if custom params invalid', () => {
+ const query = {
+ [DATE_RANGE_QUERY_KEY]: CUSTOM_DATE_RANGE_OPTION,
+ [DATE_RANGE_START_QUERY_KEY]: 'invalid',
+ [DATE_RANGE_END_QUERY_KEY]: 'invalid',
+ };
+ expect(queryToDateFilterObj(query)).toEqual({ value: '1h' });
+ });
+});
+
+describe('dateFilterObjToQuery', () => {
+ it('converts a default date filter', () => {
+ expect(
+ dateFilterObjToQuery({
+ value: '7d',
+ }),
+ ).toEqual({
+ [DATE_RANGE_QUERY_KEY]: '7d',
+ });
+ });
+
+ it('converts custom filter', () => {
+ const filter = {
+ value: CUSTOM_DATE_RANGE_OPTION,
+ startDate: new Date('2020-01-01T00:00:00.000Z'),
+ endDate: new Date('2020-01-02T00:00:00.000Z'),
+ };
+ expect(dateFilterObjToQuery(filter)).toEqual({
+ [DATE_RANGE_QUERY_KEY]: CUSTOM_DATE_RANGE_OPTION,
+ [DATE_RANGE_START_QUERY_KEY]: '2020-01-01T00:00:00.000Z',
+ [DATE_RANGE_END_QUERY_KEY]: '2020-01-02T00:00:00.000Z',
+ });
+ });
+
+ it('returns empty object if filter is empty', () => {
+ expect(dateFilterObjToQuery({})).toEqual({});
+ });
+
+ it('returns empty object if filter undefined', () => {
+ expect(dateFilterObjToQuery()).toEqual({});
+ });
+});
diff --git a/spec/frontend/packages_and_registries/package_registry/components/list/packages_search_spec.js b/spec/frontend/packages_and_registries/package_registry/components/list/packages_search_spec.js
index eb5c546d2be..fa37d9b92eb 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/list/packages_search_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/components/list/packages_search_spec.js
@@ -2,8 +2,7 @@ import { nextTick } from 'vue';
import { GlFilteredSearchToken } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { sortableFields } from '~/packages_and_registries/package_registry/utils';
-import component from '~/packages_and_registries/package_registry/components/list/package_search.vue';
-import PackageTypeToken from '~/packages_and_registries/package_registry/components/list/tokens/package_type_token.vue';
+import PackageSearch from '~/packages_and_registries/package_registry/components/list/package_search.vue';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import PersistedSearch from '~/packages_and_registries/shared/components/persisted_search.vue';
import { LIST_KEY_CREATED_AT } from '~/packages_and_registries/package_registry/constants';
@@ -12,6 +11,8 @@ import {
OPERATORS_IS,
TOKEN_TYPE_TYPE,
TOKEN_TYPE_VERSION,
+ TOKEN_TITLE_STATUS,
+ TOKEN_TYPE_STATUS,
} from '~/vue_shared/components/filtered_search_bar/constants';
describe('Package Search', () => {
@@ -26,7 +27,7 @@ describe('Package Search', () => {
const findLocalStorageSync = () => wrapper.findComponent(LocalStorageSync);
const mountComponent = (isGroupPage = false) => {
- wrapper = shallowMountExtended(component, {
+ wrapper = shallowMountExtended(PackageSearch, {
provide() {
return {
isGroupPage,
@@ -88,15 +89,25 @@ describe('Package Search', () => {
expect(findPersistedSearch().props()).toMatchObject({
tokens: expect.arrayContaining([
expect.objectContaining({
- token: PackageTypeToken,
+ token: GlFilteredSearchToken,
type: TOKEN_TYPE_TYPE,
icon: 'package',
+ unique: true,
operators: OPERATORS_IS,
}),
expect.objectContaining({
token: GlFilteredSearchToken,
type: TOKEN_TYPE_VERSION,
icon: 'doc-versions',
+ unique: true,
+ operators: OPERATORS_IS,
+ }),
+ expect.objectContaining({
+ type: TOKEN_TYPE_STATUS,
+ icon: 'status',
+ title: TOKEN_TITLE_STATUS,
+ unique: true,
+ token: GlFilteredSearchToken,
operators: OPERATORS_IS,
}),
]),
@@ -123,11 +134,7 @@ describe('Package Search', () => {
expect(wrapper.emitted('update')[0]).toEqual([
{
- filters: {
- packageName: '',
- packageType: undefined,
- packageVersion: '',
- },
+ filters: {},
sort: payload.sort,
sorting: payload.sorting,
},
@@ -140,6 +147,7 @@ describe('Package Search', () => {
filters: [
{ type: 'type', value: { data: 'Generic', operator: '=' }, id: 'token-3' },
{ type: 'version', value: { data: '1.0.1', operator: '=' }, id: 'token-6' },
+ { type: 'status', value: { data: 'HIDDEN', operator: '=' }, id: 'token-7' },
{ id: 'token-4', type: 'filtered-search-term', value: { data: 'gl' } },
{ id: 'token-5', type: 'filtered-search-term', value: { data: '' } },
],
@@ -160,6 +168,7 @@ describe('Package Search', () => {
packageName: 'gl',
packageType: 'GENERIC',
packageVersion: '1.0.1',
+ packageStatus: 'HIDDEN',
},
sort: payload.sort,
sorting: payload.sorting,
diff --git a/spec/frontend/packages_and_registries/package_registry/components/list/tokens/package_type_token_spec.js b/spec/frontend/packages_and_registries/package_registry/components/list/tokens/package_type_token_spec.js
deleted file mode 100644
index c98f5f32344..00000000000
--- a/spec/frontend/packages_and_registries/package_registry/components/list/tokens/package_type_token_spec.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import { GlFilteredSearchToken, GlFilteredSearchSuggestion } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import component from '~/packages_and_registries/package_registry/components/list/tokens/package_type_token.vue';
-import { PACKAGE_TYPES } from '~/packages_and_registries/package_registry/constants';
-
-describe('packages_filter', () => {
- let wrapper;
-
- const findFilteredSearchToken = () => wrapper.findComponent(GlFilteredSearchToken);
- const findFilteredSearchSuggestions = () => wrapper.findAllComponents(GlFilteredSearchSuggestion);
-
- const mountComponent = ({ attrs, listeners } = {}) => {
- wrapper = shallowMount(component, {
- attrs: {
- cursorPosition: 'start',
- ...attrs,
- },
- listeners,
- });
- };
-
- it('binds all of his attrs to filtered search token', () => {
- mountComponent({ attrs: { foo: 'bar' } });
-
- expect(findFilteredSearchToken().attributes('foo')).toBe('bar');
- });
-
- it('binds all of his events to filtered search token', () => {
- const clickListener = jest.fn();
- mountComponent({ listeners: { click: clickListener } });
-
- findFilteredSearchToken().vm.$emit('click');
-
- expect(clickListener).toHaveBeenCalled();
- });
-
- it.each(PACKAGE_TYPES.map((p, index) => [p, index]))(
- 'displays a suggestion for %p',
- (packageType, index) => {
- mountComponent();
- const item = findFilteredSearchSuggestions().at(index);
- expect(item.text()).toBe(packageType);
- expect(item.props('value')).toBe(packageType);
- },
- );
-});
diff --git a/spec/frontend/packages_and_registries/package_registry/pages/list_spec.js b/spec/frontend/packages_and_registries/package_registry/pages/list_spec.js
index db86be3b8ee..bbb0626bad6 100644
--- a/spec/frontend/packages_and_registries/package_registry/pages/list_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/pages/list_spec.js
@@ -44,7 +44,12 @@ describe('PackagesListApp', () => {
const searchPayload = {
sort: 'VERSION_DESC',
- filters: { packageName: 'foo', packageType: 'CONAN', packageVersion: '1.0.1' },
+ filters: {
+ packageName: 'foo',
+ packageType: 'CONAN',
+ packageVersion: '1.0.1',
+ packageStatus: 'DEFAULT',
+ },
};
const findPackageTitle = () => wrapper.findComponent(PackageTitle);
diff --git a/spec/frontend/packages_and_registries/shared/utils_spec.js b/spec/frontend/packages_and_registries/shared/utils_spec.js
index 4676544c324..e08ab4004e6 100644
--- a/spec/frontend/packages_and_registries/shared/utils_spec.js
+++ b/spec/frontend/packages_and_registries/shared/utils_spec.js
@@ -41,21 +41,22 @@ describe('Packages And Registries shared utils', () => {
});
describe('extractFilterAndSorting', () => {
it.each`
- search | type | version | sort | orderBy | result
- ${['one']} | ${'myType'} | ${'1.0.1'} | ${'asc'} | ${'foo'} | ${{ sorting: { sort: 'asc', orderBy: 'foo' }, filters: [{ type: 'type', value: { data: 'myType' } }, { type: 'version', value: { data: '1.0.1' } }, { type: FILTERED_SEARCH_TERM, value: { data: 'one' } }] }}
- ${['one']} | ${null} | ${null} | ${'asc'} | ${'foo'} | ${{ sorting: { sort: 'asc', orderBy: 'foo' }, filters: [{ type: FILTERED_SEARCH_TERM, value: { data: 'one' } }] }}
- ${[]} | ${null} | ${null} | ${'asc'} | ${'foo'} | ${{ sorting: { sort: 'asc', orderBy: 'foo' }, filters: [] }}
- ${null} | ${null} | ${null} | ${'asc'} | ${'foo'} | ${{ sorting: { sort: 'asc', orderBy: 'foo' }, filters: [] }}
- ${null} | ${null} | ${null} | ${null} | ${'foo'} | ${{ sorting: { orderBy: 'foo' }, filters: [] }}
- ${null} | ${null} | ${null} | ${null} | ${null} | ${{ sorting: {}, filters: [] }}
+ search | type | version | status | sort | orderBy | result
+ ${['one']} | ${'myType'} | ${'1.0.1'} | ${'DEFAULT'} | ${'asc'} | ${'foo'} | ${{ sorting: { sort: 'asc', orderBy: 'foo' }, filters: [{ type: 'type', value: { data: 'myType' } }, { type: 'version', value: { data: '1.0.1' } }, { type: 'status', value: { data: 'DEFAULT' } }, { type: FILTERED_SEARCH_TERM, value: { data: 'one' } }] }}
+ ${['one']} | ${undefined} | ${undefined} | ${undefined} | ${'asc'} | ${'foo'} | ${{ sorting: { sort: 'asc', orderBy: 'foo' }, filters: [{ type: FILTERED_SEARCH_TERM, value: { data: 'one' } }] }}
+ ${[]} | ${undefined} | ${undefined} | ${undefined} | ${'asc'} | ${'foo'} | ${{ sorting: { sort: 'asc', orderBy: 'foo' }, filters: [] }}
+ ${undefined} | ${undefined} | ${undefined} | ${undefined} | ${'asc'} | ${'foo'} | ${{ sorting: { sort: 'asc', orderBy: 'foo' }, filters: [] }}
+ ${undefined} | ${undefined} | ${undefined} | ${undefined} | ${undefined} | ${'foo'} | ${{ sorting: { orderBy: 'foo' }, filters: [] }}
+ ${undefined} | ${undefined} | ${undefined} | ${undefined} | ${undefined} | ${undefined} | ${{ sorting: {}, filters: [] }}
`(
'returns sorting and filters objects in the correct form',
- ({ search, type, version, sort, orderBy, result }) => {
+ ({ search, type, version, sort, status, orderBy, result }) => {
const queryObject = {
search,
type,
version,
sort,
+ status,
orderBy,
};
expect(extractFilterAndSorting(queryObject)).toStrictEqual(result);
diff --git a/spec/graphql/mutations/issues/update_spec.rb b/spec/graphql/mutations/issues/update_spec.rb
index 622ccb86b2e..25b8c49063b 100644
--- a/spec/graphql/mutations/issues/update_spec.rb
+++ b/spec/graphql/mutations/issues/update_spec.rb
@@ -61,7 +61,7 @@ RSpec.describe Mutations::Issues::Update, feature_category: :team_planning do
context 'when setting milestone to nil' do
let(:expected_attributes) { { milestone_id: nil } }
- it 'changes the milestone corrrectly' do
+ it 'changes the milestone correctly' do
issue.update_column(:milestone_id, milestone.id)
expect { subject }.to change { issue.reload.milestone }.from(milestone).to(nil)
@@ -202,6 +202,20 @@ RSpec.describe Mutations::Issues::Update, feature_category: :team_planning do
it 'updates the time estimate' do
expect { subject }.to change { issue.reload.time_estimate }.from(3600).to(5400)
end
+
+ context 'when user is a guest' do
+ let_it_be(:guest) { create(:user) }
+ let(:user) { guest }
+
+ before do
+ issue.update!(author: guest)
+ project.add_guest(guest)
+ end
+
+ it 'does not change time_estimate' do
+ expect { subject }.not_to change { issue.reload.time_estimate }
+ end
+ end
end
end
end
diff --git a/spec/services/work_items/callbacks/time_tracking_spec.rb b/spec/services/issuable/callbacks/time_tracking_spec.rb
similarity index 95%
rename from spec/services/work_items/callbacks/time_tracking_spec.rb
rename to spec/services/issuable/callbacks/time_tracking_spec.rb
index 547e31494c4..3a4a2c7b502 100644
--- a/spec/services/work_items/callbacks/time_tracking_spec.rb
+++ b/spec/services/issuable/callbacks/time_tracking_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WorkItems::Callbacks::TimeTracking, feature_category: :team_planning do
+RSpec.describe Issuable::Callbacks::TimeTracking, feature_category: :team_planning do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :private, group: group) }
let_it_be(:reporter) do
@@ -38,8 +38,8 @@ RSpec.describe WorkItems::Callbacks::TimeTracking, feature_category: :team_plann
let(:callback) { described_class.new(issuable: issuable, current_user: current_user, params: params) }
describe '#after_initialize' do
- shared_examples 'raises a WidgetError' do
- it { expect { subject }.to raise_error(::WorkItems::Widgets::BaseService::WidgetError, message) }
+ shared_examples 'raises an Error' do
+ it { expect { subject }.to raise_error(::Issuable::Callbacks::Base::Error, message) }
end
shared_examples 'sets work item time tracking data' do
@@ -212,7 +212,7 @@ RSpec.describe WorkItems::Callbacks::TimeTracking, feature_category: :team_plann
context 'when time_estimate is invalid' do
let(:params) { { time_estimate: "12abc" } }
- it_behaves_like 'raises a WidgetError' do
+ it_behaves_like 'raises an Error' do
let(:message) { 'Time estimate must be formatted correctly. For example: 1h 30m.' }
end
end
@@ -220,7 +220,7 @@ RSpec.describe WorkItems::Callbacks::TimeTracking, feature_category: :team_plann
context 'when time_spent is invalid' do
let(:params) { { timelog: { time_spent: "2abc" } } }
- it_behaves_like 'raises a WidgetError' do
+ it_behaves_like 'raises an Error' do
let(:message) { 'Time spent must be formatted correctly. For example: 1h 30m.' }
end
end
diff --git a/spec/services/notes/quick_actions_service_spec.rb b/spec/services/notes/quick_actions_service_spec.rb
index 8160bbf33c7..10f6a264cfb 100644
--- a/spec/services/notes/quick_actions_service_spec.rb
+++ b/spec/services/notes/quick_actions_service_spec.rb
@@ -153,10 +153,11 @@ RSpec.describe Notes::QuickActionsService, feature_category: :team_planning do
shared_examples 'does not update time_estimate and displays the correct error message' do
it 'shows validation error message' do
- content = execute(note)
+ content, update_params = service.execute(note)
+ service_response = service.apply_updates(update_params, note)
expect(content).to be_empty
- expect(note.noteable.errors[:time_estimate]).to include('must have a valid format and be greater than or equal to zero.')
+ expect(service_response.message).to include('Time estimate must have a valid format and be greater than or equal to zero.')
expect(note.noteable.reload.time_estimate).to eq(600)
end
end
diff --git a/workhorse/internal/transport/transport.go b/workhorse/internal/transport/transport.go
index 87fa73985a9..ad4758fa969 100644
--- a/workhorse/internal/transport/transport.go
+++ b/workhorse/internal/transport/transport.go
@@ -1,3 +1,4 @@
+// Package transport provides a roundtripper for HTTP clients with Workhorse integration.
package transport
import (
@@ -11,12 +12,12 @@ import (
"gitlab.com/gitlab-org/gitlab/workhorse/internal/version"
)
-// Creates a new default transport that has Workhorse's User-Agent header set.
+// NewDefaultTransport creates a new default transport that has Workhorse's User-Agent header set.
func NewDefaultTransport() http.RoundTripper {
return &DefaultTransport{Next: http.DefaultTransport}
}
-// Defines a http.Transport with values that are more restrictive than for
+// NewRestrictedTransport defines a http.Transport with values that are more restrictive than for
// http.DefaultTransport, they define shorter TLS Handshake, and more
// aggressive connection closing to prevent the connection hanging and reduce
// FD usage
@@ -24,24 +25,29 @@ func NewRestrictedTransport(options ...Option) http.RoundTripper {
return &DefaultTransport{Next: newRestrictedTransport(options...)}
}
+// DefaultTransport is a roundtripper that sets the User-Agent header in requests
type DefaultTransport struct {
Next http.RoundTripper
}
+// RoundTrip sets the User-Agent header in the request and then forwards the request to the next RoundTripper.
func (t DefaultTransport) RoundTrip(req *http.Request) (*http.Response, error) {
req.Header.Set("User-Agent", version.GetUserAgent())
return t.Next.RoundTrip(req)
}
+// Option is a functional option to configure the restricted transport.
type Option func(*http.Transport)
+// WithDisabledCompression disables compression for the transport.
func WithDisabledCompression() Option {
return func(t *http.Transport) {
t.DisableCompression = true
}
}
+// WithDialTimeout sets the dial timeout for the transport.
func WithDialTimeout(timeout time.Duration) Option {
return func(t *http.Transport) {
t.DialContext = (&net.Dialer{
@@ -50,6 +56,7 @@ func WithDialTimeout(timeout time.Duration) Option {
}
}
+// WithResponseHeaderTimeout sets the response header timeout for the transport.
func WithResponseHeaderTimeout(timeout time.Duration) Option {
return func(t *http.Transport) {
t.ResponseHeaderTimeout = timeout