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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/ci/rules.gitlab-ci.yml2
-rw-r--r--.gitleaksignore4
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_header.vue35
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_name.vue12
-rw-r--r--app/assets/javascripts/ci/runner/constants.js1
-rw-r--r--app/assets/javascripts/ci/runner/utils.js11
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue43
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_details_header.vue200
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue25
-rw-r--r--app/assets/javascripts/pipelines/graphql/queries/get_pipeline_header_data.query.graphql7
-rw-r--r--app/assets/javascripts/pipelines/pipeline_details_header.js2
-rw-r--r--app/assets/javascripts/projects/settings/branch_rules/components/edit/branch_dropdown.vue2
-rw-r--r--app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js2
-rw-r--r--app/helpers/projects/pipeline_helper.rb3
-rw-r--r--app/helpers/projects_helper.rb1
-rw-r--r--app/policies/project_policy.rb7
-rw-r--r--app/presenters/ci/pipeline_presenter.rb26
-rw-r--r--app/presenters/merge_request_presenter.rb4
-rw-r--r--app/views/projects/mirrors/_mirror_repos_push.html.haml15
-rw-r--r--app/views/projects/pipelines/_info.html.haml2
-rw-r--r--app/views/protected_branches/shared/_create_protected_branch.html.haml2
-rw-r--r--app/workers/incident_management/close_incident_worker.rb2
-rw-r--r--config/feature_flags/development/model_registry.yml (renamed from config/feature_flags/ops/circuit_breaker.yml)12
-rw-r--r--doc/api/protected_branches.md4
-rw-r--r--doc/architecture/blueprints/runner_tokens/index.md72
-rw-r--r--doc/ci/pipelines/schedules.md2
-rw-r--r--doc/development/jh_features_review.md13
-rw-r--r--doc/integration/datadog.md2
-rw-r--r--doc/integration/jenkins.md2
-rw-r--r--doc/integration/jira/configure.md4
-rw-r--r--doc/user/admin_area/moderate_users.md11
-rw-r--r--doc/user/project/deploy_keys/index.md2
-rw-r--r--doc/user/project/import/github.md4
-rw-r--r--doc/user/project/integrations/asana.md3
-rw-r--r--doc/user/project/integrations/bamboo.md3
-rw-r--r--doc/user/project/integrations/bugzilla.md3
-rw-r--r--doc/user/project/integrations/clickup.md3
-rw-r--r--doc/user/project/integrations/ewm.md3
-rw-r--r--doc/user/project/integrations/hangouts_chat.md2
-rw-r--r--doc/user/project/integrations/irker.md5
-rw-r--r--doc/user/project/integrations/pivotal_tracker.md3
-rw-r--r--doc/user/project/integrations/pumble.md2
-rw-r--r--doc/user/project/integrations/redmine.md3
-rw-r--r--doc/user/project/integrations/slack.md4
-rw-r--r--doc/user/project/integrations/unify_circuit.md3
-rw-r--r--doc/user/project/integrations/webex_teams.md3
-rw-r--r--doc/user/project/integrations/youtrack.md3
-rw-r--r--doc/user/project/integrations/zentao.md2
-rw-r--r--doc/user/project/protected_branches.md21
-rw-r--r--doc/user/project/repository/push_rules.md1
-rw-r--r--doc/user/project/wiki/index.md2
-rw-r--r--lib/gitlab_settings/settings.rb10
-rw-r--r--locale/gitlab.pot48
-rw-r--r--package.json4
-rw-r--r--spec/features/admin/admin_runners_spec.rb4
-rw-r--r--spec/frontend/ci/runner/admin_runner_show/admin_runner_show_app_spec.js3
-rw-r--r--spec/frontend/ci/runner/components/runner_header_spec.js5
-rw-r--r--spec/frontend/ci/runner/group_runner_show/group_runner_show_app_spec.js3
-rw-r--r--spec/frontend/ci/runner/runner_edit/runner_edit_app_spec.js3
-rw-r--r--spec/frontend/fixtures/pipeline_header.rb62
-rw-r--r--spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js77
-rw-r--r--spec/frontend/pipelines/mock_data.js5
-rw-r--r--spec/frontend/pipelines/pipeline_details_header_spec.js118
-rw-r--r--spec/frontend/pipelines/time_ago_spec.js25
-rw-r--r--spec/helpers/projects/pipeline_helper_spec.rb3
-rw-r--r--spec/helpers/projects_helper_spec.rb1
-rw-r--r--spec/lib/gitlab_settings/settings_spec.rb9
-rw-r--r--spec/policies/project_policy_spec.rb20
-rw-r--r--spec/presenters/ci/pipeline_presenter_spec.rb75
-rw-r--r--spec/presenters/merge_request_presenter_spec.rb4
-rw-r--r--spec/support/shared_examples/features/runners_shared_examples.rb2
-rw-r--r--yarn.lock18
72 files changed, 809 insertions, 295 deletions
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml
index 9ab2e1591d5..3e1eb2c75ec 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -1367,6 +1367,8 @@
rules:
- <<: *if-not-canonical-namespace
when: never
+ - <<: *if-security-merge-request
+ when: never
- <<: *if-merge-request-targeting-stable-branch
changes: *setup-test-env-patterns
when: always
diff --git a/.gitleaksignore b/.gitleaksignore
index 56652ab4dae..df50b4bdfae 100644
--- a/.gitleaksignore
+++ b/.gitleaksignore
@@ -1,3 +1,3 @@
-7e07fe42d34916b276a7b068f4faa8bdc0ebc984:doc/architecture/blueprints/runner_tokens/index.md:gitlab-rrt:485
-f6504b498548380198ad38295d9caa71412115f0:doc/architecture/blueprints/runner_tokens/index.md:generic-api-key:506
+5f8440d74ba194204935669f6f98fe9c08a21200:doc/architecture/blueprints/runner_tokens/index.md:gitlab-rrt:504
+a349496b88d2add528669f5566ef458d90fc7fba:doc/architecture/blueprints/runner_tokens/index.md:generic-api-key:516
afedb913baf4203aa688421873fdb9f94649578e:doc/api/users.md:generic-api-key:2201
diff --git a/app/assets/javascripts/ci/runner/components/runner_header.vue b/app/assets/javascripts/ci/runner/components/runner_header.vue
index 9e29dc7a52e..61d8d264601 100644
--- a/app/assets/javascripts/ci/runner/components/runner_header.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_header.vue
@@ -1,9 +1,8 @@
<script>
import { GlIcon, GlSprintf, GlTooltipDirective } from '@gitlab/ui';
-import { sprintf } from '~/locale';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
-import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { I18N_DETAILS_TITLE, I18N_LOCKED_RUNNER_DESCRIPTION } from '../constants';
+import { I18N_LOCKED_RUNNER_DESCRIPTION } from '../constants';
+import { formatRunnerName } from '../utils';
import RunnerTypeBadge from './runner_type_badge.vue';
import RunnerStatusBadge from './runner_status_badge.vue';
@@ -25,9 +24,8 @@ export default {
},
},
computed: {
- heading() {
- const id = getIdFromGraphQLId(this.runner.id);
- return sprintf(I18N_DETAILS_TITLE, { runner_id: id });
+ name() {
+ return formatRunnerName(this.runner);
},
},
I18N_LOCKED_RUNNER_DESCRIPTION,
@@ -35,16 +33,16 @@ export default {
</script>
<template>
<div
- class="gl-display-flex gl-justify-content-space-between gl-align-items-center gl-gap-3 gl-flex-wrap gl-py-5 gl-border-b-1 gl-border-b-solid gl-border-b-gray-100"
+ class="gl-display-flex gl-justify-content-space-between gl-align-items-flex-start gl-gap-3 gl-flex-wrap gl-py-5"
>
- <div class="gl-display-flex gl-align-items-flex-start gl-gap-3 gl-flex-wrap">
- <runner-status-badge :runner="runner" />
- <runner-type-badge v-if="runner" :type="runner.runnerType" />
- <span>
- <template v-if="runner.createdAt">
- <gl-sprintf :message="__('%{runner} created %{timeago}')">
- <template #runner>
- <strong>{{ heading }}</strong>
+ <div>
+ <h1 class="gl-font-size-h-display gl-my-0">{{ name }}</h1>
+ <div class="gl-display-flex gl-align-items-flex-start gl-gap-3 gl-flex-wrap gl-mt-3">
+ <runner-status-badge :runner="runner" />
+ <runner-type-badge :type="runner.runnerType" />
+ <span v-if="runner.createdAt">
+ <gl-sprintf :message="__('%{locked} created %{timeago}')">
+ <template #locked>
<gl-icon
v-if="runner.locked"
v-gl-tooltip="$options.I18N_LOCKED_RUNNER_DESCRIPTION"
@@ -56,11 +54,8 @@ export default {
<time-ago :time="runner.createdAt" />
</template>
</gl-sprintf>
- </template>
- <template v-else>
- <strong>{{ heading }}</strong>
- </template>
- </span>
+ </span>
+ </div>
</div>
<div class="gl-display-flex gl-gap-3 gl-flex-wrap"><slot name="actions"></slot></div>
</div>
diff --git a/app/assets/javascripts/ci/runner/components/runner_name.vue b/app/assets/javascripts/ci/runner/components/runner_name.vue
index d4ecfd2d776..a877ff0f06c 100644
--- a/app/assets/javascripts/ci/runner/components/runner_name.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_name.vue
@@ -1,5 +1,5 @@
<script>
-import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import { formatRunnerName } from '../utils';
export default {
props: {
@@ -8,13 +8,13 @@ export default {
required: true,
},
},
- methods: {
- getIdFromGraphQLId,
+ computed: {
+ name() {
+ return formatRunnerName(this.runner);
+ },
},
};
</script>
<template>
- <span class="gl-font-weight-bold gl-vertical-align-middle"
- >#{{ getIdFromGraphQLId(runner.id) }} ({{ runner.shortSha }})</span
- >
+ <span class="gl-font-weight-bold gl-vertical-align-middle">{{ name }}</span>
</template>
diff --git a/app/assets/javascripts/ci/runner/constants.js b/app/assets/javascripts/ci/runner/constants.js
index b5fcc14be37..28263b5cfd9 100644
--- a/app/assets/javascripts/ci/runner/constants.js
+++ b/app/assets/javascripts/ci/runner/constants.js
@@ -9,7 +9,6 @@ export const RUNNER_DETAILS_PROJECTS_PAGE_SIZE = 5;
export const RUNNER_DETAILS_JOBS_PAGE_SIZE = 30;
export const I18N_FETCH_ERROR = s__('Runners|Something went wrong while fetching runner data.');
-export const I18N_DETAILS_TITLE = s__('Runners|Runner #%{runner_id}');
export const FILTER_CSS_CLASSES =
'gl-bg-gray-10 gl-p-5 gl-border-solid gl-border-gray-100 gl-border-0 gl-border-t-1 gl-border-b-1';
diff --git a/app/assets/javascripts/ci/runner/utils.js b/app/assets/javascripts/ci/runner/utils.js
index 1ca0a9e86b5..bb1ffca62ee 100644
--- a/app/assets/javascripts/ci/runner/utils.js
+++ b/app/assets/javascripts/ci/runner/utils.js
@@ -1,4 +1,5 @@
import { formatNumber } from '~/locale';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { RUNNER_JOB_COUNT_LIMIT } from './constants';
/**
@@ -81,3 +82,13 @@ export const getPaginationVariables = (pagination, pageSize = 10) => {
export const parseInterval = (interval) => {
return typeof interval === 'string' ? parseInt(interval, 10) : null;
};
+
+/**
+ * Creates formatted runner name
+ *
+ * @param {Object} runner - Runner object
+ * @returns Formatted name
+ */
+export const formatRunnerName = ({ id, shortSha }) => {
+ return `#${getIdFromGraphQLId(id)} (${shortSha})`;
+};
diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
index 031910b1cdb..9bde88da2b8 100644
--- a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
+++ b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
@@ -2,7 +2,6 @@
import { GlButton, GlIcon, GlSprintf, GlLink, GlFormCheckbox, GlToggle } from '@gitlab/ui';
import ConfirmDanger from '~/vue_shared/components/confirm_danger/confirm_danger.vue';
import settingsMixin from 'ee_else_ce/pages/projects/shared/permissions/mixins/settings_pannel_mixin';
-import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { __, s__ } from '~/locale';
import {
VISIBILITY_LEVEL_PRIVATE_INTEGER,
@@ -93,7 +92,7 @@ export default {
'jh_component/pages/projects/shared/permissions/components/other_project_settings.vue'
),
},
- mixins: [settingsMixin, glFeatureFlagsMixin()],
+ mixins: [settingsMixin],
props: {
requestCveAvailable: {
@@ -250,7 +249,6 @@ export default {
wikiAccessLevel: featureAccessLevel.EVERYONE,
snippetsAccessLevel: featureAccessLevel.EVERYONE,
pagesAccessLevel: featureAccessLevel.EVERYONE,
- metricsDashboardAccessLevel: featureAccessLevel.PROJECT_MEMBERS,
analyticsAccessLevel: featureAccessLevel.EVERYONE,
requirementsAccessLevel: featureAccessLevel.EVERYONE,
securityAndComplianceAccessLevel: featureAccessLevel.PROJECT_MEMBERS,
@@ -360,9 +358,6 @@ export default {
packageRegistryApiForEveryoneEnabledShown() {
return this.visibilityLevel !== VISIBILITY_LEVEL_PUBLIC_INTEGER;
},
- monitorOperationsFeatureAccessLevelOptions() {
- return this.featureAccessLevelOptions.filter(([value]) => value <= this.monitorAccessLevel);
- },
},
watch: {
@@ -397,10 +392,6 @@ export default {
featureAccessLevel.PROJECT_MEMBERS,
this.snippetsAccessLevel,
);
- this.metricsDashboardAccessLevel = Math.min(
- featureAccessLevel.PROJECT_MEMBERS,
- this.metricsDashboardAccessLevel,
- );
this.analyticsAccessLevel = Math.min(
featureAccessLevel.PROJECT_MEMBERS,
this.analyticsAccessLevel,
@@ -464,8 +455,6 @@ export default {
this.pagesAccessLevel = featureAccessLevel.EVERYONE;
if (this.analyticsAccessLevel > featureAccessLevel.NOT_ENABLED)
this.analyticsAccessLevel = featureAccessLevel.EVERYONE;
- if (this.metricsDashboardAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
- this.metricsDashboardAccessLevel = featureAccessLevel.EVERYONE;
if (this.requirementsAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
this.requirementsAccessLevel = featureAccessLevel.EVERYONE;
if (this.environmentsAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
@@ -503,22 +492,9 @@ export default {
else if (oldValue === featureAccessLevel.NOT_ENABLED)
toggleHiddenClassBySelector('.merge-requests-feature', false);
},
-
- monitorAccessLevel(value, oldValue) {
- this.updateSubFeatureAccessLevel(value, oldValue);
- },
},
methods: {
- updateSubFeatureAccessLevel(value, oldValue) {
- if (value < oldValue) {
- // sub-features cannot have more permissive access level
- this.metricsDashboardAccessLevel = Math.min(this.metricsDashboardAccessLevel, value);
- } else if (oldValue === 0) {
- this.metricsDashboardAccessLevel = value;
- }
- },
-
highlightChanges() {
this.highlightChangesClass = true;
this.$nextTick(() => {
@@ -930,23 +906,6 @@ export default {
name="project[project_feature_attributes][monitor_access_level]"
/>
</project-setting-row>
- <div
- v-if="!glFeatures.removeMonitorMetrics"
- class="project-feature-setting-group gl-pl-5 gl-md-pl-7"
- >
- <project-setting-row
- ref="metrics-visibility-settings"
- :label="__('Metrics Dashboard')"
- :help-text="s__('ProjectSettings|Visualize the project\'s performance metrics.')"
- >
- <project-feature-setting
- v-model="metricsDashboardAccessLevel"
- :show-toggle="false"
- :options="monitorOperationsFeatureAccessLevelOptions"
- name="project[project_feature_attributes][metrics_dashboard_access_level]"
- />
- </project-setting-row>
- </div>
<project-setting-row
ref="environments-settings"
:label="$options.i18n.environmentsLabel"
diff --git a/app/assets/javascripts/pipelines/components/pipeline_details_header.vue b/app/assets/javascripts/pipelines/components/pipeline_details_header.vue
index e7cc55f95fa..7d4395dd579 100644
--- a/app/assets/javascripts/pipelines/components/pipeline_details_header.vue
+++ b/app/assets/javascripts/pipelines/components/pipeline_details_header.vue
@@ -1,9 +1,12 @@
<script>
-import { GlLoadingIcon } from '@gitlab/ui';
-import { __ } from '~/locale';
+import { GlBadge, GlIcon, GlLink, GlLoadingIcon, GlSprintf, GlTooltipDirective } from '@gitlab/ui';
+import { __, s__, sprintf } from '~/locale';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { LOAD_FAILURE, POST_FAILURE, DELETE_FAILURE, DEFAULT } from '../constants';
import getPipelineQuery from '../graphql/queries/get_pipeline_header_data.query.graphql';
+import TimeAgo from './pipelines_list/time_ago.vue';
import { getQueryHeaders } from './graph/utils';
const POLL_INTERVAL = 10000;
@@ -13,7 +16,41 @@ export default {
finishedStatuses: ['FAILED', 'SUCCESS', 'CANCELED'],
components: {
CiBadgeLink,
+ ClipboardButton,
+ GlBadge,
+ GlIcon,
+ GlLink,
GlLoadingIcon,
+ GlSprintf,
+ TimeAgo,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ SafeHtml,
+ },
+ i18n: {
+ scheduleBadgeText: s__('Pipelines|Scheduled'),
+ scheduleBadgeTooltip: __('This pipeline was triggered by a schedule'),
+ childBadgeText: s__('Pipelines|Child pipeline (%{linkStart}parent%{linkEnd})'),
+ childBadgeTooltip: __('This is a child pipeline within the parent pipeline'),
+ latestBadgeText: s__('Pipelines|latest'),
+ latestBadgeTooltip: __('Latest pipeline for the most recent commit on this branch'),
+ mergeTrainBadgeText: s__('Pipelines|merge train'),
+ mergeTrainBadgeTooltip: s__(
+ 'Pipelines|This pipeline ran on the contents of this merge request combined with the contents of all other merge requests queued for merging into the target branch.',
+ ),
+ invalidBadgeText: s__('Pipelines|yaml invalid'),
+ failedBadgeText: s__('Pipelines|error'),
+ autoDevopsBadgeText: s__('Pipelines|Auto DevOps'),
+ autoDevopsBadgeTooltip: __(
+ 'This pipeline makes use of a predefined CI/CD configuration enabled by Auto DevOps.',
+ ),
+ detachedBadgeText: s__('Pipelines|merge request'),
+ detachedBadgeTooltip: s__(
+ "Pipelines|This pipeline ran on the contents of this merge request's source branch, not the target branch.",
+ ),
+ stuckBadgeText: s__('Pipelines|stuck'),
+ stuckBadgeTooltip: s__('Pipelines|This pipeline is stuck'),
},
errorTexts: {
[LOAD_FAILURE]: __('We are currently unable to fetch data for the pipeline header.'),
@@ -58,6 +95,11 @@ export default {
required: false,
default: '',
},
+ refText: {
+ type: String,
+ required: false,
+ default: '',
+ },
badges: {
type: Object,
required: false,
@@ -76,7 +118,9 @@ export default {
iid: this.pipelineIid,
};
},
- update: (data) => data.project.pipeline,
+ update(data) {
+ return data.project.pipeline;
+ },
error() {
this.reportFailure(LOAD_FAILURE);
},
@@ -150,6 +194,36 @@ export default {
};
}
},
+ usersName() {
+ return this.pipeline?.user?.name || '';
+ },
+ userPath() {
+ return this.pipeline?.user?.webPath || '';
+ },
+ shortId() {
+ return this.pipeline?.commit?.shortId || '';
+ },
+ commitPath() {
+ return this.pipeline?.commit?.webPath || '';
+ },
+ totalJobsText() {
+ return sprintf(__('%{jobs} Jobs'), {
+ jobs: this.totalJobs,
+ });
+ },
+ triggeredText() {
+ return sprintf(__('%{linkStart}%{name}%{linkEnd} triggered pipeline for commit'), {
+ name: this.usersName,
+ });
+ },
+ inProgress() {
+ return this.status === 'RUNNING';
+ },
+ inProgressText() {
+ return sprintf(__('In progress, queued for %{queuedDuration} seconds'), {
+ queuedDuration: this.pipeline?.queuedDuration || 0,
+ });
+ },
},
methods: {
reportFailure(errorType, errorMessages = []) {
@@ -164,8 +238,126 @@ export default {
<div class="gl-mt-3">
<gl-loading-icon v-if="loading" class="gl-text-left" size="lg" />
<template v-else>
- <div class="gl-mb-2">
+ <h3 v-if="name" class="gl-mt-0 gl-mb-2" data-testid="pipeline-name">{{ name }}</h3>
+ <div>
<ci-badge-link :status="detailedStatus" />
+ <div class="gl-ml-2 gl-mb-2 gl-display-inline-block gl-h-6">
+ <gl-sprintf :message="triggeredText">
+ <template #link="{ content }">
+ <gl-link
+ :href="userPath"
+ class="gl-text-gray-900 gl-font-weight-bold"
+ target="_blank"
+ >
+ {{ content }}
+ </gl-link>
+ </template>
+ </gl-sprintf>
+ <gl-link
+ :href="commitPath"
+ class="gl-bg-blue-50 gl-rounded-base gl-px-2 gl-mx-2"
+ data-testid="commit-link"
+ >
+ {{ shortId }}
+ </gl-link>
+ <clipboard-button
+ :text="shortId"
+ category="tertiary"
+ :title="__('Copy commit SHA')"
+ size="small"
+ />
+ <time-ago
+ v-if="isFinished"
+ :pipeline="pipeline"
+ class="gl-display-inline gl-mb-0"
+ :display-calendar-icon="false"
+ font-size="gl-font-md"
+ />
+ </div>
+ </div>
+ <div v-safe-html="refText" class="gl-mb-2" data-testid="pipeline-ref-text"></div>
+ <div>
+ <gl-badge
+ v-if="badges.schedule"
+ v-gl-tooltip
+ :title="$options.i18n.scheduleBadgeTooltip"
+ variant="info"
+ >
+ {{ $options.i18n.scheduleBadgeText }}
+ </gl-badge>
+ <gl-badge
+ v-if="badges.child"
+ v-gl-tooltip
+ :title="$options.i18n.childBadgeTooltip"
+ variant="info"
+ >
+ <gl-sprintf :message="$options.i18n.childBadgeText">
+ <template #link="{ content }">
+ <gl-link :href="paths.triggeredByPath" target="_blank">
+ {{ content }}
+ </gl-link>
+ </template>
+ </gl-sprintf>
+ </gl-badge>
+ <gl-badge
+ v-if="badges.latest"
+ v-gl-tooltip
+ :title="$options.i18n.latestBadgeTooltip"
+ variant="success"
+ >
+ {{ $options.i18n.latestBadgeText }}
+ </gl-badge>
+ <gl-badge
+ v-if="badges.mergeTrainPipeline"
+ v-gl-tooltip
+ :title="$options.i18n.mergeTrainBadgeTooltip"
+ variant="info"
+ >
+ {{ $options.i18n.mergeTrainBadgeText }}
+ </gl-badge>
+ <gl-badge v-if="badges.invalid" v-gl-tooltip :title="yamlErrors" variant="danger">
+ {{ $options.i18n.invalidBadgeText }}
+ </gl-badge>
+ <gl-badge v-if="badges.failed" v-gl-tooltip :title="failureReason" variant="danger">
+ {{ $options.i18n.failedBadgeText }}
+ </gl-badge>
+ <gl-badge
+ v-if="badges.autoDevops"
+ v-gl-tooltip
+ :title="$options.i18n.autoDevopsBadgeTooltip"
+ variant="info"
+ >
+ {{ $options.i18n.autoDevopsBadgeText }}
+ </gl-badge>
+ <gl-badge
+ v-if="badges.detached"
+ v-gl-tooltip
+ :title="$options.i18n.detachedBadgeTooltip"
+ variant="info"
+ data-qa-selector="merge_request_badge_tag"
+ >
+ {{ $options.i18n.detachedBadgeText }}
+ </gl-badge>
+ <gl-badge
+ v-if="badges.stuck"
+ v-gl-tooltip
+ :title="$options.i18n.stuckBadgeTooltip"
+ variant="warning"
+ >
+ {{ $options.i18n.stuckBadgeText }}
+ </gl-badge>
+ <span class="gl-ml-2" data-testid="total-jobs">
+ <gl-icon name="pipeline" />
+ {{ totalJobsText }}
+ </span>
+ <span v-if="isFinished" class="gl-ml-2" data-testid="compute-credits">
+ <gl-icon name="quota" />
+ {{ computeCredits }}
+ </span>
+ <span v-if="inProgress" class="gl-ml-2" data-testid="pipeline-running-text">
+ <gl-icon name="timer" />
+ {{ inProgressText }}
+ </span>
</div>
</template>
</div>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue b/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue
index d068eb16ed4..b7c812162b1 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue
@@ -14,6 +14,17 @@ export default {
type: Object,
required: true,
},
+ displayCalendarIcon: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ fontSize: {
+ type: String,
+ required: false,
+ default: 'gl-font-sm',
+ validator: (fontSize) => ['gl-font-sm', 'gl-font-md'].includes(fontSize),
+ },
},
computed: {
duration() {
@@ -23,7 +34,7 @@ export default {
return formatTime(this.duration * 1000);
},
finishedTime() {
- return this.pipeline?.details?.finished_at;
+ return this.pipeline?.details?.finished_at || this.pipeline?.finishedAt;
},
showInProgress() {
return !this.duration && !this.finishedTime && !this.skipped;
@@ -35,13 +46,13 @@ export default {
return this.pipeline?.details?.status?.label === 'skipped';
},
stuck() {
- return this.pipeline.flags.stuck;
+ return this.pipeline?.flags?.stuck;
},
},
};
</script>
<template>
- <div class="gl-display-flex gl-flex-direction-column gl-font-sm time-ago">
+ <div class="gl-display-flex gl-flex-direction-column time-ago" :class="fontSize">
<span
v-if="showInProgress"
class="gl-display-inline-flex gl-align-items-center"
@@ -63,7 +74,13 @@ export default {
</p>
<p v-if="finishedTime" class="finished-at gl-display-inline-flex gl-align-items-center">
- <gl-icon name="calendar" class="gl-mr-2" :size="12" />
+ <gl-icon
+ v-if="displayCalendarIcon"
+ name="calendar"
+ class="gl-mr-2"
+ :size="12"
+ data-testid="calendar-icon"
+ />
<time
v-gl-tooltip
diff --git a/app/assets/javascripts/pipelines/graphql/queries/get_pipeline_header_data.query.graphql b/app/assets/javascripts/pipelines/graphql/queries/get_pipeline_header_data.query.graphql
index 47bc167ca52..a47df2c0197 100644
--- a/app/assets/javascripts/pipelines/graphql/queries/get_pipeline_header_data.query.graphql
+++ b/app/assets/javascripts/pipelines/graphql/queries/get_pipeline_header_data.query.graphql
@@ -32,6 +32,13 @@ query getPipelineHeaderData($fullPath: ID!, $iid: ID!) {
emoji
}
}
+ commit {
+ id
+ shortId
+ webPath
+ }
+ finishedAt
+ queuedDuration
}
}
}
diff --git a/app/assets/javascripts/pipelines/pipeline_details_header.js b/app/assets/javascripts/pipelines/pipeline_details_header.js
index e3e95ba3ccc..807ef225edd 100644
--- a/app/assets/javascripts/pipelines/pipeline_details_header.js
+++ b/app/assets/javascripts/pipelines/pipeline_details_header.js
@@ -62,6 +62,7 @@ export const createPipelineDetailsHeaderApp = (elSelector, apolloProvider, graph
autoDevops,
detached,
stuck,
+ refText,
} = el.dataset;
// eslint-disable-next-line no-new
@@ -86,6 +87,7 @@ export const createPipelineDetailsHeaderApp = (elSelector, apolloProvider, graph
computeCredits,
yamlErrors,
failureReason,
+ refText,
badges: {
schedule: parseBoolean(schedule),
child: parseBoolean(child),
diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/edit/branch_dropdown.vue b/app/assets/javascripts/projects/settings/branch_rules/components/edit/branch_dropdown.vue
index 3dcacf9eb34..6494456d560 100644
--- a/app/assets/javascripts/projects/settings/branch_rules/components/edit/branch_dropdown.vue
+++ b/app/assets/javascripts/projects/settings/branch_rules/components/edit/branch_dropdown.vue
@@ -55,7 +55,7 @@ export default {
},
searchInputDelay: 250,
wildcardsHelpPath: helpPagePath('user/project/protected_branches', {
- anchor: 'configure-multiple-protected-branches-by-using-a-wildcard',
+ anchor: 'protect-multiple-branches-with-wildcard-rules',
}),
props: {
projectPath: {
diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js b/app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js
index b71c33d2b91..a45ed5c68af 100644
--- a/app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js
+++ b/app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js
@@ -49,7 +49,7 @@ export const BRANCH_PARAM_NAME = 'branch';
export const ALL_BRANCHES_WILDCARD = '*';
export const WILDCARDS_HELP_PATH =
- 'user/project/protected_branches#configure-multiple-protected-branches-by-using-a-wildcard';
+ 'user/project/protected_branches#protect-multiple-branches-with-wildcard-rules';
export const PROTECTED_BRANCHES_HELP_PATH = 'user/project/protected_branches';
diff --git a/app/helpers/projects/pipeline_helper.rb b/app/helpers/projects/pipeline_helper.rb
index 8a65340062e..caebbd5250e 100644
--- a/app/helpers/projects/pipeline_helper.rb
+++ b/app/helpers/projects/pipeline_helper.rb
@@ -44,7 +44,8 @@ module Projects
failed: pipeline.failure_reason?.to_s,
auto_devops: pipeline.auto_devops_source?.to_s,
detached: pipeline.detached_merge_request_pipeline?.to_s,
- stuck: pipeline.stuck?
+ stuck: pipeline.stuck?,
+ ref_text: pipeline.ref_text
}
end
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 1e87d2861d4..bf967dce7bf 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -737,7 +737,6 @@ module ProjectsHelper
containerRegistryEnabled: !!project.container_registry_enabled,
lfsEnabled: !!project.lfs_enabled,
emailsDisabled: project.emails_disabled?,
- metricsDashboardAccessLevel: feature.metrics_dashboard_access_level,
monitorAccessLevel: feature.monitor_access_level,
showDefaultAwardEmojis: project.show_default_award_emojis?,
warnAboutPotentiallyUnwantedCharacters: project.warn_about_potentially_unwanted_characters?,
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 47d8d0eef3e..15f91cae86b 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -163,6 +163,9 @@ class ProjectPolicy < BasePolicy
condition(:service_desk_enabled) { @subject.service_desk_enabled? }
with_scope :subject
+ condition(:model_registry_enabled) { Feature.enabled?(:model_registry, @subject) }
+
+ with_scope :subject
condition(:resource_access_token_feature_available) do
resource_access_token_feature_available?
end
@@ -892,6 +895,10 @@ class ProjectPolicy < BasePolicy
enable :add_catalog_resource
end
+ rule { model_registry_enabled }.policy do
+ enable :read_model_registry
+ end
+
private
def user_is_user?
diff --git a/app/presenters/ci/pipeline_presenter.rb b/app/presenters/ci/pipeline_presenter.rb
index 8c9ff49b0e7..4ad88188e45 100644
--- a/app/presenters/ci/pipeline_presenter.rb
+++ b/app/presenters/ci/pipeline_presenter.rb
@@ -65,7 +65,7 @@ module Ci
'%.2f' % pipeline.coverage
end
- def ref_text
+ def ref_text_legacy
if pipeline.detached_merge_request_pipeline?
_("for %{link_to_merge_request} with %{link_to_merge_request_source_branch}")
.html_safe % {
@@ -87,6 +87,28 @@ module Ci
end
end
+ def ref_text
+ if pipeline.detached_merge_request_pipeline?
+ _("For merge request %{link_to_merge_request} to merge %{link_to_merge_request_source_branch}")
+ .html_safe % {
+ link_to_merge_request: link_to_merge_request,
+ link_to_merge_request_source_branch: link_to_merge_request_source_branch
+ }
+ elsif pipeline.merged_result_pipeline?
+ _("For merge request %{link_to_merge_request} to merge %{link_to_merge_request_source_branch} into %{link_to_merge_request_target_branch}")
+ .html_safe % {
+ link_to_merge_request: link_to_merge_request,
+ link_to_merge_request_source_branch: link_to_merge_request_source_branch,
+ link_to_merge_request_target_branch: link_to_merge_request_target_branch
+ }
+ elsif pipeline.ref && pipeline.ref_exists?
+ _("For %{link_to_pipeline_ref}")
+ .html_safe % { link_to_pipeline_ref: link_to_pipeline_ref }
+ elsif pipeline.ref
+ _("For %{ref}").html_safe % { ref: plain_ref_name }
+ end
+ end
+
def all_related_merge_request_text(limit: nil)
if all_related_merge_requests.none?
_("No related merge requests found.")
@@ -106,7 +128,7 @@ module Ci
def link_to_pipeline_ref
ApplicationController.helpers.link_to(pipeline.ref,
project_commits_path(pipeline.project, pipeline.ref),
- class: "ref-name")
+ class: "ref-name gl-link gl-bg-blue-50 gl-rounded-base gl-px-2")
end
def link_to_merge_request
diff --git a/app/presenters/merge_request_presenter.rb b/app/presenters/merge_request_presenter.rb
index 12f4b0496e4..8d2baa6ee99 100644
--- a/app/presenters/merge_request_presenter.rb
+++ b/app/presenters/merge_request_presenter.rb
@@ -197,7 +197,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
def source_branch_link
if source_branch_exists?
- link_to(source_branch, source_branch_commits_path, class: 'ref-name')
+ link_to(source_branch, source_branch_commits_path, class: 'ref-name gl-link gl-bg-blue-50 gl-rounded-base gl-px-2')
else
content_tag(:span, source_branch, class: 'ref-name')
end
@@ -205,7 +205,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
def target_branch_link
if target_branch_exists?
- link_to(target_branch, target_branch_commits_path, class: 'ref-name')
+ link_to(target_branch, target_branch_commits_path, class: 'ref-name gl-link gl-bg-blue-50 gl-rounded-base gl-px-2')
else
content_tag(:span, target_branch, class: 'ref-name')
end
diff --git a/app/views/projects/mirrors/_mirror_repos_push.html.haml b/app/views/projects/mirrors/_mirror_repos_push.html.haml
index 136f504084e..0947359a753 100644
--- a/app/views/projects/mirrors/_mirror_repos_push.html.haml
+++ b/app/views/projects/mirrors/_mirror_repos_push.html.haml
@@ -8,9 +8,12 @@
= rm_f.hidden_field :keep_divergent_refs, class: 'js-mirror-keep-divergent-refs-hidden'
= render partial: 'projects/mirrors/ssh_host_keys', locals: { f: rm_f }
= render partial: 'projects/mirrors/authentication_method', locals: { f: rm_f }
- .form-check.gl-mb-3
- = check_box_tag :keep_divergent_refs, '1', false, class: 'js-mirror-keep-divergent-refs form-check-input'
- = label_tag :keep_divergent_refs, _('Keep divergent refs'), class: 'form-check-label'
- .form-text.text-muted
- - link_opening_tag = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe
- = html_escape(_('Do not force push over diverged refs. After the mirror is created, this setting can only be modified using the API. %{mirroring_docs_link_start}Learn more about this option%{link_closing_tag} and %{mirroring_api_docs_link_start}the API.%{link_closing_tag}')) % { mirroring_docs_link_start: link_opening_tag % {url: help_page_path('user/project/repository/mirror/push.md', anchor: 'keep-divergent-refs')}, mirroring_api_docs_link_start: link_opening_tag % {url: help_page_path('api/remote_mirrors')}, link_closing_tag: '</a>'.html_safe }
+ .form-group
+ = render Pajamas::CheckboxTagComponent.new(name: :keep_divergent_refs,
+ checkbox_options: { class: 'js-mirror-keep-divergent-refs' },
+ label_options: { class: 'gl-mb-0!' }) do |c|
+ = c.label do
+ = _('Keep divergent refs')
+ = c.help_text do
+ - link_opening_tag = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe
+ = html_escape(_('Do not force push over diverged refs. After the mirror is created, this setting can only be modified using the API. %{mirroring_docs_link_start}Learn more about this option%{link_closing_tag} and %{mirroring_api_docs_link_start}the API.%{link_closing_tag}')) % { mirroring_docs_link_start: link_opening_tag % {url: help_page_path('user/project/repository/mirror/push.md', anchor: 'keep-divergent-refs')}, mirroring_api_docs_link_start: link_opening_tag % {url: help_page_path('api/remote_mirrors')}, link_closing_tag: '</a>'.html_safe }
diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml
index 3ff370dfaa4..753bb77e755 100644
--- a/app/views/projects/pipelines/_info.html.haml
+++ b/app/views/projects/pipelines/_info.html.haml
@@ -16,7 +16,7 @@
.icon-container
= sprite_icon('clock', css_class: 'gl-top-0!')
= n_('%d job', '%d jobs', @pipeline.total_size) % @pipeline.total_size
- = @pipeline.ref_text
+ = @pipeline.ref_text_legacy
- if @pipeline.finished_at
- duration = time_interval_in_words(@pipeline.duration)
- queued_duration = time_interval_in_words(@pipeline.queued_duration)
diff --git a/app/views/protected_branches/shared/_create_protected_branch.html.haml b/app/views/protected_branches/shared/_create_protected_branch.html.haml
index 62cf81a59d4..d97347b89de 100644
--- a/app/views/protected_branches/shared/_create_protected_branch.html.haml
+++ b/app/views/protected_branches/shared/_create_protected_branch.html.haml
@@ -13,7 +13,7 @@
- else
= render partial: "protected_branches/shared/dropdown", locals: { f: f, toggle_classes: 'gl-w-full! gl-form-input-lg' }
.form-text.text-muted
- - wildcards_url = help_page_url('user/project/protected_branches', anchor: 'configure-multiple-protected-branches-by-using-a-wildcard')
+ - wildcards_url = help_page_url('user/project/protected_branches', anchor: 'protect-multiple-branches-with-wildcard-rules')
- wildcards_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: wildcards_url }
- placeholders = { wildcards_link_start: wildcards_link_start, wildcards_link_end: '</a>', code_tag_start: '<code>', code_tag_end: '</code>' }
- if protected_branch_entity.is_a?(Group)
diff --git a/app/workers/incident_management/close_incident_worker.rb b/app/workers/incident_management/close_incident_worker.rb
index 6b3e1c5321b..c820a8a97bf 100644
--- a/app/workers/incident_management/close_incident_worker.rb
+++ b/app/workers/incident_management/close_incident_worker.rb
@@ -14,7 +14,7 @@ module IncidentManagement
worker_has_external_dependencies!
def perform(issue_id)
- incident = Issue.incident.opened.find_by_id(issue_id)
+ incident = Issue.with_issue_type(:incident).opened.find_by_id(issue_id)
return unless incident
diff --git a/config/feature_flags/ops/circuit_breaker.yml b/config/feature_flags/development/model_registry.yml
index b6a4e1cc327..e73acd9c859 100644
--- a/config/feature_flags/ops/circuit_breaker.yml
+++ b/config/feature_flags/development/model_registry.yml
@@ -1,8 +1,8 @@
---
-name: circuit_breaker
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/117993
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/409619
-milestone: '16.0'
-type: ops
-group: group::ai-enablement
+name: model_registry
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/120096
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/385047
+milestone: '16.1'
+type: development
+group: "group::mlops"
default_enabled: false
diff --git a/doc/api/protected_branches.md b/doc/api/protected_branches.md
index d3091cb1e15..4a82fab125d 100644
--- a/doc/api/protected_branches.md
+++ b/doc/api/protected_branches.md
@@ -21,7 +21,9 @@ The access levels are defined in the `ProtectedRefAccess.allowed_access_levels`
> Deploy key information [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/116846) in GitLab 16.0.
-Gets a list of protected branches from a project as they are defined [in the UI](../user/project/protected_branches.md#configure-a-protected-branch). If a wildcard is set, it is returned instead of the exact name of the branches that match that wildcard.
+Gets a list of [protected branches](../user/project/protected_branches.md) from a project
+as they are defined in the UI. If a wildcard is set, it is returned instead of the exact name
+of the branches that match that wildcard.
```plaintext
GET /projects/:id/protected_branches
diff --git a/doc/architecture/blueprints/runner_tokens/index.md b/doc/architecture/blueprints/runner_tokens/index.md
index 0d3cc9c3e17..1cc47c05d83 100644
--- a/doc/architecture/blueprints/runner_tokens/index.md
+++ b/doc/architecture/blueprints/runner_tokens/index.md
@@ -54,13 +54,13 @@ The remaining concerns become non-issues due to the elimination of the registrat
graph TD
subgraph new[<b>New registration flow</b>]
A[<b>GitLab</b>: User creates a runner in GitLab UI and adds the runner configuration] -->|<b>GitLab</b>: creates ci_runners record and returns<br/>new 'glrt-' prefixed authentication token| B
- B(<b>Runner</b>: User runs 'gitlab-runner register' command with</br>authentication token to register new runner machine with<br/>the GitLab instance) --> C{<b>Runner</b>: Does a .runner_system_id file exist in<br/>the gitlab-runner configuration directory?}
+ B(<b>Runner</b>: User runs 'gitlab-runner register' command with</br>authentication token to register new runner manager with<br/>the GitLab instance) --> C{<b>Runner</b>: Does a .runner_system_id file exist in<br/>the gitlab-runner configuration directory?}
C -->|Yes| D[<b>Runner</b>: Reads existing system ID] --> F
C -->|No| E[<b>Runner</b>: Generates and persists unique system ID] --> F
F[<b>Runner</b>: Issues 'POST /runner/verify' request<br/>to verify authentication token validity] --> G{<b>GitLab</b>: Is the authentication token valid?}
G -->|Yes| H[<b>GitLab</b>: Creates ci_runner_machine database record if missing] --> J[<b>Runner</b>: Store authentication token in .config.toml]
G -->|No| I(<b>GitLab</b>: Returns '403 Forbidden' error) --> K(gitlab-runner register command fails)
- J --> Z(Runner and runner machine are ready for use)
+ J --> Z(Runner and runner manager are ready for use)
end
subgraph current[<b>Current registration flow</b>]
@@ -158,7 +158,7 @@ wherever it publishes the short token SHA.
Given that the runner can potentially be reused with different unique system identifiers,
we should store the unique system ID in the database.
This ensures the unique system ID maps to a GitLab Runner's `system_id` value with the runner token.
-A new `ci_runner_machines` table holds information about each unique runner machine,
+A new `ci_runner_machines` table holds information about each unique runner manager,
with information regarding when the runner last connected, and what type of runner it was.
In the long term, the relevant fields are to be moved from the `ci_runners` into
@@ -398,7 +398,7 @@ scope.
| GitLab Runner | `%15.10` | Make the `gitlab-runner register` command happen in a single operation. |
| GitLab Rails app | `%15.10` | Define feature flag and policies for "New Runner creation workflow" for groups and projects. |
| GitLab Rails app | `%15.10` | Only update runner `contacted_at` and `status` when polled for jobs. |
-| GitLab Rails app | `%15.10` | Add GraphQL type to represent runner machines under `CiRunner`. |
+| GitLab Rails app | `%15.10` | Add GraphQL type to represent runner managers under `CiRunner`. |
| GitLab Rails app | `%15.11` | Implement UI to create new instance runner. |
| GitLab Rails app | `%15.11` | Update service and mutation to accept groups and projects. |
| GitLab Rails app | `%15.11` | Implement UI to create new group/project runners. |
@@ -417,7 +417,6 @@ scope.
| GitLab Rails app | | Add UI to allow disabling use of registration tokens at project or group level. |
| GitLab Rails app | | Introduce `:enforce_create_runner_workflow` feature flag (disabled by default) to control whether use of registration tokens is allowed. |
| GitLab Rails app | | Make [`POST /api/v4/runners` endpoint](../../../api/runners.md#register-a-new-runner) permanently return `HTTP 410 Gone` if either `allow_runner_registration_token` setting or `:enforce_create_runner_workflow` feature flag disables registration tokens.<br/>A future v5 version of the API should return `HTTP 404 Not Found`. |
-| GitLab Rails app | | Start refusing job requests that don't include a unique ID, if either `allow_runner_registration_token` setting or `:enforce_create_runner_workflow` feature flag disables registration tokens. |
| GitLab Rails app | | Hide legacy UI showing registration with a registration token, if `:enforce_create_runner_workflow` feature flag disables registration tokens. |
### Stage 6 - Enforcement
@@ -425,7 +424,8 @@ scope.
| Component | Milestone | Changes |
|------------------|----------:|---------|
| GitLab Rails app | `%16.6` | Enable `:enforce_create_runner_workflow` feature flag by default. |
-| GitLab Rails app | `%16.6` | Start reject job requests that don't include `system_id` value. |
+| GitLab Rails app | | Implement new `:create_runner` PPGAT scope so that we don't require a full `api` scope. |
+| GitLab Rails app | | Document gotchas when [automatically rotating runner tokens](../../../ci/runners/configure_runners.md#automatically-rotate-authentication-tokens) with multiple machines. |
### Stage 7 - Removals
@@ -442,7 +442,8 @@ scope.
### Will my runner registration workflow break?
If no action is taken before your GitLab instance is upgraded to 16.6, then your runner registration
-worflow will break.
+workflow will break.
+Until then, both the new and the old workflow will coexist side-by-side.
For self-managed instances, to continue using the previous runner registration process,
you can disable the `enforce_create_runner_workflow` feature flag until GitLab 17.0.
@@ -456,7 +457,9 @@ When the new runner registration process is introduced, you will:
1. Create a runner directly in the GitLab UI.
1. Receive an authentication token in return.
-1. Use the authentication token instead of the registration token.
+1. Use the authentication token instead of the registration token, whenever you need to register a runner with this
+ configuration. Runner managers registered in multiple hosts will appear under the same runner in the GitLab UI,
+ but with an identifying system ID.
This has added benefits such as preserved ownership records for runners, and minimizes
impact on users.
@@ -491,14 +494,14 @@ Example command for GitLab 15.9:
```shell
gitlab-runner register
+ --non-interactive \
--executor "shell" \
--url "https://gitlab.com/" \
--tag-list "shell,mac,gdk,test" \
- --run-untagged="false" \
- --locked="false" \
- --access-level="not_protected" \
- --non-interactive \
- --registration-token="GR1348941C6YcZVddc8kjtdU-yWYD"
+ --run-untagged "false" \
+ --locked "false" \
+ --access-level "not_protected" \
+ --registration-token "GR1348941C6YcZVddc8kjtdU-yWYD"
```
In GitLab 16.0, the runner will be created in the UI where some of its attributes can be
@@ -508,10 +511,10 @@ to `register`. The following example shows the new command:
```shell
gitlab-runner register
+ --non-interactive \
--executor "shell" \
--url "https://gitlab.com/" \
- --non-interactive \
- --registration-token="glrt-2CR8_eVxiioB1QmzPZwa"
+ --token "glrt-2CR8_eVxiioB1QmzPZwa"
```
### How does this change impact auto-scaling scenarios?
@@ -523,6 +526,41 @@ for each job.
The specific runner can be identified by the unique system ID that is generated when the runner
process is started.
+### Will existing runners continue to work?
+
+Yes, existing runners will continue to work as usual. This change only affects registration of new runners.
+
+### Can runners still be created programmatically?
+
+A new [POST /user/runners REST API](../../../api/users.md#create-a-ci-runner) was introduced in
+GitLab 15.11, which allows a runner to be created in the context of an authenticated user. This should only be used in
+scenarios where the runner configuration is dynamic, or not reusable. If the runner configuration is static, it is
+preferable to reuse the authentication token of an existing runner.
+
+The following snippet shows how a group runner could be created and registered with a
+[Group Access Token](../../../user/group/settings/group_access_tokens.md) using the new creation flow.
+The process is very similar when using [Project Access Tokens](../../../user/project/settings/project_access_tokens.md)
+or [Personal Access Tokens](../../../user/profile/personal_access_tokens.md):
+
+```shell
+# `GROUP_ID` contains the numerical ID of the group where the runner will be created
+# `GITLAB_TOKEN` can be a Personal Access Token for a group owner, or a Group Access Token on the respective group
+# created with `owner` access and `api` scope.
+#
+# The output will be parsed by `jq` to extract the token of the newly created runner
+RUNNER_TOKEN=$(curl --silent --method POST "https://gitlab.com/api/v4/user/runners" \
+ --header "private-token: $GITLAB_TOKEN" \
+ --header 'content-type: application/json' \
+ --data "{\"runner_type\":\"group_type\",\"group_id\":\"$GROUP_ID\",\"description\":\"My runner\",\"tag-list\":\"java,linux\"}" \
+ | jq -r '.token')
+
+gitlab-runner register
+ --non-interactive \
+ --executor "shell" \
+ --url "https://gitlab.com/" \
+ --token "$RUNNER_TOKEN"
+```
+
## Status
Status: RFC.
@@ -537,7 +575,7 @@ Proposal:
|------------------------------|--------------------------------------------------|
| Authors | Kamil Trzciński, Tomasz Maczukin, Pedro Pombeiro |
| Architecture Evolution Coach | Kamil Trzciński |
-| Engineering Leader | Elliot Rushton, Cheryl Li |
+| Engineering Leader | Nicole Williams, Cheryl Li |
| Product Manager | Darren Eastman, Jackie Porter |
| Domain Expert / Runner | Tomasz Maczukin |
@@ -545,7 +583,7 @@ DRIs:
| Role | Who |
|------------------------------|---------------------------------|
-| Leadership | Elliot Rushton |
+| Leadership | Nicole Williams |
| Product | Darren Eastman |
| Engineering | Tomasz Maczukin, Pedro Pombeiro |
diff --git a/doc/ci/pipelines/schedules.md b/doc/ci/pipelines/schedules.md
index e5a2eb86f14..d08a511515f 100644
--- a/doc/ci/pipelines/schedules.md
+++ b/doc/ci/pipelines/schedules.md
@@ -14,7 +14,7 @@ Use scheduled pipelines to run GitLab CI/CD [pipelines](index.md) at regular int
For a scheduled pipeline to run:
- The schedule owner must have the Developer role. For pipelines on protected branches,
- the schedule owner must be [allowed to merge](../../user/project/protected_branches.md#configure-a-protected-branch)
+ the schedule owner must be [allowed to merge](../../user/project/protected_branches.md#add-protection-to-existing-branches)
to the branch.
- The [CI/CD configuration](../yaml/index.md) must be valid.
diff --git a/doc/development/jh_features_review.md b/doc/development/jh_features_review.md
index 7b81ecfe8f5..7e90d79c067 100644
--- a/doc/development/jh_features_review.md
+++ b/doc/development/jh_features_review.md
@@ -20,6 +20,10 @@ We have two kinds of changes related to JH:
codes under `jh/`.
- We will generalize this so both EE and JH can share the same mechanism,
then we wouldn't have to treat them differently.
+ - Database migrations and database schema changes which are required to
+ support running JH edition. See
+ [JiHu guidelines for database changes](https://about.gitlab.com/handbook/ceo/chief-of-staff-team/jihu-support/jihu-database-change-process.html)
+ for details.
If needed, review the corresponding JH merge request located at [JH repository](https://jihulab.com/gitlab-cn/gitlab).
@@ -33,9 +37,12 @@ the reference from being misidentified as a missing partial and subsequently del
## Process overview
-See the [merge request process](https://about.gitlab.com/handbook/ceo/chief-of-staff-team/jihu-support/#merge-request-process)
-on the JiHu Support handbook.
-This page is the single source of truth for JiHu-related processes.
+Read the following process guides:
+
+- [Contribution review process](https://about.gitlab.com/handbook/ceo/chief-of-staff-team/jihu-support/jihu-contribution-review-process.html)
+- [Database change process](https://about.gitlab.com/handbook/ceo/chief-of-staff-team/jihu-support/jihu-database-change-process.html)
+- [Security review process](https://about.gitlab.com/handbook/ceo/chief-of-staff-team/jihu-support/jihu-security-review-process.html)
+- [Merge request process](https://about.gitlab.com/handbook/ceo/chief-of-staff-team/jihu-support/#merge-request-process)
## Act as EE when `jh/` does not exist or when `EE_ONLY=1`
diff --git a/doc/integration/datadog.md b/doc/integration/datadog.md
index edae3d0f9bd..cc4e350d2bb 100644
--- a/doc/integration/datadog.md
+++ b/doc/integration/datadog.md
@@ -45,7 +45,7 @@ project, group, or instance level:
<!-- vale gitlab.Spelling = YES -->
1. Optional. To define any custom tags for all spans at which the integration is being configured,
enter one tag per line in **Tags**. Each line must be in the format `key:value`. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79665) in GitLab 14.8.)
-1. Optional. Select **Test settings** to test your integration.
+1. Optional. Select **Test settings**.
1. Select **Save changes**.
When the integration sends data, you can view it in the [CI Visibility](https://app.datadoghq.com/ci)
diff --git a/doc/integration/jenkins.md b/doc/integration/jenkins.md
index 3db02ed1221..89ba8702a83 100644
--- a/doc/integration/jenkins.md
+++ b/doc/integration/jenkins.md
@@ -134,7 +134,7 @@ than the [webhook integration](#configure-a-webhook).
project.
1. If your Jenkins server requires
authentication, enter the **Username** and **Password**.
-1. To test the connection to Jenkins, select **Test settings**.
+1. Optional. Select **Test settings**.
1. Select **Save changes**.
### Configure a webhook
diff --git a/doc/integration/jira/configure.md b/doc/integration/jira/configure.md
index 81bfddb01da..22c12786060 100644
--- a/doc/integration/jira/configure.md
+++ b/doc/integration/jira/configure.md
@@ -63,7 +63,7 @@ To configure your project settings in GitLab:
can view all issues from the Jira project you've specified.
- To [create Jira issues for vulnerabilities](../../user/application_security/vulnerabilities/index.md#create-a-jira-issue-for-a-vulnerability), select the **Enable Jira issue creation from vulnerabilities** checkbox.
-1. Optional. Select **Test settings** to verify the connection.
+1. Optional. Select **Test settings**.
1. Select **Save changes**.
Your GitLab project can now interact with all Jira projects in your instance, and the project
@@ -96,7 +96,7 @@ To migrate from Jira Server to Jira Cloud in GitLab and maintain your Jira integ
1. In **Email or username**, enter the email or username registered on your Jira profile.
1. [Create a Jira Cloud API token](#create-a-jira-cloud-api-token), and copy the token value.
1. In **API token or password**, paste the API token value.
-1. Optional. Select **Test settings** to verify the connection.
+1. Optional. Select **Test settings**.
1. Select **Save changes**.
To update existing Jira issue references in GitLab to use the new Jira site URL, you must [invalidate the Markdown cache](../../administration/invalidate_markdown_cache.md#invalidate-the-cache).
diff --git a/doc/user/admin_area/moderate_users.md b/doc/user/admin_area/moderate_users.md
index b0e24559e47..eb1d70c7b08 100644
--- a/doc/user/admin_area/moderate_users.md
+++ b/doc/user/admin_area/moderate_users.md
@@ -218,14 +218,11 @@ Users can also be activated using the [GitLab API](../../api/users.md#activate-u
## Ban and unban users
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/327353) in GitLab 14.2 [with a flag](../../administration/feature_flags.md) named `ban_user_feature_flag`. Disabled by default.
-> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/330667) in GitLab 14.8.
+> - Ban and unban users [generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/327353) in GitLab 14.8. Feature flag `ban_user_feature_flag` removed.
+> - Hiding merge requests of banned users [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/107836) in GitLab 15.8 [with a flag](../../administration/feature_flags.md) named `hide_merge_requests_from_banned_users`. Disabled by default.
+> - Hiding comments of banned users [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/112973) in GitLab 15.11 [with a flag](../../administration/feature_flags.md) named `hidden_notes`. Disabled by default.
-FLAG:
-On self-managed GitLab, by default this feature is available.
-On GitLab.com, this feature is available to GitLab.com administrators only.
-
-GitLab administrators can ban and unban users. Banned users are blocked, and their issues and merge requests are hidden.
-The banned user's comments are still displayed. Hiding a banned user's comments is [tracked in this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/327356).
+GitLab administrators can ban and unban users. Banned users are blocked, and their issues, merge requests, and comments are hidden.
### Ban a user
diff --git a/doc/user/project/deploy_keys/index.md b/doc/user/project/deploy_keys/index.md
index 13ee07097e1..f7dc8079af8 100644
--- a/doc/user/project/deploy_keys/index.md
+++ b/doc/user/project/deploy_keys/index.md
@@ -160,7 +160,7 @@ There are a few scenarios where a deploy key fails to push to a
- The owner associated to a deploy key does not have access to the protected branch.
- The owner associated to a deploy key does not have [membership](../members/index.md) to the project of the protected branch.
-- **No one** is selected in [the **Allowed to push and merge** section](../protected_branches.md#configure-a-protected-branch) of the protected branch.
+- **No one** is selected in [the **Allowed to push and merge** section](../protected_branches.md#add-protection-to-existing-branches) of the protected branch.
All deploy keys are associated to an account. Since the permissions for an account can change, this might lead to scenarios where a deploy key that was working is suddenly unable to push to a protected branch.
diff --git a/doc/user/project/import/github.md b/doc/user/project/import/github.md
index 068663af0c9..118e7caf123 100644
--- a/doc/user/project/import/github.md
+++ b/doc/user/project/import/github.md
@@ -282,11 +282,11 @@ When they are imported, supported GitHub branch protection rules are mapped to e
| GitHub rule | GitLab rule | Introduced in |
| :---------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------ |
| **Require conversation resolution before merging** for the project's default branch | **All threads must be resolved** [project setting](../../discussions/index.md#prevent-merge-unless-all-threads-are-resolved) | [GitLab 15.5](https://gitlab.com/gitlab-org/gitlab/-/issues/371110) |
-| **Require a pull request before merging** | **No one** option in the **Allowed to push and merge** list of [branch protection settings](../protected_branches.md#configure-a-protected-branch) | [GitLab 15.5](https://gitlab.com/gitlab-org/gitlab/-/issues/370951) |
+| **Require a pull request before merging** | **No one** option in the **Allowed to push and merge** list of [branch protection settings](../protected_branches.md#add-protection-to-existing-branches) | [GitLab 15.5](https://gitlab.com/gitlab-org/gitlab/-/issues/370951) |
| **Require signed commits** for the project's default branch | **Reject unsigned commits** GitLab [push rule](../repository/push_rules.md#prevent-unintended-consequences) **(PREMIUM)** | [GitLab 15.5](https://gitlab.com/gitlab-org/gitlab/-/issues/370949) |
| **Allow force pushes - Everyone** | **Allowed to force push** [branch protection setting](../protected_branches.md#allow-force-push-on-a-protected-branch) | [GitLab 15.6](https://gitlab.com/gitlab-org/gitlab/-/issues/370943) |
| **Require a pull request before merging - Require review from Code Owners** | **Require approval from code owners** [branch protection setting](../protected_branches.md#require-code-owner-approval-on-a-protected-branch) **(PREMIUM)** | [GitLab 15.6](https://gitlab.com/gitlab-org/gitlab/-/issues/376683) |
-| **Require a pull request before merging - Allow specified actors to bypass required pull requests** | List of users in the **Allowed to push and merge** list of [branch protection settings](../protected_branches.md#configure-a-protected-branch) **(PREMIUM)**. Without a **Premium** subscription, the list of users that are allowed to push and merge is limited to roles. | [GitLab 15.8](https://gitlab.com/gitlab-org/gitlab/-/issues/384939) |
+| **Require a pull request before merging - Allow specified actors to bypass required pull requests** | List of users in the **Allowed to push and merge** list of [branch protection settings](../protected_branches.md#add-protection-to-existing-branches) **(PREMIUM)**. Without a **Premium** subscription, the list of users that are allowed to push and merge is limited to roles. | [GitLab 15.8](https://gitlab.com/gitlab-org/gitlab/-/issues/384939) |
Mapping GitHub rule **Require status checks to pass before merging** to
[external status checks](../merge_requests/status_checks.md) was considered in issue
diff --git a/doc/user/project/integrations/asana.md b/doc/user/project/integrations/asana.md
index b1a4d2a2f78..352f6cdc2bb 100644
--- a/doc/user/project/integrations/asana.md
+++ b/doc/user/project/integrations/asana.md
@@ -39,6 +39,7 @@ Complete these steps in GitLab:
1. Paste the token you generated in Asana.
1. Optional. To restrict this setting to specific branches, list them in the **Restrict to branch**
field, separated with commas.
-1. Select **Save changes** or optionally select **Test settings**.
+1. Optional. Select **Test settings**.
+1. Select **Save changes**.
<!-- ## Troubleshooting -->
diff --git a/doc/user/project/integrations/bamboo.md b/doc/user/project/integrations/bamboo.md
index 23990036f4e..05bdd039f12 100644
--- a/doc/user/project/integrations/bamboo.md
+++ b/doc/user/project/integrations/bamboo.md
@@ -47,8 +47,7 @@ integration in GitLab.
1. If necessary, enter a username and password for a Bamboo user that has
access to trigger the build plan. Leave these fields blank if you do not require
authentication.
-1. Optional. To test the configuration and trigger a build in Bamboo,
- select **Test Settings**.
+1. Optional. Select **Test settings**.
1. Select **Save changes**.
### Identify the Bamboo build plan build key
diff --git a/doc/user/project/integrations/bugzilla.md b/doc/user/project/integrations/bugzilla.md
index f2af4dc6e4d..cb6abaa4c7b 100644
--- a/doc/user/project/integrations/bugzilla.md
+++ b/doc/user/project/integrations/bugzilla.md
@@ -31,7 +31,8 @@ To enable the Bugzilla integration in a project:
For example, for a project named "My Cool App":
`https://bugzilla.example.org/enter_bug.cgi#h=dupes%7CMy+Cool+App`.
-1. Select **Save changes** or optionally select **Test settings**.
+1. Optional. Select **Test settings**.
+1. Select **Save changes**.
After you configure and enable Bugzilla, a link appears on the GitLab
project pages. This link takes you to the appropriate Bugzilla project.
diff --git a/doc/user/project/integrations/clickup.md b/doc/user/project/integrations/clickup.md
index 255f0c3f56b..4353749dd33 100644
--- a/doc/user/project/integrations/clickup.md
+++ b/doc/user/project/integrations/clickup.md
@@ -21,7 +21,8 @@ To enable the ClickUp integration in a project:
- **Issue URL**: The URL to the ClickUp project issue to link to this GitLab project.
The URL must contain `:id`. GitLab replaces this ID with the issue number.
-1. Select **Save changes** or optionally select **Test settings**.
+1. Optional. Select **Test settings**.
+1. Select **Save changes**.
After you have configured and enabled ClickUp, you see the ClickUp link on the GitLab project pages,
which takes you to your ClickUp project.
diff --git a/doc/user/project/integrations/ewm.md b/doc/user/project/integrations/ewm.md
index 39dd548e7ca..7f87e9fc81f 100644
--- a/doc/user/project/integrations/ewm.md
+++ b/doc/user/project/integrations/ewm.md
@@ -35,7 +35,8 @@ To enable the EWM integration, in a project:
Append the following fragment to your project area URL: `#action=com.ibm.team.workitem.newWorkItem`.
For example, `https://example.com/ccm/web/projects/JKE%20Banking#action=com.ibm.team.workitem.newWorkItem`.
-1. Select **Save changes** or optionally select **Test settings**.
+1. Optional. Select **Test settings**.
+1. Select **Save changes**.
## Reference EWM work items in commit messages
diff --git a/doc/user/project/integrations/hangouts_chat.md b/doc/user/project/integrations/hangouts_chat.md
index 1cfe1a02692..4046869072d 100644
--- a/doc/user/project/integrations/hangouts_chat.md
+++ b/doc/user/project/integrations/hangouts_chat.md
@@ -61,7 +61,7 @@ To enable the integration in GitLab:
1. Scroll down to the end of the page where you find a **Webhook** field.
1. Enter the webhook URL you copied from Google Chat.
1. Select the events you want to be notified about in your Google Chat space.
-1. Optional. Select **Test settings** to verify the connection.
+1. Optional. Select **Test settings**.
1. Select **Save changes**.
To test the integration, make a change based on the events you selected and
diff --git a/doc/user/project/integrations/irker.md b/doc/user/project/integrations/irker.md
index df11ed3e57c..c77fffaa1ba 100644
--- a/doc/user/project/integrations/irker.md
+++ b/doc/user/project/integrations/irker.md
@@ -50,8 +50,9 @@ network. For more details, read
It's prepended to every channel or user provided under **Recipients**, which is not a full URI.
1. Under **Recipients**, enter the users or channels to receive updates, separated by spaces
(for example, `#channel1 user1`). For more details, see [Enter irker recipients](#enter-irker-recipients).
-1. Optional. Under **Colorize messages**, select the checkbox. irker will highlight your messages.
-1. Select **Save changes** or optionally select **Test Settings**.
+1. Optional. To highlight messages, select the **Colorize messages** checkbox.
+1. Optional. Select **Test settings**.
+1. Select **Save changes**.
## Enter irker recipients
diff --git a/doc/user/project/integrations/pivotal_tracker.md b/doc/user/project/integrations/pivotal_tracker.md
index e1d48037ba8..c2392061b8f 100644
--- a/doc/user/project/integrations/pivotal_tracker.md
+++ b/doc/user/project/integrations/pivotal_tracker.md
@@ -44,4 +44,5 @@ Complete these steps in GitLab:
1. Paste the token you generated in Pivotal Tracker.
1. Optional. To restrict this setting to specific branches, list them in the **Restrict to branch**
field, separated with commas.
-1. Select **Save changes** or optionally select **Test settings**.
+1. Optional. Select **Test settings**.
+1. Select **Save changes**.
diff --git a/doc/user/project/integrations/pumble.md b/doc/user/project/integrations/pumble.md
index e05ff9489ca..5199d1a9935 100644
--- a/doc/user/project/integrations/pumble.md
+++ b/doc/user/project/integrations/pumble.md
@@ -33,7 +33,7 @@ notifications:
1. Select the checkboxes corresponding to the GitLab events you want to receive in Pumble.
1. Paste the **Webhook** URL for the Pumble channel.
1. Configure the remaining options.
-1. Optional. To test the integration, select **Test settings**.
+1. Optional. Select **Test settings**.
1. Select **Save changes**.
The Pumble channel begins to receive all applicable GitLab events.
diff --git a/doc/user/project/integrations/redmine.md b/doc/user/project/integrations/redmine.md
index df8a6681e2b..5ef30cf413d 100644
--- a/doc/user/project/integrations/redmine.md
+++ b/doc/user/project/integrations/redmine.md
@@ -24,7 +24,8 @@ To enable the Redmine integration in a project:
**This URL is not used and removal is planned in a future release.**
For more information, see [issue 327503](https://gitlab.com/gitlab-org/gitlab/-/issues/327503).
-1. Select **Save changes** or optionally select **Test settings**.
+1. Optional. Select **Test settings**.
+1. Select **Save changes**.
After you have configured and enabled Redmine, you see the Redmine link on the GitLab project pages,
which takes you to your Redmine project.
diff --git a/doc/user/project/integrations/slack.md b/doc/user/project/integrations/slack.md
index 5b769b84663..2ce1e15ff63 100644
--- a/doc/user/project/integrations/slack.md
+++ b/doc/user/project/integrations/slack.md
@@ -56,8 +56,8 @@ to control GitLab from Slack. Slash commands are configured separately.
1. Leave the **Labels to be notified** field blank to get all notifications, or
add labels that the issue or merge request must have to trigger a
notification.
-1. Select **Test settings** to verify your information, and then select
- **Save changes**.
+1. Optional. Select **Test settings**.
+1. Select **Save changes**.
Your Slack team now starts receiving GitLab event notifications as configured.
diff --git a/doc/user/project/integrations/unify_circuit.md b/doc/user/project/integrations/unify_circuit.md
index d20b19a3aaa..4a40a6250f1 100644
--- a/doc/user/project/integrations/unify_circuit.md
+++ b/doc/user/project/integrations/unify_circuit.md
@@ -23,6 +23,7 @@ In GitLab:
1. Paste the **Webhook URL** that you copied from the Unify Circuit configuration step.
1. Select the **Notify only broken pipelines** checkbox to notify only on failures.
1. In the **Branches for which notifications are to be sent** dropdown list, select which types of branches to send notifications for.
-1. Select `Save changes` or optionally select **Test settings**.
+1. Optional. Select **Test settings**.
+1. Select **Save changes**.
Your Unify Circuit conversation now starts receiving GitLab event notifications.
diff --git a/doc/user/project/integrations/webex_teams.md b/doc/user/project/integrations/webex_teams.md
index c755c7a5c17..2388014db06 100644
--- a/doc/user/project/integrations/webex_teams.md
+++ b/doc/user/project/integrations/webex_teams.md
@@ -33,6 +33,7 @@ notifications:
1. Ensure that the **Active** toggle is enabled.
1. Select the checkboxes corresponding to the GitLab events you want to receive in Webex Teams.
1. Paste the **Webhook** URL for the Webex Teams space.
-1. Configure the remaining options and then select **Test settings and save changes**.
+1. Optional. Select **Test settings**.
+1. Select **Save changes**.
The Webex Teams space begins to receive all applicable GitLab events.
diff --git a/doc/user/project/integrations/youtrack.md b/doc/user/project/integrations/youtrack.md
index ee5ca8eca78..b1c7b533c0f 100644
--- a/doc/user/project/integrations/youtrack.md
+++ b/doc/user/project/integrations/youtrack.md
@@ -22,7 +22,8 @@ To enable the YouTrack integration in a project:
- **Project URL**: The URL to the project in YouTrack.
- **Issue URL**: The URL to view an issue in the YouTrack project.
The URL must contain `:id`. GitLab replaces `:id` with the issue number.
-1. Select **Save changes** or optionally select **Test settings**.
+1. Optional. Select **Test settings**.
+1. Select **Save changes**.
After you configure and enable YouTrack, a link appears on the GitLab
project pages. This link takes you to the appropriate YouTrack project.
diff --git a/doc/user/project/integrations/zentao.md b/doc/user/project/integrations/zentao.md
index 19f6a3e315c..b14b9eac9da 100644
--- a/doc/user/project/integrations/zentao.md
+++ b/doc/user/project/integrations/zentao.md
@@ -52,7 +52,7 @@ Complete these steps in GitLab:
![ZenTao settings page](img/zentao_product_id.png)
-1. To verify the ZenTao connection is working, select **Test settings**.
+1. Optional. Select **Test settings**.
1. Select **Save changes**.
<!--- end_remove -->
diff --git a/doc/user/project/protected_branches.md b/doc/user/project/protected_branches.md
index 3255c38fd25..2f303bf5316 100644
--- a/doc/user/project/protected_branches.md
+++ b/doc/user/project/protected_branches.md
@@ -42,7 +42,7 @@ When a branch is protected, the default behavior enforces these restrictions on
When a branch matches multiple rules, the **most permissive rule** determines the
level of protection for the branch. For example, consider these rules, which include
-[wildcards](#configure-multiple-protected-branches-by-using-a-wildcard):
+[wildcards](#protect-multiple-branches-with-wildcard-rules):
| Branch name pattern | Allowed to merge | Allowed to push and merge |
|---------------------|------------------------|-----------------|
@@ -77,11 +77,11 @@ that matches `v1.x` must set `Allowed to push and merge` to `No one`, like this:
Administrators can set a default branch protection level in the
[Admin Area](../project/repository/branches/default.md#instance-level-default-branch-protection).
-## Configure a protected branch
+## Add protection to existing branches
Configure protected branches for all projects in a group, or just for a project.
-### For a project
+### For one project
Prerequisites:
@@ -133,11 +133,12 @@ To protect a branch for all the projects in a group:
The protected branch is added to the list of protected branches.
-## Configure multiple protected branches by using a wildcard
+## Protect multiple branches with wildcard rules
-If both a specific rule and a wildcard rule apply to the same branch, the most
-permissive rule controls how the branch behaves. For merge controls to work properly,
-set **Allowed to push and merge** to a broader set of users than **Allowed to merge**.
+When using wildcards, multiple rules can apply to a single branch.
+If more than one rule applies to a branch, the _most permissive_ rule controls
+how the branch behaves. For merge controls to work properly, set
+**Allowed to push and merge** to a broader set of users than **Allowed to merge**.
Prerequisite:
@@ -165,13 +166,13 @@ To protect multiple branches at the same time:
The protected branch displays in the list of protected branches.
-## Create a protected branch
+## Create a new branch with protections
-Users with at least the Developer role can create a protected branch.
+Users with at least the Developer role can create new protected branches.
Prerequisites:
-- **Allowed to push and merge** is set to **No one**
+- **Allowed to push and merge** is set to **No one**.
- **Allowed to merge** is set to **Developers**.
You can create a protected branch by using the UI or API only.
diff --git a/doc/user/project/repository/push_rules.md b/doc/user/project/repository/push_rules.md
index 28afba375fc..4d05ea701b7 100644
--- a/doc/user/project/repository/push_rules.md
+++ b/doc/user/project/repository/push_rules.md
@@ -19,6 +19,7 @@ can and can't be pushed to your repository. While GitLab offers
GitLab uses [RE2 syntax](https://github.com/google/re2/wiki/Syntax) for regular expressions
in push rules. You can test them at the [regex101 regex tester](https://regex101.com/).
+Each regular expression is limited to 255 characters.
For custom push rules use [server hooks](../../../administration/server_hooks.md).
diff --git a/doc/user/project/wiki/index.md b/doc/user/project/wiki/index.md
index eb13814f2ad..a6784e598da 100644
--- a/doc/user/project/wiki/index.md
+++ b/doc/user/project/wiki/index.md
@@ -288,7 +288,7 @@ To add a link to an external wiki from a project's left sidebar:
1. On the left sidebar, select **Settings > Integrations**.
1. Select **External wiki**.
1. Add the URL to your external wiki.
-1. Optional. To verify the connection, select **Test settings**.
+1. Optional. Select **Test settings**.
1. Select **Save changes**.
You can now see the **External wiki** option from your project's
diff --git a/lib/gitlab_settings/settings.rb b/lib/gitlab_settings/settings.rb
index ae2ed4eac05..dc077fdef81 100644
--- a/lib/gitlab_settings/settings.rb
+++ b/lib/gitlab_settings/settings.rb
@@ -8,6 +8,16 @@ module GitlabSettings
raise(ArgumentError, 'config source is required') if source.blank?
raise(ArgumentError, 'config section is required') if section.blank?
+ # Rails will set the default encoding to UTF-8
+ # (https://github.com/rails/rails/blob/v6.1.7.2/railties/lib/rails.rb#L21C1-L24),
+ # but it's possible this class is used before `require 'rails'` is
+ # called, as in the case of `sidekiq-cluster`. Ensure the
+ # configuration file is parsed as UTF-8, or
+ # ActiveSupport::ConfigurationFile.parse will blow up if the
+ # configuration file contains UTF-8 characters.
+ Encoding.default_external = Encoding::UTF_8
+ Encoding.default_internal = Encoding::UTF_8
+
@source = source
@section = section
@loaded = false
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index bf30a4500a8..3b50a0f0d30 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -789,6 +789,9 @@ msgstr ""
msgid "%{itemsCount} issues with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{jobs} Jobs"
+msgstr ""
+
msgid "%{key} is not a valid URL."
msgstr ""
@@ -870,6 +873,9 @@ msgstr ""
msgid "%{linkStart} Learn more%{linkEnd}."
msgstr ""
+msgid "%{linkStart}%{name}%{linkEnd} triggered pipeline for commit"
+msgstr ""
+
msgid "%{listToShow}, and %{awardsListLength} more"
msgstr ""
@@ -879,6 +885,9 @@ msgstr ""
msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
msgstr ""
+msgid "%{locked} created %{timeago}"
+msgstr ""
+
msgid "%{mergeLength}/%{usersLength} can merge"
msgstr ""
@@ -1042,9 +1051,6 @@ msgstr ""
msgid "%{rotation} has been recalculated with the remaining participants. Please review the new setup for %{rotation}. It is recommended that you reach out to the current on-call responder to ensure continuity of on-call coverage."
msgstr ""
-msgid "%{runner} created %{timeago}"
-msgstr ""
-
msgid "%{scope} results for term '%{term}'"
msgstr ""
@@ -19151,6 +19157,12 @@ msgstr ""
msgid "Footer message"
msgstr ""
+msgid "For %{link_to_pipeline_ref}"
+msgstr ""
+
+msgid "For %{ref}"
+msgstr ""
+
msgid "For a faster browsing experience, only %{strongStart}%{visible} of %{total}%{strongEnd} files are shown. Download one of the files below to see all changes."
msgstr ""
@@ -19187,6 +19199,12 @@ msgstr ""
msgid "For investigating IT service disruptions or outages"
msgstr ""
+msgid "For merge request %{link_to_merge_request} to merge %{link_to_merge_request_source_branch}"
+msgstr ""
+
+msgid "For merge request %{link_to_merge_request} to merge %{link_to_merge_request_source_branch} into %{link_to_merge_request_target_branch}"
+msgstr ""
+
msgid "For more info, read the documentation."
msgstr ""
@@ -23053,6 +23071,9 @@ msgstr ""
msgid "In progress"
msgstr ""
+msgid "In progress, queued for %{queuedDuration} seconds"
+msgstr ""
+
msgid "In the background, we're attempting to connect you again."
msgstr ""
@@ -33250,6 +33271,9 @@ msgstr ""
msgid "Pipelines|CI/CD Catalog"
msgstr ""
+msgid "Pipelines|Child pipeline (%{linkStart}parent%{linkEnd})"
+msgstr ""
+
msgid "Pipelines|Child pipeline (%{link_start}parent%{link_end})"
msgstr ""
@@ -33367,6 +33391,9 @@ msgstr ""
msgid "Pipelines|Revoke trigger"
msgstr ""
+msgid "Pipelines|Scheduled"
+msgstr ""
+
msgid "Pipelines|Set up a runner"
msgstr ""
@@ -33415,6 +33442,9 @@ msgstr ""
msgid "Pipelines|This is a child pipeline within the parent pipeline"
msgstr ""
+msgid "Pipelines|This pipeline is stuck"
+msgstr ""
+
msgid "Pipelines|This pipeline ran on the contents of this merge request combined with the contents of all other merge requests queued for merging into the target branch."
msgstr ""
@@ -35950,9 +35980,6 @@ msgstr ""
msgid "ProjectSettings|Visibility options for this fork are limited by the current visibility of the source project."
msgstr ""
-msgid "ProjectSettings|Visualize the project's performance metrics."
-msgstr ""
-
msgid "ProjectSettings|Warn about Potentially Unwanted Characters"
msgstr ""
@@ -46361,6 +46388,9 @@ msgstr ""
msgid "This is a Jira user."
msgstr ""
+msgid "This is a child pipeline within the parent pipeline"
+msgstr ""
+
msgid "This is a confidential %{noteableTypeText}."
msgstr ""
@@ -46565,6 +46595,12 @@ msgstr ""
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{strongStart}Auto DevOps.%{strongEnd}"
msgstr ""
+msgid "This pipeline makes use of a predefined CI/CD configuration enabled by Auto DevOps."
+msgstr ""
+
+msgid "This pipeline was triggered by a schedule"
+msgstr ""
+
msgid "This pipeline was triggered by a schedule."
msgstr ""
diff --git a/package.json b/package.json
index 6c979699811..162f3998977 100644
--- a/package.json
+++ b/package.json
@@ -56,8 +56,8 @@
"@gitlab/cluster-client": "^1.2.0",
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/fonts": "^1.2.0",
- "@gitlab/svgs": "3.47.0",
- "@gitlab/ui": "62.12.0",
+ "@gitlab/svgs": "3.49.0",
+ "@gitlab/ui": "62.12.1",
"@gitlab/visual-review-tools": "1.7.3",
"@gitlab/web-ide": "0.0.1-dev-20230524134151",
"@mattiasbuelens/web-streams-adapter": "^0.1.0",
diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb
index 582535790bd..0b06ee5f40f 100644
--- a/spec/features/admin/admin_runners_spec.rb
+++ b/spec/features/admin/admin_runners_spec.rb
@@ -603,7 +603,9 @@ RSpec.describe "Admin Runners", feature_category: :runner_fleet do
describe 'runner header', :js do
it 'contains the runner status, type and id' do
- expect(page).to have_content("#{s_('Runners|Never contacted')} Project Runner ##{project_runner.id} created")
+ expect(page).to have_content(
+ "##{project_runner.id} (#{project_runner.short_sha}) #{s_('Runners|Never contacted')} Project created"
+ )
end
end
diff --git a/spec/frontend/ci/runner/admin_runner_show/admin_runner_show_app_spec.js b/spec/frontend/ci/runner/admin_runner_show/admin_runner_show_app_spec.js
index a4ba9815c8d..c4ed6d1bdb5 100644
--- a/spec/frontend/ci/runner/admin_runner_show/admin_runner_show_app_spec.js
+++ b/spec/frontend/ci/runner/admin_runner_show/admin_runner_show_app_spec.js
@@ -34,6 +34,7 @@ jest.mock('~/lib/utils/url_utility', () => ({
const mockRunner = runnerData.data.runner;
const mockRunnerGraphqlId = mockRunner.id;
const mockRunnerId = `${getIdFromGraphQLId(mockRunnerGraphqlId)}`;
+const mockRunnerSha = mockRunner.shortSha;
const mockRunnersPath = '/admin/runners';
Vue.use(VueApollo);
@@ -89,7 +90,7 @@ describe('AdminRunnerShowApp', () => {
});
it('displays the runner header', () => {
- expect(findRunnerHeader().text()).toContain(`Runner #${mockRunnerId}`);
+ expect(findRunnerHeader().text()).toContain(`#${mockRunnerId} (${mockRunnerSha})`);
});
it('displays the runner edit and pause buttons', () => {
diff --git a/spec/frontend/ci/runner/components/runner_header_spec.js b/spec/frontend/ci/runner/components/runner_header_spec.js
index c851966431d..f5091226eaa 100644
--- a/spec/frontend/ci/runner/components/runner_header_spec.js
+++ b/spec/frontend/ci/runner/components/runner_header_spec.js
@@ -17,6 +17,7 @@ import RunnerStatusBadge from '~/ci/runner/components/runner_status_badge.vue';
import { runnerData } from '../mock_data';
const mockRunner = runnerData.data.runner;
+const mockRunnerSha = mockRunner.shortSha;
describe('RunnerHeader', () => {
let wrapper;
@@ -71,7 +72,7 @@ describe('RunnerHeader', () => {
},
});
- expect(wrapper.text()).toContain('Runner #99');
+ expect(wrapper.text()).toContain(`#99 (${mockRunnerSha})`);
});
it('displays the runner locked icon', () => {
@@ -100,7 +101,7 @@ describe('RunnerHeader', () => {
},
});
- expect(wrapper.text()).toContain('Runner #99');
+ expect(wrapper.text()).toContain(`#99 (${mockRunnerSha})`);
expect(wrapper.text()).not.toMatch(/created .+/);
expect(findTimeAgo().exists()).toBe(false);
});
diff --git a/spec/frontend/ci/runner/group_runner_show/group_runner_show_app_spec.js b/spec/frontend/ci/runner/group_runner_show/group_runner_show_app_spec.js
index 5a4c34fc374..120388900b5 100644
--- a/spec/frontend/ci/runner/group_runner_show/group_runner_show_app_spec.js
+++ b/spec/frontend/ci/runner/group_runner_show/group_runner_show_app_spec.js
@@ -34,6 +34,7 @@ jest.mock('~/lib/utils/url_utility', () => ({
const mockRunner = runnerData.data.runner;
const mockRunnerGraphqlId = mockRunner.id;
const mockRunnerId = `${getIdFromGraphQLId(mockRunnerGraphqlId)}`;
+const mockRunnerSha = mockRunner.shortSha;
const mockRunnersPath = '/groups/group1/-/runners';
const mockEditGroupRunnerPath = `/groups/group1/-/runners/${mockRunnerId}/edit`;
@@ -91,7 +92,7 @@ describe('GroupRunnerShowApp', () => {
});
it('displays the runner header', () => {
- expect(findRunnerHeader().text()).toContain(`Runner #${mockRunnerId}`);
+ expect(findRunnerHeader().text()).toContain(`#${mockRunnerId} (${mockRunnerSha})`);
});
it('displays the runner edit and pause buttons', () => {
diff --git a/spec/frontend/ci/runner/runner_edit/runner_edit_app_spec.js b/spec/frontend/ci/runner/runner_edit/runner_edit_app_spec.js
index 79bbf95f8f0..ee4bd9ccc92 100644
--- a/spec/frontend/ci/runner/runner_edit/runner_edit_app_spec.js
+++ b/spec/frontend/ci/runner/runner_edit/runner_edit_app_spec.js
@@ -21,6 +21,7 @@ jest.mock('~/ci/runner/sentry_utils');
const mockRunner = runnerFormData.data.runner;
const mockRunnerGraphqlId = mockRunner.id;
const mockRunnerId = `${getIdFromGraphQLId(mockRunnerGraphqlId)}`;
+const mockRunnerSha = mockRunner.shortSha;
const mockRunnerPath = `/admin/runners/${mockRunnerId}`;
Vue.use(VueApollo);
@@ -62,7 +63,7 @@ describe('RunnerEditApp', () => {
it('displays the runner id and creation date', async () => {
await createComponentWithApollo({ mountFn: mount });
- expect(findRunnerHeader().text()).toContain(`Runner #${mockRunnerId}`);
+ expect(findRunnerHeader().text()).toContain(`#${mockRunnerId} (${mockRunnerSha})`);
expect(findRunnerHeader().text()).toContain('created');
});
diff --git a/spec/frontend/fixtures/pipeline_header.rb b/spec/frontend/fixtures/pipeline_header.rb
new file mode 100644
index 00000000000..a4fba7e8675
--- /dev/null
+++ b/spec/frontend/fixtures/pipeline_header.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe "GraphQL Pipeline Header", '(JavaScript fixtures)', type: :request, feature_category: :pipeline_composition do
+ include ApiHelpers
+ include GraphqlHelpers
+ include JavaScriptFixturesHelpers
+
+ let_it_be(:namespace) { create(:namespace, name: 'frontend-fixtures') }
+ let_it_be(:project) { create(:project, :public, :repository) }
+ let_it_be(:user) { project.first_owner }
+ let_it_be(:commit) { create(:commit, project: project) }
+
+ let(:query_path) { 'pipelines/graphql/queries/get_pipeline_header_data.query.graphql' }
+
+ context 'with successful pipeline' do
+ let_it_be(:pipeline) do
+ create(
+ :ci_pipeline,
+ project: project,
+ sha: commit.id,
+ ref: 'master',
+ user: user,
+ status: :success,
+ started_at: 1.hour.ago,
+ finished_at: Time.current
+ )
+ end
+
+ it "graphql/pipelines/pipeline_header_success.json" do
+ query = get_graphql_query_as_string(query_path)
+
+ post_graphql(query, current_user: user, variables: { fullPath: project.full_path, iid: pipeline.iid })
+
+ expect_graphql_errors_to_be_empty
+ end
+ end
+
+ context 'with running pipeline' do
+ let_it_be(:pipeline) do
+ create(
+ :ci_pipeline,
+ project: project,
+ sha: commit.id,
+ ref: 'master',
+ user: user,
+ status: :running,
+ created_at: 2.hours.ago,
+ started_at: 1.hour.ago
+ )
+ end
+
+ it "graphql/pipelines/pipeline_header_running.json" do
+ query = get_graphql_query_as_string(query_path)
+
+ post_graphql(query, current_user: user, variables: { fullPath: project.full_path, iid: pipeline.iid })
+
+ expect_graphql_errors_to_be_empty
+ end
+ end
+end
diff --git a/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js b/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js
index a7a1e649cd0..b6fb2c32cc0 100644
--- a/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js
+++ b/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js
@@ -24,7 +24,6 @@ const defaultProps = {
buildsAccessLevel: 20,
wikiAccessLevel: 20,
snippetsAccessLevel: 20,
- metricsDashboardAccessLevel: 20,
pagesAccessLevel: 10,
analyticsAccessLevel: 20,
containerRegistryAccessLevel: 20,
@@ -126,10 +125,6 @@ describe('Settings Panel', () => {
wrapper.find(
'input[name="project[project_setting_attributes][warn_about_potentially_unwanted_characters]"]',
);
- const findMetricsVisibilitySettings = () =>
- wrapper.findComponent({ ref: 'metrics-visibility-settings' });
- const findMetricsVisibilityInput = () =>
- findMetricsVisibilitySettings().findComponent(ProjectFeatureSetting);
const findConfirmDangerButton = () => wrapper.findComponent(ConfirmDanger);
const findEnvironmentsSettings = () => wrapper.findComponent({ ref: 'environments-settings' });
const findFeatureFlagsSettings = () => wrapper.findComponent({ ref: 'feature-flags-settings' });
@@ -137,8 +132,6 @@ describe('Settings Panel', () => {
wrapper.findComponent({ ref: 'infrastructure-settings' });
const findReleasesSettings = () => wrapper.findComponent({ ref: 'environments-settings' });
const findMonitorSettings = () => wrapper.findComponent({ ref: 'monitor-settings' });
- const findMonitorVisibilityInput = () =>
- findMonitorSettings().findComponent(ProjectFeatureSetting);
describe('Project Visibility', () => {
it('should set the project visibility help path', () => {
@@ -682,69 +675,6 @@ describe('Settings Panel', () => {
});
});
- describe('Metrics dashboard', () => {
- it('should show the metrics dashboard access select', () => {
- wrapper = mountComponent();
-
- expect(findMetricsVisibilitySettings().exists()).toBe(true);
- });
-
- it('should contain help text', () => {
- wrapper = mountComponent();
-
- expect(findMetricsVisibilitySettings().props('helpText')).toBe(
- "Visualize the project's performance metrics.",
- );
- });
-
- it.each`
- before | after
- ${featureAccessLevel.NOT_ENABLED} | ${featureAccessLevel.EVERYONE}
- ${featureAccessLevel.NOT_ENABLED} | ${featureAccessLevel.PROJECT_MEMBERS}
- ${featureAccessLevel.EVERYONE} | ${featureAccessLevel.PROJECT_MEMBERS}
- ${featureAccessLevel.EVERYONE} | ${featureAccessLevel.NOT_ENABLED}
- ${featureAccessLevel.PROJECT_MEMBERS} | ${featureAccessLevel.NOT_ENABLED}
- `(
- 'when updating Monitor access level from `$before` to `$after`, Metric Dashboard access is updated to `$after` as well',
- async ({ before, after }) => {
- wrapper = mountComponent({
- currentSettings: { monitorAccessLevel: before, metricsDashboardAccessLevel: before },
- });
-
- await findMonitorVisibilityInput().vm.$emit('change', after);
-
- expect(findMetricsVisibilityInput().props('value')).toBe(after);
- },
- );
-
- it('when updating Monitor access level from `10` to `20`, Metric Dashboard access is not increased', async () => {
- wrapper = mountComponent({
- currentSettings: {
- monitorAccessLevel: featureAccessLevel.PROJECT_MEMBERS,
- metricsDashboardAccessLevel: featureAccessLevel.PROJECT_MEMBERS,
- },
- });
-
- await findMonitorVisibilityInput().vm.$emit('change', featureAccessLevel.EVERYONE);
-
- expect(findMetricsVisibilityInput().props('value')).toBe(featureAccessLevel.PROJECT_MEMBERS);
- });
-
- it('should reduce Metrics visibility level when visibility is set to private', async () => {
- wrapper = mountComponent({
- currentSettings: {
- visibilityLevel: VISIBILITY_LEVEL_PUBLIC_INTEGER,
- monitorAccessLevel: featureAccessLevel.EVERYONE,
- metricsDashboardAccessLevel: featureAccessLevel.EVERYONE,
- },
- });
-
- await findProjectVisibilityLevelInput().setValue(VISIBILITY_LEVEL_PRIVATE_INTEGER);
-
- expect(findMetricsVisibilityInput().props('value')).toBe(featureAccessLevel.PROJECT_MEMBERS);
- });
- });
-
describe('Analytics', () => {
it('should show the analytics toggle', () => {
wrapper = mountComponent();
@@ -794,12 +724,5 @@ describe('Settings Panel', () => {
expectedAccessLevel,
);
});
- it('when monitorAccessLevel is for project members, it is also for everyone', () => {
- wrapper = mountComponent({
- currentSettings: { monitorAccessLevel: featureAccessLevel.PROJECT_MEMBERS },
- });
-
- expect(findMetricsVisibilityInput().props('value')).toBe(featureAccessLevel.EVERYONE);
- });
});
});
diff --git a/spec/frontend/pipelines/mock_data.js b/spec/frontend/pipelines/mock_data.js
index a4b8d223a0c..fd654eb6f10 100644
--- a/spec/frontend/pipelines/mock_data.js
+++ b/spec/frontend/pipelines/mock_data.js
@@ -1,3 +1,6 @@
+import pipelineHeaderSuccess from 'test_fixtures/graphql/pipelines/pipeline_header_success.json';
+import pipelineHeaderRunning from 'test_fixtures/graphql/pipelines/pipeline_header_running.json';
+
const PIPELINE_RUNNING = 'RUNNING';
const PIPELINE_CANCELED = 'CANCELED';
const PIPELINE_FAILED = 'FAILED';
@@ -5,6 +8,8 @@ const PIPELINE_FAILED = 'FAILED';
const threeWeeksAgo = new Date();
threeWeeksAgo.setDate(threeWeeksAgo.getDate() - 21);
+export { pipelineHeaderSuccess, pipelineHeaderRunning };
+
export const mockPipelineHeader = {
detailedStatus: {},
id: 123,
diff --git a/spec/frontend/pipelines/pipeline_details_header_spec.js b/spec/frontend/pipelines/pipeline_details_header_spec.js
index c049dad9e53..08ae35fe808 100644
--- a/spec/frontend/pipelines/pipeline_details_header_spec.js
+++ b/spec/frontend/pipelines/pipeline_details_header_spec.js
@@ -1,44 +1,78 @@
-import { GlLoadingIcon } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
+import { GlBadge, GlLoadingIcon } from '@gitlab/ui';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import PipelineDetailsHeader from '~/pipelines/components/pipeline_details_header.vue';
+import TimeAgo from '~/pipelines/components/pipelines_list/time_ago.vue';
import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue';
import getPipelineDetailsQuery from '~/pipelines/graphql/queries/get_pipeline_header_data.query.graphql';
-import { mockSuccessfulPipelineHeader } from './mock_data';
+import { pipelineHeaderSuccess, pipelineHeaderRunning } from './mock_data';
Vue.use(VueApollo);
describe('Pipeline details header', () => {
let wrapper;
- const successHandler = jest.fn().mockResolvedValue(mockSuccessfulPipelineHeader);
+ const successHandler = jest.fn().mockResolvedValue(pipelineHeaderSuccess);
+ const runningHandler = jest.fn().mockResolvedValue(pipelineHeaderRunning);
const findStatus = () => wrapper.findComponent(CiBadgeLink);
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+ const findTimeAgo = () => wrapper.findComponent(TimeAgo);
+ const findAllBadges = () => wrapper.findAllComponents(GlBadge);
+ const findPipelineName = () => wrapper.findByTestId('pipeline-name');
+ const findTotalJobs = () => wrapper.findByTestId('total-jobs');
+ const findComputeCredits = () => wrapper.findByTestId('compute-credits');
+ const findCommitLink = () => wrapper.findByTestId('commit-link');
+ const findPipelineRunningText = () => wrapper.findByTestId('pipeline-running-text').text();
+ const findPipelineRefText = () => wrapper.findByTestId('pipeline-ref-text').text();
const defaultHandlers = [[getPipelineDetailsQuery, successHandler]];
const defaultProvideOptions = {
- pipelineId: '14',
pipelineIid: 1,
paths: {
pipelinesPath: '/namespace/my-project/-/pipelines',
fullProject: '/namespace/my-project',
+ triggeredByPath: '',
},
};
+ const defaultProps = {
+ name: 'Ruby 3.0 master branch pipeline',
+ totalJobs: '50',
+ computeCredits: '0.65',
+ yamlErrors: 'errors',
+ failureReason: 'pipeline failed',
+ badges: {
+ schedule: true,
+ child: false,
+ latest: true,
+ mergeTrainPipeline: false,
+ invalid: false,
+ failed: false,
+ autoDevops: false,
+ detached: false,
+ stuck: false,
+ },
+ refText:
+ 'For merge request <a class="mr-iid" href="/root/ci-project/-/merge_requests/1">!1</a> to merge <a class="ref-name" href="/root/ci-project/-/commits/test">test</a>',
+ };
+
const createMockApolloProvider = (handlers) => {
return createMockApollo(handlers);
};
- const createComponent = (handlers = defaultHandlers) => {
- wrapper = shallowMount(PipelineDetailsHeader, {
+ const createComponent = (handlers = defaultHandlers, props = defaultProps) => {
+ wrapper = shallowMountExtended(PipelineDetailsHeader, {
provide: {
...defaultProvideOptions,
},
+ propsData: {
+ ...props,
+ },
apolloProvider: createMockApolloProvider(handlers),
});
};
@@ -51,21 +85,83 @@ describe('Pipeline details header', () => {
});
});
- describe('loaded state', () => {
- it('does not display loading icon', async () => {
+ describe('defaults', () => {
+ beforeEach(async () => {
createComponent();
await waitForPromises();
+ });
+ it('does not display loading icon', () => {
expect(findLoadingIcon().exists()).toBe(false);
});
- it('displays pipeline status', async () => {
+ it('displays pipeline status', () => {
+ expect(findStatus().exists()).toBe(true);
+ });
+
+ it('displays pipeline name', () => {
+ expect(findPipelineName().text()).toBe(defaultProps.name);
+ });
+
+ it('displays total jobs', () => {
+ expect(findTotalJobs().text()).toBe('50 Jobs');
+ });
+
+ it('has link to commit', () => {
+ const {
+ data: {
+ project: { pipeline },
+ },
+ } = pipelineHeaderSuccess;
+
+ expect(findCommitLink().attributes('href')).toBe(pipeline.commit.webPath);
+ });
+
+ it('displays correct badges', () => {
+ expect(findAllBadges()).toHaveLength(2);
+ expect(wrapper.findByText('latest').exists()).toBe(true);
+ expect(wrapper.findByText('Scheduled').exists()).toBe(true);
+ });
+
+ it('displays ref text', () => {
+ expect(findPipelineRefText()).toBe('For merge request !1 to merge test');
+ });
+ });
+
+ describe('finished pipeline', () => {
+ beforeEach(async () => {
createComponent();
await waitForPromises();
+ });
- expect(findStatus().exists()).toBe(true);
+ it('displays compute credits', () => {
+ expect(findComputeCredits().text()).toBe('0.65');
+ });
+
+ it('displays time ago', () => {
+ expect(findTimeAgo().exists()).toBe(true);
+ });
+ });
+
+ describe('running pipeline', () => {
+ beforeEach(async () => {
+ createComponent([[getPipelineDetailsQuery, runningHandler]]);
+
+ await waitForPromises();
+ });
+
+ it('does not display compute credits', () => {
+ expect(findComputeCredits().exists()).toBe(false);
+ });
+
+ it('does not display time ago', () => {
+ expect(findTimeAgo().exists()).toBe(false);
+ });
+
+ it('displays pipeline running text', () => {
+ expect(findPipelineRunningText()).toBe('In progress, queued for 3600 seconds');
});
});
});
diff --git a/spec/frontend/pipelines/time_ago_spec.js b/spec/frontend/pipelines/time_ago_spec.js
index efb1bf09d20..ccc07d9b1a8 100644
--- a/spec/frontend/pipelines/time_ago_spec.js
+++ b/spec/frontend/pipelines/time_ago_spec.js
@@ -8,7 +8,7 @@ describe('Timeago component', () => {
const defaultProps = { duration: 0, finished_at: '' };
- const createComponent = (props = defaultProps, stuck = false) => {
+ const createComponent = (props = defaultProps, stuck = false, extraProps) => {
wrapper = extendedWrapper(
shallowMount(TimeAgo, {
propsData: {
@@ -20,6 +20,7 @@ describe('Timeago component', () => {
stuck,
},
},
+ ...extraProps,
},
data() {
return {
@@ -36,6 +37,7 @@ describe('Timeago component', () => {
const findSkipped = () => wrapper.findByTestId('pipeline-skipped');
const findHourGlassIcon = () => wrapper.findByTestId('hourglass-icon');
const findWarningIcon = () => wrapper.findByTestId('warning-icon');
+ const findCalendarIcon = () => wrapper.findByTestId('calendar-icon');
describe('with duration', () => {
beforeEach(() => {
@@ -61,18 +63,28 @@ describe('Timeago component', () => {
});
describe('with finishedTime', () => {
- beforeEach(() => {
+ it('should render time', () => {
createComponent({ duration: 0, finished_at: '2017-04-26T12:40:23.277Z' });
- });
- it('should render time and calendar icon', () => {
- const icon = finishedAt().findComponent(GlIcon);
const time = finishedAt().find('time');
expect(finishedAt().exists()).toBe(true);
- expect(icon.props('name')).toBe('calendar');
expect(time.exists()).toBe(true);
});
+
+ it('should display calendar icon by default', () => {
+ createComponent({ duration: 0, finished_at: '2017-04-26T12:40:23.277Z' });
+
+ expect(findCalendarIcon().exists()).toBe(true);
+ });
+
+ it('should hide calendar icon if correct prop is passed', () => {
+ createComponent({ duration: 0, finished_at: '2017-04-26T12:40:23.277Z' }, false, {
+ displayCalendarIcon: false,
+ });
+
+ expect(findCalendarIcon().exists()).toBe(false);
+ });
});
describe('without finishedTime', () => {
@@ -82,6 +94,7 @@ describe('Timeago component', () => {
it('should not render time and calendar icon', () => {
expect(finishedAt().exists()).toBe(false);
+ expect(findCalendarIcon().exists()).toBe(false);
});
});
diff --git a/spec/helpers/projects/pipeline_helper_spec.rb b/spec/helpers/projects/pipeline_helper_spec.rb
index e9b2738cfad..a69da915990 100644
--- a/spec/helpers/projects/pipeline_helper_spec.rb
+++ b/spec/helpers/projects/pipeline_helper_spec.rb
@@ -61,7 +61,8 @@ RSpec.describe Projects::PipelineHelper do
failed: pipeline.failure_reason?.to_s,
auto_devops: pipeline.auto_devops_source?.to_s,
detached: pipeline.detached_merge_request_pipeline?.to_s,
- stuck: pipeline.stuck?
+ stuck: pipeline.stuck?,
+ ref_text: pipeline.ref_text
})
end
end
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 3eb1090c9dc..9c4cfa3038f 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -1038,7 +1038,6 @@ RSpec.describe ProjectsHelper, feature_category: :source_code_management do
containerRegistryEnabled: !!project.container_registry_enabled,
lfsEnabled: !!project.lfs_enabled,
emailsDisabled: project.emails_disabled?,
- metricsDashboardAccessLevel: project.project_feature.metrics_dashboard_access_level,
showDefaultAwardEmojis: project.show_default_award_emojis?,
securityAndComplianceAccessLevel: project.security_and_compliance_access_level,
containerRegistryAccessLevel: project.project_feature.container_registry_access_level,
diff --git a/spec/lib/gitlab_settings/settings_spec.rb b/spec/lib/gitlab_settings/settings_spec.rb
index 161c26dbb9f..4422bea4a63 100644
--- a/spec/lib/gitlab_settings/settings_spec.rb
+++ b/spec/lib/gitlab_settings/settings_spec.rb
@@ -31,6 +31,15 @@ RSpec.describe GitlabSettings::Settings, :aggregate_failures, feature_category:
expect { described_class.new(source, '') }
.to raise_error(ArgumentError, 'config section is required')
end
+
+ it 'sets encodings' do
+ expect(Encoding).to receive(:default_external=).with(Encoding::UTF_8).and_call_original
+ expect(Encoding).to receive(:default_internal=).with(Encoding::UTF_8).and_call_original
+
+ File.write(source, { section1: { config1: { value1: 2 } } }.to_yaml)
+
+ described_class.new(source, 'section1')
+ end
end
describe '#reload!' do
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index ae2a11bdbf0..d07a4e9f207 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -3263,6 +3263,26 @@ RSpec.describe ProjectPolicy, feature_category: :system_access do
specify { is_expected.to be_disallowed(:read_namespace_catalog) }
end
+ describe 'read_model_registry' do
+ let(:project_with_feature) { project }
+ let(:current_user) { owner }
+
+ before do
+ stub_feature_flags(model_registry: false)
+ stub_feature_flags(model_registry: project_with_feature) if project_with_feature
+ end
+
+ context 'feature flag is enabled' do
+ specify { is_expected.to be_allowed(:read_model_registry) }
+ end
+
+ context 'feature flag is disabled' do
+ let(:project_with_feature) { nil }
+
+ specify { is_expected.not_to be_allowed(:read_model_registry) }
+ end
+ end
+
private
def project_subject(project_type)
diff --git a/spec/presenters/ci/pipeline_presenter_spec.rb b/spec/presenters/ci/pipeline_presenter_spec.rb
index 7f4c8120e17..86e4bb703dc 100644
--- a/spec/presenters/ci/pipeline_presenter_spec.rb
+++ b/spec/presenters/ci/pipeline_presenter_spec.rb
@@ -146,8 +146,8 @@ RSpec.describe Ci::PipelinePresenter do
end
end
- describe '#ref_text' do
- subject { presenter.ref_text }
+ describe '#ref_text_legacy' do
+ subject { presenter.ref_text_legacy }
context 'when pipeline is detached merge request pipeline' do
let(:merge_request) { create(:merge_request, :with_detached_merge_request_pipeline) }
@@ -155,7 +155,7 @@ RSpec.describe Ci::PipelinePresenter do
it 'returns a correct ref text' do
is_expected.to eq("for <a class=\"mr-iid\" href=\"#{project_merge_request_path(merge_request.project, merge_request)}\">#{merge_request.to_reference}</a> " \
- "with <a class=\"ref-name\" href=\"#{project_commits_path(merge_request.source_project, merge_request.source_branch)}\">#{merge_request.source_branch}</a>")
+ "with <a class=\"ref-name gl-link gl-bg-blue-50 gl-rounded-base gl-px-2\" href=\"#{project_commits_path(merge_request.source_project, merge_request.source_branch)}\">#{merge_request.source_branch}</a>")
end
end
@@ -165,8 +165,8 @@ RSpec.describe Ci::PipelinePresenter do
it 'returns a correct ref text' do
is_expected.to eq("for <a class=\"mr-iid\" href=\"#{project_merge_request_path(merge_request.project, merge_request)}\">#{merge_request.to_reference}</a> " \
- "with <a class=\"ref-name\" href=\"#{project_commits_path(merge_request.source_project, merge_request.source_branch)}\">#{merge_request.source_branch}</a> " \
- "into <a class=\"ref-name\" href=\"#{project_commits_path(merge_request.target_project, merge_request.target_branch)}\">#{merge_request.target_branch}</a>")
+ "with <a class=\"ref-name gl-link gl-bg-blue-50 gl-rounded-base gl-px-2\" href=\"#{project_commits_path(merge_request.source_project, merge_request.source_branch)}\">#{merge_request.source_branch}</a> " \
+ "into <a class=\"ref-name gl-link gl-bg-blue-50 gl-rounded-base gl-px-2\" href=\"#{project_commits_path(merge_request.target_project, merge_request.target_branch)}\">#{merge_request.target_branch}</a>")
end
end
@@ -177,7 +177,7 @@ RSpec.describe Ci::PipelinePresenter do
end
it 'returns a correct ref text' do
- is_expected.to eq("for <a class=\"ref-name\" href=\"#{project_commits_path(pipeline.project, pipeline.ref)}\">#{pipeline.ref}</a>")
+ is_expected.to eq("for <a class=\"ref-name gl-link gl-bg-blue-50 gl-rounded-base gl-px-2\" href=\"#{project_commits_path(pipeline.project, pipeline.ref)}\">#{pipeline.ref}</a>")
end
context 'when ref contains malicious script' do
@@ -209,6 +209,69 @@ RSpec.describe Ci::PipelinePresenter do
end
end
+ describe '#ref_text' do
+ subject { presenter.ref_text }
+
+ context 'when pipeline is detached merge request pipeline' do
+ let(:merge_request) { create(:merge_request, :with_detached_merge_request_pipeline) }
+ let(:pipeline) { merge_request.all_pipelines.last }
+
+ it 'returns a correct ref text' do
+ is_expected.to eq("For merge request <a class=\"mr-iid\" href=\"#{project_merge_request_path(merge_request.project, merge_request)}\">#{merge_request.to_reference}</a> " \
+ "to merge <a class=\"ref-name gl-link gl-bg-blue-50 gl-rounded-base gl-px-2\" href=\"#{project_commits_path(merge_request.source_project, merge_request.source_branch)}\">#{merge_request.source_branch}</a>")
+ end
+ end
+
+ context 'when pipeline is merge request pipeline' do
+ let(:merge_request) { create(:merge_request, :with_merge_request_pipeline) }
+ let(:pipeline) { merge_request.all_pipelines.last }
+
+ it 'returns a correct ref text' do
+ is_expected.to eq("For merge request <a class=\"mr-iid\" href=\"#{project_merge_request_path(merge_request.project, merge_request)}\">#{merge_request.to_reference}</a> " \
+ "to merge <a class=\"ref-name gl-link gl-bg-blue-50 gl-rounded-base gl-px-2\" href=\"#{project_commits_path(merge_request.source_project, merge_request.source_branch)}\">#{merge_request.source_branch}</a> " \
+ "into <a class=\"ref-name gl-link gl-bg-blue-50 gl-rounded-base gl-px-2\" href=\"#{project_commits_path(merge_request.target_project, merge_request.target_branch)}\">#{merge_request.target_branch}</a>")
+ end
+ end
+
+ context 'when pipeline is branch pipeline' do
+ context 'when ref exists in the repository' do
+ before do
+ allow(pipeline).to receive(:ref_exists?) { true }
+ end
+
+ it 'returns a correct ref text' do
+ is_expected.to eq("For <a class=\"ref-name gl-link gl-bg-blue-50 gl-rounded-base gl-px-2\" href=\"#{project_commits_path(pipeline.project, pipeline.ref)}\">#{pipeline.ref}</a>")
+ end
+
+ context 'when ref contains malicious script' do
+ let(:pipeline) { create(:ci_pipeline, ref: "<script>alter('1')</script>", project: project) }
+
+ it 'does not include the malicious script' do
+ is_expected.not_to include("<script>alter('1')</script>")
+ end
+ end
+ end
+
+ context 'when ref does not exist in the repository' do
+ before do
+ allow(pipeline).to receive(:ref_exists?) { false }
+ end
+
+ it 'returns a correct ref text' do
+ is_expected.to eq("For <span class=\"ref-name\">#{pipeline.ref}</span>")
+ end
+
+ context 'when ref contains malicious script' do
+ let(:pipeline) { create(:ci_pipeline, ref: "<script>alter('1')</script>", project: project) }
+
+ it 'does not include the malicious script' do
+ is_expected.not_to include("<script>alter('1')</script>")
+ end
+ end
+ end
+ end
+ end
+
describe '#all_related_merge_request_text' do
subject { presenter.all_related_merge_request_text }
diff --git a/spec/presenters/merge_request_presenter_spec.rb b/spec/presenters/merge_request_presenter_spec.rb
index 6f40d3f5b48..d0febf64035 100644
--- a/spec/presenters/merge_request_presenter_spec.rb
+++ b/spec/presenters/merge_request_presenter_spec.rb
@@ -474,7 +474,7 @@ RSpec.describe MergeRequestPresenter do
allow(resource).to receive(:source_branch_exists?) { true }
is_expected
- .to eq("<a class=\"ref-name\" href=\"#{presenter.source_branch_commits_path}\">#{presenter.source_branch}</a>")
+ .to eq("<a class=\"ref-name gl-link gl-bg-blue-50 gl-rounded-base gl-px-2\" href=\"#{presenter.source_branch_commits_path}\">#{presenter.source_branch}</a>")
end
end
@@ -497,7 +497,7 @@ RSpec.describe MergeRequestPresenter do
allow(resource).to receive(:target_branch_exists?) { true }
is_expected
- .to eq("<a class=\"ref-name\" href=\"#{presenter.target_branch_commits_path}\">#{presenter.target_branch}</a>")
+ .to eq("<a class=\"ref-name gl-link gl-bg-blue-50 gl-rounded-base gl-px-2\" href=\"#{presenter.target_branch_commits_path}\">#{presenter.target_branch}</a>")
end
end
diff --git a/spec/support/shared_examples/features/runners_shared_examples.rb b/spec/support/shared_examples/features/runners_shared_examples.rb
index 4da684c4c10..db685a4d0a1 100644
--- a/spec/support/shared_examples/features/runners_shared_examples.rb
+++ b/spec/support/shared_examples/features/runners_shared_examples.rb
@@ -201,7 +201,7 @@ RSpec.shared_examples 'submits edit runner form' do
describe 'runner header', :js do
it 'contains the runner id' do
- expect(page).to have_content("Runner ##{runner.id} created")
+ expect(page).to have_content("##{runner.id} (#{runner.short_sha})")
end
end
diff --git a/yarn.lock b/yarn.lock
index e2736e2e3a8..754e42785f4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1110,15 +1110,15 @@
stylelint-declaration-strict-value "1.8.0"
stylelint-scss "4.2.0"
-"@gitlab/svgs@3.47.0":
- version "3.47.0"
- resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.47.0.tgz#1a18f008aef1ecb5407688017c3bbdbc597b7ec1"
- integrity sha512-xP8AyuFYRFmlxtcBYRqCnLmBgMjrACa0mUliRk/hAKUWcXoz/U4vdK69T1DhWalVi4cpUqmi4+rrIWI6fBdzew==
-
-"@gitlab/ui@62.12.0":
- version "62.12.0"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-62.12.0.tgz#936f09121dcdf985281e622b1c102c1c343d22bf"
- integrity sha512-a29tvQQBQATkMy1BxPrnxhmSJk8miuRNq5bK/Tt94nRzf+4tRQ3JVf9eJX/km1/V1WgqvAzDSPZV46ptT061XA==
+"@gitlab/svgs@3.49.0":
+ version "3.49.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.49.0.tgz#5ac366ce709c20f3a94f108556d00c3baf637e1e"
+ integrity sha512-2HkBtkf4X7NtTgd+1b7pnmeTRFDYoEmXduNov4yWRFB7UHy3SlGcmeH7HHWfXKwjr52iuYrmwdqSsXQUV3sbvg==
+
+"@gitlab/ui@62.12.1":
+ version "62.12.1"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-62.12.1.tgz#5b5eab048f71c6b0645358d8e2fbce25828719a2"
+ integrity sha512-WfUIcs4wgVPHv+gZpSb8WkGEG3OrUGv68SRIRSyV5ZpQzxftiKev3kkmzILrhcm1weeKgoMmT+31t4QN5Gz2iA==
dependencies:
"@popperjs/core" "^2.11.2"
bootstrap-vue "2.23.1"