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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-11-21 12:15:06 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-11-21 12:15:06 +0300
commitf73fa6daff38ea21d33a71f7bdcba34a86421333 (patch)
tree2efcadb09b56150b0f01209d217f167f446c02d6
parentcb9d96285c52d95a49782688e4ec8dd3f3942c89 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/ml_candidates_show.vue206
-rw-r--r--app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/translations.js26
-rw-r--r--app/assets/javascripts/ml/model_registry/components/candidate_detail.vue207
-rw-r--r--app/assets/javascripts/ml/model_registry/components/candidate_detail_row.vue (renamed from app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row.vue)0
-rw-r--r--app/assets/javascripts/ml/model_registry/translations.js20
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue10
-rw-r--r--app/assets/javascripts/vue_shared/components/source_viewer/source_viewer_new.vue1
-rw-r--r--app/helpers/ci/runners_helper.rb2
-rw-r--r--app/helpers/notes_helper.rb13
-rw-r--r--app/helpers/notifications_helper.rb36
-rw-r--r--app/helpers/packages_helper.rb4
-rw-r--r--app/helpers/profiles_helper.rb4
-rw-r--r--app/helpers/tab_helper.rb8
-rw-r--r--app/views/ci/runner/_how_to_setup_runner.html.haml2
-rw-r--r--config/feature_flags/development/adherence_report_ui.yml8
-rw-r--r--config/feature_flags/development/approval_rules_disable_joins.yml8
-rw-r--r--config/feature_flags/development/compliance_adherence_report.yml8
-rw-r--r--db/post_migrate/20231120161159_prepare_index_for_org_id_and_id_on_projects.rb15
-rw-r--r--db/schema_migrations/202311201611591
-rw-r--r--doc/api/members.md6
-rw-r--r--doc/development/ai_features/index.md10
-rw-r--r--doc/user/analytics/ci_cd_analytics.md15
-rw-r--r--doc/user/compliance/compliance_center/index.md6
-rw-r--r--locale/gitlab.pot91
-rw-r--r--qa/qa/page/project/settings/runners.rb19
-rw-r--r--qa/qa/page/project/settings/visibility_features_permissions.rb8
-rw-r--r--spec/frontend/ml/experiment_tracking/routes/candidates/show/ml_candidates_show_spec.js203
-rw-r--r--spec/frontend/ml/experiment_tracking/routes/candidates/show/mock_data.js41
-rw-r--r--spec/frontend/ml/model_registry/components/candidate_detail_row_spec.js (renamed from spec/frontend/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row_spec.js)2
-rw-r--r--spec/frontend/ml/model_registry/components/candidate_detail_spec.js183
-rw-r--r--spec/frontend/ml/model_registry/mock_data.js42
-rw-r--r--spec/helpers/notes_helper_spec.rb17
-rw-r--r--spec/helpers/notifications_helper_spec.rb6
-rw-r--r--vendor/project_templates/android.tar.gzbin131955 -> 145318 bytes
-rw-r--r--vendor/project_templates/astro_tailwind.tar.gzbin181171 -> 181120 bytes
-rw-r--r--vendor/project_templates/bridgetown.tar.gzbin38125 -> 38135 bytes
-rw-r--r--vendor/project_templates/cluster_management.tar.gzbin12083 -> 11542 bytes
-rw-r--r--vendor/project_templates/dotnetcore.tar.gzbin9775 -> 8634 bytes
-rw-r--r--vendor/project_templates/express.tar.gzbin28962 -> 28675 bytes
-rw-r--r--vendor/project_templates/gatsby.tar.gzbin739260 -> 451708 bytes
-rw-r--r--vendor/project_templates/gitpod_spring_petclinic.tar.gzbin402980 -> 402764 bytes
-rw-r--r--vendor/project_templates/gomicro.tar.gzbin4495 -> 39222 bytes
-rw-r--r--vendor/project_templates/hexo.tar.gzbin546434 -> 537155 bytes
-rw-r--r--vendor/project_templates/hugo.tar.gzbin26755 -> 26666 bytes
-rw-r--r--vendor/project_templates/iosswift.tar.gzbin2421857 -> 2424935 bytes
-rw-r--r--vendor/project_templates/jekyll.tar.gzbin59551 -> 16962 bytes
-rw-r--r--vendor/project_templates/jsonnet.tar.gzbin3857 -> 4139 bytes
-rw-r--r--vendor/project_templates/kotlin_native_linux.tar.gzbin60265 -> 59894 bytes
-rw-r--r--vendor/project_templates/laravel.tar.gzbin74342 -> 73958 bytes
-rw-r--r--vendor/project_templates/middleman.tar.gzbin37406 -> 37654 bytes
-rw-r--r--vendor/project_templates/nfgitbook.tar.gzbin121618 -> 3822 bytes
-rw-r--r--vendor/project_templates/nfhexo.tar.gzbin654150 -> 536459 bytes
-rw-r--r--vendor/project_templates/nfhugo.tar.gzbin1158440 -> 1040704 bytes
-rw-r--r--vendor/project_templates/nfjekyll.tar.gzbin131307 -> 13761 bytes
-rw-r--r--vendor/project_templates/nfplainhtml.tar.gzbin121444 -> 3652 bytes
-rw-r--r--vendor/project_templates/pelican.tar.gzbin42448 -> 44308 bytes
-rw-r--r--vendor/project_templates/plainhtml.tar.gzbin10445 -> 4391 bytes
-rw-r--r--vendor/project_templates/rails.tar.gzbin149743 -> 150754 bytes
-rw-r--r--vendor/project_templates/salesforcedx.tar.gzbin431023 -> 432762 bytes
-rw-r--r--vendor/project_templates/serverless_framework.tar.gzbin94665 -> 94151 bytes
-rw-r--r--vendor/project_templates/spring.tar.gzbin48912 -> 53487 bytes
-rw-r--r--vendor/project_templates/tencent_serverless_framework.tar.gzbin119709 -> 119287 bytes
-rw-r--r--vendor/project_templates/typo3_distribution.tar.gzbin77280 -> 75443 bytes
63 files changed, 576 insertions, 652 deletions
diff --git a/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/ml_candidates_show.vue b/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/ml_candidates_show.vue
index 43d28e3d699..ea942012af3 100644
--- a/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/ml_candidates_show.vue
+++ b/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/ml_candidates_show.vue
@@ -1,45 +1,15 @@
<script>
-import { GlAvatarLabeled, GlLink, GlTableLite } from '@gitlab/ui';
-import { isEmpty, maxBy, range } from 'lodash';
import ModelExperimentsHeader from '~/ml/experiment_tracking/components/model_experiments_header.vue';
import DeleteButton from '~/ml/experiment_tracking/components/delete_button.vue';
-import { __, sprintf } from '~/locale';
-import DetailRow from './components/candidate_detail_row.vue';
-
-import {
- TITLE_LABEL,
- INFO_LABEL,
- ID_LABEL,
- STATUS_LABEL,
- EXPERIMENT_LABEL,
- ARTIFACTS_LABEL,
- PARAMETERS_LABEL,
- METRICS_LABEL,
- METADATA_LABEL,
- DELETE_CANDIDATE_CONFIRMATION_MESSAGE,
- DELETE_CANDIDATE_PRIMARY_ACTION_LABEL,
- DELETE_CANDIDATE_MODAL_TITLE,
- MLFLOW_ID_LABEL,
- CI_SECTION_LABEL,
- JOB_LABEL,
- CI_USER_LABEL,
- CI_MR_LABEL,
- PERFORMANCE_LABEL,
- NO_PARAMETERS_MESSAGE,
- NO_METRICS_MESSAGE,
- NO_METADATA_MESSAGE,
- NO_CI_MESSAGE,
-} from './translations';
+import CandidateDetail from '~/ml/model_registry/components/candidate_detail.vue';
+import { s__ } from '~/locale';
export default {
name: 'MlCandidatesShow',
components: {
ModelExperimentsHeader,
DeleteButton,
- DetailRow,
- GlAvatarLabeled,
- GlLink,
- GlTableLite,
+ CandidateDetail,
},
props: {
candidate: {
@@ -47,70 +17,18 @@ export default {
required: true,
},
},
- i18n: {
- TITLE_LABEL,
- INFO_LABEL,
- ID_LABEL,
- STATUS_LABEL,
- EXPERIMENT_LABEL,
- ARTIFACTS_LABEL,
- DELETE_CANDIDATE_CONFIRMATION_MESSAGE,
- DELETE_CANDIDATE_PRIMARY_ACTION_LABEL,
- DELETE_CANDIDATE_MODAL_TITLE,
- MLFLOW_ID_LABEL,
- CI_SECTION_LABEL,
- JOB_LABEL,
- CI_USER_LABEL,
- CI_MR_LABEL,
- PARAMETERS_LABEL,
- METRICS_LABEL,
- METADATA_LABEL,
- PERFORMANCE_LABEL,
- NO_PARAMETERS_MESSAGE,
- NO_METRICS_MESSAGE,
- NO_METADATA_MESSAGE,
- NO_CI_MESSAGE,
- },
computed: {
info() {
return Object.freeze(this.candidate.info);
},
- ciJob() {
- return Object.freeze(this.info.ci_job);
- },
- hasMetadata() {
- return !isEmpty(this.candidate.metadata);
- },
- hasParameters() {
- return !isEmpty(this.candidate.params);
- },
- hasMetrics() {
- return !isEmpty(this.candidate.metrics);
- },
- metricsTableFields() {
- const maxStep = maxBy(this.candidate.metrics, 'step').step;
- const rowClass = 'gl-p-3!';
-
- const cssClasses = { thClass: rowClass, tdClass: rowClass };
-
- const fields = range(maxStep + 1).map((step) => ({
- key: step.toString(),
- label: sprintf(__('Step %{step}'), { step }),
- ...cssClasses,
- }));
-
- return [{ key: 'name', label: __('Metric'), ...cssClasses }, ...fields];
- },
- metricsTableItems() {
- const items = {};
- this.candidate.metrics.forEach((metric) => {
- const metricRow = items[metric.name] || { name: metric.name };
- metricRow[metric.step] = metric.value;
- items[metric.name] = metricRow;
- });
-
- return Object.values(items);
- },
+ },
+ i18n: {
+ TITLE_LABEL: s__('MlExperimentTracking|Model candidate details'),
+ DELETE_CANDIDATE_CONFIRMATION_MESSAGE: s__(
+ 'MlExperimentTracking|Deleting this candidate will delete the associated parameters, metrics, and metadata.',
+ ),
+ DELETE_CANDIDATE_PRIMARY_ACTION_LABEL: s__('MlExperimentTracking|Delete candidate'),
+ DELETE_CANDIDATE_MODAL_TITLE: s__('MlExperimentTracking|Delete candidate?'),
},
};
</script>
@@ -126,106 +44,6 @@ export default {
/>
</model-experiments-header>
- <section class="gl-mb-6">
- <table class="candidate-details">
- <tbody>
- <detail-row :label="$options.i18n.ID_LABEL">
- {{ info.iid }}
- </detail-row>
-
- <detail-row :label="$options.i18n.MLFLOW_ID_LABEL">{{ info.eid }}</detail-row>
-
- <detail-row :label="$options.i18n.STATUS_LABEL">{{ info.status }}</detail-row>
-
- <detail-row :label="$options.i18n.EXPERIMENT_LABEL">
- <gl-link :href="info.path_to_experiment">
- {{ info.experiment_name }}
- </gl-link>
- </detail-row>
-
- <detail-row v-if="info.path_to_artifact" :label="$options.i18n.ARTIFACTS_LABEL">
- <gl-link :href="info.path_to_artifact">
- {{ $options.i18n.ARTIFACTS_LABEL }}
- </gl-link>
- </detail-row>
- </tbody>
- </table>
- </section>
-
- <section class="gl-mb-6">
- <h4>{{ $options.i18n.CI_SECTION_LABEL }}</h4>
-
- <table v-if="ciJob" class="candidate-details">
- <tbody>
- <detail-row
- :label="$options.i18n.JOB_LABEL"
- :section-label="$options.i18n.CI_SECTION_LABEL"
- >
- <gl-link :href="ciJob.path">
- {{ ciJob.name }}
- </gl-link>
- </detail-row>
-
- <detail-row v-if="ciJob.user" :label="$options.i18n.CI_USER_LABEL">
- <gl-avatar-labeled label="" :size="24" :src="ciJob.user.avatar">
- <gl-link :href="ciJob.user.path">
- {{ ciJob.user.name }}
- </gl-link>
- </gl-avatar-labeled>
- </detail-row>
-
- <detail-row v-if="ciJob.merge_request" :label="$options.i18n.CI_MR_LABEL">
- <gl-link :href="ciJob.merge_request.path">
- !{{ ciJob.merge_request.iid }} {{ ciJob.merge_request.title }}
- </gl-link>
- </detail-row>
- </tbody>
- </table>
-
- <div v-else class="gl-text-secondary">{{ $options.i18n.NO_CI_MESSAGE }}</div>
- </section>
-
- <section class="gl-mb-6">
- <h4>{{ $options.i18n.PARAMETERS_LABEL }}</h4>
-
- <table v-if="hasParameters" class="candidate-details">
- <tbody>
- <detail-row v-for="item in candidate.params" :key="item.name" :label="item.name">
- {{ item.value }}
- </detail-row>
- </tbody>
- </table>
-
- <div v-else class="gl-text-secondary">{{ $options.i18n.NO_PARAMETERS_MESSAGE }}</div>
- </section>
-
- <section class="gl-mb-6">
- <h4>{{ $options.i18n.METADATA_LABEL }}</h4>
-
- <table v-if="hasMetadata" class="candidate-details">
- <tbody>
- <detail-row v-for="item in candidate.metadata" :key="item.name" :label="item.name">
- {{ item.value }}
- </detail-row>
- </tbody>
- </table>
-
- <div v-else class="gl-text-secondary">{{ $options.i18n.NO_METADATA_MESSAGE }}</div>
- </section>
-
- <section class="gl-mb-6">
- <h4>{{ $options.i18n.PERFORMANCE_LABEL }}</h4>
-
- <div v-if="hasMetrics" class="gl-overflow-x-auto">
- <gl-table-lite
- :items="metricsTableItems"
- :fields="metricsTableFields"
- class="gl-w-auto"
- hover
- />
- </div>
-
- <div v-else class="gl-text-secondary">{{ $options.i18n.NO_METRICS_MESSAGE }}</div>
- </section>
+ <candidate-detail :candidate="candidate" />
</div>
</template>
diff --git a/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/translations.js b/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/translations.js
deleted file mode 100644
index 98988e1db35..00000000000
--- a/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/translations.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import { __, s__ } from '~/locale';
-
-export const TITLE_LABEL = s__('MlExperimentTracking|Model candidate details');
-export const INFO_LABEL = s__('MlExperimentTracking|Info');
-export const ID_LABEL = s__('MlExperimentTracking|ID');
-export const MLFLOW_ID_LABEL = s__('MlExperimentTracking|MLflow run ID');
-export const STATUS_LABEL = s__('MlExperimentTracking|Status');
-export const EXPERIMENT_LABEL = s__('MlExperimentTracking|Experiment');
-export const ARTIFACTS_LABEL = s__('MlExperimentTracking|Artifacts');
-export const PARAMETERS_LABEL = s__('MlExperimentTracking|Parameters');
-export const METRICS_LABEL = s__('MlExperimentTracking|Metrics');
-export const PERFORMANCE_LABEL = s__('MlExperimentTracking|Model performance');
-export const METADATA_LABEL = s__('MlExperimentTracking|Metadata');
-export const NO_PARAMETERS_MESSAGE = s__('MlExperimentTracking|No logged parameters');
-export const NO_METRICS_MESSAGE = s__('MlExperimentTracking|No logged metrics');
-export const NO_METADATA_MESSAGE = s__('MlExperimentTracking|No logged metadata');
-export const NO_CI_MESSAGE = s__('MlExperimentTracking|Candidate not linked to a CI build');
-export const DELETE_CANDIDATE_CONFIRMATION_MESSAGE = s__(
- 'MlExperimentTracking|Deleting this candidate will delete the associated parameters, metrics, and metadata.',
-);
-export const DELETE_CANDIDATE_PRIMARY_ACTION_LABEL = s__('MlExperimentTracking|Delete candidate');
-export const DELETE_CANDIDATE_MODAL_TITLE = s__('MLExperimentTracking|Delete candidate?');
-export const CI_SECTION_LABEL = s__('MLExperimentTracking|CI Info');
-export const JOB_LABEL = __('Job');
-export const CI_USER_LABEL = s__('MlExperimentTracking|Triggered by');
-export const CI_MR_LABEL = __('Merge request');
diff --git a/app/assets/javascripts/ml/model_registry/components/candidate_detail.vue b/app/assets/javascripts/ml/model_registry/components/candidate_detail.vue
new file mode 100644
index 00000000000..8c32fb3a2c6
--- /dev/null
+++ b/app/assets/javascripts/ml/model_registry/components/candidate_detail.vue
@@ -0,0 +1,207 @@
+<script>
+import { GlAvatarLabeled, GlLink, GlTableLite } from '@gitlab/ui';
+import { isEmpty, maxBy, range } from 'lodash';
+import { __, sprintf } from '~/locale';
+import {
+ INFO_LABEL,
+ ID_LABEL,
+ STATUS_LABEL,
+ EXPERIMENT_LABEL,
+ ARTIFACTS_LABEL,
+ PARAMETERS_LABEL,
+ METADATA_LABEL,
+ MLFLOW_ID_LABEL,
+ CI_SECTION_LABEL,
+ JOB_LABEL,
+ CI_USER_LABEL,
+ CI_MR_LABEL,
+ PERFORMANCE_LABEL,
+ NO_PARAMETERS_MESSAGE,
+ NO_METRICS_MESSAGE,
+ NO_METADATA_MESSAGE,
+ NO_CI_MESSAGE,
+} from '../translations';
+import DetailRow from './candidate_detail_row.vue';
+
+export default {
+ name: 'MlCandidatesShow',
+ components: {
+ DetailRow,
+ GlAvatarLabeled,
+ GlLink,
+ GlTableLite,
+ },
+ props: {
+ candidate: {
+ type: Object,
+ required: true,
+ },
+ },
+ i18n: {
+ INFO_LABEL,
+ ID_LABEL,
+ STATUS_LABEL,
+ EXPERIMENT_LABEL,
+ ARTIFACTS_LABEL,
+ MLFLOW_ID_LABEL,
+ CI_SECTION_LABEL,
+ JOB_LABEL,
+ CI_USER_LABEL,
+ CI_MR_LABEL,
+ PARAMETERS_LABEL,
+ METADATA_LABEL,
+ PERFORMANCE_LABEL,
+ NO_PARAMETERS_MESSAGE,
+ NO_METRICS_MESSAGE,
+ NO_METADATA_MESSAGE,
+ NO_CI_MESSAGE,
+ },
+ computed: {
+ info() {
+ return Object.freeze(this.candidate.info);
+ },
+ ciJob() {
+ return Object.freeze(this.info.ci_job);
+ },
+ hasMetadata() {
+ return !isEmpty(this.candidate.metadata);
+ },
+ hasParameters() {
+ return !isEmpty(this.candidate.params);
+ },
+ hasMetrics() {
+ return !isEmpty(this.candidate.metrics);
+ },
+ metricsTableFields() {
+ const maxStep = maxBy(this.candidate.metrics, 'step').step;
+ const rowClass = 'gl-p-3!';
+
+ const cssClasses = { thClass: rowClass, tdClass: rowClass };
+
+ const fields = range(maxStep + 1).map((step) => ({
+ key: step.toString(),
+ label: sprintf(__('Step %{step}'), { step }),
+ ...cssClasses,
+ }));
+
+ return [{ key: 'name', label: __('Metric'), ...cssClasses }, ...fields];
+ },
+ metricsTableItems() {
+ const items = {};
+ this.candidate.metrics.forEach((metric) => {
+ const metricRow = items[metric.name] || { name: metric.name };
+ metricRow[metric.step] = metric.value;
+ items[metric.name] = metricRow;
+ });
+
+ return Object.values(items);
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <section class="gl-mb-6">
+ <table class="candidate-details">
+ <tbody>
+ <detail-row :label="$options.i18n.ID_LABEL">
+ {{ info.iid }}
+ </detail-row>
+
+ <detail-row :label="$options.i18n.MLFLOW_ID_LABEL">{{ info.eid }}</detail-row>
+
+ <detail-row :label="$options.i18n.STATUS_LABEL">{{ info.status }}</detail-row>
+
+ <detail-row :label="$options.i18n.EXPERIMENT_LABEL">
+ <gl-link :href="info.path_to_experiment">
+ {{ info.experiment_name }}
+ </gl-link>
+ </detail-row>
+
+ <detail-row v-if="info.path_to_artifact" :label="$options.i18n.ARTIFACTS_LABEL">
+ <gl-link :href="info.path_to_artifact">
+ {{ $options.i18n.ARTIFACTS_LABEL }}
+ </gl-link>
+ </detail-row>
+ </tbody>
+ </table>
+ </section>
+
+ <section class="gl-mb-6">
+ <h4>{{ $options.i18n.CI_SECTION_LABEL }}</h4>
+
+ <table v-if="ciJob" class="candidate-details">
+ <tbody>
+ <detail-row
+ :label="$options.i18n.JOB_LABEL"
+ :section-label="$options.i18n.CI_SECTION_LABEL"
+ >
+ <gl-link :href="ciJob.path">
+ {{ ciJob.name }}
+ </gl-link>
+ </detail-row>
+
+ <detail-row v-if="ciJob.user" :label="$options.i18n.CI_USER_LABEL">
+ <gl-avatar-labeled label="" :size="24" :src="ciJob.user.avatar">
+ <gl-link :href="ciJob.user.path">
+ {{ ciJob.user.name }}
+ </gl-link>
+ </gl-avatar-labeled>
+ </detail-row>
+
+ <detail-row v-if="ciJob.merge_request" :label="$options.i18n.CI_MR_LABEL">
+ <gl-link :href="ciJob.merge_request.path">
+ !{{ ciJob.merge_request.iid }} {{ ciJob.merge_request.title }}
+ </gl-link>
+ </detail-row>
+ </tbody>
+ </table>
+
+ <div v-else class="gl-text-secondary">{{ $options.i18n.NO_CI_MESSAGE }}</div>
+ </section>
+
+ <section class="gl-mb-6">
+ <h4>{{ $options.i18n.PARAMETERS_LABEL }}</h4>
+
+ <table v-if="hasParameters" class="candidate-details">
+ <tbody>
+ <detail-row v-for="item in candidate.params" :key="item.name" :label="item.name">
+ {{ item.value }}
+ </detail-row>
+ </tbody>
+ </table>
+
+ <div v-else class="gl-text-secondary">{{ $options.i18n.NO_PARAMETERS_MESSAGE }}</div>
+ </section>
+
+ <section class="gl-mb-6">
+ <h4>{{ $options.i18n.METADATA_LABEL }}</h4>
+
+ <table v-if="hasMetadata" class="candidate-details">
+ <tbody>
+ <detail-row v-for="item in candidate.metadata" :key="item.name" :label="item.name">
+ {{ item.value }}
+ </detail-row>
+ </tbody>
+ </table>
+
+ <div v-else class="gl-text-secondary">{{ $options.i18n.NO_METADATA_MESSAGE }}</div>
+ </section>
+
+ <section class="gl-mb-6">
+ <h4>{{ $options.i18n.PERFORMANCE_LABEL }}</h4>
+
+ <div v-if="hasMetrics" class="gl-overflow-x-auto">
+ <gl-table-lite
+ :items="metricsTableItems"
+ :fields="metricsTableFields"
+ class="gl-w-auto"
+ hover
+ />
+ </div>
+
+ <div v-else class="gl-text-secondary">{{ $options.i18n.NO_METRICS_MESSAGE }}</div>
+ </section>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row.vue b/app/assets/javascripts/ml/model_registry/components/candidate_detail_row.vue
index 8c7460940a0..8c7460940a0 100644
--- a/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row.vue
+++ b/app/assets/javascripts/ml/model_registry/components/candidate_detail_row.vue
diff --git a/app/assets/javascripts/ml/model_registry/translations.js b/app/assets/javascripts/ml/model_registry/translations.js
index 89b3f45ed94..689eeb45b00 100644
--- a/app/assets/javascripts/ml/model_registry/translations.js
+++ b/app/assets/javascripts/ml/model_registry/translations.js
@@ -1,4 +1,4 @@
-import { s__, n__ } from '~/locale';
+import { __, s__, n__ } from '~/locale';
export const MODEL_DETAILS_TAB_LABEL = s__('MlModelRegistry|Details');
export const MODEL_OTHER_VERSIONS_TAB_LABEL = s__('MlModelRegistry|Versions');
@@ -14,3 +14,21 @@ export const NO_MODELS_LABEL = s__('MlModelRegistry|No models registered in this
export const modelsCountLabel = (modelCount) =>
n__('MlModelRegistry|%d model', 'MlModelRegistry|%d models', modelCount);
+
+export const INFO_LABEL = s__('MlModelRegistry|Info');
+export const ID_LABEL = s__('MlModelRegistry|ID');
+export const MLFLOW_ID_LABEL = s__('MlModelRegistry|MLflow run ID');
+export const STATUS_LABEL = s__('MlModelRegistry|Status');
+export const EXPERIMENT_LABEL = s__('MlModelRegistry|Experiment');
+export const ARTIFACTS_LABEL = s__('MlModelRegistry|Artifacts');
+export const PARAMETERS_LABEL = s__('MlModelRegistry|Parameters');
+export const PERFORMANCE_LABEL = s__('MlModelRegistry|Model performance');
+export const METADATA_LABEL = s__('MlModelRegistry|Metadata');
+export const NO_PARAMETERS_MESSAGE = s__('MlModelRegistry|No logged parameters');
+export const NO_METRICS_MESSAGE = s__('MlModelRegistry|No logged metrics');
+export const NO_METADATA_MESSAGE = s__('MlModelRegistry|No logged metadata');
+export const NO_CI_MESSAGE = s__('MlModelRegistry|Candidate not linked to a CI build');
+export const CI_SECTION_LABEL = s__('MlModelRegistry|CI Info');
+export const JOB_LABEL = __('Job');
+export const CI_USER_LABEL = s__('MlModelRegistry|Triggered by');
+export const CI_MR_LABEL = __('Merge request');
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 6ff48b7de95..c6d18a1328b 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
@@ -571,7 +571,7 @@ export default {
:disabled="!canChangeVisibilityLevel"
name="project[visibility_level]"
class="form-control select-control"
- data-qa-selector="project_visibility_dropdown"
+ data-testid="project-visibility-dropdown"
>
<option
:value="$options.VISIBILITY_LEVEL_PRIVATE_INTEGER"
@@ -1060,13 +1060,7 @@ export default {
data-testid="project-features-save-button"
@confirm="$emit('confirm')"
/>
- <gl-button
- v-else
- type="submit"
- variant="confirm"
- data-testid="project-features-save-button"
- data-qa-selector="visibility_features_permissions_save_button"
- >
+ <gl-button v-else type="submit" variant="confirm" data-testid="project-features-save-button">
{{ $options.i18n.confirmButtonText }}
</gl-button>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer_new.vue b/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer_new.vue
index 7ced12952dd..bc46f11ab2d 100644
--- a/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer_new.vue
+++ b/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer_new.vue
@@ -148,6 +148,7 @@ export default {
:class="$options.userColorScheme"
data-type="simple"
:data-path="blob.path"
+ data-testid="blob-viewer-file-content"
>
<codeowners-validation
v-if="isCodeownersFile"
diff --git a/app/helpers/ci/runners_helper.rb b/app/helpers/ci/runners_helper.rb
index 7cc554bbeeb..c5d62b49a3c 100644
--- a/app/helpers/ci/runners_helper.rb
+++ b/app/helpers/ci/runners_helper.rb
@@ -31,7 +31,7 @@ module Ci
span_class = 'gl-text-orange-500'
end
- content_tag(:span, class: span_class, title: title, data: { toggle: 'tooltip', container: 'body', testid: 'runner_status_icon', qa_selector: "runner_status_#{status}_content" }) do
+ content_tag(:span, class: span_class, title: title, data: { toggle: 'tooltip', container: 'body', testid: 'runner-status-icon', qa_status: status }) do
sprite_icon(icon, size: size, css_class: icon_class)
end
end
diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb
index 75e89a7d7bc..aad3e07ea28 100644
--- a/app/helpers/notes_helper.rb
+++ b/app/helpers/notes_helper.rb
@@ -21,15 +21,6 @@ module NotesHelper
Notes::QuickActionsService.supported?(note)
end
- def noteable_json(noteable)
- {
- id: noteable.id,
- class: noteable.class.name,
- resources: noteable.class.table_name,
- project_id: noteable.project.id
- }.to_json
- end
-
def diff_view_data
return {} unless @new_diff_note_attrs
@@ -87,10 +78,6 @@ module NotesHelper
end
end
- def note_max_access_for_user(note)
- note.project.team.max_member_access(note.author_id)
- end
-
def note_human_max_access(note)
note.project.team.human_max_access(note.author_id)
end
diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb
index ddaef4652b4..b6e435986ce 100644
--- a/app/helpers/notifications_helper.rb
+++ b/app/helpers/notifications_helper.rb
@@ -35,42 +35,6 @@ module NotificationsHelper
sprite_icon(icon)
end
- def notification_title(level)
- # Can be anything in `NotificationSetting.level:
- case level.to_sym
- when :participating
- s_('NotificationLevel|Participate')
- when :mention
- s_('NotificationLevel|On mention')
- else
- N_('NotificationLevel|Global')
- N_('NotificationLevel|Watch')
- N_('NotificationLevel|Disabled')
- N_('NotificationLevel|Custom')
- level = "NotificationLevel|#{level.to_s.humanize}"
- s_(level)
- end
- end
-
- def notification_description(level)
- case level.to_sym
- when :participating
- _('You will only receive notifications for threads you have participated in')
- when :mention
- _('You will receive notifications only for comments in which you were @mentioned')
- when :watch
- _('You will receive notifications for any activity')
- when :disabled
- _('You will not get any notifications via email')
- when :global
- _('Use your global notification setting')
- when :custom
- _('You will only receive notifications for the events you choose')
- when :owner_disabled
- _('Notifications have been disabled by the project or group owner')
- end
- end
-
def show_unsubscribe_title?(noteable)
can?(current_user, "read_#{noteable.to_ability_name}".to_sym, noteable)
end
diff --git a/app/helpers/packages_helper.rb b/app/helpers/packages_helper.rb
index fefc19d7c1a..887f63ce05d 100644
--- a/app/helpers/packages_helper.rb
+++ b/app/helpers/packages_helper.rb
@@ -3,10 +3,6 @@
module PackagesHelper
include ::API::Helpers::RelatedResourcesHelpers
- def package_sort_path(options = {})
- "#{request.path}?#{options.to_param}"
- end
-
def nuget_package_registry_url(project_id)
expose_url(api_v4_projects_packages_nuget_index_path(id: project_id, format: '.json'))
end
diff --git a/app/helpers/profiles_helper.rb b/app/helpers/profiles_helper.rb
index 8d260d5e455..c115e4c594a 100644
--- a/app/helpers/profiles_helper.rb
+++ b/app/helpers/profiles_helper.rb
@@ -27,10 +27,6 @@ module ProfilesHelper
params[:controller] == 'users'
end
- def availability_values
- Types::AvailabilityEnum.enum
- end
-
def middle_dot_divider_classes(stacking, breakpoint)
['gl-mb-3'].tap do |classes|
if stacking
diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb
index 137b24102e0..90f3c1e6ae6 100644
--- a/app/helpers/tab_helper.rb
+++ b/app/helpers/tab_helper.rb
@@ -171,14 +171,6 @@ module TabHelper
current_controller?(c) && current_action?(a)
end
- def branches_tab_class
- if current_controller?(:protected_branches) ||
- current_controller?(:branches) ||
- current_page?(project_repository_path(@project))
- 'active'
- end
- end
-
private
def route_matches_paths?(paths)
diff --git a/app/views/ci/runner/_how_to_setup_runner.html.haml b/app/views/ci/runner/_how_to_setup_runner.html.haml
index c46aabf2604..29c2e364c37 100644
--- a/app/views/ci/runner/_how_to_setup_runner.html.haml
+++ b/app/views/ci/runner/_how_to_setup_runner.html.haml
@@ -13,7 +13,7 @@
%br
= _("And this registration token:")
%br
- %code#registration_token{ data: {testid: 'registration_token' } }= registration_token
+ %code#registration_token= registration_token
= deprecated_clipboard_button(target: '#registration_token', title: _("Copy token"))
.gl-mt-3.gl-mb-3
diff --git a/config/feature_flags/development/adherence_report_ui.yml b/config/feature_flags/development/adherence_report_ui.yml
deleted file mode 100644
index 7db4fbb756f..00000000000
--- a/config/feature_flags/development/adherence_report_ui.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: adherence_report_ui
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/122374
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/414495
-milestone: '16.1'
-type: development
-group: group::compliance
-default_enabled: true
diff --git a/config/feature_flags/development/approval_rules_disable_joins.yml b/config/feature_flags/development/approval_rules_disable_joins.yml
deleted file mode 100644
index 1ee33b45ba7..00000000000
--- a/config/feature_flags/development/approval_rules_disable_joins.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: approval_rules_disable_joins
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/136588
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/431564
-milestone: '16.7'
-type: development
-group: group::source code
-default_enabled: false
diff --git a/config/feature_flags/development/compliance_adherence_report.yml b/config/feature_flags/development/compliance_adherence_report.yml
deleted file mode 100644
index f67ff7bdec3..00000000000
--- a/config/feature_flags/development/compliance_adherence_report.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: compliance_adherence_report
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124167
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/416988
-milestone: '16.2'
-type: development
-group: group::compliance
-default_enabled: true
diff --git a/db/post_migrate/20231120161159_prepare_index_for_org_id_and_id_on_projects.rb b/db/post_migrate/20231120161159_prepare_index_for_org_id_and_id_on_projects.rb
new file mode 100644
index 00000000000..1a49f8ee43f
--- /dev/null
+++ b/db/post_migrate/20231120161159_prepare_index_for_org_id_and_id_on_projects.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class PrepareIndexForOrgIdAndIdOnProjects < Gitlab::Database::Migration[2.2]
+ milestone '16.7'
+
+ INDEX_NAME = 'index_projects_on_organization_id_and_id'
+
+ def up
+ prepare_async_index :projects, [:organization_id, :id], name: INDEX_NAME
+ end
+
+ def down
+ unprepare_async_index :projects, [:organization_id, :id], name: INDEX_NAME
+ end
+end
diff --git a/db/schema_migrations/20231120161159 b/db/schema_migrations/20231120161159
new file mode 100644
index 00000000000..96a59184bde
--- /dev/null
+++ b/db/schema_migrations/20231120161159
@@ -0,0 +1 @@
+c2ac99a5b648b8f369ca84f4270b393d650de1e42834545e736faaf7fc2029b2 \ No newline at end of file
diff --git a/doc/api/members.md b/doc/api/members.md
index de59c66890d..af9d47a005b 100644
--- a/doc/api/members.md
+++ b/doc/api/members.md
@@ -27,9 +27,11 @@ In GitLab 14.8 and earlier, projects in personal namespaces have an `access_leve
## Limitations
-The `group_saml_identity` attribute is only visible to a group owner for [SSO enabled groups](../user/group/saml_sso/index.md).
+The `group_saml_identity` attribute is only visible to group owners for [SSO-enabled groups](../user/group/saml_sso/index.md).
-The `email` attribute is only visible to group Owners for any [enterprise user](../user/enterprise_user/index.md).
+The `email` attribute is only visible to group owners for users provisioned by the group with [SCIM](../user/group/saml_sso/scim_setup.md).
+In GitLab 16.7 and later, the attribute is only visible to group owners for all [enterprise users](../user/enterprise_user/index.md).
+For more information, see [issue 391453](https://gitlab.com/gitlab-org/gitlab/-/issues/391453).
## List all members of a group or project
diff --git a/doc/development/ai_features/index.md b/doc/development/ai_features/index.md
index 9a980b89757..87c8abea405 100644
--- a/doc/development/ai_features/index.md
+++ b/doc/development/ai_features/index.md
@@ -87,19 +87,13 @@ For features that use the embedding database, additional setup is needed.
1. Run `gdk reconfigure`
1. Run database migrations to create the embedding database
-### Setup for GitLab documentation chat (legacy chat)
-
-To populate the embedding database for GitLab chat:
-
-1. Open a rails console
-1. Run [this script](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/10588#note_1373586079) to populate the embedding database
-
### Configure GCP Vertex access
In order to obtain a GCP service key for local development, please follow the steps below:
- Create a sandbox GCP project by visiting [this page](https://about.gitlab.com/handbook/infrastructure-standards/#individual-environment) and following the instructions, or by requesting access to our existing group GCP project by using [this template](https://gitlab.com/gitlab-com/it/infra/issue-tracker/-/issues/new?issuable_template=gcp_group_account_iam_update_request).
- If you are using an individual GCP project, you may also need to enable the Vertex AI API:
+ 1. Visit [welcome page](https://console.cloud.google.com/welcome), choose your project (e.g. jdoe-5d23dpe).
1. Go to **APIs & Services > Enabled APIs & services**.
1. Select **+ Enable APIs and Services**.
1. Search for `Vertex AI API`.
@@ -141,7 +135,7 @@ we can add a few selected embeddings to the table from a pre-generated fixture.
For instance, to test that the question "How can I reset my password" is correctly
retrieving the relevant embeddings and answered, we can extract the top N closet embeddings
to the question into a fixture and only restore a small number of embeddings quickly.
-To faciliate an extraction process, a Rake task been written.
+To facilitate an extraction process, a Rake task has been written.
You can add or remove the questions needed to be tested in the Rake task and run the task to generate a new fixture.
```shell
diff --git a/doc/user/analytics/ci_cd_analytics.md b/doc/user/analytics/ci_cd_analytics.md
index 61bc77e4469..e86e773caca 100644
--- a/doc/user/analytics/ci_cd_analytics.md
+++ b/doc/user/analytics/ci_cd_analytics.md
@@ -30,6 +30,17 @@ View pipeline duration history:
## View CI/CD analytics
+You can view CI/CD analytics for a group or project.
+
+### For a group **(ULTIMATE ALL)**
+
+To view CI/CD analytics:
+
+1. On the left sidebar, select **Search or go to** and find your group.
+1. Select **Analyze > CI/CD analytics**.
+
+### For a project **(FREE ALL)**
+
To view CI/CD analytics:
1. On the left sidebar, select **Search or go to** and find your project.
@@ -44,7 +55,7 @@ frequency to the `production` environment. The environment must be part of the
[production deployment tier](../../ci/environments/index.md#deployment-tier-of-environments)
for its deployment information to appear on the graphs.
- Deployment frequency is one of the four DORA metrics that DevOps teams use for measuring excellence in software delivery.
+Deployment frequency is one of the four DORA metrics that DevOps teams use for measuring excellence in software delivery.
The deployment frequency chart is available for groups and projects.
@@ -68,7 +79,7 @@ merge requests to be deployed to a production environment. This chart is availab
- For time periods in which no merge requests were deployed, the charts render a
red, dashed line.
- Lead time for changes is one of the four DORA metrics that DevOps teams use for measuring excellence in software delivery.
+Lead time for changes is one of the four DORA metrics that DevOps teams use for measuring excellence in software delivery.
To view the lead time for changes chart:
diff --git a/doc/user/compliance/compliance_center/index.md b/doc/user/compliance/compliance_center/index.md
index 4a42a70a7e7..e17f5c1ec39 100644
--- a/doc/user/compliance/compliance_center/index.md
+++ b/doc/user/compliance/compliance_center/index.md
@@ -16,11 +16,7 @@ See report and manage standards adherence, violations, and compliance frameworks
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/125875) GraphQL APIs in GitLab 16.2 [with a flag](../../../administration/feature_flags.md) named `compliance_adherence_report`. Disabled by default.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/125444) standards adherence dashboard in GitLab 16.3 [with a flag](../../../administration/feature_flags.md) named `adherence_report_ui`. Disabled by default.
> - [Enabled](https://gitlab.com/gitlab-org/gitlab/-/issues/414495) in GitLab 16.5.
-
-FLAG:
-On self-managed GitLab, by default this feature is available. To hide the feature per project or for your entire instance, an administrator can
-[disable the feature flags](../../../administration/feature_flags.md) named `compliance_adherence_report` and `adherence_report_ui`. On GitLab.com,
-this feature is available.
+> - [Feature flag `compliance_adherence_report` and `adherence_report_ui`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/137398) removed in GitLab 16.7.
Standards adherence dashboard lists the adherence status of projects complying to GitLab standard.
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 61eded19625..8487440719f 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -28822,12 +28822,6 @@ msgstr ""
msgid "MD5"
msgstr ""
-msgid "MLExperimentTracking|CI Info"
-msgstr ""
-
-msgid "MLExperimentTracking|Delete candidate?"
-msgstr ""
-
msgid "MLExperimentTracking|Delete experiment?"
msgstr ""
@@ -30696,9 +30690,6 @@ msgstr ""
msgid "MlExperimentTracking|CI Job"
msgstr ""
-msgid "MlExperimentTracking|Candidate not linked to a CI build"
-msgstr ""
-
msgid "MlExperimentTracking|Candidate removed"
msgstr ""
@@ -30714,6 +30705,9 @@ msgstr ""
msgid "MlExperimentTracking|Delete candidate"
msgstr ""
+msgid "MlExperimentTracking|Delete candidate?"
+msgstr ""
+
msgid "MlExperimentTracking|Delete experiment"
msgstr ""
@@ -30744,36 +30738,18 @@ msgstr ""
msgid "MlExperimentTracking|Get started with model experiments!"
msgstr ""
-msgid "MlExperimentTracking|ID"
-msgstr ""
-
-msgid "MlExperimentTracking|Info"
-msgstr ""
-
msgid "MlExperimentTracking|Logged candidates for experiment"
msgstr ""
-msgid "MlExperimentTracking|MLflow run ID"
-msgstr ""
-
msgid "MlExperimentTracking|Machine learning experiment tracking"
msgstr ""
-msgid "MlExperimentTracking|Metadata"
-msgstr ""
-
-msgid "MlExperimentTracking|Metrics"
-msgstr ""
-
msgid "MlExperimentTracking|Model candidate details"
msgstr ""
msgid "MlExperimentTracking|Model experiments"
msgstr ""
-msgid "MlExperimentTracking|Model performance"
-msgstr ""
-
msgid "MlExperimentTracking|Model removed"
msgstr ""
@@ -30789,55 +30765,82 @@ msgstr ""
msgid "MlExperimentTracking|No candidates logged for the query. Create new candidates using the MLflow client."
msgstr ""
-msgid "MlExperimentTracking|No logged metadata"
+msgid "MlExperimentTracking|No name"
+msgstr ""
+
+msgid "MlModelRegistry|%d model"
+msgid_plural "MlModelRegistry|%d models"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "MlModelRegistry|%d version"
+msgid_plural "MlModelRegistry|%d versions"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "MlModelRegistry|Artifacts"
msgstr ""
-msgid "MlExperimentTracking|No logged metrics"
+msgid "MlModelRegistry|CI Info"
msgstr ""
-msgid "MlExperimentTracking|No logged parameters"
+msgid "MlModelRegistry|Candidate not linked to a CI build"
msgstr ""
-msgid "MlExperimentTracking|No name"
+msgid "MlModelRegistry|Details"
msgstr ""
-msgid "MlExperimentTracking|Parameters"
+msgid "MlModelRegistry|Experiment"
msgstr ""
-msgid "MlExperimentTracking|Status"
+msgid "MlModelRegistry|ID"
msgstr ""
-msgid "MlExperimentTracking|Triggered by"
+msgid "MlModelRegistry|Info"
msgstr ""
-msgid "MlModelRegistry|%d model"
-msgid_plural "MlModelRegistry|%d models"
-msgstr[0] ""
-msgstr[1] ""
+msgid "MlModelRegistry|Latest version"
+msgstr ""
-msgid "MlModelRegistry|%d version"
-msgid_plural "MlModelRegistry|%d versions"
-msgstr[0] ""
-msgstr[1] ""
+msgid "MlModelRegistry|MLflow run ID"
+msgstr ""
-msgid "MlModelRegistry|Details"
+msgid "MlModelRegistry|Metadata"
msgstr ""
-msgid "MlModelRegistry|Latest version"
+msgid "MlModelRegistry|Model performance"
msgstr ""
msgid "MlModelRegistry|Model registry"
msgstr ""
+msgid "MlModelRegistry|No logged metadata"
+msgstr ""
+
+msgid "MlModelRegistry|No logged metrics"
+msgstr ""
+
+msgid "MlModelRegistry|No logged parameters"
+msgstr ""
+
msgid "MlModelRegistry|No models registered in this project"
msgstr ""
msgid "MlModelRegistry|No registered versions"
msgstr ""
+msgid "MlModelRegistry|Parameters"
+msgstr ""
+
+msgid "MlModelRegistry|Status"
+msgstr ""
+
msgid "MlModelRegistry|This model has no versions"
msgstr ""
+msgid "MlModelRegistry|Triggered by"
+msgstr ""
+
msgid "MlModelRegistry|Version candidates"
msgstr ""
diff --git a/qa/qa/page/project/settings/runners.rb b/qa/qa/page/project/settings/runners.rb
index aa1ac216ae2..ce3bc1a5396 100644
--- a/qa/qa/page/project/settings/runners.rb
+++ b/qa/qa/page/project/settings/runners.rb
@@ -5,27 +5,12 @@ module QA
module Project
module Settings
class Runners < Page::Base
- view 'app/views/ci/runner/_how_to_setup_runner.html.haml' do
- element :registration_token, '%code#registration_token' # rubocop:disable QA/ElementWithPattern
- element :coordinator_address, '%code#coordinator_address' # rubocop:disable QA/ElementWithPattern
- end
-
view 'app/helpers/ci/runners_helper.rb' do
- # rubocop:disable Lint/InterpolationCheck
- element :runner_status_icon, 'qa_selector: "runner_status_#{status}_content"' # rubocop:disable QA/ElementWithPattern
- # rubocop:enable Lint/InterpolationCheck
- end
-
- def registration_token
- find('code#registration_token').text
- end
-
- def coordinator_address
- find('code#coordinator_address').text
+ element 'runner-status-icon'
end
def has_online_runner?
- has_element?(:runner_status_online_content)
+ has_element?('runner-status-icon', status: 'online')
end
end
end
diff --git a/qa/qa/page/project/settings/visibility_features_permissions.rb b/qa/qa/page/project/settings/visibility_features_permissions.rb
index 60cea6de7f5..a0b21fd1698 100644
--- a/qa/qa/page/project/settings/visibility_features_permissions.rb
+++ b/qa/qa/page/project/settings/visibility_features_permissions.rb
@@ -6,13 +6,13 @@ module QA
module Settings
class VisibilityFeaturesPermissions < Page::Base
view 'app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue' do
- element :project_visibility_dropdown
- element :visibility_features_permissions_save_button
+ element 'project-visibility-dropdown'
+ element 'project-features-save-button'
end
def set_project_visibility(visibility)
- select_element(:project_visibility_dropdown, visibility)
- click_element :visibility_features_permissions_save_button
+ select_element('project-visibility-dropdown', visibility)
+ click_element 'project-features-save-button'
end
end
end
diff --git a/spec/frontend/ml/experiment_tracking/routes/candidates/show/ml_candidates_show_spec.js b/spec/frontend/ml/experiment_tracking/routes/candidates/show/ml_candidates_show_spec.js
index 296728af46a..3999e906cec 100644
--- a/spec/frontend/ml/experiment_tracking/routes/candidates/show/ml_candidates_show_spec.js
+++ b/spec/frontend/ml/experiment_tracking/routes/candidates/show/ml_candidates_show_spec.js
@@ -1,206 +1,39 @@
-import { GlAvatarLabeled, GlLink, GlTableLite } from '@gitlab/ui';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { shallowMount } from '@vue/test-utils';
import MlCandidatesShow from '~/ml/experiment_tracking/routes/candidates/show';
-import DetailRow from '~/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row.vue';
-import {
- TITLE_LABEL,
- NO_PARAMETERS_MESSAGE,
- NO_METRICS_MESSAGE,
- NO_METADATA_MESSAGE,
- NO_CI_MESSAGE,
-} from '~/ml/experiment_tracking/routes/candidates/show/translations';
import DeleteButton from '~/ml/experiment_tracking/components/delete_button.vue';
+import CandidateDetail from '~/ml/model_registry/components/candidate_detail.vue';
import ModelExperimentsHeader from '~/ml/experiment_tracking/components/model_experiments_header.vue';
-import { stubComponent } from 'helpers/stub_component';
-import { newCandidate } from './mock_data';
+import { newCandidate } from 'jest/ml/model_registry/mock_data';
describe('MlCandidatesShow', () => {
let wrapper;
const CANDIDATE = newCandidate();
- const USER_ROW = 1;
- const INFO_SECTION = 0;
- const CI_SECTION = 1;
- const PARAMETER_SECTION = 2;
- const METADATA_SECTION = 3;
-
- const createWrapper = (createCandidate = () => CANDIDATE) => {
- wrapper = shallowMountExtended(MlCandidatesShow, {
- propsData: { candidate: createCandidate() },
- stubs: {
- GlTableLite: { ...stubComponent(GlTableLite), props: ['items', 'fields'] },
- },
+ const createWrapper = () => {
+ wrapper = shallowMount(MlCandidatesShow, {
+ propsData: { candidate: CANDIDATE },
});
};
const findDeleteButton = () => wrapper.findComponent(DeleteButton);
const findHeader = () => wrapper.findComponent(ModelExperimentsHeader);
- const findSection = (section) => wrapper.findAll('section').at(section);
- const findRowInSection = (section, row) =>
- findSection(section).findAllComponents(DetailRow).at(row);
- const findLinkAtRow = (section, rowIndex) =>
- findRowInSection(section, rowIndex).findComponent(GlLink);
- const findNoDataMessage = (label) => wrapper.findByText(label);
- const findLabel = (label) => wrapper.find(`[label='${label}']`);
- const findCiUserDetailRow = () => findRowInSection(CI_SECTION, USER_ROW);
- const findCiUserAvatar = () => findCiUserDetailRow().findComponent(GlAvatarLabeled);
- const findCiUserAvatarNameLink = () => findCiUserAvatar().findComponent(GlLink);
- const findMetricsTable = () => wrapper.findComponent(GlTableLite);
-
- describe('Header', () => {
- beforeEach(() => createWrapper());
-
- it('shows delete button', () => {
- expect(findDeleteButton().exists()).toBe(true);
- });
+ const findCandidateDetail = () => wrapper.findComponent(CandidateDetail);
- it('passes the delete path to delete button', () => {
- expect(findDeleteButton().props('deletePath')).toBe('path_to_candidate');
- });
+ beforeEach(() => createWrapper());
- it('passes the right title', () => {
- expect(findHeader().props('pageTitle')).toBe(TITLE_LABEL);
- });
+ it('shows delete button', () => {
+ expect(findDeleteButton().exists()).toBe(true);
});
- describe('Detail Table', () => {
- describe('All info available', () => {
- beforeEach(() => createWrapper());
-
- const mrText = `!${CANDIDATE.info.ci_job.merge_request.iid} ${CANDIDATE.info.ci_job.merge_request.title}`;
- const expectedTable = [
- [INFO_SECTION, 0, 'ID', CANDIDATE.info.iid],
- [INFO_SECTION, 1, 'MLflow run ID', CANDIDATE.info.eid],
- [INFO_SECTION, 2, 'Status', CANDIDATE.info.status],
- [INFO_SECTION, 3, 'Experiment', CANDIDATE.info.experiment_name],
- [INFO_SECTION, 4, 'Artifacts', 'Artifacts'],
- [CI_SECTION, 0, 'Job', CANDIDATE.info.ci_job.name],
- [CI_SECTION, 1, 'Triggered by', 'CI User'],
- [CI_SECTION, 2, 'Merge request', mrText],
- [PARAMETER_SECTION, 0, CANDIDATE.params[0].name, CANDIDATE.params[0].value],
- [PARAMETER_SECTION, 1, CANDIDATE.params[1].name, CANDIDATE.params[1].value],
- [METADATA_SECTION, 0, CANDIDATE.metadata[0].name, CANDIDATE.metadata[0].value],
- [METADATA_SECTION, 1, CANDIDATE.metadata[1].name, CANDIDATE.metadata[1].value],
- ];
-
- it.each(expectedTable)('row %s is created correctly', (section, rowIndex, label, text) => {
- const row = findRowInSection(section, rowIndex);
-
- expect(row.props()).toMatchObject({ label });
- expect(row.text()).toBe(text);
- });
-
- describe('Table links', () => {
- const linkRows = [
- [INFO_SECTION, 3, CANDIDATE.info.path_to_experiment],
- [INFO_SECTION, 4, CANDIDATE.info.path_to_artifact],
- [CI_SECTION, 0, CANDIDATE.info.ci_job.path],
- [CI_SECTION, 2, CANDIDATE.info.ci_job.merge_request.path],
- ];
-
- it.each(linkRows)('row %s is created correctly', (section, rowIndex, href) => {
- expect(findLinkAtRow(section, rowIndex).attributes().href).toBe(href);
- });
- });
-
- describe('Metrics table', () => {
- it('computes metrics table items correctly', () => {
- expect(findMetricsTable().props('items')).toEqual([
- { name: 'AUC', 0: '.55' },
- { name: 'Accuracy', 1: '.99', 2: '.98', 3: '.97' },
- { name: 'F1', 3: '.1' },
- ]);
- });
-
- it('computes metrics table fields correctly', () => {
- expect(findMetricsTable().props('fields')).toEqual([
- expect.objectContaining({ key: 'name', label: 'Metric' }),
- expect.objectContaining({ key: '0', label: 'Step 0' }),
- expect.objectContaining({ key: '1', label: 'Step 1' }),
- expect.objectContaining({ key: '2', label: 'Step 2' }),
- expect.objectContaining({ key: '3', label: 'Step 3' }),
- ]);
- });
- });
-
- describe('CI triggerer', () => {
- it('renders user row', () => {
- const avatar = findCiUserAvatar();
- expect(avatar.props()).toMatchObject({
- label: '',
- });
- expect(avatar.attributes().src).toEqual('/img.png');
- });
-
- it('renders user name', () => {
- const nameLink = findCiUserAvatarNameLink();
-
- expect(nameLink.attributes().href).toEqual('path/to/ci/user');
- expect(nameLink.text()).toEqual('CI User');
- });
- });
- });
-
- describe('No artifact path', () => {
- beforeEach(() =>
- createWrapper(() => {
- const candidate = newCandidate();
- delete candidate.info.path_to_artifact;
- return candidate;
- }),
- );
-
- it('does not render artifact row', () => {
- expect(findLabel('Artifacts').exists()).toBe(false);
- });
- });
-
- describe('No params, metrics, ci or metadata available', () => {
- beforeEach(() =>
- createWrapper(() => {
- const candidate = newCandidate();
- delete candidate.params;
- delete candidate.metrics;
- delete candidate.metadata;
- delete candidate.info.ci_job;
- return candidate;
- }),
- );
-
- it('does not render params', () => {
- expect(findNoDataMessage(NO_PARAMETERS_MESSAGE).exists()).toBe(true);
- });
-
- it('does not render metadata', () => {
- expect(findNoDataMessage(NO_METADATA_MESSAGE).exists()).toBe(true);
- });
-
- it('does not render metrics', () => {
- expect(findNoDataMessage(NO_METRICS_MESSAGE).exists()).toBe(true);
- });
-
- it('does not render CI info', () => {
- expect(findNoDataMessage(NO_CI_MESSAGE).exists()).toBe(true);
- });
- });
-
- describe('Has CI, but no user or mr', () => {
- beforeEach(() =>
- createWrapper(() => {
- const candidate = newCandidate();
- delete candidate.info.ci_job.user;
- delete candidate.info.ci_job.merge_request;
- return candidate;
- }),
- );
+ it('passes the delete path to delete button', () => {
+ expect(findDeleteButton().props('deletePath')).toBe('path_to_candidate');
+ });
- it('does not render MR info', () => {
- expect(findLabel('Merge request').exists()).toBe(false);
- });
+ it('passes the right title', () => {
+ expect(findHeader().props('pageTitle')).toBe('Model candidate details');
+ });
- it('does not render CI user info', () => {
- expect(findLabel('Triggered by').exists()).toBe(false);
- });
- });
+ it('creates the candidate detail section', () => {
+ expect(findCandidateDetail().props('candidate')).toBe(CANDIDATE);
});
});
diff --git a/spec/frontend/ml/experiment_tracking/routes/candidates/show/mock_data.js b/spec/frontend/ml/experiment_tracking/routes/candidates/show/mock_data.js
deleted file mode 100644
index 4ea23ed2513..00000000000
--- a/spec/frontend/ml/experiment_tracking/routes/candidates/show/mock_data.js
+++ /dev/null
@@ -1,41 +0,0 @@
-export const newCandidate = () => ({
- params: [
- { name: 'Algorithm', value: 'Decision Tree' },
- { name: 'MaxDepth', value: '3' },
- ],
- metrics: [
- { name: 'AUC', value: '.55', step: 0 },
- { name: 'Accuracy', value: '.99', step: 1 },
- { name: 'Accuracy', value: '.98', step: 2 },
- { name: 'Accuracy', value: '.97', step: 3 },
- { name: 'F1', value: '.1', step: 3 },
- ],
- metadata: [
- { name: 'FileName', value: 'test.py' },
- { name: 'ExecutionTime', value: '.0856' },
- ],
- info: {
- iid: 'candidate_iid',
- eid: 'abcdefg',
- path_to_artifact: 'path_to_artifact',
- experiment_name: 'The Experiment',
- path_to_experiment: 'path/to/experiment',
- status: 'SUCCESS',
- path: 'path_to_candidate',
- ci_job: {
- name: 'test',
- path: 'path/to/job',
- merge_request: {
- path: 'path/to/mr',
- iid: 1,
- title: 'Some MR',
- },
- user: {
- path: 'path/to/ci/user',
- name: 'CI User',
- username: 'ciuser',
- avatar: '/img.png',
- },
- },
- },
-});
diff --git a/spec/frontend/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row_spec.js b/spec/frontend/ml/model_registry/components/candidate_detail_row_spec.js
index cd252560590..24b18b6b42d 100644
--- a/spec/frontend/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row_spec.js
+++ b/spec/frontend/ml/model_registry/components/candidate_detail_row_spec.js
@@ -1,5 +1,5 @@
import { shallowMount } from '@vue/test-utils';
-import DetailRow from '~/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row.vue';
+import DetailRow from '~/ml/model_registry/components/candidate_detail_row.vue';
describe('CandidateDetailRow', () => {
const ROW_LABEL_CELL = 0;
diff --git a/spec/frontend/ml/model_registry/components/candidate_detail_spec.js b/spec/frontend/ml/model_registry/components/candidate_detail_spec.js
new file mode 100644
index 00000000000..f894af105ac
--- /dev/null
+++ b/spec/frontend/ml/model_registry/components/candidate_detail_spec.js
@@ -0,0 +1,183 @@
+import { GlAvatarLabeled, GlLink, GlTableLite } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import CandidateDetail from '~/ml/model_registry/components/candidate_detail.vue';
+import DetailRow from '~/ml/model_registry/components/candidate_detail_row.vue';
+import {
+ NO_PARAMETERS_MESSAGE,
+ NO_METRICS_MESSAGE,
+ NO_METADATA_MESSAGE,
+ NO_CI_MESSAGE,
+} from '~/ml/model_registry/translations';
+import { stubComponent } from 'helpers/stub_component';
+import { newCandidate } from '../mock_data';
+
+describe('ml/model_registry/components/candidate_detail.vue', () => {
+ let wrapper;
+ const CANDIDATE = newCandidate();
+ const USER_ROW = 1;
+
+ const INFO_SECTION = 0;
+ const CI_SECTION = 1;
+ const PARAMETER_SECTION = 2;
+ const METADATA_SECTION = 3;
+
+ const createWrapper = (createCandidate = () => CANDIDATE) => {
+ wrapper = shallowMountExtended(CandidateDetail, {
+ propsData: { candidate: createCandidate() },
+ stubs: {
+ GlTableLite: { ...stubComponent(GlTableLite), props: ['items', 'fields'] },
+ },
+ });
+ };
+
+ const findSection = (section) => wrapper.findAll('section').at(section);
+ const findRowInSection = (section, row) =>
+ findSection(section).findAllComponents(DetailRow).at(row);
+ const findLinkAtRow = (section, rowIndex) =>
+ findRowInSection(section, rowIndex).findComponent(GlLink);
+ const findNoDataMessage = (label) => wrapper.findByText(label);
+ const findLabel = (label) => wrapper.find(`[label='${label}']`);
+ const findCiUserDetailRow = () => findRowInSection(CI_SECTION, USER_ROW);
+ const findCiUserAvatar = () => findCiUserDetailRow().findComponent(GlAvatarLabeled);
+ const findCiUserAvatarNameLink = () => findCiUserAvatar().findComponent(GlLink);
+ const findMetricsTable = () => wrapper.findComponent(GlTableLite);
+
+ describe('All info available', () => {
+ beforeEach(() => createWrapper());
+
+ const mrText = `!${CANDIDATE.info.ci_job.merge_request.iid} ${CANDIDATE.info.ci_job.merge_request.title}`;
+ const expectedTable = [
+ [INFO_SECTION, 0, 'ID', CANDIDATE.info.iid],
+ [INFO_SECTION, 1, 'MLflow run ID', CANDIDATE.info.eid],
+ [INFO_SECTION, 2, 'Status', CANDIDATE.info.status],
+ [INFO_SECTION, 3, 'Experiment', CANDIDATE.info.experiment_name],
+ [INFO_SECTION, 4, 'Artifacts', 'Artifacts'],
+ [CI_SECTION, 0, 'Job', CANDIDATE.info.ci_job.name],
+ [CI_SECTION, 1, 'Triggered by', 'CI User'],
+ [CI_SECTION, 2, 'Merge request', mrText],
+ [PARAMETER_SECTION, 0, CANDIDATE.params[0].name, CANDIDATE.params[0].value],
+ [PARAMETER_SECTION, 1, CANDIDATE.params[1].name, CANDIDATE.params[1].value],
+ [METADATA_SECTION, 0, CANDIDATE.metadata[0].name, CANDIDATE.metadata[0].value],
+ [METADATA_SECTION, 1, CANDIDATE.metadata[1].name, CANDIDATE.metadata[1].value],
+ ];
+
+ it.each(expectedTable)('row %s is created correctly', (section, rowIndex, label, text) => {
+ const row = findRowInSection(section, rowIndex);
+
+ expect(row.props()).toMatchObject({ label });
+ expect(row.text()).toBe(text);
+ });
+
+ describe('Table links', () => {
+ const linkRows = [
+ [INFO_SECTION, 3, CANDIDATE.info.path_to_experiment],
+ [INFO_SECTION, 4, CANDIDATE.info.path_to_artifact],
+ [CI_SECTION, 0, CANDIDATE.info.ci_job.path],
+ [CI_SECTION, 2, CANDIDATE.info.ci_job.merge_request.path],
+ ];
+
+ it.each(linkRows)('row %s is created correctly', (section, rowIndex, href) => {
+ expect(findLinkAtRow(section, rowIndex).attributes().href).toBe(href);
+ });
+ });
+
+ describe('Metrics table', () => {
+ it('computes metrics table items correctly', () => {
+ expect(findMetricsTable().props('items')).toEqual([
+ { name: 'AUC', 0: '.55' },
+ { name: 'Accuracy', 1: '.99', 2: '.98', 3: '.97' },
+ { name: 'F1', 3: '.1' },
+ ]);
+ });
+
+ it('computes metrics table fields correctly', () => {
+ expect(findMetricsTable().props('fields')).toEqual([
+ expect.objectContaining({ key: 'name', label: 'Metric' }),
+ expect.objectContaining({ key: '0', label: 'Step 0' }),
+ expect.objectContaining({ key: '1', label: 'Step 1' }),
+ expect.objectContaining({ key: '2', label: 'Step 2' }),
+ expect.objectContaining({ key: '3', label: 'Step 3' }),
+ ]);
+ });
+ });
+
+ describe('CI triggerer', () => {
+ it('renders user row', () => {
+ const avatar = findCiUserAvatar();
+ expect(avatar.props()).toMatchObject({
+ label: '',
+ });
+ expect(avatar.attributes().src).toEqual('/img.png');
+ });
+
+ it('renders user name', () => {
+ const nameLink = findCiUserAvatarNameLink();
+
+ expect(nameLink.attributes().href).toEqual('path/to/ci/user');
+ expect(nameLink.text()).toEqual('CI User');
+ });
+ });
+ });
+
+ describe('No artifact path', () => {
+ beforeEach(() =>
+ createWrapper(() => {
+ const candidate = newCandidate();
+ delete candidate.info.path_to_artifact;
+ return candidate;
+ }),
+ );
+
+ it('does not render artifact row', () => {
+ expect(findLabel('Artifacts').exists()).toBe(false);
+ });
+ });
+
+ describe('No params, metrics, ci or metadata available', () => {
+ beforeEach(() =>
+ createWrapper(() => {
+ const candidate = newCandidate();
+ delete candidate.params;
+ delete candidate.metrics;
+ delete candidate.metadata;
+ delete candidate.info.ci_job;
+ return candidate;
+ }),
+ );
+
+ it('does not render params', () => {
+ expect(findNoDataMessage(NO_PARAMETERS_MESSAGE).exists()).toBe(true);
+ });
+
+ it('does not render metadata', () => {
+ expect(findNoDataMessage(NO_METADATA_MESSAGE).exists()).toBe(true);
+ });
+
+ it('does not render metrics', () => {
+ expect(findNoDataMessage(NO_METRICS_MESSAGE).exists()).toBe(true);
+ });
+
+ it('does not render CI info', () => {
+ expect(findNoDataMessage(NO_CI_MESSAGE).exists()).toBe(true);
+ });
+ });
+
+ describe('Has CI, but no user or mr', () => {
+ beforeEach(() =>
+ createWrapper(() => {
+ const candidate = newCandidate();
+ delete candidate.info.ci_job.user;
+ delete candidate.info.ci_job.merge_request;
+ return candidate;
+ }),
+ );
+
+ it('does not render MR info', () => {
+ expect(findLabel('Merge request').exists()).toBe(false);
+ });
+
+ it('does not render CI user info', () => {
+ expect(findLabel('Triggered by').exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/ml/model_registry/mock_data.js b/spec/frontend/ml/model_registry/mock_data.js
index a820c323103..c214194af9e 100644
--- a/spec/frontend/ml/model_registry/mock_data.js
+++ b/spec/frontend/ml/model_registry/mock_data.js
@@ -46,3 +46,45 @@ export const defaultPageInfo = Object.freeze({
hasNextPage: true,
hasPreviousPage: true,
});
+
+export const newCandidate = () => ({
+ params: [
+ { name: 'Algorithm', value: 'Decision Tree' },
+ { name: 'MaxDepth', value: '3' },
+ ],
+ metrics: [
+ { name: 'AUC', value: '.55', step: 0 },
+ { name: 'Accuracy', value: '.99', step: 1 },
+ { name: 'Accuracy', value: '.98', step: 2 },
+ { name: 'Accuracy', value: '.97', step: 3 },
+ { name: 'F1', value: '.1', step: 3 },
+ ],
+ metadata: [
+ { name: 'FileName', value: 'test.py' },
+ { name: 'ExecutionTime', value: '.0856' },
+ ],
+ info: {
+ iid: 'candidate_iid',
+ eid: 'abcdefg',
+ path_to_artifact: 'path_to_artifact',
+ experiment_name: 'The Experiment',
+ path_to_experiment: 'path/to/experiment',
+ status: 'SUCCESS',
+ path: 'path_to_candidate',
+ ci_job: {
+ name: 'test',
+ path: 'path/to/job',
+ merge_request: {
+ path: 'path/to/mr',
+ iid: 1,
+ title: 'Some MR',
+ },
+ user: {
+ path: 'path/to/ci/user',
+ name: 'CI User',
+ username: 'ciuser',
+ avatar: '/img.png',
+ },
+ },
+ },
+});
diff --git a/spec/helpers/notes_helper_spec.rb b/spec/helpers/notes_helper_spec.rb
index 62c0d1b1ff7..58d39caa90c 100644
--- a/spec/helpers/notes_helper_spec.rb
+++ b/spec/helpers/notes_helper_spec.rb
@@ -55,23 +55,6 @@ RSpec.describe NotesHelper, feature_category: :team_planning do
end
end
- describe "#notes_max_access_for_users" do
- it 'returns access levels' do
- expect(helper.note_max_access_for_user(owner_note)).to eq(Gitlab::Access::OWNER)
- expect(helper.note_max_access_for_user(maintainer_note)).to eq(Gitlab::Access::MAINTAINER)
- expect(helper.note_max_access_for_user(reporter_note)).to eq(Gitlab::Access::REPORTER)
- end
-
- it 'handles access in different projects' do
- second_project = create(:project)
- second_project.add_reporter(maintainer)
- other_note = create(:note, author: maintainer, project: second_project)
-
- expect(helper.note_max_access_for_user(maintainer_note)).to eq(Gitlab::Access::MAINTAINER)
- expect(helper.note_max_access_for_user(other_note)).to eq(Gitlab::Access::REPORTER)
- end
- end
-
describe '#discussion_path' do
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/helpers/notifications_helper_spec.rb b/spec/helpers/notifications_helper_spec.rb
index a5338659659..8acd72b76a1 100644
--- a/spec/helpers/notifications_helper_spec.rb
+++ b/spec/helpers/notifications_helper_spec.rb
@@ -13,12 +13,6 @@ RSpec.describe NotificationsHelper do
it { expect(notification_icon(:custom)).to equal('') }
end
- describe 'notification_title' do
- it { expect(notification_title(:watch)).to match('Watch') }
- it { expect(notification_title(:mention)).to match('On mention') }
- it { expect(notification_title(:global)).to match('Global') }
- end
-
describe '#notification_icon_level' do
let(:user) { create(:user) }
let(:global_setting) { user.global_notification_setting }
diff --git a/vendor/project_templates/android.tar.gz b/vendor/project_templates/android.tar.gz
index ee0689a6e45..6ce41349e39 100644
--- a/vendor/project_templates/android.tar.gz
+++ b/vendor/project_templates/android.tar.gz
Binary files differ
diff --git a/vendor/project_templates/astro_tailwind.tar.gz b/vendor/project_templates/astro_tailwind.tar.gz
index 4724b01d3f6..343edbda86b 100644
--- a/vendor/project_templates/astro_tailwind.tar.gz
+++ b/vendor/project_templates/astro_tailwind.tar.gz
Binary files differ
diff --git a/vendor/project_templates/bridgetown.tar.gz b/vendor/project_templates/bridgetown.tar.gz
index 8b4c63e1be5..8295ebfbe76 100644
--- a/vendor/project_templates/bridgetown.tar.gz
+++ b/vendor/project_templates/bridgetown.tar.gz
Binary files differ
diff --git a/vendor/project_templates/cluster_management.tar.gz b/vendor/project_templates/cluster_management.tar.gz
index 252fb2da75a..a76ca916691 100644
--- a/vendor/project_templates/cluster_management.tar.gz
+++ b/vendor/project_templates/cluster_management.tar.gz
Binary files differ
diff --git a/vendor/project_templates/dotnetcore.tar.gz b/vendor/project_templates/dotnetcore.tar.gz
index 63b86320cb1..d798b602595 100644
--- a/vendor/project_templates/dotnetcore.tar.gz
+++ b/vendor/project_templates/dotnetcore.tar.gz
Binary files differ
diff --git a/vendor/project_templates/express.tar.gz b/vendor/project_templates/express.tar.gz
index 3babc60a5bc..7d0b0c8fcf3 100644
--- a/vendor/project_templates/express.tar.gz
+++ b/vendor/project_templates/express.tar.gz
Binary files differ
diff --git a/vendor/project_templates/gatsby.tar.gz b/vendor/project_templates/gatsby.tar.gz
index 1c0e413b2ec..be2c30c500e 100644
--- a/vendor/project_templates/gatsby.tar.gz
+++ b/vendor/project_templates/gatsby.tar.gz
Binary files differ
diff --git a/vendor/project_templates/gitpod_spring_petclinic.tar.gz b/vendor/project_templates/gitpod_spring_petclinic.tar.gz
index 76308dc73df..9d146dcd4b7 100644
--- a/vendor/project_templates/gitpod_spring_petclinic.tar.gz
+++ b/vendor/project_templates/gitpod_spring_petclinic.tar.gz
Binary files differ
diff --git a/vendor/project_templates/gomicro.tar.gz b/vendor/project_templates/gomicro.tar.gz
index 50899e661c7..b0d286365fa 100644
--- a/vendor/project_templates/gomicro.tar.gz
+++ b/vendor/project_templates/gomicro.tar.gz
Binary files differ
diff --git a/vendor/project_templates/hexo.tar.gz b/vendor/project_templates/hexo.tar.gz
index cc30ff0a67e..57c6cf7a4d1 100644
--- a/vendor/project_templates/hexo.tar.gz
+++ b/vendor/project_templates/hexo.tar.gz
Binary files differ
diff --git a/vendor/project_templates/hugo.tar.gz b/vendor/project_templates/hugo.tar.gz
index 3d037bbf1df..14154a92b46 100644
--- a/vendor/project_templates/hugo.tar.gz
+++ b/vendor/project_templates/hugo.tar.gz
Binary files differ
diff --git a/vendor/project_templates/iosswift.tar.gz b/vendor/project_templates/iosswift.tar.gz
index 80a71b432b5..b95d9ee828f 100644
--- a/vendor/project_templates/iosswift.tar.gz
+++ b/vendor/project_templates/iosswift.tar.gz
Binary files differ
diff --git a/vendor/project_templates/jekyll.tar.gz b/vendor/project_templates/jekyll.tar.gz
index 8dc68699baa..9dd47dfbd64 100644
--- a/vendor/project_templates/jekyll.tar.gz
+++ b/vendor/project_templates/jekyll.tar.gz
Binary files differ
diff --git a/vendor/project_templates/jsonnet.tar.gz b/vendor/project_templates/jsonnet.tar.gz
index 8da4227530a..b32b3f6a69b 100644
--- a/vendor/project_templates/jsonnet.tar.gz
+++ b/vendor/project_templates/jsonnet.tar.gz
Binary files differ
diff --git a/vendor/project_templates/kotlin_native_linux.tar.gz b/vendor/project_templates/kotlin_native_linux.tar.gz
index 6c50c96ae9b..639ce89f736 100644
--- a/vendor/project_templates/kotlin_native_linux.tar.gz
+++ b/vendor/project_templates/kotlin_native_linux.tar.gz
Binary files differ
diff --git a/vendor/project_templates/laravel.tar.gz b/vendor/project_templates/laravel.tar.gz
index 49324636f5e..163b76cb428 100644
--- a/vendor/project_templates/laravel.tar.gz
+++ b/vendor/project_templates/laravel.tar.gz
Binary files differ
diff --git a/vendor/project_templates/middleman.tar.gz b/vendor/project_templates/middleman.tar.gz
index b926aa92a20..2b2bd84a494 100644
--- a/vendor/project_templates/middleman.tar.gz
+++ b/vendor/project_templates/middleman.tar.gz
Binary files differ
diff --git a/vendor/project_templates/nfgitbook.tar.gz b/vendor/project_templates/nfgitbook.tar.gz
index f09a5f41171..cbb55129864 100644
--- a/vendor/project_templates/nfgitbook.tar.gz
+++ b/vendor/project_templates/nfgitbook.tar.gz
Binary files differ
diff --git a/vendor/project_templates/nfhexo.tar.gz b/vendor/project_templates/nfhexo.tar.gz
index 3a241f68df4..107c8947fbd 100644
--- a/vendor/project_templates/nfhexo.tar.gz
+++ b/vendor/project_templates/nfhexo.tar.gz
Binary files differ
diff --git a/vendor/project_templates/nfhugo.tar.gz b/vendor/project_templates/nfhugo.tar.gz
index 093ecdea96a..3f555584593 100644
--- a/vendor/project_templates/nfhugo.tar.gz
+++ b/vendor/project_templates/nfhugo.tar.gz
Binary files differ
diff --git a/vendor/project_templates/nfjekyll.tar.gz b/vendor/project_templates/nfjekyll.tar.gz
index f554181e1db..20275cf7744 100644
--- a/vendor/project_templates/nfjekyll.tar.gz
+++ b/vendor/project_templates/nfjekyll.tar.gz
Binary files differ
diff --git a/vendor/project_templates/nfplainhtml.tar.gz b/vendor/project_templates/nfplainhtml.tar.gz
index 13dd13a6830..ef6ccf12021 100644
--- a/vendor/project_templates/nfplainhtml.tar.gz
+++ b/vendor/project_templates/nfplainhtml.tar.gz
Binary files differ
diff --git a/vendor/project_templates/pelican.tar.gz b/vendor/project_templates/pelican.tar.gz
index bc87d498ced..9d144ef0bad 100644
--- a/vendor/project_templates/pelican.tar.gz
+++ b/vendor/project_templates/pelican.tar.gz
Binary files differ
diff --git a/vendor/project_templates/plainhtml.tar.gz b/vendor/project_templates/plainhtml.tar.gz
index dc0354172be..e8b3be52107 100644
--- a/vendor/project_templates/plainhtml.tar.gz
+++ b/vendor/project_templates/plainhtml.tar.gz
Binary files differ
diff --git a/vendor/project_templates/rails.tar.gz b/vendor/project_templates/rails.tar.gz
index 0d82bf639de..b8a0d0be235 100644
--- a/vendor/project_templates/rails.tar.gz
+++ b/vendor/project_templates/rails.tar.gz
Binary files differ
diff --git a/vendor/project_templates/salesforcedx.tar.gz b/vendor/project_templates/salesforcedx.tar.gz
index 9486b410507..58746f89766 100644
--- a/vendor/project_templates/salesforcedx.tar.gz
+++ b/vendor/project_templates/salesforcedx.tar.gz
Binary files differ
diff --git a/vendor/project_templates/serverless_framework.tar.gz b/vendor/project_templates/serverless_framework.tar.gz
index 6b5def03ed3..f8d86acdfc9 100644
--- a/vendor/project_templates/serverless_framework.tar.gz
+++ b/vendor/project_templates/serverless_framework.tar.gz
Binary files differ
diff --git a/vendor/project_templates/spring.tar.gz b/vendor/project_templates/spring.tar.gz
index 12960e91f85..9cf97f09c2a 100644
--- a/vendor/project_templates/spring.tar.gz
+++ b/vendor/project_templates/spring.tar.gz
Binary files differ
diff --git a/vendor/project_templates/tencent_serverless_framework.tar.gz b/vendor/project_templates/tencent_serverless_framework.tar.gz
index f243dc30c63..dcbb0ec16a5 100644
--- a/vendor/project_templates/tencent_serverless_framework.tar.gz
+++ b/vendor/project_templates/tencent_serverless_framework.tar.gz
Binary files differ
diff --git a/vendor/project_templates/typo3_distribution.tar.gz b/vendor/project_templates/typo3_distribution.tar.gz
index 6c29993224b..dd4024fce96 100644
--- a/vendor/project_templates/typo3_distribution.tar.gz
+++ b/vendor/project_templates/typo3_distribution.tar.gz
Binary files differ