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
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-11-23 18:07:42 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-11-23 18:07:42 +0300
commitbc0f141f2f073a971aad1eb5349bb718747df028 (patch)
tree72fcc48dfac8e3f3560e22014eacdd2eaae8bc89 /app
parent2c29837ce1692790dedccbc9b36b44dc8aaacbee (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/custom_metrics/components/custom_metrics_form_fields.vue5
-rw-r--r--app/assets/javascripts/deploy_tokens/components/new_deploy_token.vue208
-rw-r--r--app/assets/javascripts/deploy_tokens/deploy_token_translations.js41
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue6
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard_actions_menu.vue2
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard_header.vue3
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard_panel.vue8
-rw-r--r--app/assets/javascripts/monitoring/components/duplicate_dashboard_form.vue8
-rw-r--r--app/assets/javascripts/monitoring/components/variables_section.vue7
-rw-r--r--app/assets/javascripts/pipeline_editor/components/editor/ci_editor_header.vue4
-rw-r--r--app/assets/javascripts/pipeline_editor/components/ui/editor_tab.vue4
-rw-r--r--app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue19
-rw-r--r--app/assets/javascripts/projects/settings/repository/branch_rules/app.vue1
-rw-r--r--app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue57
-rw-r--r--app/assets/javascripts/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql29
-rw-r--r--app/assets/javascripts/projects/settings/utils.js17
-rw-r--r--app/assets/javascripts/sidebar/sidebar_mediator.js8
-rw-r--r--app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue1
-rw-r--r--app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue8
-rw-r--r--app/assets/javascripts/vue_shared/security_reports/store/utils.js8
-rw-r--r--app/controllers/concerns/authenticates_with_two_factor.rb2
-rw-r--r--app/controllers/projects/work_items_controller.rb1
-rw-r--r--app/graphql/resolvers/concerns/looks_ahead.rb16
-rw-r--r--app/models/group.rb4
-rw-r--r--app/models/merge_request/predictions.rb9
-rw-r--r--app/models/project.rb4
-rw-r--r--app/services/merge_requests/base_service.rb12
-rw-r--r--app/validators/json_schemas/merge_request_predictions_accepted_reviewers.json10
-rw-r--r--app/views/layouts/header/_marketing_links.html.haml16
-rw-r--r--app/views/projects/_files.html.haml12
-rw-r--r--app/views/projects/_flash_messages.html.haml2
-rw-r--r--app/views/projects/show.html.haml3
-rw-r--r--app/views/shared/_auto_devops_callout.html.haml25
-rw-r--r--app/views/shared/integrations/prometheus/_custom_metrics.html.haml4
34 files changed, 341 insertions, 223 deletions
diff --git a/app/assets/javascripts/custom_metrics/components/custom_metrics_form_fields.vue b/app/assets/javascripts/custom_metrics/components/custom_metrics_form_fields.vue
index 411e482b0ce..c6aeb6c726d 100644
--- a/app/assets/javascripts/custom_metrics/components/custom_metrics_form_fields.vue
+++ b/app/assets/javascripts/custom_metrics/components/custom_metrics_form_fields.vue
@@ -185,7 +185,6 @@ export default {
name="prometheus_metric[title]"
class="form-control"
:placeholder="s__('Metrics|e.g. Throughput')"
- data-qa-selector="custom_metric_prometheus_title_field"
required
/>
<span class="form-text text-muted">{{ s__('Metrics|Used as a title for the chart') }}</span>
@@ -209,7 +208,6 @@ export default {
<gl-form-input
id="prometheus_metric_query"
v-model.trim="query"
- data-qa-selector="custom_metric_prometheus_query_field"
name="prometheus_metric[query]"
class="form-control"
:placeholder="s__('Metrics|e.g. rate(http_requests_total[5m])')"
@@ -247,7 +245,6 @@ export default {
<gl-form-input
id="prometheus_metric_y_label"
v-model="yLabel"
- data-qa-selector="custom_metric_prometheus_y_label_field"
name="prometheus_metric[y_label]"
class="form-control"
:placeholder="s__('Metrics|e.g. Requests/second')"
@@ -267,7 +264,6 @@ export default {
<gl-form-input
id="prometheus_metric_unit"
v-model="unit"
- data-qa-selector="custom_metric_prometheus_unit_label_field"
name="prometheus_metric[unit]"
class="form-control"
:placeholder="s__('Metrics|e.g. req/sec')"
@@ -282,7 +278,6 @@ export default {
<gl-form-input
id="prometheus_metric_legend"
v-model="legend"
- data-qa-selector="custom_metric_prometheus_legend_label_field"
name="prometheus_metric[legend]"
class="form-control"
:placeholder="s__('Metrics|e.g. HTTP requests')"
diff --git a/app/assets/javascripts/deploy_tokens/components/new_deploy_token.vue b/app/assets/javascripts/deploy_tokens/components/new_deploy_token.vue
index 81d74c64124..48ab9ce0a3c 100644
--- a/app/assets/javascripts/deploy_tokens/components/new_deploy_token.vue
+++ b/app/assets/javascripts/deploy_tokens/components/new_deploy_token.vue
@@ -13,27 +13,7 @@ import { createAlert, VARIANT_INFO } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { formatDate } from '~/lib/utils/datetime_utility';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
-import { s__ } from '~/locale';
-
-function defaultData() {
- return {
- expiresAt: null,
- name: '',
- newTokenDetails: null,
- readRepository: false,
- writeRepository: false,
- readRegistry: false,
- writeRegistry: false,
- readPackageRegistry: false,
- writePackageRegistry: false,
- username: '',
- placeholders: {
- link: { link: ['link_start', 'link_end'] },
- i: { i: ['i_start', 'i_end'] },
- code: { code: ['code_start', 'code_end'] },
- },
- };
-}
+import translations from '../deploy_token_translations';
export default {
components: {
@@ -72,45 +52,9 @@ export default {
},
data() {
- return defaultData();
- },
- translations: {
- addTokenButton: s__('DeployTokens|Create deploy token'),
- addTokenExpiryLabel: s__('DeployTokens|Expiration date (optional)'),
- addTokenExpiryDescription: s__(
- 'DeployTokens|Enter an expiration date for your token. Defaults to never expire.',
- ),
- addTokenHeader: s__('DeployTokens|New deploy token'),
- addTokenDescription: s__(
- 'DeployTokens|Create a new deploy token for all projects in this group. %{link_start}What are deploy tokens?%{link_end}',
- ),
- addTokenNameLabel: s__('DeployTokens|Name'),
- addTokenNameDescription: s__('DeployTokens|Enter a unique name for your deploy token.'),
- addTokenScopesLabel: s__('DeployTokens|Scopes (select at least one)'),
- addTokenUsernameDescription: s__(
- 'DeployTokens|Enter a username for your token. Defaults to %{code_start}gitlab+deploy-token-{n}%{code_end}.',
- ),
- addTokenUsernameLabel: s__('DeployTokens|Username (optional)'),
- newTokenCopyMessage: s__('DeployTokens|Copy deploy token'),
- newProjectTokenCreated: s__('DeployTokens|Your new project deploy token has been created.'),
- newGroupTokenCreated: s__('DeployTokens|Your new group deploy token has been created.'),
- newTokenDescription: s__(
- 'DeployTokens|Use this token as a password. Save it. This password can %{i_start}not%{i_end} be recovered.',
- ),
- newTokenMessage: s__('DeployTokens|Your New Deploy Token'),
- newTokenUsernameCopy: s__('DeployTokens|Copy username'),
- newTokenUsernameDescription: s__(
- 'DeployTokens|This username supports access. %{link_start}What kind of access?%{link_end}',
- ),
- readRepositoryHelp: s__('DeployTokens|Allows read-only access to the repository.'),
- readRegistryHelp: s__('DeployTokens|Allows read-only access to registry images.'),
- writeRegistryHelp: s__('DeployTokens|Allows read and write access to registry images.'),
- readPackageRegistryHelp: s__('DeployTokens|Allows read-only access to the package registry.'),
- writePackageRegistryHelp: s__(
- 'DeployTokens|Allows read and write access to the package registry.',
- ),
- createTokenFailedAlert: s__('DeployTokens|Failed to create a new deployment token'),
+ return this.defaultData();
},
+ translations,
computed: {
formattedExpiryDate() {
return this.expiresAt ? formatDate(this.expiresAt, 'yyyy-mm-dd') : '';
@@ -122,20 +66,78 @@ export default {
},
},
methods: {
+ defaultData() {
+ return {
+ expiresAt: null,
+ name: '',
+ newTokenDetails: null,
+ readRepository: false,
+ writeRepository: false,
+ readRegistry: false,
+ writeRegistry: false,
+ readPackageRegistry: false,
+ writePackageRegistry: false,
+ scopes: [
+ {
+ id: 'deploy_token_read_repository',
+ isShown: true,
+ value: false,
+ helpText: this.$options.translations.readRepositoryHelp,
+ scopeName: 'read_repository',
+ },
+ {
+ id: 'deploy_token_read_registry',
+ isShown: this.$props.containerRegistryEnabled,
+ value: false,
+ helpText: this.$options.translations.readRegistryHelp,
+ scopeName: 'read_registry',
+ },
+ {
+ id: 'deploy_token_write_registry',
+ isShown: this.$props.containerRegistryEnabled,
+ value: false,
+ helpText: this.$options.translations.writeRegistryHelp,
+ scopeName: 'write_registry',
+ },
+ {
+ id: 'deploy_token_read_package_registry',
+ isShown: this.$props.packagesRegistryEnabled,
+ value: false,
+ helpText: this.$options.translations.readPackageRegistryHelp,
+ scopeName: 'read_package_registry',
+ },
+ {
+ id: 'deploy_token_write_package_registry',
+ isShown: this.$props.packagesRegistryEnabled,
+ value: false,
+ helpText: this.$options.translations.writePackageRegistryHelp,
+ scopeName: 'write_package_registry',
+ },
+ ],
+ username: '',
+ placeholders: {
+ link: { link: ['link_start', 'link_end'] },
+ i: { i: ['i_start', 'i_end'] },
+ code: { code: ['code_start', 'code_end'] },
+ },
+ };
+ },
createDeployToken() {
+ const scopes = {};
+ this.scopes.forEach((scope) => {
+ scopes[scope.scopeName] = scope.value;
+ });
+ const body = {
+ deploy_token: {
+ expires_at: this.expiresAt,
+ name: this.name,
+ username: this.username,
+ ...scopes,
+ },
+ };
+
return axios
- .post(this.createNewTokenPath, {
- deploy_token: {
- expires_at: this.expiresAt,
- name: this.name,
- read_repository: this.readRepository,
- read_registry: this.readRegistry,
- write_registry: this.writeRegistry,
- read_package_registry: this.readPackageRegistry,
- write_package_registry: this.writePackageRegistry,
- username: this.username,
- },
- })
+ .post(this.createNewTokenPath, body)
.then((response) => {
this.newTokenDetails = response.data;
this.resetData();
@@ -152,7 +154,7 @@ export default {
});
},
resetData() {
- const newData = defaultData();
+ const newData = this.defaultData();
delete newData.newTokenDetails;
Object.keys(newData).forEach((k) => {
this[k] = newData[k];
@@ -269,55 +271,19 @@ export default {
>
<div id="deploy-token-scopes">
<!-- eslint-disable @gitlab/vue-require-i18n-strings -->
- <gl-form-checkbox
- id="deploy_token_read_repository"
- v-model="readRepository"
- name="deploy_token_read_repository"
- data-qa-selector="deploy_token_read_repository_checkbox"
- >
- read_repository
- <template #help>{{ $options.translations.readRepositoryHelp }}</template>
- </gl-form-checkbox>
- <gl-form-checkbox
- v-if="containerRegistryEnabled"
- id="deploy_token_read_registry"
- v-model="readRegistry"
- name="deploy_token_read_registry"
- data-qa-selector="deploy_token_read_registry_checkbox"
- >
- read_registry
- <template #help>{{ $options.translations.readRegistryHelp }}</template>
- </gl-form-checkbox>
- <gl-form-checkbox
- v-if="containerRegistryEnabled"
- id="deploy_token_write_registry"
- v-model="writeRegistry"
- name="deploy_token_write_registry"
- data-qa-selector="deploy_token_write_registry_checkbox"
- >
- write_registry
- <template #help>{{ $options.translations.writeRegistryHelp }}</template>
- </gl-form-checkbox>
- <gl-form-checkbox
- v-if="packagesRegistryEnabled"
- id="deploy_token_read_package_registry"
- v-model="readPackageRegistry"
- name="deploy_token_read_package_registry"
- data-qa-selector="deploy_token_read_package_registry_checkbox"
- >
- read_package_registry
- <template #help>{{ $options.translations.readPackageRegistryHelp }}</template>
- </gl-form-checkbox>
- <gl-form-checkbox
- v-if="packagesRegistryEnabled"
- id="deploy_token_write_package_registry"
- v-model="writePackageRegistry"
- name="deploy_token_write_package_registry"
- data-qa-selector="deploy_token_write_package_registry_checkbox"
- >
- write_package_registry
- <template #help>{{ $options.translations.writePackageRegistryHelp }}</template>
- </gl-form-checkbox>
+ <template v-for="scope in scopes">
+ <gl-form-checkbox
+ v-if="scope.isShown"
+ :id="scope.id"
+ :key="scope.id"
+ v-model="scope.value"
+ :name="scope.id"
+ :data-qa-selector="`${scope.id}_checkbox`"
+ >
+ {{ scope.scopeName }}
+ <template #help>{{ scope.helpText }}</template>
+ </gl-form-checkbox>
+ </template>
<!-- eslint-enable @gitlab/vue-require-i18n-strings -->
</div>
</gl-form-group>
diff --git a/app/assets/javascripts/deploy_tokens/deploy_token_translations.js b/app/assets/javascripts/deploy_tokens/deploy_token_translations.js
new file mode 100644
index 00000000000..3767e9e6170
--- /dev/null
+++ b/app/assets/javascripts/deploy_tokens/deploy_token_translations.js
@@ -0,0 +1,41 @@
+import { s__ } from '~/locale';
+
+const translations = {
+ addTokenButton: s__('DeployTokens|Create deploy token'),
+ addTokenExpiryLabel: s__('DeployTokens|Expiration date (optional)'),
+ addTokenExpiryDescription: s__(
+ 'DeployTokens|Enter an expiration date for your token. Defaults to never expire.',
+ ),
+ addTokenHeader: s__('DeployTokens|New deploy token'),
+ addTokenDescription: s__(
+ 'DeployTokens|Create a new deploy token for all projects in this group. %{link_start}What are deploy tokens?%{link_end}',
+ ),
+ addTokenNameLabel: s__('DeployTokens|Name'),
+ addTokenNameDescription: s__('DeployTokens|Enter a unique name for your deploy token.'),
+ addTokenScopesLabel: s__('DeployTokens|Scopes (select at least one)'),
+ addTokenUsernameDescription: s__(
+ 'DeployTokens|Enter a username for your token. Defaults to %{code_start}gitlab+deploy-token-{n}%{code_end}.',
+ ),
+ addTokenUsernameLabel: s__('DeployTokens|Username (optional)'),
+ newTokenCopyMessage: s__('DeployTokens|Copy deploy token'),
+ newProjectTokenCreated: s__('DeployTokens|Your new project deploy token has been created.'),
+ newGroupTokenCreated: s__('DeployTokens|Your new group deploy token has been created.'),
+ newTokenDescription: s__(
+ 'DeployTokens|Use this token as a password. Save it. This password can %{i_start}not%{i_end} be recovered.',
+ ),
+ newTokenMessage: s__('DeployTokens|Your New Deploy Token'),
+ newTokenUsernameCopy: s__('DeployTokens|Copy username'),
+ newTokenUsernameDescription: s__(
+ 'DeployTokens|This username supports access. %{link_start}What kind of access?%{link_end}',
+ ),
+ readRepositoryHelp: s__('DeployTokens|Allows read-only access to the repository.'),
+ readRegistryHelp: s__('DeployTokens|Allows read-only access to registry images.'),
+ writeRegistryHelp: s__('DeployTokens|Allows read and write access to registry images.'),
+ readPackageRegistryHelp: s__('DeployTokens|Allows read-only access to the package registry.'),
+ writePackageRegistryHelp: s__(
+ 'DeployTokens|Allows read and write access to the package registry.',
+ ),
+ createTokenFailedAlert: s__('DeployTokens|Failed to create a new deployment token'),
+};
+
+export default translations;
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index b6ad2d21757..2c185794d17 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -391,11 +391,7 @@ export default {
};
</script>
<template>
- <div
- class="prometheus-graphs"
- data-qa-selector="prometheus_graphs_content"
- data-testid="prometheus-graphs"
- >
+ <div class="prometheus-graphs" data-testid="prometheus-graphs">
<div>
<gl-alert
v-if="!isDeprecationNoticeDismissed"
diff --git a/app/assets/javascripts/monitoring/components/dashboard_actions_menu.vue b/app/assets/javascripts/monitoring/components/dashboard_actions_menu.vue
index 7f8fb3c223d..d67154b7697 100644
--- a/app/assets/javascripts/monitoring/components/dashboard_actions_menu.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard_actions_menu.vue
@@ -146,7 +146,6 @@ export default {
<gl-dropdown
v-gl-tooltip
data-testid="actions-menu"
- data-qa-selector="actions_menu_dropdown"
right
no-caret
toggle-class="gl-px-3!"
@@ -223,7 +222,6 @@ export default {
<gl-dropdown-item
v-if="isMenuItemEnabled.editDashboard"
:href="selectedDashboard ? selectedDashboard.project_blob_path : null"
- data-qa-selector="edit_dashboard_button_enabled"
data-testid="edit-dashboard-item-enabled"
>
{{ $options.i18n.editDashboard }}
diff --git a/app/assets/javascripts/monitoring/components/dashboard_header.vue b/app/assets/javascripts/monitoring/components/dashboard_header.vue
index 90d2498ac19..7bb0d3874d1 100644
--- a/app/assets/javascripts/monitoring/components/dashboard_header.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard_header.vue
@@ -173,7 +173,6 @@ export default {
<div class="gl-mb-3 gl-mr-3 gl-display-flex gl-sm-display-block">
<dashboards-dropdown
id="monitor-dashboards-dropdown"
- data-qa-selector="dashboards_filter_dropdown"
class="flex-grow-1"
toggle-class="dropdown-menu-toggle"
:default-branch="defaultBranch"
@@ -188,7 +187,6 @@ export default {
id="monitor-environments-dropdown"
ref="monitorEnvironmentsDropdown"
class="flex-grow-1"
- data-qa-selector="environments_dropdown"
data-testid="environments-dropdown"
toggle-class="dropdown-menu-toggle"
menu-class="monitor-environment-dropdown-menu"
@@ -225,7 +223,6 @@ export default {
<date-time-picker
ref="dateTimePicker"
class="flex-grow-1 show-last-dropdown"
- data-qa-selector="range_picker_dropdown"
:value="selectedTimeRange"
:options="$options.timeRanges"
:utc="displayUtc"
diff --git a/app/assets/javascripts/monitoring/components/dashboard_panel.vue b/app/assets/javascripts/monitoring/components/dashboard_panel.vue
index 7e7dcef7639..9ad6da35d6b 100644
--- a/app/assets/javascripts/monitoring/components/dashboard_panel.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard_panel.vue
@@ -292,11 +292,7 @@ export default {
<div v-if="graphDataIsLoading" class="mx-1 mt-1">
<gl-loading-icon size="sm" />
</div>
- <div
- v-if="isContextualMenuShown"
- ref="contextualMenu"
- data-qa-selector="prometheus_graph_widgets"
- >
+ <div v-if="isContextualMenuShown" ref="contextualMenu">
<div data-testid="dropdown-wrapper" class="d-flex align-items-center">
<!--
This component should be replaced with a variant developed
@@ -310,7 +306,6 @@ export default {
:text-sr-only="true"
toggle-class="gl-px-3!"
no-caret
- data-qa-selector="prometheus_widgets_dropdown"
right
:title="__('More actions')"
>
@@ -339,7 +334,6 @@ export default {
ref="copyChartLink"
v-track-event="generateLinkToChartOptions(clipboardText)"
:data-clipboard-text="clipboardText"
- data-qa-selector="generate_chart_link_menu_item"
@click="showToast(clipboardText)"
>
{{ __('Copy link to chart') }}
diff --git a/app/assets/javascripts/monitoring/components/duplicate_dashboard_form.vue b/app/assets/javascripts/monitoring/components/duplicate_dashboard_form.vue
index a63008aa382..9ad14b3d52e 100644
--- a/app/assets/javascripts/monitoring/components/duplicate_dashboard_form.vue
+++ b/app/assets/javascripts/monitoring/components/duplicate_dashboard_form.vue
@@ -104,13 +104,7 @@ export default {
label-size="sm"
label-for="fileName"
>
- <gl-form-input
- id="fileName"
- ref="fileName"
- v-model="form.fileName"
- data-qa-selector="duplicate_dashboard_filename_field"
- :required="true"
- />
+ <gl-form-input id="fileName" ref="fileName" v-model="form.fileName" :required="true" />
</gl-form-group>
<gl-form-group :label="__('Branch')" label-size="sm" label-for="branch">
<gl-form-radio-group
diff --git a/app/assets/javascripts/monitoring/components/variables_section.vue b/app/assets/javascripts/monitoring/components/variables_section.vue
index 493d37ce263..971f188e9f3 100644
--- a/app/assets/javascripts/monitoring/components/variables_section.vue
+++ b/app/assets/javascripts/monitoring/components/variables_section.vue
@@ -37,11 +37,7 @@ export default {
};
</script>
<template>
- <div
- ref="variablesSection"
- class="d-sm-flex flex-sm-wrap pt-2 pr-1 pb-0 pl-2 variables-section"
- data-qa-selector="variables_content"
- >
+ <div ref="variablesSection" class="d-sm-flex flex-sm-wrap pt-2 pr-1 pb-0 pl-2 variables-section">
<div v-for="variable in variables" :key="variable.name" class="mb-1 pr-2 d-flex d-sm-block">
<component
:is="variableField(variable.type)"
@@ -50,7 +46,6 @@ export default {
:value="variable.value"
:name="variable.name"
:options="variable.options"
- data-qa-selector="variable_item"
@input="refreshDashboard(variable, $event)"
/>
</div>
diff --git a/app/assets/javascripts/pipeline_editor/components/editor/ci_editor_header.vue b/app/assets/javascripts/pipeline_editor/components/editor/ci_editor_header.vue
index 189690ce2c3..201fba837e2 100644
--- a/app/assets/javascripts/pipeline_editor/components/editor/ci_editor_header.vue
+++ b/app/assets/javascripts/pipeline_editor/components/editor/ci_editor_header.vue
@@ -43,9 +43,7 @@ export default {
</script>
<template>
- <div
- class="gl-bg-gray-10 gl-display-flex gl-p-3 gl-gap-3 gl-border-solid gl-border-gray-100 gl-border-1"
- >
+ <div class="gl-display-flex gl-p-3 gl-gap-3 gl-border-solid gl-border-gray-100 gl-border-1">
<gl-button
:href="$options.TEMPLATE_REPOSITORY_URL"
size="small"
diff --git a/app/assets/javascripts/pipeline_editor/components/ui/editor_tab.vue b/app/assets/javascripts/pipeline_editor/components/ui/editor_tab.vue
index 65f399d1912..22b82f2e96f 100644
--- a/app/assets/javascripts/pipeline_editor/components/ui/editor_tab.vue
+++ b/app/assets/javascripts/pipeline_editor/components/ui/editor_tab.vue
@@ -41,7 +41,9 @@ import { __, s__ } from '~/locale';
export default {
i18n: {
- invalid: __('Your CI/CD configuration syntax is invalid. View Lint tab for more details.'),
+ invalid: __(
+ 'Your CI/CD configuration syntax is invalid. Select the Validate tab for more details.',
+ ),
unavailable: __(
"We're experiencing difficulties and this tab content is currently unavailable.",
),
diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue b/app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue
index eb11e17dd1b..f78222d54d4 100644
--- a/app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue
+++ b/app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue
@@ -4,6 +4,7 @@ import { sprintf } from '~/locale';
import { getParameterByName } from '~/lib/utils/url_utility';
import { helpPagePath } from '~/helpers/help_page_helper';
import branchRulesQuery from '../../queries/branch_rules_details.query.graphql';
+import { getAccessLevels } from '../../../utils';
import Protection from './protection.vue';
import {
I18N,
@@ -120,23 +121,7 @@ export default {
},
},
methods: {
- getAccessLevels(accessLevels = {}) {
- const total = accessLevels.edges?.length;
- const accessLevelTypes = { total, users: [], groups: [], roles: [] };
-
- accessLevels.edges?.forEach(({ node }) => {
- if (node.user) {
- const src = node.user.avatarUrl;
- accessLevelTypes.users.push({ src, ...node.user });
- } else if (node.group) {
- accessLevelTypes.groups.push(node);
- } else {
- accessLevelTypes.roles.push(node);
- }
- });
-
- return accessLevelTypes;
- },
+ getAccessLevels,
},
};
</script>
diff --git a/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue b/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue
index a9eb2a53fbf..ecf194ccc3a 100644
--- a/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue
+++ b/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue
@@ -58,6 +58,7 @@ export default {
:branch-protection="rule.branchProtection"
:status-checks-total="rule.externalStatusChecks.nodes.length"
:approval-rules-total="rule.approvalRules.nodes.length"
+ :matching-branches-count="rule.matchingBranchesCount"
/>
<span v-if="!branchRules.length" data-testid="empty">{{ $options.i18n.emptyState }}</span>
diff --git a/app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue b/app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue
index 78c824c66d1..41947834bdb 100644
--- a/app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue
+++ b/app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue
@@ -1,6 +1,7 @@
<script>
import { GlBadge, GlButton } from '@gitlab/ui';
import { s__, sprintf, n__ } from '~/locale';
+import { getAccessLevels } from '../../../utils';
export const i18n = {
defaultLabel: s__('BranchRules|default'),
@@ -9,6 +10,9 @@ export const i18n = {
codeOwnerApprovalRequired: s__('BranchRules|Requires CODEOWNERS approval'),
statusChecks: s__('BranchRules|%{total} status %{subject}'),
approvalRules: s__('BranchRules|%{total} approval %{subject}'),
+ matchingBranches: s__('BranchRules|%{total} matching %{subject}'),
+ pushAccessLevels: s__('BranchRules|Allowed to merge'),
+ mergeAccessLevels: s__('BranchRules|Allowed to push'),
};
export default {
@@ -48,8 +52,16 @@ export default {
required: false,
default: 0,
},
+ matchingBranchesCount: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
},
computed: {
+ isWildcard() {
+ return this.name.includes('*');
+ },
hasApprovalDetails() {
return this.approvalDetails.length;
},
@@ -68,8 +80,31 @@ export default {
subject: n__('rule', 'rules', this.approvalRulesTotal),
});
},
+ matchingBranchesText() {
+ return sprintf(this.$options.i18n.matchingBranches, {
+ total: this.matchingBranchesCount,
+ subject: n__('branch', 'branches', this.matchingBranchesCount),
+ });
+ },
+ mergeAccessLevels() {
+ const { mergeAccessLevels } = this.branchProtection || {};
+ return this.getAccessLevels(mergeAccessLevels);
+ },
+ pushAccessLevels() {
+ const { pushAccessLevels } = this.branchProtection || {};
+ return this.getAccessLevels(pushAccessLevels);
+ },
+ pushAccessLevelsText() {
+ return this.getAccessLevelsText(this.$options.i18n.pushAccessLevels, this.pushAccessLevels);
+ },
+ mergeAccessLevelsText() {
+ return this.getAccessLevelsText(this.$options.i18n.mergeAccessLevels, this.mergeAccessLevels);
+ },
approvalDetails() {
const approvalDetails = [];
+ if (this.isWildcard) {
+ approvalDetails.push(this.matchingBranchesText);
+ }
if (this.branchProtection.allowForcePush) {
approvalDetails.push(this.$options.i18n.allowForcePush);
}
@@ -82,9 +117,31 @@ export default {
if (this.approvalRulesTotal) {
approvalDetails.push(this.approvalRulesText);
}
+ if (this.mergeAccessLevels.total > 0) {
+ approvalDetails.push(this.mergeAccessLevelsText);
+ }
+ if (this.pushAccessLevels.total > 0) {
+ approvalDetails.push(this.pushAccessLevelsText);
+ }
return approvalDetails;
},
},
+ methods: {
+ getAccessLevels,
+ getAccessLevelsText(beginString = '', accessLevels) {
+ const textParts = [];
+ if (accessLevels.roles.length) {
+ textParts.push(n__('1 role', '%d roles', accessLevels.roles.length));
+ }
+ if (accessLevels.groups.length) {
+ textParts.push(n__('1 group', '%d groups', accessLevels.groups.length));
+ }
+ if (accessLevels.users.length) {
+ textParts.push(n__('1 user', '%d users', accessLevels.users.length));
+ }
+ return `${beginString}: ${textParts.join(', ')}`;
+ },
+ },
};
</script>
diff --git a/app/assets/javascripts/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql b/app/assets/javascripts/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql
index 49e089e7805..7bd8e6d4a64 100644
--- a/app/assets/javascripts/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql
+++ b/app/assets/javascripts/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql
@@ -5,9 +5,38 @@ query getBranchRules($projectPath: ID!) {
nodes {
name
isDefault
+ matchingBranchesCount
branchProtection {
allowForcePush
codeOwnerApprovalRequired
+ mergeAccessLevels {
+ edges {
+ node {
+ accessLevel
+ accessLevelDescription
+ group {
+ id
+ }
+ user {
+ id
+ }
+ }
+ }
+ }
+ pushAccessLevels {
+ edges {
+ node {
+ accessLevel
+ accessLevelDescription
+ group {
+ id
+ }
+ user {
+ id
+ }
+ }
+ }
+ }
}
externalStatusChecks {
nodes {
diff --git a/app/assets/javascripts/projects/settings/utils.js b/app/assets/javascripts/projects/settings/utils.js
new file mode 100644
index 00000000000..7bcfde39178
--- /dev/null
+++ b/app/assets/javascripts/projects/settings/utils.js
@@ -0,0 +1,17 @@
+export const getAccessLevels = (accessLevels = {}) => {
+ const total = accessLevels.edges?.length;
+ const accessLevelTypes = { total, users: [], groups: [], roles: [] };
+
+ accessLevels.edges?.forEach(({ node }) => {
+ if (node.user) {
+ const src = node.user.avatarUrl;
+ accessLevelTypes.users.push({ src, ...node.user });
+ } else if (node.group) {
+ accessLevelTypes.groups.push(node);
+ } else {
+ accessLevelTypes.roles.push(node);
+ }
+ });
+
+ return accessLevelTypes;
+};
diff --git a/app/assets/javascripts/sidebar/sidebar_mediator.js b/app/assets/javascripts/sidebar/sidebar_mediator.js
index 912f0fdcbef..b58b3cb4df3 100644
--- a/app/assets/javascripts/sidebar/sidebar_mediator.js
+++ b/app/assets/javascripts/sidebar/sidebar_mediator.js
@@ -56,12 +56,14 @@ export default class SidebarMediator {
}
async saveReviewers(field) {
- const selected = this.store.reviewers.map((u) => u.id);
+ const selectedReviewers = this.store.reviewers;
+ const selectedIds = selectedReviewers.map((u) => u.id);
+ const suggestedSelectedIds = selectedReviewers.filter((u) => u.suggested).map((u) => u.id);
// If there are no ids, that means we have to unassign (which is id = 0)
// And it only accepts an array, hence [0]
- const reviewers = selected.length === 0 ? [0] : selected;
- const data = { reviewer_ids: reviewers };
+ const reviewers = selectedIds.length === 0 ? [0] : selectedIds;
+ const data = { reviewer_ids: reviewers, suggested_reviewer_ids: suggestedSelectedIds };
try {
const res = await this.service.update(field, data);
diff --git a/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue b/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue
index 181c1b89e31..d8a2789a419 100644
--- a/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue
+++ b/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue
@@ -265,7 +265,6 @@ export default {
<gl-dropdown-item
v-for="(option, index) in options"
:key="index"
- data-qa-selector="quick_range_item"
:active="isOptionActive(option)"
active-class="active"
@click="setQuickRange(option)"
diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue b/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue
index 428fa9f8279..28a16cd846a 100644
--- a/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue
+++ b/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue
@@ -1,6 +1,6 @@
<script>
import { GlIntersectionObserver } from '@gitlab/ui';
-import { scrollToElement } from '~/lib/utils/common_utils';
+import LineHighlighter from '~/blob/line_highlighter';
import ChunkLine from './chunk_line.vue';
/*
@@ -81,12 +81,14 @@ export default {
return;
}
- window.requestIdleCallback(() => {
+ window.requestIdleCallback(async () => {
this.isLoading = false;
const { hash } = this.$route;
if (hash && this.totalChunks > 0 && this.totalChunks === this.chunkIndex + 1) {
// when the last chunk is loaded scroll to the hash
- scrollToElement(hash, { behavior: 'auto' });
+ await this.$nextTick();
+ const lineHighlighter = new LineHighlighter({ scrollBehavior: 'auto' });
+ lineHighlighter.highlightHash(hash);
}
});
},
diff --git a/app/assets/javascripts/vue_shared/security_reports/store/utils.js b/app/assets/javascripts/vue_shared/security_reports/store/utils.js
index a6628fa0f9f..f3cb5fc16f0 100644
--- a/app/assets/javascripts/vue_shared/security_reports/store/utils.js
+++ b/app/assets/javascripts/vue_shared/security_reports/store/utils.js
@@ -29,7 +29,13 @@ export const fetchDiffData = (state, endpoint, category) => {
*/
export const enrichVulnerabilityWithFeedback = (vulnerability, feedback = []) =>
feedback
- .filter((fb) => fb.project_fingerprint === vulnerability.project_fingerprint)
+ .filter((fb) =>
+ // Some records still have a `finding_uuid` with null, we need to fallback to using `project_fingerprint` in those cases. Once all entries have been fixed, we can remove the fallback.
+ // related epic: https://gitlab.com/groups/gitlab-org/-/epics/2791
+ fb.finding_uuid !== null
+ ? fb.finding_uuid === vulnerability.finding_uuid
+ : fb.project_fingerprint === vulnerability.project_fingerprint,
+ )
.reduce((vuln, fb) => {
if (fb.feedback_type === FEEDBACK_TYPE_DISMISSAL) {
return {
diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb
index 817f82085e6..b4a36b7db22 100644
--- a/app/controllers/concerns/authenticates_with_two_factor.rb
+++ b/app/controllers/concerns/authenticates_with_two_factor.rb
@@ -23,6 +23,8 @@ module AuthenticatesWithTwoFactor
session[:otp_user_id] = user.id
session[:user_password_hash] = Digest::SHA256.hexdigest(user.encrypted_password)
+
+ add_gon_variables
push_frontend_feature_flag(:webauthn)
if Feature.enabled?(:webauthn)
diff --git a/app/controllers/projects/work_items_controller.rb b/app/controllers/projects/work_items_controller.rb
index a7e59a28fb7..e9f5a7207bb 100644
--- a/app/controllers/projects/work_items_controller.rb
+++ b/app/controllers/projects/work_items_controller.rb
@@ -3,6 +3,7 @@
class Projects::WorkItemsController < Projects::ApplicationController
before_action do
push_force_frontend_feature_flag(:work_items, project&.work_items_feature_flag_enabled?)
+ push_force_frontend_feature_flag(:work_items_mvc, project&.work_items_mvc_feature_flag_enabled?)
push_force_frontend_feature_flag(:work_items_mvc_2, project&.work_items_mvc_2_feature_flag_enabled?)
push_frontend_feature_flag(:use_iid_in_work_items_path, project)
end
diff --git a/app/graphql/resolvers/concerns/looks_ahead.rb b/app/graphql/resolvers/concerns/looks_ahead.rb
index 81099c04e9f..1d532eb2486 100644
--- a/app/graphql/resolvers/concerns/looks_ahead.rb
+++ b/app/graphql/resolvers/concerns/looks_ahead.rb
@@ -39,7 +39,7 @@ module LooksAhead
def filtered_preloads
nodes = node_selection
- return [] unless nodes
+ return [] unless nodes&.selected?
selected_fields = nodes.selections.map(&:name)
root_level_preloads = preloads_from_node_selection(selected_fields, preloads)
@@ -65,13 +65,13 @@ module LooksAhead
end.flatten
end
- def node_selection
- return unless lookahead
+ def node_selection(selection = lookahead)
+ return selection unless selection&.selected?
+ return selection.selection(:edges).selection(:node) if selection.selects?(:edges)
- if lookahead.selects?(:nodes)
- lookahead.selection(:nodes)
- elsif lookahead.selects?(:edges)
- lookahead.selection(:edges).selection(:node)
- end
+ # Will return a NullSelection object if :nodes is not a selection. This
+ # is better than returning nil as we can continue chaining selections on
+ # without raising errors.
+ selection.selection(:nodes)
end
end
diff --git a/app/models/group.rb b/app/models/group.rb
index edbc8825663..e50a886f9ba 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -892,6 +892,10 @@ class Group < Namespace
feature_flag_enabled_for_self_or_ancestor?(:work_items)
end
+ def work_items_mvc_feature_flag_enabled?
+ feature_flag_enabled_for_self_or_ancestor?(:work_items_mvc)
+ end
+
def work_items_mvc_2_feature_flag_enabled?
feature_flag_enabled_for_self_or_ancestor?(:work_items_mvc_2)
end
diff --git a/app/models/merge_request/predictions.rb b/app/models/merge_request/predictions.rb
index ef9e00b5f74..61494ca584c 100644
--- a/app/models/merge_request/predictions.rb
+++ b/app/models/merge_request/predictions.rb
@@ -4,4 +4,13 @@ class MergeRequest::Predictions < ApplicationRecord # rubocop:disable Style/Cla
belongs_to :merge_request, inverse_of: :predictions
validates :suggested_reviewers, json_schema: { filename: 'merge_request_predictions_suggested_reviewers' }
+ validates :accepted_reviewers, json_schema: { filename: 'merge_request_predictions_accepted_reviewers' }
+
+ def suggested_reviewer_usernames
+ Array.wrap(suggested_reviewers['reviewers'])
+ end
+
+ def accepted_reviewer_usernames
+ Array.wrap(accepted_reviewers['reviewers'])
+ end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index a07d4147228..d07e9ff759e 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -2994,6 +2994,10 @@ class Project < ApplicationRecord
group&.work_items_feature_flag_enabled? || Feature.enabled?(:work_items, self)
end
+ def work_items_mvc_feature_flag_enabled?
+ group&.work_items_mvc_feature_flag_enabled? || Feature.enabled?(:work_items_mvc)
+ end
+
def work_items_mvc_2_feature_flag_enabled?
group&.work_items_mvc_2_feature_flag_enabled? || Feature.enabled?(:work_items_mvc_2)
end
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index e7ab2c062ee..fabf006979b 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -59,6 +59,8 @@ module MergeRequests
merge_request_activity_counter.track_users_review_requested(users: new_reviewers)
merge_request_activity_counter.track_reviewers_changed_action(user: current_user)
trigger_merge_request_reviewers_updated(merge_request)
+
+ capture_suggested_reviewers_accepted(merge_request)
end
def cleanup_environments(merge_request)
@@ -161,6 +163,12 @@ module MergeRequests
else
params.delete(:reviewer_ids)
end
+
+ filter_suggested_reviewers
+ end
+
+ def filter_suggested_reviewers
+ # Implemented in EE
end
def merge_request_metrics_service(merge_request)
@@ -253,6 +261,10 @@ module MergeRequests
def trigger_merge_request_merge_status_updated(merge_request)
GraphqlTriggers.merge_request_merge_status_updated(merge_request)
end
+
+ def capture_suggested_reviewers_accepted(merge_request)
+ # Implemented in EE
+ end
end
end
diff --git a/app/validators/json_schemas/merge_request_predictions_accepted_reviewers.json b/app/validators/json_schemas/merge_request_predictions_accepted_reviewers.json
new file mode 100644
index 00000000000..fcbe8903df4
--- /dev/null
+++ b/app/validators/json_schemas/merge_request_predictions_accepted_reviewers.json
@@ -0,0 +1,10 @@
+{
+ "description": "Merge request predictions accepted reviewers",
+ "type": "object",
+ "properties": {
+ "reviewers": {
+ "type": "array"
+ }
+ },
+ "additionalProperties": true
+} \ No newline at end of file
diff --git a/app/views/layouts/header/_marketing_links.html.haml b/app/views/layouts/header/_marketing_links.html.haml
index 24069de394d..c33229e4ec4 100644
--- a/app/views/layouts/header/_marketing_links.html.haml
+++ b/app/views/layouts/header/_marketing_links.html.haml
@@ -6,29 +6,29 @@
.dropdown-menu
%ul
%li
- = link_to 'https://about.gitlab.com/stages-devops-lifecycle/' do
+ = link_to Gitlab::Utils.append_path(promo_url, 'stages-devops-lifecycle') do
= s_('LoggedOutMarketingHeader|GitLab: the DevOps platform')
%li
= link_to explore_root_path do
= s_('LoggedOutMarketingHeader|Explore GitLab')
%li
- = link_to 'https://about.gitlab.com/install/' do
+ = link_to Gitlab::Utils.append_path(promo_url, 'install') do
= s_('LoggedOutMarketingHeader|Install GitLab')
%li
- = link_to 'https://about.gitlab.com/is-it-any-good/' do
+ = link_to Gitlab::Utils.append_path(promo_url, 'is-it-any-good') do
= s_('LoggedOutMarketingHeader|How GitLab compares')
%li
- = link_to 'https://about.gitlab.com/get-started/' do
+ = link_to Gitlab::Utils.append_path(promo_url, 'get-started') do
= s_('LoggedOutMarketingHeader|Get started')
%li
- = link_to 'https://docs.gitlab.com/' do
+ = link_to Gitlab::Saas::doc_url do
= s_('LoggedOutMarketingHeader|GitLab docs')
%li
- = link_to 'https://about.gitlab.com/learn/' do
+ = link_to Gitlab::Utils.append_path(promo_url, 'learn') do
= s_('LoggedOutMarketingHeader|GitLab Learn')
%li.gl-mr-3
- = link_to 'https://about.gitlab.com/pricing/' do
+ = link_to Gitlab::Utils.append_path(promo_url, 'pricing') do
= s_('LoggedOutMarketingHeader|Pricing')
%li.gl-mr-3
- = link_to 'https://about.gitlab.com/sales/' do
+ = link_to Gitlab::Utils.append_path(promo_url, 'sales') do
= s_('LoggedOutMarketingHeader|Talk to an expert')
diff --git a/app/views/projects/_files.html.haml b/app/views/projects/_files.html.haml
index 8bf397d0796..712d6fabf82 100644
--- a/app/views/projects/_files.html.haml
+++ b/app/views/projects/_files.html.haml
@@ -1,23 +1,23 @@
+- show_auto_devops_callout = show_auto_devops_callout?(@project)
- is_project_overview = local_assigns.fetch(:is_project_overview, false)
- ref = local_assigns.fetch(:ref) { current_ref }
- project = local_assigns.fetch(:project) { @project }
-- show_auto_devops_callout = show_auto_devops_callout?(@project)
- add_page_startup_api_call logs_file_project_ref_path(@project, ref, @path, format: "json", offset: 0)
- if readme_path = @project.repository.readme_path
- add_page_startup_api_call project_blob_path(@project, tree_join(@ref, readme_path), viewer: "rich", format: "json")
#tree-holder.tree-holder.clearfix.js-per-page{ data: { blame_per_page: Projects::BlameService::PER_PAGE } }
+ .info-well.gl-display-none.gl-sm-display-flex.project-last-commit.gl-flex-direction-column
+ #js-last-commit.gl-m-auto
+ = gl_loading_icon(size: 'md')
+ #js-code-owners
+
.nav-block.gl-display-flex.gl-xs-flex-direction-column.gl-align-items-stretch
= render 'projects/tree/tree_header', tree: @tree, is_project_overview: is_project_overview
- if project.forked? && Feature.enabled?(:fork_divergence_counts, @project.fork_source)
= render 'projects/fork_info'
- .info-well.gl-display-none.gl-sm-display-flex.project-last-commit.gl-flex-direction-column
- #js-last-commit.gl-m-auto
- = gl_loading_icon(size: 'md')
- #js-code-owners
-
- if is_project_overview
.project-buttons.gl-mb-5.js-show-on-project-root{ data: { qa_selector: 'project_buttons' } }
= render 'stat_anchor_list', anchors: @project.statistics_buttons(show_auto_devops_callout: show_auto_devops_callout), project_buttons: true
diff --git a/app/views/projects/_flash_messages.html.haml b/app/views/projects/_flash_messages.html.haml
index e8e1bd87abb..2d9f7e49ddc 100644
--- a/app/views/projects/_flash_messages.html.haml
+++ b/app/views/projects/_flash_messages.html.haml
@@ -7,6 +7,8 @@
= render 'shared/no_password'
- unless project.empty_repo?
= render 'shared/auto_devops_implicitly_enabled_banner', project: project
+ - if show_auto_devops_callout?(@project)
+ = render 'shared/auto_devops_callout'
= render_if_exists 'projects/above_size_limit_warning', project: project
= render_if_exists 'shared/shared_runners_minutes_limit', project: project, classes: [container_class, ("limit-container-width" unless fluid_layout)]
= render_if_exists 'projects/terraform_banner', project: project
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index 2c20a77937e..5fa70c3af32 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -25,8 +25,5 @@
- view_path = @project.default_view
-- if show_auto_devops_callout?(@project)
- = render 'shared/auto_devops_callout'
-
%div{ class: project_child_container_class(view_path) }
= render view_path, is_project_overview: true
diff --git a/app/views/shared/_auto_devops_callout.html.haml b/app/views/shared/_auto_devops_callout.html.haml
index c2b941c6106..93f919f01d9 100644
--- a/app/views/shared/_auto_devops_callout.html.haml
+++ b/app/views/shared/_auto_devops_callout.html.haml
@@ -1,13 +1,16 @@
-= render Pajamas::BannerComponent.new(button_text: s_('AutoDevOps|Enable in settings'),
- button_link: project_settings_ci_cd_path(@project, anchor: 'autodevops-settings'),
- svg_path: 'illustrations/autodevops.svg',
- banner_options: { class: 'js-autodevops-banner', data: { uid: 'auto_devops_settings_dismissed', project_path: project_path(@project) } },
- close_options: { 'aria-label' => s_('AutoDevOps|Dismiss Auto DevOps box'), class: 'js-close-callout' }) do |c|
- - c.title do
- = s_('AutoDevOps|Auto DevOps')
+- container = @no_breadcrumb_container ? 'container-fluid' : container_class
- %p= s_('AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration.')
+%div{ class: [container, @content_class, 'gl-pt-5!'] }
+ = render Pajamas::BannerComponent.new(button_text: s_('AutoDevOps|Enable in settings'),
+ button_link: project_settings_ci_cd_path(@project, anchor: 'autodevops-settings'),
+ svg_path: 'illustrations/autodevops.svg',
+ banner_options: { class: 'js-autodevops-banner auto-devops-callout', data: { uid: 'auto_devops_settings_dismissed', project_path: project_path(@project) } },
+ close_options: { 'aria-label' => s_('AutoDevOps|Dismiss Auto DevOps box'), class: 'js-close-callout' }) do |c|
+ - c.title do
+ = s_('AutoDevOps|Auto DevOps')
- %p
- - link = link_to(s_('AutoDevOps|Auto DevOps documentation'), help_page_path('topics/autodevops/index.md'), target: '_blank', rel: 'noopener noreferrer')
- = s_('AutoDevOps|Learn more in the %{link_to_documentation}').html_safe % { link_to_documentation: link }
+ %p= s_('AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration.')
+
+ %p
+ - link = link_to(s_('AutoDevOps|Auto DevOps documentation'), help_page_path('topics/autodevops/index.md'), target: '_blank', rel: 'noopener noreferrer')
+ = s_('AutoDevOps|Learn more in the %{link_to_documentation}').html_safe % { link_to_documentation: link }
diff --git a/app/views/shared/integrations/prometheus/_custom_metrics.html.haml b/app/views/shared/integrations/prometheus/_custom_metrics.html.haml
index 896249c6163..dda84e0fb9e 100644
--- a/app/views/shared/integrations/prometheus/_custom_metrics.html.haml
+++ b/app/views/shared/integrations/prometheus/_custom_metrics.html.haml
@@ -6,12 +6,12 @@
= link_to s_('PrometheusService|More information'), help_page_path('operations/metrics/index.md', anchor: 'adding-custom-metrics'), target: '_blank', rel: "noopener noreferrer"
.col-lg-9
- .card.custom-monitored-metrics.js-panel-custom-monitored-metrics{ data: { qa_selector: 'custom_metrics_container', active_custom_metrics: project_prometheus_metrics_path(project), environments_data: environments_list_data, service_active: "#{integration.active}" } }
+ .card.custom-monitored-metrics.js-panel-custom-monitored-metrics{ data: { active_custom_metrics: project_prometheus_metrics_path(project), environments_data: environments_list_data, service_active: "#{integration.active}" } }
.card-header
%strong
= s_('PrometheusService|Custom metrics')
= gl_badge_tag 0, nil, class: 'js-custom-monitored-count'
- = link_to s_('PrometheusService|New metric'), new_project_prometheus_metric_path(project), class: 'btn gl-button btn-confirm gl-ml-auto js-new-metric-button hidden', data: { qa_selector: 'new_metric_button' }
+ = link_to s_('PrometheusService|New metric'), new_project_prometheus_metric_path(project), class: 'btn gl-button btn-confirm gl-ml-auto js-new-metric-button hidden'
.card-body
.flash-container.hidden
.flash-warning