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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-09-15 21:11:45 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-09-15 21:11:45 +0300
commit7a15fb07cf363079c4c4683850ee131d80e75f75 (patch)
tree60932d6cd96a9df0678ec56eafe373ba8bff8beb
parentf357b1fa2f6a1a204f1ab9070e8a64d717c8960c (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/CODEOWNERS1
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--app/assets/javascripts/jira_connect/branches/components/new_branch_form.vue1
-rw-r--r--app/assets/javascripts/jira_connect/branches/components/source_branch_dropdown.vue54
-rw-r--r--app/assets/javascripts/jira_connect/branches/index.js1
-rw-r--r--app/assets/javascripts/search/sidebar/components/app.vue13
-rw-r--r--app/assets/javascripts/search/sidebar/components/archived_filter/data.js1
-rw-r--r--app/assets/javascripts/search/sidebar/components/notes_filters.vue18
-rw-r--r--app/assets/javascripts/search/sidebar/constants/index.js1
-rw-r--r--app/assets/javascripts/security_configuration/components/constants.js2
-rw-r--r--app/assets/javascripts/security_configuration/components/continuous_vulnerability_scan.vue127
-rw-r--r--app/assets/javascripts/security_configuration/components/feature_card.vue7
-rw-r--r--app/assets/javascripts/security_configuration/graphql/project_set_continuous_vulnerability_scanning.graphql8
-rw-r--r--app/assets/javascripts/security_configuration/index.js2
-rw-r--r--app/assets/javascripts/super_sidebar/components/nav_item.vue2
-rw-r--r--app/models/ci/build.rb8
-rw-r--r--app/models/concerns/chronic_duration_attribute.rb4
-rw-r--r--app/models/container_expiration_policy.rb4
-rw-r--r--app/services/projects/container_repository/cleanup_tags_base_service.rb4
-rw-r--r--app/validators/duration_validator.rb2
-rw-r--r--app/views/dashboard/todos/_todo.html.haml2
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml2
-rw-r--r--config/feature_flags/development/member_expiring_email_notification.yml2
-rw-r--r--config/feature_flags/development/update_chronic_duration.yml8
-rw-r--r--doc/.vale/gitlab/Substitutions.yml1
-rw-r--r--doc/api/dependency_list_export.md15
-rw-r--r--doc/api/graphql/reference/index.md3
-rw-r--r--doc/ci/environments/kubernetes_dashboard.md3
-rw-r--r--doc/ci/migration/plan_a_migration.md8
-rw-r--r--doc/ci/yaml/index.md21
-rw-r--r--doc/development/migration_style_guide.md42
-rw-r--r--doc/operations/error_tracking.md2
-rw-r--r--doc/operations/tracing.md8
-rw-r--r--doc/user/packages/composer_repository/index.md50
-rw-r--r--doc/user/profile/account/two_factor_authentication.md26
-rw-r--r--doc/user/project/repository/mirror/index.md3
-rw-r--r--doc/user/project/repository/mirror/pull.md5
-rw-r--r--doc/user/project/repository/mirror/push.md23
-rw-r--r--doc/user/usage_quotas.md23
-rw-r--r--lib/gitlab/ci/build/duration_parser.rb2
-rw-r--r--lib/gitlab/ci/config/entry/job.rb2
-rw-r--r--lib/gitlab/config/entry/legacy_validation_helpers.rb6
-rw-r--r--lib/gitlab/time_tracking_formatter.rb2
-rw-r--r--locale/gitlab.pot12
-rw-r--r--qa/qa/page/dashboard/todos.rb14
-rw-r--r--qa/qa/specs/features/shared_contexts/merge_train_spec_with_user_prep.rb80
-rw-r--r--spec/frontend/jira_connect/branches/components/source_branch_dropdown_spec.js146
-rw-r--r--spec/frontend/jira_connect/branches/mock_data.js15
-rw-r--r--spec/frontend/search/sidebar/components/app_spec.js74
-rw-r--r--spec/frontend/search/sidebar/components/notes_filters_spec.js28
-rw-r--r--spec/frontend/search/sidebar/components/projects_filters_spec.js (renamed from spec/frontend/search/sidebar/components/projects_filters_specs.js)2
-rw-r--r--spec/frontend/security_configuration/components/continuous_vulnerability_scan_spec.js124
-rw-r--r--spec/frontend/security_configuration/components/feature_card_spec.js18
-rw-r--r--spec/lib/gitlab/ci/config/entry/job_spec.rb12
-rw-r--r--spec/lib/gitlab/time_tracking_formatter_spec.rb46
-rw-r--r--spec/models/ci/build_spec.rb36
-rw-r--r--spec/models/concerns/chronic_duration_attribute_spec.rb30
-rw-r--r--spec/support/shared_examples/projects/container_repository/cleanup_tags_service_shared_examples.rb11
58 files changed, 756 insertions, 413 deletions
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS
index 5d6d218ef81..9dab471bb7c 100644
--- a/.gitlab/CODEOWNERS
+++ b/.gitlab/CODEOWNERS
@@ -1388,7 +1388,6 @@ lib/gitlab/checks/**
/lib/tasks/gitlab/seed/runner_fleet.rake @gitlab-org/ci-cd/runner-fleet-team/backend-approvers
# CI/CD templates require approval from specific owners.
/lib/gitlab/ci/templates/ @gitlab-org/maintainers/cicd-templates
-/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml @DylanGriffith @mayra-cabrera @tkuah
/lib/gitlab/ci/templates/Security/ @gonzoyumo @twoodham @amarpatel
/lib/gitlab/ci/templates/Security/API-Fuzzing.*.yml @gitlab-org/secure/dynamic-analysis
/lib/gitlab/ci/templates/Security/Container-Scanning.*.yml @gitlab-org/secure/composition-analysis-be
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index c4ed752e07b..28370ccb017 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-16cd7e7fa2bb9afdd7061ae6187f739dc2fe120f
+3958129f4b15a62ed6a11b8dbb7f8ba5134963db
diff --git a/app/assets/javascripts/jira_connect/branches/components/new_branch_form.vue b/app/assets/javascripts/jira_connect/branches/components/new_branch_form.vue
index 46c27c33f56..1d926c0d0c5 100644
--- a/app/assets/javascripts/jira_connect/branches/components/new_branch_form.vue
+++ b/app/assets/javascripts/jira_connect/branches/components/new_branch_form.vue
@@ -193,6 +193,7 @@ export default {
>
<source-branch-dropdown
id="source-branch-select"
+ :key="selectedProject.id"
:selected-project="selectedProject"
:selected-branch-name="selectedSourceBranchName"
@change="onSourceBranchSelect"
diff --git a/app/assets/javascripts/jira_connect/branches/components/source_branch_dropdown.vue b/app/assets/javascripts/jira_connect/branches/components/source_branch_dropdown.vue
index e443df35ad2..3f9dd4eb6c6 100644
--- a/app/assets/javascripts/jira_connect/branches/components/source_branch_dropdown.vue
+++ b/app/assets/javascripts/jira_connect/branches/components/source_branch_dropdown.vue
@@ -2,6 +2,8 @@
import { GlCollapsibleListbox } from '@gitlab/ui';
import { debounce } from 'lodash';
import { __ } from '~/locale';
+import { logError } from '~/lib/logger';
+
import { BRANCHES_PER_PAGE } from '../constants';
import getProjectQuery from '../graphql/queries/get_project.query.graphql';
@@ -26,6 +28,7 @@ export default {
return {
initialSourceBranchNamesLoading: false,
sourceBranchNamesLoading: false,
+ sourceBranchNamesLoadingMore: false,
sourceBranchNames: [],
};
},
@@ -36,6 +39,11 @@ export default {
hasSelectedSourceBranch() {
return Boolean(this.selectedBranchName);
},
+ hasMoreBranches() {
+ return (
+ this.sourceBranchNames.length > 0 && this.sourceBranchNames.length % BRANCHES_PER_PAGE === 0
+ );
+ },
branchDropdownText() {
return this.selectedBranchName || __('Select a branch');
},
@@ -59,45 +67,63 @@ export default {
onSearch: debounce(function debouncedSearch(branchSearchQuery) {
this.onSourceBranchSearchQuery(branchSearchQuery);
}, 250),
- onSourceBranchSearchQuery(branchSearchQuery) {
+ async onSourceBranchSearchQuery(branchSearchQuery) {
this.branchSearchQuery = branchSearchQuery;
- this.fetchSourceBranchNames({
+ this.sourceBranchNamesLoading = true;
+
+ await this.fetchSourceBranchNames({
+ projectPath: this.selectedProject.fullPath,
+ searchPattern: this.branchSearchQuery,
+ });
+ this.sourceBranchNamesLoading = false;
+ },
+ async onBottomReached() {
+ this.sourceBranchNamesLoadingMore = true;
+
+ await this.fetchSourceBranchNames({
projectPath: this.selectedProject.fullPath,
searchPattern: this.branchSearchQuery,
+ append: true,
});
+
+ this.sourceBranchNamesLoadingMore = false;
},
onError({ message } = {}) {
this.$emit('error', { message });
},
- async fetchSourceBranchNames({ projectPath, searchPattern } = {}) {
- this.sourceBranchNamesLoading = true;
+ async fetchSourceBranchNames({ projectPath, searchPattern, append = false } = {}) {
try {
const { data } = await this.$apollo.query({
query: getProjectQuery,
variables: {
projectPath,
branchNamesLimit: this.$options.BRANCHES_PER_PAGE,
- branchNamesOffset: 0,
+ branchNamesOffset: append ? this.sourceBranchNames.length : 0,
branchNamesSearchPattern: searchPattern ? `*${searchPattern}*` : '*',
},
});
const { branchNames, rootRef } = data?.project.repository || {};
- this.sourceBranchNames =
- branchNames.map((value) => {
+ const branchNameItems =
+ branchNames?.map((value) => {
return { text: value, value };
}) || [];
- // Use root ref as the default selection
- if (rootRef && !this.hasSelectedSourceBranch) {
- this.onSourceBranchSelect(rootRef);
+ if (append) {
+ this.sourceBranchNames.push(...branchNameItems);
+ } else {
+ this.sourceBranchNames = branchNameItems;
+
+ // Use root ref as the default selection
+ if (rootRef && !this.hasSelectedSourceBranch) {
+ this.onSourceBranchSelect(rootRef);
+ }
}
} catch (err) {
+ logError(err);
this.onError({
message: __('Something went wrong while fetching source branches.'),
});
- } finally {
- this.sourceBranchNamesLoading = false;
}
},
},
@@ -107,6 +133,7 @@ export default {
<template>
<gl-collapsible-listbox
:class="{ 'gl-font-monospace': hasSelectedSourceBranch }"
+ :selected="selectedBranchName"
:disabled="!hasSelectedProject"
:items="sourceBranchNames"
:loading="initialSourceBranchNamesLoading"
@@ -114,6 +141,9 @@ export default {
:searching="sourceBranchNamesLoading"
:toggle-text="branchDropdownText"
fluid-width
+ :infinite-scroll="hasMoreBranches"
+ :infinite-scroll-loading="sourceBranchNamesLoadingMore"
+ @bottom-reached="onBottomReached"
@search="onSearch"
@select="onSourceBranchSelect"
/>
diff --git a/app/assets/javascripts/jira_connect/branches/index.js b/app/assets/javascripts/jira_connect/branches/index.js
index a9a56a6362e..893b9dfa1c7 100644
--- a/app/assets/javascripts/jira_connect/branches/index.js
+++ b/app/assets/javascripts/jira_connect/branches/index.js
@@ -19,6 +19,7 @@ export default function initJiraConnectBranches() {
return new Vue({
el,
+ name: 'JiraConnectNewBranchRoot',
apolloProvider,
provide: {
initialBranchName,
diff --git a/app/assets/javascripts/search/sidebar/components/app.vue b/app/assets/javascripts/search/sidebar/components/app.vue
index da743f5c496..a2e857dc5e4 100644
--- a/app/assets/javascripts/search/sidebar/components/app.vue
+++ b/app/assets/javascripts/search/sidebar/components/app.vue
@@ -13,12 +13,14 @@ import {
SCOPE_MERGE_REQUESTS,
SCOPE_BLOB,
SCOPE_PROJECTS,
+ SCOPE_NOTES,
SEARCH_TYPE_ADVANCED,
} from '../constants';
import IssuesFilters from './issues_filters.vue';
import MergeRequestsFilters from './merge_requests_filters.vue';
import BlobsFilters from './blobs_filters.vue';
import ProjectsFilters from './projects_filters.vue';
+import NotesFilters from './notes_filters.vue';
export default {
name: 'GlobalSearchSidebar',
@@ -27,6 +29,7 @@ export default {
MergeRequestsFilters,
BlobsFilters,
ProjectsFilters,
+ NotesFilters,
ScopeLegacyNavigation,
ScopeSidebarNavigation,
SidebarPortal,
@@ -51,6 +54,13 @@ export default {
// for now the feature flag is here. Since we have only one filter in projects scope
return this.currentScope === SCOPE_PROJECTS;
},
+ showNotesFilters() {
+ return (
+ this.currentScope === SCOPE_NOTES &&
+ this.searchType === SEARCH_TYPE_ADVANCED &&
+ this.glFeatures.searchNotesHideArchivedProjects
+ );
+ },
showScopeNavigation() {
// showScopeNavigation refers to whether the scope navigation should be shown
// while the legacy navigation is being used and there are no search results
@@ -75,6 +85,7 @@ export default {
<merge-requests-filters v-if="showMergeRequestFilters" />
<blobs-filters v-if="showBlobFilters" />
<projects-filters v-if="showProjectsFilters" />
+ <notes-filters v-if="showNotesFilters" />
</sidebar-portal>
</section>
@@ -88,6 +99,7 @@ export default {
<merge-requests-filters v-if="showMergeRequestFilters" />
<blobs-filters v-if="showBlobFilters" />
<projects-filters v-if="showProjectsFilters" />
+ <notes-filters v-if="showNotesFilters" />
</div>
<small-screen-drawer-navigation class="gl-lg-display-none">
<scope-legacy-navigation />
@@ -95,6 +107,7 @@ export default {
<merge-requests-filters v-if="showMergeRequestFilters" />
<blobs-filters v-if="showBlobFilters" />
<projects-filters v-if="showProjectsFilters" />
+ <notes-filters v-if="showNotesFilters" />
</small-screen-drawer-navigation>
</section>
</template>
diff --git a/app/assets/javascripts/search/sidebar/components/archived_filter/data.js b/app/assets/javascripts/search/sidebar/components/archived_filter/data.js
index 831e253a0b6..7a3dc8ad96b 100644
--- a/app/assets/javascripts/search/sidebar/components/archived_filter/data.js
+++ b/app/assets/javascripts/search/sidebar/components/archived_filter/data.js
@@ -9,6 +9,7 @@ const scopes = {
PROJECTS: 'projects',
ISSUES: 'issues',
MERGE_REQUESTS: 'merge_requests',
+ NOTES: 'notes',
};
const filterParam = 'include_archived';
diff --git a/app/assets/javascripts/search/sidebar/components/notes_filters.vue b/app/assets/javascripts/search/sidebar/components/notes_filters.vue
new file mode 100644
index 00000000000..3a9f582d554
--- /dev/null
+++ b/app/assets/javascripts/search/sidebar/components/notes_filters.vue
@@ -0,0 +1,18 @@
+<script>
+import ArchivedFilter from './archived_filter/index.vue';
+import FiltersTemplate from './filters_template.vue';
+
+export default {
+ name: 'NotesFilters',
+ components: {
+ ArchivedFilter,
+ FiltersTemplate,
+ },
+};
+</script>
+
+<template>
+ <filters-template>
+ <archived-filter class="gl-mb-5" />
+ </filters-template>
+</template>
diff --git a/app/assets/javascripts/search/sidebar/constants/index.js b/app/assets/javascripts/search/sidebar/constants/index.js
index e5b803b92cb..e6808082185 100644
--- a/app/assets/javascripts/search/sidebar/constants/index.js
+++ b/app/assets/javascripts/search/sidebar/constants/index.js
@@ -2,6 +2,7 @@ export const SCOPE_ISSUES = 'issues';
export const SCOPE_MERGE_REQUESTS = 'merge_requests';
export const SCOPE_BLOB = 'blobs';
export const SCOPE_PROJECTS = 'projects';
+export const SCOPE_NOTES = 'notes';
export const LABEL_DEFAULT_CLASSES = [
'gl-display-flex',
'gl-flex-direction-row',
diff --git a/app/assets/javascripts/security_configuration/components/constants.js b/app/assets/javascripts/security_configuration/components/constants.js
index fd713a7a504..da213b0ed43 100644
--- a/app/assets/javascripts/security_configuration/components/constants.js
+++ b/app/assets/javascripts/security_configuration/components/constants.js
@@ -1,5 +1,6 @@
import { helpPagePath } from '~/helpers/help_page_helper';
import { __, s__ } from '~/locale';
+import ContinuousVulnerabilityScan from '~/security_configuration/components/continuous_vulnerability_scan.vue';
import {
REPORT_TYPE_SAST,
@@ -210,6 +211,7 @@ export const securityFeatures = [
configurationHelpPath: DEPENDENCY_SCANNING_CONFIG_HELP_PATH,
type: REPORT_TYPE_DEPENDENCY_SCANNING,
anchor: 'dependency-scanning',
+ slotComponent: ContinuousVulnerabilityScan,
},
{
name: CONTAINER_SCANNING_NAME,
diff --git a/app/assets/javascripts/security_configuration/components/continuous_vulnerability_scan.vue b/app/assets/javascripts/security_configuration/components/continuous_vulnerability_scan.vue
new file mode 100644
index 00000000000..61cbde2107c
--- /dev/null
+++ b/app/assets/javascripts/security_configuration/components/continuous_vulnerability_scan.vue
@@ -0,0 +1,127 @@
+<script>
+import { GlBadge, GlIcon, GlToggle, GlLink, GlSprintf, GlAlert } from '@gitlab/ui';
+import ProjectSetContinuousVulnerabilityScanning from '~/security_configuration/graphql/project_set_continuous_vulnerability_scanning.graphql';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import { __, s__ } from '~/locale';
+import { helpPagePath } from '~/helpers/help_page_helper';
+
+export default {
+ name: 'ContinuousVulnerabilityscan',
+ components: { GlBadge, GlIcon, GlToggle, GlLink, GlSprintf, GlAlert },
+ mixins: [glFeatureFlagsMixin()],
+ inject: ['continuousVulnerabilityScansEnabled', 'projectFullPath'],
+ i18n: {
+ badgeLabel: __('Experiment'),
+ title: s__('CVS|Continuous Vulnerability Scan'),
+ description: s__(
+ 'CVS|Detect vulnerabilities outside a pipeline as new data is added to the GitLab Advisory Database.',
+ ),
+ learnMore: __('Learn more'),
+ testingAgreementMessage: s__(
+ 'CVS|By enabling this feature, you accept the %{linkStart}Testing Terms of Use%{linkEnd}',
+ ),
+ },
+ props: {
+ feature: {
+ type: Object,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ toggleValue: this.continuousVulnerabilityScansEnabled,
+ errorMessage: '',
+ isAlertDismissed: false,
+ };
+ },
+ computed: {
+ isFeatureConfigured() {
+ return this.feature.available && this.feature.configured;
+ },
+ shouldShowAlert() {
+ return this.errorMessage && !this.isAlertDismissed;
+ },
+ },
+ methods: {
+ reportError(error) {
+ this.errorMessage = error;
+ this.isAlertDismissed = false;
+ },
+ async toggleCVS(checked) {
+ try {
+ const { data } = await this.$apollo.mutate({
+ mutation: ProjectSetContinuousVulnerabilityScanning,
+ variables: {
+ input: {
+ projectPath: this.projectFullPath,
+ enable: checked,
+ },
+ },
+ });
+
+ const { errors } = data.projectSetContinuousVulnerabilityScanning;
+
+ if (errors.length > 0) {
+ this.reportError(errors[0].message);
+ }
+ if (data.projectSetContinuousVulnerabilityScanning !== null) {
+ this.toggleValue = checked;
+ }
+ } catch (error) {
+ this.reportError(error);
+ }
+ },
+ },
+ CVSHelpPagePath: helpPagePath(
+ 'user/application_security/continuous_vulnerability_scanning/index',
+ ),
+ experimentHelpPagePath: helpPagePath('policy/experiment-beta-support', { anchor: 'experiment' }),
+};
+</script>
+
+<template>
+ <div v-if="glFeatures.dependencyScanningOnAdvisoryIngestion">
+ <h4 class="gl-font-base gl-m-0 gl-mt-6">
+ {{ $options.i18n.title }}
+ <gl-badge
+ ref="badge"
+ :href="$options.experimentHelpPagePath"
+ target="_blank"
+ size="sm"
+ variant="neutral"
+ class="gl-cursor-pointer"
+ >{{ $options.i18n.badgeLabel }}</gl-badge
+ >
+ </h4>
+ <gl-alert
+ v-if="shouldShowAlert"
+ class="gl-mb-5 gl-mt-2"
+ variant="danger"
+ @dismiss="isAlertDismissed = true"
+ >{{ errorMessage }}</gl-alert
+ >
+ <gl-toggle
+ class="gl-mt-5"
+ :disabled="!isFeatureConfigured"
+ :value="toggleValue"
+ :label="s__('CVS|Toggle CVS')"
+ label-position="hidden"
+ @change="toggleCVS"
+ />
+
+ <p class="gl-mb-0 gl-mt-5">
+ {{ $options.i18n.description }}
+ <gl-link :href="$options.CVSHelpPagePath" target="_blank">{{
+ $options.i18n.learnMore
+ }}</gl-link>
+ <br />
+ <gl-sprintf :message="$options.i18n.testingAgreementMessage">
+ <template #link="{ content }">
+ <gl-link href="https://about.gitlab.com/handbook/legal/testing-agreement" target="_blank">
+ {{ content }} <gl-icon name="external-link" />
+ </gl-link>
+ </template>
+ </gl-sprintf>
+ </p>
+ </div>
+</template>
diff --git a/app/assets/javascripts/security_configuration/components/feature_card.vue b/app/assets/javascripts/security_configuration/components/feature_card.vue
index a757657339b..7f0a049a6ad 100644
--- a/app/assets/javascripts/security_configuration/components/feature_card.vue
+++ b/app/assets/javascripts/security_configuration/components/feature_card.vue
@@ -73,6 +73,9 @@ export default {
hasSecondary() {
return Boolean(this.feature.secondary);
},
+ hasSlotComponent() {
+ return Boolean(this.feature.slotComponent);
+ },
// This condition is a temporary hack to not display any wrong information
// until this BE Bug is fixed: https://gitlab.com/gitlab-org/gitlab/-/issues/350307.
// More Information: https://gitlab.com/gitlab-org/gitlab/-/issues/350307#note_825447417
@@ -215,5 +218,9 @@ export default {
{{ $options.i18n.configurationGuide }}
</gl-button>
</div>
+
+ <div v-if="hasSlotComponent">
+ <component :is="feature.slotComponent" :feature="feature" />
+ </div>
</gl-card>
</template>
diff --git a/app/assets/javascripts/security_configuration/graphql/project_set_continuous_vulnerability_scanning.graphql b/app/assets/javascripts/security_configuration/graphql/project_set_continuous_vulnerability_scanning.graphql
new file mode 100644
index 00000000000..79f4316d106
--- /dev/null
+++ b/app/assets/javascripts/security_configuration/graphql/project_set_continuous_vulnerability_scanning.graphql
@@ -0,0 +1,8 @@
+mutation ProjectSetContinuousVulnerabilityScanning(
+ $input: ProjectSetContinuousVulnerabilityScanningInput!
+) {
+ projectSetContinuousVulnerabilityScanning(input: $input) {
+ continuousVulnerabilityScanningEnabled
+ errors
+ }
+}
diff --git a/app/assets/javascripts/security_configuration/index.js b/app/assets/javascripts/security_configuration/index.js
index aa3c9c87622..4b498091134 100644
--- a/app/assets/javascripts/security_configuration/index.js
+++ b/app/assets/javascripts/security_configuration/index.js
@@ -26,6 +26,7 @@ export const initSecurityConfiguration = (el) => {
autoDevopsHelpPagePath,
autoDevopsPath,
vulnerabilityTrainingDocsPath,
+ continuousVulnerabilityScansEnabled,
} = el.dataset;
const { augmentedSecurityFeatures } = augmentFeatures(
@@ -43,6 +44,7 @@ export const initSecurityConfiguration = (el) => {
autoDevopsHelpPagePath,
autoDevopsPath,
vulnerabilityTrainingDocsPath,
+ continuousVulnerabilityScansEnabled,
},
render(createElement) {
return createElement(SecurityConfigurationApp, {
diff --git a/app/assets/javascripts/super_sidebar/components/nav_item.vue b/app/assets/javascripts/super_sidebar/components/nav_item.vue
index 8320da30ba8..7f5cce8341f 100644
--- a/app/assets/javascripts/super_sidebar/components/nav_item.vue
+++ b/app/assets/javascripts/super_sidebar/components/nav_item.vue
@@ -217,7 +217,7 @@ export default {
</div>
</div>
<slot name="actions"></slot>
- <span v-if="hasPill" class="gl-text-right gl-relative gl-min-w-8">
+ <span v-if="hasPill || isPinnable" class="gl-text-right gl-relative gl-min-w-8">
<gl-badge
v-if="hasPill"
size="sm"
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 394ba3c51bf..2abb8e4be48 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -414,9 +414,7 @@ module Ci
end
def options_scheduled_at
- ChronicDuration.parse(
- options[:start_in], use_complete_matcher: Feature.enabled?(:update_chronic_duration)
- )&.seconds&.from_now
+ ChronicDuration.parse(options[:start_in], use_complete_matcher: true)&.seconds&.from_now
end
def action?
@@ -740,9 +738,7 @@ module Ci
def artifacts_expire_in=(value)
self.artifacts_expire_at =
if value
- ChronicDuration.parse(
- value, use_complete_matcher: Feature.enabled?(:update_chronic_duration)
- )&.seconds&.from_now
+ ChronicDuration.parse(value, use_complete_matcher: true)&.seconds&.from_now
end
end
diff --git a/app/models/concerns/chronic_duration_attribute.rb b/app/models/concerns/chronic_duration_attribute.rb
index 944ad252cc6..7b7b61fdf06 100644
--- a/app/models/concerns/chronic_duration_attribute.rb
+++ b/app/models/concerns/chronic_duration_attribute.rb
@@ -18,9 +18,7 @@ module ChronicDurationAttribute
begin
new_value = if value.present?
- ChronicDuration.parse(
- value, use_complete_matcher: Feature.enabled?(:update_chronic_duration)
- ).to_i
+ ChronicDuration.parse(value, use_complete_matcher: true).to_i
else
parameters[:default].presence
end
diff --git a/app/models/container_expiration_policy.rb b/app/models/container_expiration_policy.rb
index 857b7eccf70..f643fa7730b 100644
--- a/app/models/container_expiration_policy.rb
+++ b/app/models/container_expiration_policy.rb
@@ -80,9 +80,7 @@ class ContainerExpirationPolicy < ApplicationRecord
end
def set_next_run_at
- cadence_seconds = ChronicDuration.parse(
- cadence, use_complete_matcher: Feature.enabled?(:update_chronic_duration)
- ).seconds
+ cadence_seconds = ChronicDuration.parse(cadence, use_complete_matcher: true).seconds
self.next_run_at = Time.zone.now + cadence_seconds
end
diff --git a/app/services/projects/container_repository/cleanup_tags_base_service.rb b/app/services/projects/container_repository/cleanup_tags_base_service.rb
index 185895698af..61b09de1643 100644
--- a/app/services/projects/container_repository/cleanup_tags_base_service.rb
+++ b/app/services/projects/container_repository/cleanup_tags_base_service.rb
@@ -100,9 +100,7 @@ module Projects
def older_than_in_seconds
strong_memoize(:older_than_in_seconds) do
- ChronicDuration.parse(
- older_than, use_complete_matcher: Feature.enabled?(:update_chronic_duration)
- ).seconds
+ ChronicDuration.parse(older_than, use_complete_matcher: true).seconds
end
end
end
diff --git a/app/validators/duration_validator.rb b/app/validators/duration_validator.rb
index 5d7eba09122..bcdcf665cba 100644
--- a/app/validators/duration_validator.rb
+++ b/app/validators/duration_validator.rb
@@ -12,7 +12,7 @@
#
class DurationValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
- ChronicDuration.parse(value, use_complete_matcher: Feature.enabled?(:update_chronic_duration))
+ ChronicDuration.parse(value, use_complete_matcher: true)
rescue ChronicDuration::DurationParseError
if options[:message]
record.errors.add(:base, options[:message])
diff --git a/app/views/dashboard/todos/_todo.html.haml b/app/views/dashboard/todos/_todo.html.haml
index 1cd8015934e..6877e3456ee 100644
--- a/app/views/dashboard/todos/_todo.html.haml
+++ b/app/views/dashboard/todos/_todo.html.haml
@@ -5,7 +5,7 @@
= todo_target_state_pill(todo)
- %span.todo-target-title{ data: { qa_selector: "todo_target_title_content" }, :id => dom_id(todo) + "_describer" }
+ %span.todo-target-title{ :id => dom_id(todo) + "_describer" }
= todo_target_title(todo)
- if !todo.for_design? && !todo.member_access_requested?
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index 4a7aa9a86ab..066b2ef98dd 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -13,7 +13,7 @@
.merge-request-title.title
%span.merge-request-title-text.js-onboarding-mr-item
= hidden_merge_request_icon(merge_request)
- = link_to merge_request.title, merge_request_path(merge_request), class: 'js-prefetch-document'
+ = link_to markdown_field(merge_request, :title), merge_request_path(merge_request), class: 'js-prefetch-document'
- if merge_request.tasks?
%span.task-status.d-none.d-sm-inline-block.gl-font-sm
&nbsp;
diff --git a/config/feature_flags/development/member_expiring_email_notification.yml b/config/feature_flags/development/member_expiring_email_notification.yml
index 1775cc67b52..36a15c27daf 100644
--- a/config/feature_flags/development/member_expiring_email_notification.yml
+++ b/config/feature_flags/development/member_expiring_email_notification.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/416581
milestone: '16.3'
type: development
group: group::authentication and authorization
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/update_chronic_duration.yml b/config/feature_flags/development/update_chronic_duration.yml
deleted file mode 100644
index 4af65299619..00000000000
--- a/config/feature_flags/development/update_chronic_duration.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: update_chronic_duration
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/130531
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/423696
-milestone: '16.4'
-type: development
-group: group::pipeline authoring
-default_enabled: false
diff --git a/doc/.vale/gitlab/Substitutions.yml b/doc/.vale/gitlab/Substitutions.yml
index 10369669ca5..0d49ac583dd 100644
--- a/doc/.vale/gitlab/Substitutions.yml
+++ b/doc/.vale/gitlab/Substitutions.yml
@@ -14,6 +14,7 @@ swap:
codequality: code quality
Customer [Pp]ortal: Customers Portal
disallow: prevent
+ '(?<!GitLab )Duo': GitLab Duo
frontmatter: front matter
GitLabber: GitLab team member
GitLabbers: GitLab team members
diff --git a/doc/api/dependency_list_export.md b/doc/api/dependency_list_export.md
index 79bf5f1a68f..083f7a640fc 100644
--- a/doc/api/dependency_list_export.md
+++ b/doc/api/dependency_list_export.md
@@ -6,11 +6,20 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Dependency list export API **(ULTIMATE ALL)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/333463) in GitLab 16.4.
-
Every call to this endpoint requires authentication.
-## Create a pipeline-level dependency list export
+## Create a pipeline-level dependency list export **(EXPERIMENT)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/333463) in GitLab 16.4 [with a flag](../administration/feature_flags.md) named `merge_sbom_api`. Enabled by default. This feature is an [Experiment](../policy/experiment-beta-support.md#experiment).
+
+FLAG:
+On self-managed GitLab, by default this feature is available.
+To hide the feature, an administrator can [disable the feature flag](../administration/feature_flags.md) named `merge_sbom_api`.
+On GitLab.com, this feature is available.
+
+WARNING:
+This feature is an [Experiment](../policy/experiment-beta-support.md#experiment)
+and subject to change without notice.
Create a new CycloneDX JSON export for all the project dependencies detected in a pipeline.
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 163c6174c9d..d9d69f6d2f5 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -867,6 +867,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="queryvulnerabilitiesclusterid"></a>`clusterId` | [`[ClustersClusterID!]`](#clustersclusterid) | Filter vulnerabilities by `cluster_id`. Vulnerabilities with a `reportType` of `cluster_image_scanning` are only included with this filter. |
| <a id="queryvulnerabilitiesdismissalreason"></a>`dismissalReason` | [`[VulnerabilityDismissalReason!]`](#vulnerabilitydismissalreason) | Filter by dismissal reason. Only dismissed Vulnerabilities will be included with the filter. |
| <a id="queryvulnerabilitieshasissues"></a>`hasIssues` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have linked issues. |
+| <a id="queryvulnerabilitieshasmergerequest"></a>`hasMergeRequest` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have linked merge requests. |
| <a id="queryvulnerabilitieshasresolution"></a>`hasResolution` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have been resolved on default branch. |
| <a id="queryvulnerabilitiesimage"></a>`image` | [`[String!]`](#string) | Filter vulnerabilities by location image. When this filter is present, the response only matches entries for a `reportType` that includes `container_scanning`, `cluster_image_scanning`. |
| <a id="queryvulnerabilitiesprojectid"></a>`projectId` | [`[ID!]`](#id) | Filter vulnerabilities by project. |
@@ -18209,6 +18210,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="groupvulnerabilitiesclusterid"></a>`clusterId` | [`[ClustersClusterID!]`](#clustersclusterid) | Filter vulnerabilities by `cluster_id`. Vulnerabilities with a `reportType` of `cluster_image_scanning` are only included with this filter. |
| <a id="groupvulnerabilitiesdismissalreason"></a>`dismissalReason` | [`[VulnerabilityDismissalReason!]`](#vulnerabilitydismissalreason) | Filter by dismissal reason. Only dismissed Vulnerabilities will be included with the filter. |
| <a id="groupvulnerabilitieshasissues"></a>`hasIssues` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have linked issues. |
+| <a id="groupvulnerabilitieshasmergerequest"></a>`hasMergeRequest` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have linked merge requests. |
| <a id="groupvulnerabilitieshasresolution"></a>`hasResolution` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have been resolved on default branch. |
| <a id="groupvulnerabilitiesimage"></a>`image` | [`[String!]`](#string) | Filter vulnerabilities by location image. When this filter is present, the response only matches entries for a `reportType` that includes `container_scanning`, `cluster_image_scanning`. |
| <a id="groupvulnerabilitiesprojectid"></a>`projectId` | [`[ID!]`](#id) | Filter vulnerabilities by project. |
@@ -23161,6 +23163,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="projectvulnerabilitiesclusterid"></a>`clusterId` | [`[ClustersClusterID!]`](#clustersclusterid) | Filter vulnerabilities by `cluster_id`. Vulnerabilities with a `reportType` of `cluster_image_scanning` are only included with this filter. |
| <a id="projectvulnerabilitiesdismissalreason"></a>`dismissalReason` | [`[VulnerabilityDismissalReason!]`](#vulnerabilitydismissalreason) | Filter by dismissal reason. Only dismissed Vulnerabilities will be included with the filter. |
| <a id="projectvulnerabilitieshasissues"></a>`hasIssues` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have linked issues. |
+| <a id="projectvulnerabilitieshasmergerequest"></a>`hasMergeRequest` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have linked merge requests. |
| <a id="projectvulnerabilitieshasresolution"></a>`hasResolution` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have been resolved on default branch. |
| <a id="projectvulnerabilitiesimage"></a>`image` | [`[String!]`](#string) | Filter vulnerabilities by location image. When this filter is present, the response only matches entries for a `reportType` that includes `container_scanning`, `cluster_image_scanning`. |
| <a id="projectvulnerabilitiesprojectid"></a>`projectId` | [`[ID!]`](#id) | Filter vulnerabilities by project. |
diff --git a/doc/ci/environments/kubernetes_dashboard.md b/doc/ci/environments/kubernetes_dashboard.md
index f8b127cd011..0f9e1d808ec 100644
--- a/doc/ci/environments/kubernetes_dashboard.md
+++ b/doc/ci/environments/kubernetes_dashboard.md
@@ -15,9 +15,6 @@ Use the Dashboard for Kubernetes to understand the status of your clusters with
The dashboard works with every connected Kubernetes cluster, whether you deployed them
with CI/CD or GitOps.
-For Flux users, the synchronization status of a given environment is not displayed in the dashboard.
-[Issue 391581](https://gitlab.com/gitlab-org/gitlab/-/issues/391581) proposes to add this functionality.
-
## Configure a dashboard
> - Filtering resources by namespace [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/403618) in GitLab 16.2 [with a flag](../../administration/feature_flags.md) named `kubernetes_namespace_for_environment`. Disabled by default.
diff --git a/doc/ci/migration/plan_a_migration.md b/doc/ci/migration/plan_a_migration.md
index 84d11f53eed..488b2abf3a2 100644
--- a/doc/ci/migration/plan_a_migration.md
+++ b/doc/ci/migration/plan_a_migration.md
@@ -23,7 +23,7 @@ the behavior of your old tool.
## Manage organizational changes
-An important part of transitioning from Jenkins to GitLab is the cultural and organizational
+An important part of transitioning to GitLab CI/CD is the cultural and organizational
changes that come with the move, and successfully managing them.
A few things that organizations have reported as helping:
@@ -37,8 +37,8 @@ A few things that organizations have reported as helping:
- Finding ways to sequence or delay parts of the migration can help a lot. Importantly though,
try not to leave things in a non-migrated (or partially-migrated) state for too
long.
-- To gain all the benefits of GitLab, moving your existing Jenkins setup over
- as-is, including any current problems, isn't enough. Take advantage of the improvements
+- To gain all the benefits of GitLab, moving your existing configuration over as-is,
+ including any current problems, isn't enough. Take advantage of the improvements
that GitLab CI/CD offers, and update your implementation as part of the transition.
## Technical questions to ask before starting a migration
@@ -57,7 +57,7 @@ the migration requirements:
### Jenkins
-If you are migrating from Jenkins, these additional questions can help with planning
+If you are [migrating from Jenkins](jenkins.md), these additional questions can help with planning
the migration:
- What plugins are used by jobs in Jenkins today?
diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md
index 956544ceb82..6275d4293ea 100644
--- a/doc/ci/yaml/index.md
+++ b/doc/ci/yaml/index.md
@@ -79,8 +79,9 @@ or import additional pipeline configuration.
> Support for `id_tokens` [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/419750) in GitLab 16.4.
-You can set global defaults for some keywords. Jobs that do not define one or more
-of the listed keywords use the value defined in the `default` section.
+You can set global defaults for some keywords. Each default keyword is copied to every job
+that doesn't already have it defined. If the job already has a keyword defined, that default
+is not used.
**Keyword type**: Global keyword.
@@ -104,6 +105,7 @@ of the listed keywords use the value defined in the `default` section.
```yaml
default:
image: ruby:3.0
+ retry: 2
rspec:
script: bundle exec rspec
@@ -113,16 +115,17 @@ rspec 2.7:
script: bundle exec rspec
```
-In this example, `ruby:3.0` is the default `image` value for all jobs in the pipeline.
-The `rspec 2.7` job does not use the default, because it overrides the default with
-a job-specific `image` section:
+In this example:
+
+- `image: ruby:3.0` and `retry: 2` are the default keywords for all jobs in the pipeline.
+- The `rspec` job does not have `image` or `retry` defined, so it uses the defaults of
+ `image: ruby:3.0` and `retry: 2`.
+- The `rspec 2.7` job does not have `retry` defined, but it does have `image` explictly defined.
+ It uses the default `retry: 2`, but ignores the default `image` and uses the `image: ruby:2.7`
+ defined in the job.
**Additional details**:
-- When the pipeline is created, each default is copied to all jobs that don't have
- that keyword defined.
-- If a job already has one of the keywords configured, the configuration in the job
- takes precedence and is not replaced by the default.
- Control inheritance of default keywords in jobs with [`inherit:default`](#inheritdefault).
### `include`
diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md
index 65dc4de30d1..9be322812e3 100644
--- a/doc/development/migration_style_guide.md
+++ b/doc/development/migration_style_guide.md
@@ -1290,6 +1290,48 @@ class BuildMetadata
end
```
+Additionally, you can expose the keys in a `JSONB` column as
+ActiveRecord attributes. Do this when you need complex validations,
+or ActiveRecord change tracking. This feature is provided by the
+[`jsonb_accessor`](https://github.com/madeintandem/jsonb_accessor) gem,
+and does not replace `JsonSchemaValidator`.
+
+```ruby
+module Organizations
+ class OrganizationSetting < ApplicationRecord
+ belongs_to :organization
+
+ validates :settings, json_schema: { filename: "organization_settings" }
+
+ jsonb_accessor :settings,
+ restricted_visibility_levels: [:integer, { array: true }]
+
+ validates_each :restricted_visibility_levels do |record, attr, value|
+ value&.each do |level|
+ unless Gitlab::VisibilityLevel.options.value?(level)
+ record.errors.add(attr, format(_("'%{level}' is not a valid visibility level"), level: level))
+ end
+ end
+ end
+ end
+end
+```
+
+You can now use `restricted_visibility_levels` as an ActiveRecord attribute:
+
+```ruby
+> s = Organizations::OrganizationSetting.find(1)
+=> #<Organizations::OrganizationSetting:0x0000000148d67628>
+> s.settings
+=> {"restricted_visibility_levels"=>[20]}
+> s.restricted_visibility_levels
+=> [20]
+> s.restricted_visibility_levels = [0]
+=> [0]
+> s.changes
+=> {"settings"=>[{"restricted_visibility_levels"=>[20]}, {"restricted_visibility_levels"=>[0]}], "restricted_visibility_levels"=>[[20], [0]]}
+```
+
## Encrypted attributes
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/227779) in GitLab 14.0.
diff --git a/doc/operations/error_tracking.md b/doc/operations/error_tracking.md
index f0000ea19db..82982a03016 100644
--- a/doc/operations/error_tracking.md
+++ b/doc/operations/error_tracking.md
@@ -8,6 +8,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
Error Tracking allows developers to discover and view errors generated by their application. Because error information is surfaced where the code is developed, this increases efficiency and awareness. Users can choose between [GitLab Integrated error tracking](#integrated-error-tracking) and [Sentry based](#sentry-error-tracking) backends.
+To leave feedback about Error Tracking bugs or functionality, please comment in the [feedback issue](https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2362) or open a [new issue](https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/new).
+
## How error tracking works
For error tracking to work, you need:
diff --git a/doc/operations/tracing.md b/doc/operations/tracing.md
index aca10690a7f..608a9c998f8 100644
--- a/doc/operations/tracing.md
+++ b/doc/operations/tracing.md
@@ -6,9 +6,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Distributed tracing **(ULTIMATE SAAS EXPERIMENT)**
-This feature is an [Experiment](../policy/experiment-beta-support.md). If you find a bug,
-[open an issue in our issue tracker](https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/).
-
> Introduced in GitLab 16.3 [with flags](../administration/feature_flags.md) named `observability_group_tab` and `observability_tracing`. Disabled by default.
FLAG:
@@ -16,10 +13,9 @@ On GitLab.com, by default this feature is not available. To make it available,
an administrator can [enable the feature flags](../administration/feature_flags.md) named `observability_group_tab` and `observability_tracing`.
The feature is not ready for production use.
-With distributed tracing you can inspect how a request moves through different services and systems,
-the timing of each operation, and any errors or logs as they occur.
+With distributed tracing, you can troubleshoot application performance issues by inspecting how a request moves through different services and systems, the timing of each operation, and any errors or logs as they occur. Tracing is particularly useful in the context of microservice applications, which group multiple independent services collaborating to fulfill user requests.
-Tracing is particularly useful in the context of microservice applications, which group multiple independent services collaborating to fulfill user requests.
+This feature is an [Experiment](../policy/experiment-beta-support.md). For more information, see the [group direction page](https://about.gitlab.com/direction/analytics/observability/). To leave feedback about tracing bugs or functionality, please comment in the [feedback issue](https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2363) or open a [new issue](https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/new).
## Configure distributed tracing for a project
diff --git a/doc/user/packages/composer_repository/index.md b/doc/user/packages/composer_repository/index.md
index 40fef13e590..3c80e739465 100644
--- a/doc/user/packages/composer_repository/index.md
+++ b/doc/user/packages/composer_repository/index.md
@@ -280,21 +280,53 @@ To install a package:
composer req <package-name>:<package-version>
```
- If successful, you should see output indicating that the package installed successfully.
-
- You can also install from source (by pulling the Git repository directly) using the
- `--prefer-source` option:
-
- ```shell
- composer update --prefer-source
- ```
-
WARNING:
Never commit the `auth.json` file to your repository. To install packages from a CI/CD job,
consider using the [`composer config`](https://getcomposer.org/doc/articles/handling-private-packages.md#satis) tool with your access token
stored in a [GitLab CI/CD variable](../../../ci/variables/index.md) or in
[HashiCorp Vault](../../../ci/secrets/index.md).
+### Install from source
+
+You can install from source by pulling the Git repository directly. To do so, either:
+
+- Use the `--prefer-source` option:
+
+ ```shell
+ composer update --prefer-source
+ ```
+
+- In the `composer.json`, use the [`preferred-install` field under the `config` key](https://getcomposer.org/doc/06-config.md#preferred-install):
+
+ ```json
+ {
+ ...
+ "config": {
+ "preferred-install": {
+ "<package name>": "source"
+ }
+ }
+ ...
+ }
+ ```
+
+#### SSH access
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/119739) in GitLab 16.4 [with a flag](../../../administration/feature_flags.md) named `composer_use_ssh_source_urls`. Disabled by default.
+
+FLAG:
+On self-managed GitLab, by default this feature is not available. To make it available, an administrator can
+[enable the feature flag](../../../administration/feature_flags.md) named `composer_use_ssh_source_urls`.
+
+When you install from source, the `composer` configures an
+access to the project's Git repository.
+Depending on the project visibility, the access type is different:
+
+- On public projects, the `https` Git URL is used. Make sure you can [clone the repository with HTTPS](../../../gitlab-basics/start-using-git.md#clone-with-https).
+- On internal or private projects, the `ssh` Git URL is used. Make sure you can [clone the repository with SSH](../../../gitlab-basics/start-using-git.md#clone-with-ssh).
+
+You can access the `ssh` Git URL from a CI/CD job using [SSH keys with GitLab CI/CD](../../../ci/ssh_keys/index.md).
+
### Working with Deploy Tokens
Although Composer packages are accessed at the group level, a group or project deploy token can be
diff --git a/doc/user/profile/account/two_factor_authentication.md b/doc/user/profile/account/two_factor_authentication.md
index c2b02f143db..193320f48d5 100644
--- a/doc/user/profile/account/two_factor_authentication.md
+++ b/doc/user/profile/account/two_factor_authentication.md
@@ -62,6 +62,8 @@ In GitLab 14.3 and later, your account email must be confirmed to enable 2FA.
To enable 2FA with a one-time password:
+<!-- vale gitlab.Substitutions = NO -->
+
1. **In GitLab:**
1. Access your [**User settings**](../index.md#access-your-user-settings).
1. Select **Account**.
@@ -70,7 +72,7 @@ To enable 2FA with a one-time password:
1. Install a compatible application. For example:
- Cloud-based (recommended because you can restore access if you lose the hardware device):
- [Authy](https://authy.com/).
- - [Duo](https://duo.com/).
+ - [Cisco Duo](https://duo.com/).
- Other (proprietary):
- [Google Authenticator](https://support.google.com/accounts/answer/1066447?hl=en).
- [Microsoft Authenticator](https://www.microsoft.com/en-us/security/mobile-authenticator-app).
@@ -85,6 +87,8 @@ To enable 2FA with a one-time password:
1. Enter your current password.
1. Select **Submit**.
+<!-- vale gitlab.Substitutions = YES -->
+
If you entered the correct pin, GitLab displays a list of [recovery codes](#recovery-codes). Download them and keep them
in a safe place.
@@ -152,27 +156,31 @@ Configure FortiAuthenticator in GitLab. On your GitLab server:
(Linux package installations) or [restart](../../../administration/restart_gitlab.md#self-compiled-installations)
(self-compiled installations).
-### Enable one-time password using Duo
+<!-- vale gitlab.Substitutions = NO -->
+
+### Enable one-time password using Cisco Duo
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/15760) in GitLab 15.10.
FLAG:
On self-managed GitLab, by default this feature is available. On GitLab.com this feature is not available.
-You can use Duo as an OTP provider in GitLab.
+You can use Cisco Duo as an OTP provider in GitLab.
#### Prerequisites
-To use Duo as an OTP provider:
+To use Cisco Duo as an OTP provider:
+
+- Your account must exist in both Cisco Duo and GitLab, with the same username in both applications.
+- You must have [configured Cisco Duo](https://admin.duosecurity.com/) and have an integration key, secret key, and API hostname.
-- Your account must exist in both Duo and GitLab, with the same username in both applications.
-- You must have [configured Duo](https://admin.duosecurity.com/) and have an integration key, secret key, and API hostname.
+For more information, see the [Cisco Duo API documentation](https://duo.com/docs/authapi).
-For more information, see the [Duo API documentation](https://duo.com/docs/authapi).
+GitLab 15.10 has been tested with Cisco Duo version D261.14
-GitLab 15.10 has been tested with Duo version D261.14
+#### Configure Cisco Duo in GitLab
-#### Configure Duo in GitLab
+<!-- vale gitlab.Substitutions = YES -->
On your GitLab server:
diff --git a/doc/user/project/repository/mirror/index.md b/doc/user/project/repository/mirror/index.md
index 36b184c1ad0..b16197e45b2 100644
--- a/doc/user/project/repository/mirror/index.md
+++ b/doc/user/project/repository/mirror/index.md
@@ -124,9 +124,8 @@ When you create a mirror, you must configure the authentication method for it.
GitLab supports these authentication methods:
- [SSH authentication](#ssh-authentication).
-- Password.
+- Username and password.
-When using password authentication, ensure you specify the username.
For a [project access token](../../settings/project_access_tokens.md) or
[group access token](../../../group/settings/group_access_tokens.md),
use the username (not token name) and the token as the password.
diff --git a/doc/user/project/repository/mirror/pull.md b/doc/user/project/repository/mirror/pull.md
index cced9a52caf..5b3c76d982a 100644
--- a/doc/user/project/repository/mirror/pull.md
+++ b/doc/user/project/repository/mirror/pull.md
@@ -67,11 +67,10 @@ Prerequisites:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Settings > Repository**.
1. Expand **Mirroring repositories**.
-1. Enter the **Git repository URL**. Include the username
- in the URL, if required: `https://MYUSERNAME@gitlab.com/GROUPNAME/PROJECTNAME.git`
+1. Enter the **Git repository URL**.
NOTE:
- To mirror the `gitlab` repository, use `git@gitlab.com:gitlab-org/gitlab.git`
+ To mirror the `gitlab` repository, use `gitlab.com:gitlab-org/gitlab.git`
or `https://gitlab.com/gitlab-org/gitlab.git`.
1. In **Mirror direction**, select **Pull**.
diff --git a/doc/user/project/repository/mirror/push.md b/doc/user/project/repository/mirror/push.md
index a6312dec162..e18e3631d7f 100644
--- a/doc/user/project/repository/mirror/push.md
+++ b/doc/user/project/repository/mirror/push.md
@@ -89,12 +89,12 @@ To configure a mirror from GitLab to GitHub:
1. Enter a **Git repository URL** with this format, changing the variables as needed:
```plaintext
- https://USERNAME@github.com/GROUP/PROJECT.git
+ https://github.com/GROUP/PROJECT.git
```
- - `USERNAME`: The username of the owner of the personal access token.
- `GROUP`: The group on GitHub.
- `PROJECT`: The project on GitHub.
+1. For **Username**, enter the username of the owner of the personal access token.
1. For **Password**, enter your GitHub personal access token.
1. Select **Mirror repository**.
@@ -164,19 +164,17 @@ To set up a mirror from GitLab to AWS CodeCommit:
1. Open your new repository, and then select **Clone URL > Clone HTTPS** (not **Clone HTTPS (GRC)**).
1. In GitLab, open the repository to be push-mirrored.
1. Select **Settings > Repository**, and then expand **Mirroring repositories**.
-1. Fill in the **Git repository URL** field using this format:
+1. Fill in the **Git repository URL** field using this format, replacing
+ `<aws-region>` with your AWS region, and
+ `<your_codecommit_repo>` with the name of your repository in CodeCommit:
```plaintext
- https://<your_aws_git_userid>@git-codecommit.<aws-region>.amazonaws.com/v1/repos/<your_codecommit_repo>
+ https://git-codecommit.<aws-region>.amazonaws.com/v1/repos/<your_codecommit_repo>
```
- Replace `<your_aws_git_userid>` with the AWS **special HTTPS Git user ID**
- from the IAM Git credentials created earlier. Replace `<your_codecommit_repo>`
- with the name of your repository in CodeCommit.
-
-1. For **Mirror direction**, select **Push**.
-1. For **Authentication method**, select **Password**. Fill in the **Password** box
- with the special IAM Git clone user ID **password** created earlier in AWS.
+1. For **Authentication method**, select **Username and Password**.
+1. For **Username**, enter the AWS **special HTTPS Git user ID**.
+1. For **Password**, enter the special IAM Git clone user ID password created earlier in AWS.
1. Leave the option **Only mirror protected branches** for CodeCommit. It pushes more
frequently (from every five minutes to every minute).
@@ -202,7 +200,8 @@ If it isn't working correctly, a red `error` tag appears, and shows the error me
[personal access token](../../../profile/personal_access_tokens.md) with `write_repository` scope.
1. On the source GitLab instance:
1. Enter the **Git repository URL** using this format:
- `https://oauth2@<destination host>/<your_gitlab_group_or_name>/<your_gitlab_project>.git`.
+ `https://<destination host>/<your_gitlab_group_or_name>/<your_gitlab_project>.git`.
+ 1. Enter the **Username** `oauth2`.
1. Enter the **Password**. Use the GitLab personal access token created on the
destination GitLab instance.
1. Select **Mirror repository**.
diff --git a/doc/user/usage_quotas.md b/doc/user/usage_quotas.md
index 7019b09d664..c6801ab8efd 100644
--- a/doc/user/usage_quotas.md
+++ b/doc/user/usage_quotas.md
@@ -179,7 +179,11 @@ available decreases. All projects no longer have the read-only status because 40
## Namespace storage limit
Namespaces on GitLab SaaS have a storage limit. For more information, see our [pricing page](https://about.gitlab.com/pricing/).
-This limit is not visible on the **Usage quotas** page, but is prior to the limit being [applied](#namespace-storage-limit-application-schedule). Self-managed deployments are not affected.
+
+After namespace storage limits are enforced, view them in the **Usage quotas** page.
+For more information about the namespace storage limit enforcement, see the FAQ pages for the [Free](https://about.gitlab.com/pricing/faq-efficient-free-tier/#storage-limits-on-gitlab-saas-free-tier) and [Paid](https://about.gitlab.com/pricing/faq-paid-storage-transfer/) tiers.
+
+Namespace storage limits do not apply to self-managed deployments, but administrators can [manage the repository size](../administration/settings/account_and_limit_settings.md#repository-size-limit).
Storage types that add to the total namespace storage are:
@@ -200,13 +204,16 @@ To notify you that you have nearly exceeded your namespace storage quota:
- In the GitLab UI, a notification displays when you've reached 75%, 95%, and 100% of your namespace storage quota.
- GitLab sends an email to members with the Owner role to notify them when namespace storage usage is at 70%, 85%, 95%, and 100%.
-To prevent exceeding the namespace storage quota, you can:
+To prevent exceeding the namespace storage limit, you can:
-- Reduce storage consumption by following the suggestions in the [Manage Your Storage Usage](#manage-your-storage-usage) section of this page.
-- Apply for [GitLab for Education](https://about.gitlab.com/solutions/education/join/), [GitLab for Open Source](https://about.gitlab.com/solutions/open-source/join/), or [GitLab for Startups](https://about.gitlab.com/solutions/startups/) if you meet the eligibility requirements.
-- Consider using a [self-managed instance](../subscriptions/self_managed/index.md) of GitLab which does not have these limits on the free tier.
+- [Manage your storage usage](#manage-your-storage-usage).
+- If you meet the eligibility requirements, you can apply for:
+ - [GitLab for Education](https://about.gitlab.com/solutions/education/join/)
+ - [GitLab for Open Source](https://about.gitlab.com/solutions/open-source/join/)
+ - [GitLab for Startups](https://about.gitlab.com/solutions/startups/)
+- Consider using a [self-managed instance](../subscriptions/self_managed/index.md) of GitLab, which does not have these limits on the Free tier.
- [Purchase additional storage](../subscriptions/gitlab_com/index.md#purchase-more-storage-and-transfer) units at $60/year for 10 GB of storage.
-- [Start a trial](https://about.gitlab.com/free-trial/) or [upgrade to GitLab Premium or Ultimate](https://about.gitlab.com/pricing/) which include higher limits and features that enable growing teams to ship faster without sacrificing on quality.
+- [Start a trial](https://about.gitlab.com/free-trial/) or [upgrade to GitLab Premium or Ultimate](https://about.gitlab.com/pricing/), which include higher limits and features to enable growing teams to ship faster without sacrificing on quality.
- [Talk to an expert](https://page.gitlab.com/usage_limits_help.html) for more information about your options.
### View project fork storage usage
@@ -222,7 +229,3 @@ To view the amount of namespace storage the fork has used:
The cost factor applies to the project repository, LFS objects, job artifacts, packages, snippets, and the wiki.
The cost factor does not apply to private forks in namespaces on the Free plan.
-
-### Namespace storage limit application schedule
-
-Information on when namespace-level storage limits are applied is available on these FAQ pages for the [Free](https://about.gitlab.com/pricing/faq-efficient-free-tier/#storage-limits-on-gitlab-saas-free-tier) and [Paid](https://about.gitlab.com/pricing/faq-paid-storage-transfer/) tier.
diff --git a/lib/gitlab/ci/build/duration_parser.rb b/lib/gitlab/ci/build/duration_parser.rb
index bc365fe4e9f..97049a4f876 100644
--- a/lib/gitlab/ci/build/duration_parser.rb
+++ b/lib/gitlab/ci/build/duration_parser.rb
@@ -41,7 +41,7 @@ module Gitlab
def parse
return if never?
- ChronicDuration.parse(value, use_complete_matcher: Feature.enabled?(:update_chronic_duration))
+ ChronicDuration.parse(value, use_complete_matcher: true)
end
def validation_cache
diff --git a/lib/gitlab/ci/config/entry/job.rb b/lib/gitlab/ci/config/entry/job.rb
index 044d920f557..c40d665f320 100644
--- a/lib/gitlab/ci/config/entry/job.rb
+++ b/lib/gitlab/ci/config/entry/job.rb
@@ -177,7 +177,7 @@ module Gitlab
def parsed_timeout
return unless has_timeout?
- ChronicDuration.parse(timeout.to_s, use_complete_matcher: Feature.enabled?(:update_chronic_duration))
+ ChronicDuration.parse(timeout.to_s, use_complete_matcher: true)
end
def ignored?
diff --git a/lib/gitlab/config/entry/legacy_validation_helpers.rb b/lib/gitlab/config/entry/legacy_validation_helpers.rb
index 9363c60e6db..ec67d65c526 100644
--- a/lib/gitlab/config/entry/legacy_validation_helpers.rb
+++ b/lib/gitlab/config/entry/legacy_validation_helpers.rb
@@ -12,7 +12,7 @@ module Gitlab
if parser && parser.respond_to?(:validate_duration)
parser.validate_duration(value)
else
- ChronicDuration.parse(value, use_complete_matcher: Feature.enabled?(:update_chronic_duration))
+ ChronicDuration.parse(value, use_complete_matcher: true)
end
rescue ChronicDuration::DurationParseError
false
@@ -25,10 +25,10 @@ module Gitlab
parser.validate_duration_limit(value, limit)
else
ChronicDuration.parse(
- value, use_complete_matcher: Feature.enabled?(:update_chronic_duration)
+ value, use_complete_matcher: true
).second.from_now <
ChronicDuration.parse(
- limit, use_complete_matcher: Feature.enabled?(:update_chronic_duration)
+ limit, use_complete_matcher: true
).second.from_now
end
rescue ChronicDuration::DurationParseError
diff --git a/lib/gitlab/time_tracking_formatter.rb b/lib/gitlab/time_tracking_formatter.rb
index 99baec347fe..26efb3b918d 100644
--- a/lib/gitlab/time_tracking_formatter.rb
+++ b/lib/gitlab/time_tracking_formatter.rb
@@ -19,7 +19,7 @@ module Gitlab
string,
CUSTOM_DAY_AND_MONTH_LENGTH.merge(
default_unit: 'hours', keep_zero: keep_zero,
- use_complete_matcher: Feature.enabled?(:update_chronic_duration)
+ use_complete_matcher: true
))
rescue StandardError
nil
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index f65a35d2697..363614d3f42 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -9172,6 +9172,18 @@ msgstr ""
msgid "CVE|Why Request a CVE ID?"
msgstr ""
+msgid "CVS|By enabling this feature, you accept the %{linkStart}Testing Terms of Use%{linkEnd}"
+msgstr ""
+
+msgid "CVS|Continuous Vulnerability Scan"
+msgstr ""
+
+msgid "CVS|Detect vulnerabilities outside a pipeline as new data is added to the GitLab Advisory Database."
+msgstr ""
+
+msgid "CVS|Toggle CVS"
+msgstr ""
+
msgid "Cadence is not automated"
msgstr ""
diff --git a/qa/qa/page/dashboard/todos.rb b/qa/qa/page/dashboard/todos.rb
index a65bba9ac39..873b687c919 100644
--- a/qa/qa/page/dashboard/todos.rb
+++ b/qa/qa/page/dashboard/todos.rb
@@ -14,7 +14,6 @@ module QA
view 'app/views/dashboard/todos/_todo.html.haml' do
element :todo_item_container
element :todo_action_name_content
- element :todo_target_title_content
element :todo_author_name_content
end
@@ -43,20 +42,15 @@ module QA
wait_for_requests
end
- def has_latest_todo_with_author?(author:, action:)
- content = { selector: :todo_author_name_content, text: author }
- has_latest_todo_with_content?(action, **content)
+ def click_todo_with_content(content)
+ click_element(:todo_item_container, text: content)
end
- def has_latest_todo_with_title?(title:, action:)
- content = { selector: :todo_target_title_content, text: title }
+ def has_latest_todo_with_author?(author:, action:)
+ content = { selector: :todo_author_name_content, text: author }
has_latest_todo_with_content?(action, **content)
end
- def click_todo_with_content(content)
- click_element(:todo_item_container, text: content)
- end
-
private
def has_latest_todo_with_content?(action, **kwargs)
diff --git a/qa/qa/specs/features/shared_contexts/merge_train_spec_with_user_prep.rb b/qa/qa/specs/features/shared_contexts/merge_train_spec_with_user_prep.rb
deleted file mode 100644
index fe0d103b6c0..00000000000
--- a/qa/qa/specs/features/shared_contexts/merge_train_spec_with_user_prep.rb
+++ /dev/null
@@ -1,80 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- RSpec.shared_context 'merge train spec with user prep' do
- let(:executor) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(number: 8)}" }
- let(:file_name) { Faker::Lorem.word }
- let(:mr_title) { Faker::Lorem.sentence }
- let(:admin_api_client) { Runtime::API::Client.as_admin }
-
- let(:user) { create(:user, api_client: admin_api_client) }
-
- let(:project) { create(:project, name: 'pipeline-for-merge-trains') }
-
- let!(:runner) do
- Resource::ProjectRunner.fabricate! do |runner|
- runner.project = project
- runner.name = executor
- runner.tags = [executor]
- end
- end
-
- let!(:project_files) do
- Resource::Repository::Commit.fabricate_via_api! do |commit|
- commit.project = project
- commit.commit_message = 'Add .gitlab-ci.yml'
- commit.add_files(
- [
- {
- file_path: '.gitlab-ci.yml',
- content: <<~YAML
- test_merge_train:
- tags:
- - #{executor}
- script:
- - sleep 10
- - echo 'OK!'
- only:
- - merge_requests
- YAML
- },
- {
- file_path: file_name,
- content: Faker::Lorem.sentence
- }
- ]
- )
- end
- end
-
- before do
- project.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
-
- Flow::Login.sign_in
- project.visit!
- Flow::MergeRequest.enable_merge_trains
-
- Flow::Login.sign_in(as: user)
-
- Resource::MergeRequest.fabricate_via_api! do |merge_request|
- merge_request.title = mr_title
- merge_request.project = project
- merge_request.description = Faker::Lorem.sentence
- merge_request.target_new_branch = false
- merge_request.update_existing_file = true
- merge_request.file_name = file_name
- merge_request.file_content = Faker::Lorem.sentence
- end.visit!
-
- Page::MergeRequest::Show.perform do |show|
- show.has_pipeline_status?('passed')
- show.try_to_merge!
- end
- end
-
- after do
- runner&.remove_via_api!
- user&.remove_via_api!
- end
- end
-end
diff --git a/spec/frontend/jira_connect/branches/components/source_branch_dropdown_spec.js b/spec/frontend/jira_connect/branches/components/source_branch_dropdown_spec.js
index cf2dacb50d8..95658f66d09 100644
--- a/spec/frontend/jira_connect/branches/components/source_branch_dropdown_spec.js
+++ b/spec/frontend/jira_connect/branches/components/source_branch_dropdown_spec.js
@@ -1,58 +1,45 @@
-import { GlCollapsibleListbox } from '@gitlab/ui';
-import { mount, shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
+import { GlCollapsibleListbox } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
+
import SourceBranchDropdown from '~/jira_connect/branches/components/source_branch_dropdown.vue';
import { BRANCHES_PER_PAGE } from '~/jira_connect/branches/constants';
import getProjectQuery from '~/jira_connect/branches/graphql/queries/get_project.query.graphql';
-import { mockProjects } from '../mock_data';
-
-const mockProject = {
- id: 'test',
- repository: {
- branchNames: ['main', 'f-test', 'release'],
- rootRef: 'main',
- },
-};
-const mockSelectedProject = mockProjects[0];
-
-const mockProjectQueryResponse = {
- data: {
- project: mockProject,
- },
-};
-const mockGetProjectQuery = jest.fn().mockResolvedValue(mockProjectQueryResponse);
-const mockQueryLoading = jest.fn().mockReturnValue(new Promise(() => {}));
+import {
+ mockBranchNames,
+ mockBranchNames2,
+ mockProjects,
+ mockProjectQueryResponse,
+} from '../mock_data';
+
+Vue.use(VueApollo);
describe('SourceBranchDropdown', () => {
let wrapper;
+ const mockSelectedProject = mockProjects[0];
+ const querySuccessHandler = jest.fn().mockResolvedValue(mockProjectQueryResponse());
+ const queryLoadingHandler = jest.fn().mockReturnValue(new Promise(() => {}));
+
const findListbox = () => wrapper.findComponent(GlCollapsibleListbox);
- const assertListboxItems = () => {
+ const assertListboxItems = (branchNames = mockBranchNames) => {
const listboxItems = findListbox().props('items');
- expect(listboxItems).toHaveLength(mockProject.repository.branchNames.length);
- expect(listboxItems.map((item) => item.text)).toEqual(mockProject.repository.branchNames);
+ expect(listboxItems).toHaveLength(branchNames.length);
+ expect(listboxItems.map((item) => item.text)).toEqual(branchNames);
};
- function createMockApolloProvider({ getProjectQueryLoading = false } = {}) {
- Vue.use(VueApollo);
-
- const mockApollo = createMockApollo([
- [getProjectQuery, getProjectQueryLoading ? mockQueryLoading : mockGetProjectQuery],
- ]);
+ const createComponent = ({ props, handler = querySuccessHandler } = {}) => {
+ const mockApollo = createMockApollo([[getProjectQuery, handler]]);
- return mockApollo;
- }
-
- function createComponent({ mockApollo, props, mountFn = shallowMount } = {}) {
- wrapper = mountFn(SourceBranchDropdown, {
- apolloProvider: mockApollo || createMockApolloProvider(),
+ wrapper = shallowMount(SourceBranchDropdown, {
+ apolloProvider: mockApollo,
propsData: props,
});
- }
+ };
describe('when `selectedProject` prop is not specified', () => {
beforeEach(() => {
@@ -78,6 +65,7 @@ describe('SourceBranchDropdown', () => {
loading: false,
searchable: true,
searching: false,
+ selected: null,
toggleText: 'Select a branch',
});
});
@@ -92,23 +80,26 @@ describe('SourceBranchDropdown', () => {
describe('when branches are loading', () => {
it('sets loading prop to true', () => {
createComponent({
- mockApollo: createMockApolloProvider({ getProjectQueryLoading: true }),
props: { selectedProject: mockSelectedProject },
+ handler: queryLoadingHandler,
});
- expect(findListbox().props('loading')).toEqual(true);
+ expect(findListbox().props('loading')).toBe(true);
});
});
describe('when branches have loaded', () => {
describe('when searching branches', () => {
it('triggers a refetch', async () => {
- createComponent({ mountFn: mount, props: { selectedProject: mockSelectedProject } });
+ createComponent({ props: { selectedProject: mockSelectedProject } });
await waitForPromises();
const mockSearchTerm = 'mai';
+ expect(querySuccessHandler).toHaveBeenCalledTimes(1);
+
await findListbox().vm.$emit('search', mockSearchTerm);
- expect(mockGetProjectQuery).toHaveBeenCalledWith({
+ expect(querySuccessHandler).toHaveBeenCalledTimes(2);
+ expect(querySuccessHandler).toHaveBeenLastCalledWith({
branchNamesLimit: BRANCHES_PER_PAGE,
branchNamesOffset: 0,
branchNamesSearchPattern: `*${mockSearchTerm}*`,
@@ -129,10 +120,15 @@ describe('SourceBranchDropdown', () => {
loading: false,
searchable: true,
searching: false,
+ selected: null,
toggleText: 'Select a branch',
});
});
+ it('disables infinite scroll', () => {
+ expect(findListbox().props('infiniteScroll')).toBe(false);
+ });
+
it('omits monospace styling from listbox', () => {
expect(findListbox().classes()).not.toContain('gl-font-monospace');
});
@@ -142,19 +138,19 @@ describe('SourceBranchDropdown', () => {
});
it("emits `change` event with the repository's `rootRef` by default", () => {
- expect(wrapper.emitted('change')[0]).toEqual([mockProject.repository.rootRef]);
+ expect(wrapper.emitted('change')[0]).toEqual([mockBranchNames[0]]);
});
describe('when selecting a listbox item', () => {
it('emits `change` event with the selected branch name', () => {
- const mockBranchName = mockProject.repository.branchNames[1];
+ const mockBranchName = mockBranchNames[1];
findListbox().vm.$emit('select', mockBranchName);
expect(wrapper.emitted('change')[1]).toEqual([mockBranchName]);
});
});
describe('when `selectedBranchName` prop is specified', () => {
- const mockBranchName = mockProject.repository.branchNames[2];
+ const mockBranchName = mockBranchNames[2];
beforeEach(() => {
wrapper.setProps({
@@ -162,6 +158,10 @@ describe('SourceBranchDropdown', () => {
});
});
+ it('sets listbox selected to `selectedBranchName`', () => {
+ expect(findListbox().props('selected')).toBe(mockBranchName);
+ });
+
it('sets listbox text to `selectedBranchName` value', () => {
expect(findListbox().props('toggleText')).toBe(mockBranchName);
});
@@ -170,6 +170,66 @@ describe('SourceBranchDropdown', () => {
expect(findListbox().classes()).toContain('gl-font-monospace');
});
});
+
+ describe('when full page of branches returns', () => {
+ const fullPageBranchNames = Array(BRANCHES_PER_PAGE)
+ .fill(1)
+ .map((_, i) => mockBranchNames[i % mockBranchNames.length]);
+
+ beforeEach(async () => {
+ createComponent({
+ props: { selectedProject: mockSelectedProject },
+ handler: () => Promise.resolve(mockProjectQueryResponse(fullPageBranchNames)),
+ });
+ await waitForPromises();
+ });
+
+ it('enables infinite scroll', () => {
+ expect(findListbox().props('infiniteScroll')).toBe(true);
+ });
+ });
+ });
+
+ describe('when loading more branches from infinite scroll', () => {
+ const queryLoadMoreHandler = jest.fn();
+
+ beforeEach(async () => {
+ queryLoadMoreHandler.mockResolvedValueOnce(mockProjectQueryResponse());
+ queryLoadMoreHandler.mockResolvedValueOnce(mockProjectQueryResponse(mockBranchNames2));
+ createComponent({
+ props: { selectedProject: mockSelectedProject },
+ handler: queryLoadMoreHandler,
+ });
+
+ await waitForPromises();
+
+ await findListbox().vm.$emit('bottom-reached');
+ });
+
+ it('sets loading more prop to true', () => {
+ expect(findListbox().props('infiniteScrollLoading')).toBe(true);
+ });
+
+ it('triggers load more query', () => {
+ expect(queryLoadMoreHandler).toHaveBeenLastCalledWith({
+ branchNamesLimit: BRANCHES_PER_PAGE,
+ branchNamesOffset: 3,
+ branchNamesSearchPattern: '*',
+ projectPath: 'test-path',
+ });
+ });
+
+ it('renders available source branches as listbox items', async () => {
+ await waitForPromises();
+
+ assertListboxItems([...mockBranchNames, ...mockBranchNames2]);
+ });
+
+ it('sets loading more prop to false once done', async () => {
+ await waitForPromises();
+
+ expect(findListbox().props('infiniteScrollLoading')).toBe(false);
+ });
});
});
});
diff --git a/spec/frontend/jira_connect/branches/mock_data.js b/spec/frontend/jira_connect/branches/mock_data.js
index 742ab5392c8..1720e0118c8 100644
--- a/spec/frontend/jira_connect/branches/mock_data.js
+++ b/spec/frontend/jira_connect/branches/mock_data.js
@@ -1,3 +1,6 @@
+export const mockBranchNames = ['main', 'f-test', 'release'];
+export const mockBranchNames2 = ['dev', 'dev-1', 'dev-2'];
+
export const mockProjects = [
{
id: 'test',
@@ -28,3 +31,15 @@ export const mockProjects = [
},
},
];
+
+export const mockProjectQueryResponse = (branchNames = mockBranchNames) => ({
+ data: {
+ project: {
+ id: 'gid://gitlab/Project/27',
+ repository: {
+ branchNames,
+ rootRef: 'main',
+ },
+ },
+ },
+});
diff --git a/spec/frontend/search/sidebar/components/app_spec.js b/spec/frontend/search/sidebar/components/app_spec.js
index 3944ba86942..72c9537bd61 100644
--- a/spec/frontend/search/sidebar/components/app_spec.js
+++ b/spec/frontend/search/sidebar/components/app_spec.js
@@ -2,7 +2,11 @@ import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
// eslint-disable-next-line no-restricted-imports
import Vuex from 'vuex';
-import { SEARCH_TYPE_ZOEKT, SEARCH_TYPE_ADVANCED } from '~/search/sidebar/constants';
+import {
+ SEARCH_TYPE_ZOEKT,
+ SEARCH_TYPE_ADVANCED,
+ SEARCH_TYPE_BASIC,
+} from '~/search/sidebar/constants';
import { MOCK_QUERY } from 'jest/search/mock_data';
import { toggleSuperSidebarCollapsed } from '~/super_sidebar/super_sidebar_collapsed_state_manager';
import GlobalSearchSidebar from '~/search/sidebar/components/app.vue';
@@ -10,6 +14,7 @@ import IssuesFilters from '~/search/sidebar/components/issues_filters.vue';
import MergeRequestsFilters from '~/search/sidebar/components/merge_requests_filters.vue';
import BlobsFilters from '~/search/sidebar/components/blobs_filters.vue';
import ProjectsFilters from '~/search/sidebar/components/projects_filters.vue';
+import NotesFilters from '~/search/sidebar/components/notes_filters.vue';
import ScopeLegacyNavigation from '~/search/sidebar/components/scope_legacy_navigation.vue';
import SmallScreenDrawerNavigation from '~/search/sidebar/components/small_screen_drawer_navigation.vue';
import ScopeSidebarNavigation from '~/search/sidebar/components/scope_sidebar_navigation.vue';
@@ -37,6 +42,11 @@ describe('GlobalSearchSidebar', () => {
wrapper = shallowMount(GlobalSearchSidebar, {
store,
+ provide: {
+ glFeatures: {
+ searchNotesHideArchivedProjects: true,
+ },
+ },
});
};
@@ -45,6 +55,7 @@ describe('GlobalSearchSidebar', () => {
const findMergeRequestsFilters = () => wrapper.findComponent(MergeRequestsFilters);
const findBlobsFilters = () => wrapper.findComponent(BlobsFilters);
const findProjectsFilters = () => wrapper.findComponent(ProjectsFilters);
+ const findNotesFilters = () => wrapper.findComponent(NotesFilters);
const findScopeLegacyNavigation = () => wrapper.findComponent(ScopeLegacyNavigation);
const findSmallScreenDrawerNavigation = () => wrapper.findComponent(SmallScreenDrawerNavigation);
const findScopeSidebarNavigation = () => wrapper.findComponent(ScopeSidebarNavigation);
@@ -62,18 +73,26 @@ describe('GlobalSearchSidebar', () => {
});
describe.each`
- scope | filter
- ${'issues'} | ${findIssuesFilters}
- ${'merge_requests'} | ${findMergeRequestsFilters}
- ${'blobs'} | ${findBlobsFilters}
- `('with sidebar $scope scope:', ({ scope, filter }) => {
+ scope | filter | searchType | isShown
+ ${'issues'} | ${findIssuesFilters} | ${SEARCH_TYPE_BASIC} | ${true}
+ ${'merge_requests'} | ${findMergeRequestsFilters} | ${SEARCH_TYPE_BASIC} | ${true}
+ ${'projects'} | ${findProjectsFilters} | ${SEARCH_TYPE_BASIC} | ${true}
+ ${'blobs'} | ${findBlobsFilters} | ${SEARCH_TYPE_BASIC} | ${false}
+ ${'blobs'} | ${findBlobsFilters} | ${SEARCH_TYPE_ADVANCED} | ${true}
+ ${'blobs'} | ${findBlobsFilters} | ${SEARCH_TYPE_ZOEKT} | ${false}
+ ${'notes'} | ${findNotesFilters} | ${SEARCH_TYPE_BASIC} | ${false}
+ ${'notes'} | ${findNotesFilters} | ${SEARCH_TYPE_ADVANCED} | ${true}
+ `('with sidebar $scope scope:', ({ scope, filter, searchType, isShown }) => {
beforeEach(() => {
getterSpies.currentScope = jest.fn(() => scope);
- createComponent({ urlQuery: { scope }, searchType: SEARCH_TYPE_ADVANCED });
+ createComponent({ urlQuery: { scope }, searchType });
});
- it(`shows filter ${filter.name.replace('find', '')}`, () => {
- expect(filter().exists()).toBe(true);
+ it(`renders correctly filter ${filter.name.replace(
+ 'find',
+ '',
+ )} when search_type ${searchType}`, () => {
+ expect(filter().exists()).toBe(isShown);
});
});
@@ -101,24 +120,27 @@ describe('GlobalSearchSidebar', () => {
describe.each`
currentScope | sidebarNavShown | legacyNavShown
${'issues'} | ${false} | ${true}
- ${''} | ${false} | ${false}
+ ${'test'} | ${false} | ${true}
${'issues'} | ${true} | ${false}
- ${''} | ${true} | ${false}
- `('renders navigation', ({ currentScope, sidebarNavShown, legacyNavShown }) => {
- beforeEach(() => {
- getterSpies.currentScope = jest.fn(() => currentScope);
- createComponent({ useSidebarNavigation: sidebarNavShown });
- });
-
- it(`${!legacyNavShown ? 'hides' : 'shows'} the legacy navigation`, () => {
- expect(findScopeLegacyNavigation().exists()).toBe(legacyNavShown);
- expect(findSmallScreenDrawerNavigation().exists()).toBe(legacyNavShown);
- });
-
- it(`${!sidebarNavShown ? 'hides' : 'shows'} the sidebar navigation`, () => {
- expect(findScopeSidebarNavigation().exists()).toBe(sidebarNavShown);
- });
- });
+ ${'test'} | ${true} | ${false}
+ `(
+ 'renders navigation for scope $currentScope',
+ ({ currentScope, sidebarNavShown, legacyNavShown }) => {
+ beforeEach(() => {
+ getterSpies.currentScope = jest.fn(() => currentScope);
+ createComponent({ useSidebarNavigation: sidebarNavShown });
+ });
+
+ it(`renders navigation correctly with legacyNavShown ${legacyNavShown}`, () => {
+ expect(findScopeLegacyNavigation().exists()).toBe(legacyNavShown);
+ expect(findSmallScreenDrawerNavigation().exists()).toBe(legacyNavShown);
+ });
+
+ it(`renders navigation correctly with sidebarNavShown ${sidebarNavShown}`, () => {
+ expect(findScopeSidebarNavigation().exists()).toBe(sidebarNavShown);
+ });
+ },
+ );
});
describe('when useSidebarNavigation=true', () => {
diff --git a/spec/frontend/search/sidebar/components/notes_filters_spec.js b/spec/frontend/search/sidebar/components/notes_filters_spec.js
new file mode 100644
index 00000000000..2fb8e731ef5
--- /dev/null
+++ b/spec/frontend/search/sidebar/components/notes_filters_spec.js
@@ -0,0 +1,28 @@
+import { shallowMount } from '@vue/test-utils';
+import NotesFilters from '~/search/sidebar/components/projects_filters.vue';
+import ArchivedFilter from '~/search/sidebar/components/archived_filter/index.vue';
+import FiltersTemplate from '~/search/sidebar/components/filters_template.vue';
+
+describe('GlobalSearch ProjectsFilters', () => {
+ let wrapper;
+
+ const findArchivedFilter = () => wrapper.findComponent(ArchivedFilter);
+ const findFiltersTemplate = () => wrapper.findComponent(FiltersTemplate);
+
+ const createComponent = () => {
+ wrapper = shallowMount(NotesFilters);
+ };
+
+ describe('Renders correctly', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+ it('renders ArchivedFilter', () => {
+ expect(findArchivedFilter().exists()).toBe(true);
+ });
+
+ it('renders FiltersTemplate', () => {
+ expect(findFiltersTemplate().exists()).toBe(true);
+ });
+ });
+});
diff --git a/spec/frontend/search/sidebar/components/projects_filters_specs.js b/spec/frontend/search/sidebar/components/projects_filters_spec.js
index 15e3254e289..930b7263ea4 100644
--- a/spec/frontend/search/sidebar/components/projects_filters_specs.js
+++ b/spec/frontend/search/sidebar/components/projects_filters_spec.js
@@ -1,6 +1,6 @@
import { shallowMount } from '@vue/test-utils';
import ProjectsFilters from '~/search/sidebar/components/projects_filters.vue';
-import ArchivedFilter from '~/search/sidebar/components/language_filter/index.vue';
+import ArchivedFilter from '~/search/sidebar/components/archived_filter/index.vue';
import FiltersTemplate from '~/search/sidebar/components/filters_template.vue';
describe('GlobalSearch ProjectsFilters', () => {
diff --git a/spec/frontend/security_configuration/components/continuous_vulnerability_scan_spec.js b/spec/frontend/security_configuration/components/continuous_vulnerability_scan_spec.js
new file mode 100644
index 00000000000..84a468e4dd8
--- /dev/null
+++ b/spec/frontend/security_configuration/components/continuous_vulnerability_scan_spec.js
@@ -0,0 +1,124 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlBadge, GlToggle } from '@gitlab/ui';
+import VueApollo from 'vue-apollo';
+import Vue from 'vue';
+import ProjectSetContinuousVulnerabilityScanning from '~/security_configuration/graphql/project_set_continuous_vulnerability_scanning.graphql';
+import ContinuousVulnerabilityScan from '~/security_configuration/components/continuous_vulnerability_scan.vue';
+import createMockApollo from 'helpers/mock_apollo_helper';
+
+Vue.use(VueApollo);
+
+const setCVSMockResponse = {
+ data: {
+ projectSetContinuousVulnerabilityScanning: {
+ continuousVulnerabilityScanningEnabled: true,
+ errors: [],
+ },
+ },
+};
+
+const defaultProvide = {
+ continuousVulnerabilityScansEnabled: true,
+ projectFullPath: 'project/full/path',
+};
+
+describe('ContinuousVulnerabilityScan', () => {
+ let wrapper;
+ let apolloProvider;
+ let requestHandlers;
+
+ const createComponent = (options) => {
+ requestHandlers = {
+ setCVSMutationHandler: jest.fn().mockResolvedValue(setCVSMockResponse),
+ };
+
+ apolloProvider = createMockApollo([
+ [ProjectSetContinuousVulnerabilityScanning, requestHandlers.setCVSMutationHandler],
+ ]);
+
+ wrapper = shallowMount(ContinuousVulnerabilityScan, {
+ propsData: {
+ feature: {
+ available: true,
+ configured: true,
+ },
+ },
+ provide: {
+ glFeatures: {
+ dependencyScanningOnAdvisoryIngestion: true,
+ },
+ ...defaultProvide,
+ },
+ apolloProvider,
+ ...options,
+ });
+ };
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ apolloProvider = null;
+ });
+
+ const findBadge = () => wrapper.findComponent(GlBadge);
+ const findToggle = () => wrapper.findComponent(GlToggle);
+
+ it('renders the component', () => {
+ expect(wrapper.exists()).toBe(true);
+ });
+
+ it('renders the correct title', () => {
+ expect(wrapper.text()).toContain('Continuous Vulnerability Scan');
+ });
+
+ it('renders the badge and toggle component with correct values', () => {
+ expect(findBadge().exists()).toBe(true);
+ expect(findBadge().text()).toBe('Experiment');
+
+ expect(findToggle().exists()).toBe(true);
+ expect(findToggle().props('value')).toBe(defaultProvide.continuousVulnerabilityScansEnabled);
+ });
+
+ it('should disable toggle when feature is not configured', () => {
+ createComponent({
+ propsData: {
+ feature: {
+ available: true,
+ configured: false,
+ },
+ },
+ });
+ expect(findToggle().props('disabled')).toBe(true);
+ });
+
+ it('calls mutation on toggle change with correct payload', () => {
+ findToggle().vm.$emit('change', true);
+
+ expect(requestHandlers.setCVSMutationHandler).toHaveBeenCalledWith({
+ input: {
+ projectPath: 'project/full/path',
+ enable: true,
+ },
+ });
+ });
+
+ describe('when feature flag is disabled', () => {
+ beforeEach(() => {
+ createComponent({
+ provide: {
+ glFeatures: {
+ dependencyScanningOnAdvisoryIngestion: false,
+ },
+ ...defaultProvide,
+ },
+ });
+ });
+
+ it('should not render toggle and badge', () => {
+ expect(findToggle().exists()).toBe(false);
+ expect(findBadge().exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/security_configuration/components/feature_card_spec.js b/spec/frontend/security_configuration/components/feature_card_spec.js
index 983a66a7fd3..c715d01dd58 100644
--- a/spec/frontend/security_configuration/components/feature_card_spec.js
+++ b/spec/frontend/security_configuration/components/feature_card_spec.js
@@ -1,5 +1,6 @@
import { GlIcon } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
+import Vue from 'vue';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { securityFeatures } from '~/security_configuration/components/constants';
import FeatureCard from '~/security_configuration/components/feature_card.vue';
@@ -13,6 +14,10 @@ import {
import { manageViaMRErrorMessage } from '../constants';
import { makeFeature } from './utils';
+const MockComponent = Vue.component('MockComponent', {
+ render: (createElement) => createElement('span'),
+});
+
describe('FeatureCard component', () => {
let feature;
let wrapper;
@@ -389,4 +394,17 @@ describe('FeatureCard component', () => {
});
});
});
+
+ describe('when a slot component is passed', () => {
+ beforeEach(() => {
+ feature = makeFeature({
+ slotComponent: MockComponent,
+ });
+ createComponent({ feature });
+ });
+
+ it('renders the component properly', () => {
+ expect(wrapper.findComponent(MockComponent).exists()).toBe(true);
+ });
+ });
});
diff --git a/spec/lib/gitlab/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb
index df58877f120..1a78d929871 100644
--- a/spec/lib/gitlab/ci/config/entry/job_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb
@@ -426,18 +426,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job, feature_category: :pipeline_compo
expect(entry.errors).to be_empty
expect(entry.timeout).to eq('1m 1s')
end
-
- context 'when update_chronic_duration is disabled' do
- before do
- stub_feature_flags(update_chronic_duration: false)
- end
-
- it 'returns correct timeout' do
- expect(entry).to be_valid
- expect(entry.errors).to be_empty
- expect(entry.timeout).to eq('1m 1s')
- end
- end
end
context 'when it is a release' do
diff --git a/spec/lib/gitlab/time_tracking_formatter_spec.rb b/spec/lib/gitlab/time_tracking_formatter_spec.rb
index dc4ce9458e4..b3372f676d4 100644
--- a/spec/lib/gitlab/time_tracking_formatter_spec.rb
+++ b/spec/lib/gitlab/time_tracking_formatter_spec.rb
@@ -12,28 +12,12 @@ RSpec.describe Gitlab::TimeTrackingFormatter, feature_category: :team_planning d
let(:duration_string) { '3h 20m' }
it { expect(subject).to eq(12_000) }
-
- context 'when update_chronic_duration is false' do
- before do
- stub_feature_flags(update_chronic_duration: false)
- end
-
- it { expect(subject).to eq(12_000) }
- end
end
context 'negative durations' do
let(:duration_string) { '-3h 20m' }
it { expect(subject).to eq(-12_000) }
-
- context 'when update_chronic_duration is false' do
- before do
- stub_feature_flags(update_chronic_duration: false)
- end
-
- it { expect(subject).to eq(-12_000) }
- end
end
context 'durations with months' do
@@ -42,16 +26,6 @@ RSpec.describe Gitlab::TimeTrackingFormatter, feature_category: :team_planning d
it 'uses our custom conversions' do
expect(subject).to eq(576_000)
end
-
- context 'when update_chronic_duration is false' do
- before do
- stub_feature_flags(update_chronic_duration: false)
- end
-
- it 'uses our custom conversions' do
- expect(subject).to eq(576_000)
- end
- end
end
context 'when the duration is nil' do
@@ -69,16 +43,6 @@ RSpec.describe Gitlab::TimeTrackingFormatter, feature_category: :team_planning d
it 'returns nil' do
expect(subject).to be_nil
end
-
- context 'when update_chronic_duration is false' do
- before do
- stub_feature_flags(update_chronic_duration: false)
- end
-
- it 'returns nil' do
- expect(subject).to be_nil
- end
- end
end
context 'when keep_zero is true' do
@@ -87,16 +51,6 @@ RSpec.describe Gitlab::TimeTrackingFormatter, feature_category: :team_planning d
it 'returns zero' do
expect(subject).to eq(0)
end
-
- context 'when update_chronic_duration is false' do
- before do
- stub_feature_flags(update_chronic_duration: false)
- end
-
- it 'returns zero' do
- expect(subject).to eq(0)
- end
- end
end
end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 8adbd27cd1c..149c0609799 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -735,18 +735,6 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
is_expected.to eq(1.day.since)
end
end
-
- context 'when update_chronic_duration is disabled' do
- before do
- stub_feature_flags(update_chronic_duration: false)
- end
-
- it 'returns date after 1 day' do
- freeze_time do
- is_expected.to eq(1.day.since)
- end
- end
- end
end
context 'when start_in is 1 week' do
@@ -757,18 +745,6 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
is_expected.to eq(1.week.since)
end
end
-
- context 'when update_chronic_duration is disabled' do
- before do
- stub_feature_flags(update_chronic_duration: false)
- end
-
- it 'returns date after 1 week' do
- freeze_time do
- is_expected.to eq(1.week.since)
- end
- end
- end
end
end
@@ -1099,18 +1075,6 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
is_expected.to be_nil
end
-
- context 'when update_chronic_duration is disabled' do
- before do
- stub_feature_flags(update_chronic_duration: false)
- end
-
- it 'assigns a valid duration' do
- build.artifacts_expire_in = '7 days'
-
- is_expected.to be_within(10).of(7.days.to_i)
- end
- end
end
describe '#commit' do
diff --git a/spec/models/concerns/chronic_duration_attribute_spec.rb b/spec/models/concerns/chronic_duration_attribute_spec.rb
index fe4a3523909..61b86455840 100644
--- a/spec/models/concerns/chronic_duration_attribute_spec.rb
+++ b/spec/models/concerns/chronic_duration_attribute_spec.rb
@@ -39,16 +39,6 @@ RSpec.shared_examples 'ChronicDurationAttribute writer' do
expect(subject.valid?).to be_truthy
end
- context 'when update_chronic_duration is disabled' do
- before do
- stub_feature_flags(update_chronic_duration: false)
- end
-
- it 'parses chronic duration input' do
- expect(subject.send(source_field)).to eq(600)
- end
- end
-
context 'when negative input is used' do
before do
subject.send("#{source_field}=", 3600)
@@ -82,16 +72,6 @@ RSpec.shared_examples 'ChronicDurationAttribute writer' do
it 'passes validation' do
expect(subject.valid?).to be_truthy
end
-
- context 'when update_chronic_duration is disabled' do
- before do
- stub_feature_flags(update_chronic_duration: false)
- end
-
- it 'writes default value' do
- expect(subject.send(source_field)).to eq(default_value)
- end
- end
end
context 'when nil input is used' do
@@ -110,16 +90,6 @@ RSpec.shared_examples 'ChronicDurationAttribute writer' do
it "doesn't raise exception" do
expect { subject.send("#{virtual_field}=", nil) }.not_to raise_error
end
-
- context 'when update_chronic_duration is disabled' do
- before do
- stub_feature_flags(update_chronic_duration: false)
- end
-
- it 'writes default value' do
- expect(subject.send(source_field)).to eq(default_value)
- end
- end
end
end
diff --git a/spec/support/shared_examples/projects/container_repository/cleanup_tags_service_shared_examples.rb b/spec/support/shared_examples/projects/container_repository/cleanup_tags_service_shared_examples.rb
index d55c99b9eb1..f9f8435c211 100644
--- a/spec/support/shared_examples/projects/container_repository/cleanup_tags_service_shared_examples.rb
+++ b/spec/support/shared_examples/projects/container_repository/cleanup_tags_service_shared_examples.rb
@@ -153,17 +153,6 @@ RSpec.shared_examples 'when removing older than 1 day' do
service_response_extra: service_response_extra,
supports_caching: supports_caching,
delete_expectations: delete_expectations
-
- context 'when update_chronic_duration is disabled' do
- before do
- stub_feature_flags(update_chronic_duration: false)
- end
-
- it_behaves_like 'removing the expected tags',
- service_response_extra: service_response_extra,
- supports_caching: supports_caching,
- delete_expectations: delete_expectations
- end
end
RSpec.shared_examples 'when combining all parameters' do