summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-01-06 12:10:04 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2023-01-06 12:10:04 +0000
commit53fb4ad16325d4963fdc5396b8510ab748a45e50 (patch)
treeb1bd9ce5cbff5bf9c94f60fee3db86d3d53ffadc
parentf57ca9a5fc582232e23b2495812d57bf6f64313b (diff)
downloadgitlab-ce-53fb4ad16325d4963fdc5396b8510ab748a45e50.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab-ci.yml42
-rw-r--r--.gitlab/ci/rules.gitlab-ci.yml30
-rw-r--r--.gitlab/ci/setup.gitlab-ci.yml6
-rw-r--r--app/assets/javascripts/groups_select.js93
-rw-r--r--app/assets/javascripts/issuable/components/related_issuable_item.vue1
-rw-r--r--app/assets/javascripts/ml/experiment_tracking/components/ml_experiment.vue38
-rw-r--r--app/assets/javascripts/pages/projects/ml/experiments/show/index.js3
-rw-r--r--app/assets/javascripts/projects/commits/index.js19
-rw-r--r--app/assets/javascripts/ref/components/ref_results_section.vue8
-rw-r--r--app/assets/javascripts/ref/components/ref_selector.vue15
-rw-r--r--app/assets/javascripts/ref/constants.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/pagination/table_pagination.vue3
-rw-r--r--app/controllers/projects/ml/experiments_controller.rb25
-rw-r--r--app/views/projects/commits/show.html.haml2
-rw-r--r--app/views/projects/ml/experiments/show.html.haml4
-rw-r--r--app/views/shared/runners/_runner_type_alert.html.haml2
-rw-r--r--config/feature_flags/development/file_line_blame.yml2
-rw-r--r--config/feature_flags/development/vue_group_select.yml8
-rw-r--r--doc/administration/geo/index.md17
-rw-r--r--doc/administration/geo/replication/single_sign_on.md122
-rw-r--r--doc/administration/geo/replication/troubleshooting.md4
-rw-r--r--doc/administration/geo/replication/version_specific_upgrades.md2
-rw-r--r--doc/api/linked_epics.md12
-rw-r--r--doc/ci/testing/code_quality.md10
-rw-r--r--doc/development/pipelines/index.md52
-rw-r--r--doc/subscriptions/self_managed/index.md44
-rw-r--r--doc/user/application_security/sast/analyzers.md2
-rw-r--r--doc/user/group/epics/linked_epics.md8
-rw-r--r--doc/user/read_only_namespaces.md1
-rw-r--r--lib/gitlab/gon_helper.rb1
-rw-r--r--locale/gitlab.pot2
-rw-r--r--qa/Dockerfile2
-rw-r--r--spec/frontend/ml/experiment_tracking/components/__snapshots__/ml_experiment_spec.js.snap142
-rw-r--r--spec/frontend/ml/experiment_tracking/components/ml_experiment_spec.js71
-rw-r--r--spec/frontend/projects/settings/components/default_branch_selector_spec.js1
-rw-r--r--spec/frontend/ref/components/ref_selector_spec.js11
-rw-r--r--spec/requests/projects/ml/experiments_controller_spec.rb44
-rw-r--r--spec/services/issue_links/create_service_spec.rb4
-rw-r--r--spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/issuable_links/destroyable_issuable_links_shared_examples.rb7
-rw-r--r--spec/tooling/quality/test_level_spec.rb12
41 files changed, 641 insertions, 235 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2062953c9ba..79c402c7fa7 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -29,6 +29,8 @@ workflow:
rules:
# If `$FORCE_GITLAB_CI` is set, create a pipeline.
- if: '$FORCE_GITLAB_CI'
+ variables:
+ RUBY_VERSION: "3.0"
# As part of the process of creating RCs automatically, we update stable
# branches with the changes of the most recent production deployment. The
# merge requests used for this merge a branch release-tools/X into a stable
@@ -36,51 +38,69 @@ workflow:
# they serve no purpose and will run anyway when the changes are merged.
- if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^release-tools\/\d+\.\d+\.\d+-rc\d+$/ && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME =~ /^[\d-]+-stable(-ee)?$/ && $CI_PROJECT_PATH == "gitlab-org/gitlab"'
when: never
- # For merge requests running exclusively in Ruby 3.0
- - if: '$CI_MERGE_REQUEST_LABELS =~ /pipeline:run-in-ruby3/'
+ # For merge requests running exclusively in Ruby 2.7
+ - if: '$CI_MERGE_REQUEST_LABELS =~ /pipeline:run-in-ruby2/'
variables:
- RUBY_VERSION: "3.0"
- PIPELINE_NAME: 'Ruby 3 $CI_MERGE_REQUEST_EVENT_TYPE MR pipeline'
+ RUBY_VERSION: "2.7"
+ PIPELINE_NAME: 'Ruby 2 $CI_MERGE_REQUEST_EVENT_TYPE MR pipeline'
# For (detached) merge request pipelines.
- if: '$CI_MERGE_REQUEST_IID'
variables:
- PIPELINE_NAME: '$CI_MERGE_REQUEST_EVENT_TYPE MR pipeline'
+ RUBY_VERSION: "3.0"
+ OMNIBUS_GITLAB_RUBY3_BUILD: "true"
+ OMNIBUS_GITLAB_CACHE_EDITION: "GITLAB_RUBY3"
+ PIPELINE_NAME: 'Ruby 3 $CI_MERGE_REQUEST_EVENT_TYPE MR pipeline'
# For the scheduled pipelines, we set specific variables.
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "schedule"'
variables:
+ RUBY_VERSION: "3.0"
+ OMNIBUS_GITLAB_RUBY3_BUILD: "true"
+ OMNIBUS_GITLAB_CACHE_EDITION: "GITLAB_RUBY3"
CRYSTALBALL: "true"
CREATE_INCIDENT_FOR_PIPELINE_FAILURE: "true"
NOTIFY_PIPELINE_FAILURE_CHANNEL: "master-broken"
PIPELINE_NAME: 'Scheduled $CI_COMMIT_BRANCH pipeline'
- # Run pipelines for ruby3 branch
- - if: '$CI_COMMIT_BRANCH == "ruby3" && $CI_PIPELINE_SOURCE == "schedule"'
+ # Run pipelines for ruby2 branch
+ - if: '$CI_COMMIT_BRANCH == "ruby2" && $CI_PIPELINE_SOURCE == "schedule"'
variables:
- RUBY_VERSION: "3.0"
+ RUBY_VERSION: "2.7"
NOTIFY_PIPELINE_FAILURE_CHANNEL: "f_ruby3"
- OMNIBUS_GITLAB_RUBY3_BUILD: "true"
- OMNIBUS_GITLAB_CACHE_EDITION: "GITLAB_RUBY3"
- PIPELINE_NAME: 'Scheduled ruby 3 pipeline'
+ PIPELINE_NAME: 'Scheduled ruby 2 pipeline'
# This work around https://gitlab.com/gitlab-org/gitlab/-/issues/332411 whichs prevents usage of dependency proxy
# when pipeline is triggered by a project access token.
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $GITLAB_USER_LOGIN =~ /project_\d+_bot\d*/'
variables:
+ RUBY_VERSION: "3.0"
+ OMNIBUS_GITLAB_RUBY3_BUILD: "true"
+ OMNIBUS_GITLAB_CACHE_EDITION: "GITLAB_RUBY3"
GITLAB_DEPENDENCY_PROXY_ADDRESS: ""
CREATE_INCIDENT_FOR_PIPELINE_FAILURE: "true"
NOTIFY_PIPELINE_FAILURE_CHANNEL: "master-broken"
# For `$CI_DEFAULT_BRANCH` branch, create a pipeline (this includes on schedules, pushes, merges, etc.).
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
variables:
+ RUBY_VERSION: "3.0"
+ OMNIBUS_GITLAB_RUBY3_BUILD: "true"
+ OMNIBUS_GITLAB_CACHE_EDITION: "GITLAB_RUBY3"
CREATE_INCIDENT_FOR_PIPELINE_FAILURE: "true"
NOTIFY_PIPELINE_FAILURE_CHANNEL: "master-broken"
# For tags, create a pipeline.
- if: '$CI_COMMIT_TAG'
+ variables:
+ RUBY_VERSION: "2.7"
# If `$GITLAB_INTERNAL` isn't set, don't create a pipeline.
- if: '$GITLAB_INTERNAL == null'
when: never
# For stable, auto-deploy, and security branches, create a pipeline.
- if: '$CI_COMMIT_BRANCH =~ /^[\d-]+-stable(-ee)?$/'
+ variables:
+ RUBY_VERSION: "2.7"
- if: '$CI_COMMIT_BRANCH =~ /^\d+-\d+-auto-deploy-\d+$/'
+ variables:
+ RUBY_VERSION: "2.7"
- if: '$CI_COMMIT_BRANCH =~ /^security\//'
+ variables:
+ RUBY_VERSION: "2.7"
variables:
PG_VERSION: "12"
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml
index 410dcec1450..b9c1b1b1143 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -21,7 +21,7 @@
if: '$FORCE_GITLAB_CI'
.if-default-refs: &if-default-refs
- if: '$CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH || $CI_COMMIT_REF_NAME =~ /^[\d-]+-stable(-ee)?$/ || $CI_COMMIT_REF_NAME =~ /^\d+-\d+-auto-deploy-\d+$/ || $CI_COMMIT_REF_NAME =~ /^security\// || $CI_COMMIT_REF_NAME == "ruby3" || $CI_MERGE_REQUEST_IID || $CI_COMMIT_TAG || $FORCE_GITLAB_CI'
+ if: '$CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH || $CI_COMMIT_REF_NAME =~ /^[\d-]+-stable(-ee)?$/ || $CI_COMMIT_REF_NAME =~ /^\d+-\d+-auto-deploy-\d+$/ || $CI_COMMIT_REF_NAME =~ /^security\// || $CI_COMMIT_REF_NAME == "ruby2" || $CI_MERGE_REQUEST_IID || $CI_COMMIT_TAG || $FORCE_GITLAB_CI'
.if-default-branch-refs: &if-default-branch-refs
if: '$CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH && $CI_MERGE_REQUEST_IID == null'
@@ -55,8 +55,8 @@
.if-merge-request-targeting-stable-branch: &if-merge-request-targeting-stable-branch
if: '$CI_MERGE_REQUEST_IID && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME =~ /^[\d-]+-stable(-ee)?$/'
-.if-merge-request-labels-run-in-ruby3: &if-merge-request-labels-run-in-ruby3
- if: '$CI_MERGE_REQUEST_LABELS =~ /pipeline:run-in-ruby3/'
+.if-merge-request-labels-run-in-ruby2: &if-merge-request-labels-run-in-ruby2
+ if: '$CI_MERGE_REQUEST_LABELS =~ /pipeline:run-in-ruby2/'
.if-merge-request-labels-as-if-foss: &if-merge-request-labels-as-if-foss
if: '$CI_MERGE_REQUEST_LABELS =~ /pipeline:run-as-if-foss/'
@@ -112,8 +112,8 @@
.if-default-branch-schedule-nightly: &if-default-branch-schedule-nightly
if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "schedule" && $SCHEDULE_TYPE == "nightly"'
-.if-ruby3-branch-schedule-nightly: &if-ruby3-branch-schedule-nightly
- if: '$CI_COMMIT_BRANCH == "ruby3" && $CI_PIPELINE_SOURCE == "schedule" && $SCHEDULE_TYPE == "nightly"'
+.if-ruby2-branch-schedule-nightly: &if-ruby2-branch-schedule-nightly
+ if: '$CI_COMMIT_BRANCH == "ruby2" && $CI_PIPELINE_SOURCE == "schedule" && $SCHEDULE_TYPE == "nightly"'
.if-security-schedule: &if-security-schedule
if: '$CI_PROJECT_NAMESPACE == "gitlab-org/security" && $CI_PIPELINE_SOURCE == "schedule"'
@@ -145,8 +145,8 @@
.if-dot-com-gitlab-org-and-security-tag: &if-dot-com-gitlab-org-and-security-tag
if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE =~ /^gitlab-org($|\/security$)/ && $CI_COMMIT_TAG'
-.if-ruby3-branch: &if-ruby3-branch
- if: '$CI_COMMIT_BRANCH == "ruby3"'
+.if-ruby2-branch: &if-ruby2-branch
+ if: '$CI_COMMIT_BRANCH == "ruby2"'
# For Security merge requests, the gitlab-release-tools-bot triggers a new
# pipeline for the "Pipelines for merged results" feature. If the pipeline
@@ -705,7 +705,7 @@
variables:
ARCH: amd64,arm64
- <<: *if-force-ci
- - <<: *if-ruby3-branch
+ - <<: *if-ruby2-branch
.build-images:rules:build-assets-image:
rules:
@@ -714,7 +714,7 @@
- <<: *if-merge-request-targeting-stable-branch
- <<: *if-merge-request-labels-run-review-app
- <<: *if-auto-deploy-branches
- - <<: *if-ruby3-branch
+ - <<: *if-ruby2-branch
- <<: *if-default-refs
changes: *ci-build-images-patterns
- <<: *if-default-refs
@@ -882,7 +882,7 @@
- <<: *if-merge-request-targeting-stable-branch
- <<: *if-merge-request-labels-run-review-app
- <<: *if-auto-deploy-branches
- - <<: *if-ruby3-branch
+ - <<: *if-ruby2-branch
- <<: *if-default-refs
changes: *ci-build-images-patterns
- <<: *if-default-refs
@@ -1102,7 +1102,7 @@
allow_failure: true
- <<: *if-force-ci
allow_failure: true
- - <<: *if-ruby3-branch
+ - <<: *if-ruby2-branch
.qa:rules:package-and-test:
rules:
@@ -1114,7 +1114,7 @@
when: never
- <<: *if-merge-request-targeting-stable-branch
allow_failure: true
- - <<: *if-ruby3-branch
+ - <<: *if-ruby2-branch
allow_failure: true
- <<: *if-dot-com-gitlab-org-and-security-merge-request-manual-ff-package-and-e2e
changes: *feature-flag-development-config-patterns
@@ -1544,7 +1544,7 @@
- <<: *if-not-ee
when: never
- <<: *if-default-branch-schedule-nightly
- - <<: *if-ruby3-branch-schedule-nightly
+ - <<: *if-ruby2-branch-schedule-nightly
- <<: *if-merge-request-labels-run-all-rspec
.rails:rules:rspec-coverage:
@@ -2065,9 +2065,9 @@
- <<: *if-default-refs
changes: *code-backstage-patterns
-.setup:rules:verify-ruby-2.7:
+.setup:rules:verify-ruby-3.0:
rules:
- - <<: *if-merge-request-labels-run-in-ruby3
+ - <<: *if-merge-request-labels-run-in-ruby2
.setup:rules:verify-tests-yml:
rules:
diff --git a/.gitlab/ci/setup.gitlab-ci.yml b/.gitlab/ci/setup.gitlab-ci.yml
index fcbbdffb93f..b7e69314c97 100644
--- a/.gitlab/ci/setup.gitlab-ci.yml
+++ b/.gitlab/ci/setup.gitlab-ci.yml
@@ -67,13 +67,13 @@ no-jh-check:
script:
- scripts/no-dir-check jh
-verify-ruby-2.7:
+verify-ruby-3.0:
extends:
- .absolutely-minimal-job
- - .setup:rules:verify-ruby-2.7
+ - .setup:rules:verify-ruby-3.0
stage: prepare
script:
- - echo 'Please remove label ~"pipeline:run-in-ruby3" so we do test against Ruby 2.7 (default version) before merging the merge request'
+ - echo 'Please remove label ~"pipeline:run-in-ruby2" so we do test against Ruby 3.0 (default version) before merging the merge request'
- exit 1
verify-tests-yml:
diff --git a/app/assets/javascripts/groups_select.js b/app/assets/javascripts/groups_select.js
index fb0c47fe018..2a2599801ca 100644
--- a/app/assets/javascripts/groups_select.js
+++ b/app/assets/javascripts/groups_select.js
@@ -1,12 +1,5 @@
import Vue from 'vue';
-import $ from 'jquery';
-import { escape } from 'lodash';
import GroupSelect from '~/vue_shared/components/group_select/group_select.vue';
-import { groupsPath } from '~/vue_shared/components/group_select/utils';
-import { __ } from '~/locale';
-import Api from './api';
-import { loadCSSFile } from './lib/utils/css_utils';
-import { select2AxiosTransport } from './lib/utils/select2_utils';
const initVueSelect = () => {
[...document.querySelectorAll('.ajax-groups-select')].forEach((el) => {
@@ -33,90 +26,6 @@ const initVueSelect = () => {
});
};
-const groupsSelect = () => {
- loadCSSFile(gon.select2_css_path)
- .then(() => {
- // Needs to be accessible in rspec
- window.GROUP_SELECT_PER_PAGE = 20;
-
- $('.ajax-groups-select').each(function setAjaxGroupsSelect2() {
- const $select = $(this);
- const allAvailable = $select.data('allAvailable');
- const skipGroups = $select.data('skipGroups') || [];
- const parentGroupID = $select.data('parentId');
- const groupsFilter = $select.data('groupsFilter');
- const minAccessLevel = $select.data('minAccessLevel');
-
- $select.select2({
- placeholder: __('Search for a group'),
- allowClear: $select.hasClass('allowClear'),
- multiple: $select.hasClass('multiselect'),
- minimumInputLength: 0,
- ajax: {
- url: Api.buildUrl(groupsPath(groupsFilter, parentGroupID)),
- dataType: 'json',
- quietMillis: 250,
- transport: select2AxiosTransport,
- data(search, page) {
- return {
- search,
- page,
- per_page: window.GROUP_SELECT_PER_PAGE,
- all_available: allAvailable,
- min_access_level: minAccessLevel,
- };
- },
- results(data, page) {
- const groups = data.length ? data : data.results || [];
- const more = data.pagination ? data.pagination.more : false;
- const results = groups.filter((group) => skipGroups.indexOf(group.id) === -1);
-
- return {
- results,
- page,
- more,
- };
- },
- },
- // eslint-disable-next-line consistent-return
- initSelection(element, callback) {
- const id = $(element).val();
- if (id !== '') {
- return Api.group(id, callback);
- }
- },
- formatResult(object) {
- return `<div class='group-result'> <div class='group-name'>${escape(
- object.full_name,
- )}</div> <div class='group-path'>${object.full_path}</div> </div>`;
- },
- formatSelection(object) {
- return escape(object.full_name);
- },
- dropdownCssClass: 'ajax-groups-dropdown select2-infinite',
- // we do not want to escape markup since we are displaying html in results
- escapeMarkup(m) {
- return m;
- },
- });
-
- $select.on('select2-loaded', () => {
- const dropdown = document.querySelector('.select2-infinite .select2-results');
- dropdown.style.height = `${Math.floor(dropdown.scrollHeight)}px`;
- });
- });
- })
- .catch(() => {});
-};
-
export default () => {
- if ($('.ajax-groups-select').length) {
- if (gon.features?.vueGroupSelect) {
- initVueSelect();
- } else {
- import(/* webpackChunkName: 'select2' */ 'select2/select2')
- .then(groupsSelect)
- .catch(() => {});
- }
- }
+ initVueSelect();
};
diff --git a/app/assets/javascripts/issuable/components/related_issuable_item.vue b/app/assets/javascripts/issuable/components/related_issuable_item.vue
index fd55f05e955..c815c7aaba9 100644
--- a/app/assets/javascripts/issuable/components/related_issuable_item.vue
+++ b/app/assets/javascripts/issuable/components/related_issuable_item.vue
@@ -135,7 +135,6 @@ export default {
<gl-link
:href="computedPath"
class="sortable-link gl-font-weight-normal"
- target="_blank"
@click="handleTitleClick"
>
{{ title }}
diff --git a/app/assets/javascripts/ml/experiment_tracking/components/ml_experiment.vue b/app/assets/javascripts/ml/experiment_tracking/components/ml_experiment.vue
index f8e269d3b57..0e601a67d85 100644
--- a/app/assets/javascripts/ml/experiment_tracking/components/ml_experiment.vue
+++ b/app/assets/javascripts/ml/experiment_tracking/components/ml_experiment.vue
@@ -1,6 +1,7 @@
<script>
-import { GlTable, GlLink } from '@gitlab/ui';
+import { GlTable, GlLink, GlPagination } from '@gitlab/ui';
import { __ } from '~/locale';
+import { getParameterValues, setUrlParams } from '~/lib/utils/url_utility';
import IncubationAlert from './incubation_alert.vue';
export default {
@@ -9,8 +10,14 @@ export default {
GlTable,
GlLink,
IncubationAlert,
+ GlPagination,
+ },
+ inject: ['candidates', 'metricNames', 'paramNames', 'pagination'],
+ data() {
+ return {
+ page: parseInt(getParameterValues('page')[0], 10) || 1,
+ };
},
- inject: ['candidates', 'metricNames', 'paramNames'],
computed: {
fields() {
return [
@@ -20,6 +27,20 @@ export default {
{ key: 'artifact', label: '' },
];
},
+ displayPagination() {
+ return this.candidates.length > 0;
+ },
+ prevPage() {
+ return this.pagination.page > 1 ? this.pagination.page - 1 : null;
+ },
+ nextPage() {
+ return !this.pagination.isLastPage ? this.pagination.page + 1 : null;
+ },
+ },
+ methods: {
+ generateLink(page) {
+ return setUrlParams({ page });
+ },
},
i18n: {
titleLabel: __('Experiment candidates'),
@@ -44,6 +65,7 @@ export default {
:empty-text="$options.i18n.emptyStateLabel"
show-empty
class="gl-mt-0!"
+ small
>
<template #cell(artifact)="data">
<gl-link v-if="data.value" :href="data.value" target="_blank">{{
@@ -55,5 +77,17 @@ export default {
<gl-link :href="data.value">{{ $options.i18n.detailsLabel }}</gl-link>
</template>
</gl-table>
+
+ <gl-pagination
+ v-if="displayPagination"
+ v-model="pagination.page"
+ :prev-page="prevPage"
+ :next-page="nextPage"
+ :total-items="pagination.totalItems"
+ :per-page="pagination.perPage"
+ :link-gen="generateLink"
+ align="center"
+ class="w-100"
+ />
</div>
</template>
diff --git a/app/assets/javascripts/pages/projects/ml/experiments/show/index.js b/app/assets/javascripts/pages/projects/ml/experiments/show/index.js
index 97e436920c7..6947b15dcbe 100644
--- a/app/assets/javascripts/pages/projects/ml/experiments/show/index.js
+++ b/app/assets/javascripts/pages/projects/ml/experiments/show/index.js
@@ -1,5 +1,6 @@
import Vue from 'vue';
import MlExperiment from '~/ml/experiment_tracking/components/ml_experiment.vue';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
const initShowExperiment = () => {
const element = document.querySelector('#js-show-ml-experiment');
@@ -13,6 +14,7 @@ const initShowExperiment = () => {
const candidates = JSON.parse(element.dataset.candidates);
const metricNames = JSON.parse(element.dataset.metrics);
const paramNames = JSON.parse(element.dataset.params);
+ const pagination = convertObjectPropsToCamelCase(JSON.parse(element.dataset.pagination));
// eslint-disable-next-line no-new
new Vue({
@@ -21,6 +23,7 @@ const initShowExperiment = () => {
candidates,
metricNames,
paramNames,
+ pagination,
},
render(h) {
return h(MlExperiment);
diff --git a/app/assets/javascripts/projects/commits/index.js b/app/assets/javascripts/projects/commits/index.js
index 53169f689c9..f56884f605f 100644
--- a/app/assets/javascripts/projects/commits/index.js
+++ b/app/assets/javascripts/projects/commits/index.js
@@ -33,20 +33,31 @@ export const initCommitsRefSwitcher = () => {
if (!el) return false;
- const { projectId, ref, commitsPath } = el.dataset;
+ const { projectId, ref, commitsPath, refType } = el.dataset;
const commitsPathPrefix = commitsPath.match(COMMITS_PATH_REGEX)?.[0];
-
+ const useSymbolicRefNames = Boolean(refType);
return new Vue({
el,
render(createElement) {
return createElement(RefSelector, {
props: {
projectId,
- value: ref,
+ value: useSymbolicRefNames ? `refs/${refType}/${ref}` : ref,
+ useSymbolicRefNames,
+ refType,
},
on: {
input(selected) {
- visitUrl(`${commitsPathPrefix}/${selected}`);
+ if (useSymbolicRefNames) {
+ const matches = selected.match(/refs\/(heads|tags)\/(.+)/);
+ if (matches) {
+ visitUrl(`${commitsPathPrefix}/${matches[2]}?ref_type=${matches[1]}`);
+ } else {
+ visitUrl(`${commitsPathPrefix}/${selected}`);
+ }
+ } else {
+ visitUrl(`${commitsPathPrefix}/${selected}`);
+ }
},
},
});
diff --git a/app/assets/javascripts/ref/components/ref_results_section.vue b/app/assets/javascripts/ref/components/ref_results_section.vue
index 4fa2a92ff03..52d1ed96b21 100644
--- a/app/assets/javascripts/ref/components/ref_results_section.vue
+++ b/app/assets/javascripts/ref/components/ref_results_section.vue
@@ -74,6 +74,11 @@ export default {
required: false,
default: '',
},
+ shouldShowCheck: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
},
computed: {
totalCountText() {
@@ -82,6 +87,9 @@ export default {
},
methods: {
showCheck(item) {
+ if (!this.shouldShowCheck) {
+ return false;
+ }
return item.name === this.selectedRef || item.value === this.selectedRef;
},
},
diff --git a/app/assets/javascripts/ref/components/ref_selector.vue b/app/assets/javascripts/ref/components/ref_selector.vue
index b75958e2ced..10967fb84ed 100644
--- a/app/assets/javascripts/ref/components/ref_selector.vue
+++ b/app/assets/javascripts/ref/components/ref_selector.vue
@@ -15,6 +15,8 @@ import {
REF_TYPE_BRANCHES,
REF_TYPE_TAGS,
REF_TYPE_COMMITS,
+ BRANCH_REF_TYPE,
+ TAG_REF_TYPE,
} from '../constants';
import createStore from '../stores';
import RefResultsSection from './ref_results_section.vue';
@@ -50,6 +52,11 @@ export default {
required: false,
default: '',
},
+ refType: {
+ type: String,
+ required: false,
+ default: null,
+ },
projectId: {
type: String,
required: true,
@@ -146,6 +153,12 @@ export default {
buttonText() {
return this.selectedRefForDisplay || this.i18n.noRefSelected;
},
+ isTagRefType() {
+ return this.refType === TAG_REF_TYPE;
+ },
+ isBranchRefType() {
+ return this.refType === BRANCH_REF_TYPE;
+ },
},
watch: {
// Keep the Vuex store synchronized if the parent
@@ -273,6 +286,7 @@ export default {
:show-header="showSectionHeaders"
data-testid="branches-section"
data-qa-selector="branches_section"
+ :should-show-check="!useSymbolicRefNames || isBranchRefType"
@selected="selectRef($event)"
/>
@@ -289,6 +303,7 @@ export default {
:error-message="i18n.tagsErrorMessage"
:show-header="showSectionHeaders"
data-testid="tags-section"
+ :should-show-check="!useSymbolicRefNames || isTagRefType"
@selected="selectRef($event)"
/>
diff --git a/app/assets/javascripts/ref/constants.js b/app/assets/javascripts/ref/constants.js
index 397e3ed2ac8..f4faa535166 100644
--- a/app/assets/javascripts/ref/constants.js
+++ b/app/assets/javascripts/ref/constants.js
@@ -5,6 +5,8 @@ export const REF_TYPE_BRANCHES = 'REF_TYPE_BRANCHES';
export const REF_TYPE_TAGS = 'REF_TYPE_TAGS';
export const REF_TYPE_COMMITS = 'REF_TYPE_COMMITS';
export const ALL_REF_TYPES = Object.freeze([REF_TYPE_BRANCHES, REF_TYPE_TAGS, REF_TYPE_COMMITS]);
+export const BRANCH_REF_TYPE = 'heads';
+export const TAG_REF_TYPE = 'tags';
export const X_TOTAL_HEADER = 'x-total';
diff --git a/app/assets/javascripts/vue_shared/components/pagination/table_pagination.vue b/app/assets/javascripts/vue_shared/components/pagination/table_pagination.vue
index 5f2a66ee0b7..e1f042f78ab 100644
--- a/app/assets/javascripts/vue_shared/components/pagination/table_pagination.vue
+++ b/app/assets/javascripts/vue_shared/components/pagination/table_pagination.vue
@@ -64,8 +64,9 @@ export default {
<template>
<gl-pagination
v-if="showPagination"
- class="justify-content-center gl-mt-3"
+ class="gl-mt-3"
v-bind="$attrs"
+ align="center"
:value="pageInfo.page"
:per-page="pageInfo.perPage"
:total-items="pageInfo.total"
diff --git a/app/controllers/projects/ml/experiments_controller.rb b/app/controllers/projects/ml/experiments_controller.rb
index c0e4783602d..575ca23f815 100644
--- a/app/controllers/projects/ml/experiments_controller.rb
+++ b/app/controllers/projects/ml/experiments_controller.rb
@@ -7,10 +7,11 @@ module Projects
feature_category :mlops
- MAX_PER_PAGE = 20
+ MAX_EXPERIMENTS_PER_PAGE = 20
+ MAX_CANDIDATES_PER_PAGE = 30
def index
- @experiments = ::Ml::Experiment.by_project_id(@project.id).page(params[:page]).per(MAX_PER_PAGE)
+ @experiments = ::Ml::Experiment.by_project_id(@project.id).page(params[:page]).per(MAX_EXPERIMENTS_PER_PAGE)
end
def show
@@ -18,7 +19,25 @@ module Projects
return redirect_to project_ml_experiments_path(@project) unless @experiment.present?
- @candidates = @experiment.candidates&.including_metrics_and_params
+ page = params[:page].to_i
+ page = 1 if page == 0
+
+ @candidates = @experiment.candidates
+ .including_metrics_and_params
+ .page(page)
+ .per(MAX_CANDIDATES_PER_PAGE)
+
+ return unless @candidates
+
+ return redirect_to(url_for(page: @candidates.total_pages)) if @candidates.out_of_range?
+
+ @pagination = {
+ page: page,
+ is_last_page: @candidates.last_page?,
+ per_page: MAX_CANDIDATES_PER_PAGE,
+ total_items: @candidates.total_count
+ }
+
@candidates.each(&:artifact_lazy)
end
diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml
index c129d978e7e..8f802792e6a 100644
--- a/app/views/projects/commits/show.html.haml
+++ b/app/views/projects/commits/show.html.haml
@@ -10,7 +10,7 @@
.nav-block
.tree-ref-container
.tree-ref-holder
- #js-project-commits-ref-switcher{ data: { "project-id" => @project.id, "ref" => @ref, "commits_path": project_commits_path(@project) } }
+ #js-project-commits-ref-switcher{ data: { "project-id" => @project.id, "ref" => @ref, "commits_path": project_commits_path(@project), "ref_type": @ref_type.to_s } }
%ul.breadcrumb.repo-breadcrumb
= commits_breadcrumbs
diff --git a/app/views/projects/ml/experiments/show.html.haml b/app/views/projects/ml/experiments/show.html.haml
index 2c350439762..143981eebe6 100644
--- a/app/views/projects/ml/experiments/show.html.haml
+++ b/app/views/projects/ml/experiments/show.html.haml
@@ -11,4 +11,6 @@
#js-show-ml-experiment{ data: {
candidates: items,
metrics: metrics,
- params: params } }
+ params: params,
+ pagination: @pagination.to_json
+} }
diff --git a/app/views/shared/runners/_runner_type_alert.html.haml b/app/views/shared/runners/_runner_type_alert.html.haml
index 9736780c436..a1599b3ec49 100644
--- a/app/views/shared/runners/_runner_type_alert.html.haml
+++ b/app/views/shared/runners/_runner_type_alert.html.haml
@@ -12,5 +12,5 @@
title: s_('Runners|This runner is associated with specific projects.'),
dismissible: false) do |c|
= c.body do
- = s_('Runners|You can set up a specific runner to be used by multiple projects but you cannot make this a shared runner.')
+ = s_('Runners|You can set up a specific runner to be used by multiple projects but you cannot make this a shared or group runner.')
= link_to _('Learn more.'), help_page_path('ci/runners/runners_scope', anchor: 'specific-runners'), target: '_blank', rel: 'noopener noreferrer'
diff --git a/config/feature_flags/development/file_line_blame.yml b/config/feature_flags/development/file_line_blame.yml
index 1be6aeee40a..f763d436041 100644
--- a/config/feature_flags/development/file_line_blame.yml
+++ b/config/feature_flags/development/file_line_blame.yml
@@ -1,7 +1,7 @@
---
name: file_line_blame
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92538
-rollout_issue_url:
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/370818
milestone: '15.3'
type: development
group: group::source code
diff --git a/config/feature_flags/development/vue_group_select.yml b/config/feature_flags/development/vue_group_select.yml
deleted file mode 100644
index d31f57a3eb9..00000000000
--- a/config/feature_flags/development/vue_group_select.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: vue_group_select
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/98597
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/381042
-milestone: '15.6'
-type: development
-group: group::foundations
-default_enabled: false
diff --git a/doc/administration/geo/index.md b/doc/administration/geo/index.md
index daafda890ed..8728ab57bba 100644
--- a/doc/administration/geo/index.md
+++ b/doc/administration/geo/index.md
@@ -165,15 +165,6 @@ To update the internal URL of the primary Geo site:
1. Select **Edit** on the primary site.
1. Change the **Internal URL**, then select **Save changes**.
-### LDAP
-
-We recommend that if you use LDAP on your **primary** site, you also set up secondary LDAP servers on each **secondary** site. Otherwise, users are unable to perform Git operations over HTTP(s) on the **secondary** site using HTTP Basic Authentication. However, Git via SSH and personal access tokens still works.
-
-NOTE:
-It is possible for all **secondary** sites to share an LDAP server, but additional latency can be an issue. Also, consider what LDAP server is available in a [disaster recovery](disaster_recovery/index.md) scenario if a **secondary** site is promoted to be a **primary** site.
-
-Check for instructions on how to set up replication in your LDAP service. Instructions are different depending on the software or service used. For example, OpenLDAP provides [these instructions](https://www.openldap.org/doc/admin24/replication.html).
-
### Geo Tracking Database
The tracking database instance is used as metadata to control what needs to be updated on the disk of the local instance. For example:
@@ -296,6 +287,14 @@ For more information on how to replicate the Container Registry, see [Container
For more information on using Geo proxying on secondary sites, see [Geo proxying for secondary sites](secondary_proxy/index.md).
+### Single Sign On (SSO)
+
+For more information on configuring Single Sign-On (SSO), see [Geo with Single Sign-On (SSO)](replication/single_sign_on.md).
+
+#### LDAP
+
+For more information on configuring LDAP, see [Geo with Single Sign-On (SSO) > LDAP](replication/single_sign_on.md#ldap).
+
### Security Review
For more information on Geo security, see [Geo security review](replication/security_review.md).
diff --git a/doc/administration/geo/replication/single_sign_on.md b/doc/administration/geo/replication/single_sign_on.md
new file mode 100644
index 00000000000..fc2f23552db
--- /dev/null
+++ b/doc/administration/geo/replication/single_sign_on.md
@@ -0,0 +1,122 @@
+---
+stage: Systems
+group: Geo
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+type: howto
+---
+
+# Geo with Single Sign On (SSO) **(PREMIUM SELF)**
+
+This documentation only discusses Geo-specific SSO considerations and configuration. For more information on general authentication, see [GitLab authentication and authorization](../../auth/index.md).
+
+## Configuring instance-wide SAML
+
+### Prerequisites
+
+[Instance-wide SAML](../../../integration/saml.md) must be working on your primary Geo site.
+
+You only configure SAML on the primary site. Configuring `gitlab_rails['omniauth_providers']` in `gitlab.rb` in a secondary site has no effect.
+
+### Determine the type of URL your secondary site uses
+
+How you configure instance-wide SAML differs depending on your secondary site configuration. Determine if your secondary site uses a:
+
+- [Unified URL](../secondary_proxy/index.md#set-up-a-unified-url-for-geo-sites), meaning the `external_url` exactly matches the `external_url` of the primary site.
+- [Separate URL](../secondary_proxy/index.md#geo-proxying-with-separate-urls) with proxying enabled. Proxying is enabled by default in GitLab 15.1 and later.
+- [Separate URL](../secondary_proxy/index.md#geo-proxying-with-separate-urls) with proxying disabled.
+
+### SAML with Unified URL
+
+If you have configured SAML on the primary site correctly, then it should work on the secondary site without additional configuration.
+
+### SAML with separate URL with proxying enabled
+
+If a secondary site uses a different `external_url` to the primary site, then configure your SAML Identity Provider (IdP) to allow the secondary site's SAML callback URL. For example, to configure Okta:
+
+1. [Sign in to Okta](https://www.okta.com/login/).
+1. Go to **Okta Admin Dashboard** > **Applications** > **Your App Name** > **General**.
+1. In **SAML Settings**, select **Edit**.
+1. In **General Settings**, select **Next** to go to **SAML Settings**.
+1. In **SAML Settings > General**, make sure the **Single sign-on URL** is your primary site's SAML callback URL. For example, `https://gitlab-primary.example.com/users/auth/saml/callback`. If it is not, enter your primary site's SAML callback URL into this field.
+1. Select **Show Advanced Settings**.
+1. In **Other Requestable SSO URLs**, enter your secondary site's SAML callback URL. For example, `https://gitlab-secondary.example.com/users/auth/saml/callback`. You can set **Index** to anything.
+1. Select **Next** and then **Finish**.
+
+You must not specify `assertion_consumer_service_url` in the SAML provider configuration in `gitlab_rails['omniauth_providers']` in `gitlab.rb` of the primary site. For example:
+
+```ruby
+gitlab_rails['omniauth_providers'] = [
+ {
+ name: "saml",
+ label: "Okta", # optional label for login button, defaults to "Saml"
+ args: {
+ idp_cert_fingerprint: "B5:AD:AA:9E:3C:05:68:AD:3B:78:ED:31:99:96:96:43:9E:6D:79:96",
+ idp_sso_target_url: "https://<dev-account>.okta.com/app/dev-account_gitlabprimary_1/exk7k2gft2VFpVFXa5d1/sso/saml",
+ issuer: "https://<gitlab-primary>",
+ name_identifier_format: "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"
+ }
+ }
+]
+```
+
+This configuration causes:
+
+- Both your sites to use `/users/auth/saml/callback` as their assertion consumer service (ACS) URL.
+- The URL's host to be set to the corresponding site's host.
+
+You can check this by visiting each site's `/users/auth/saml/metadata` path. For example, visiting `https://gitlab-primary.example.com/users/auth/saml/metadata` may respond with:
+
+```xml
+<md:EntityDescriptor ID="_b9e00d84-d34e-4e3d-95de-122e3c361617" entityID="https://gitlab-primary.example.com"
+ xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
+ xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
+ <md:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
+ <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat>
+ <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://gitlab-primary.example.com/users/auth/saml/callback" index="0" isDefault="true"/>
+ <md:AttributeConsumingService index="1" isDefault="true">
+ <md:ServiceName xml:lang="en">Required attributes</md:ServiceName>
+ <md:RequestedAttribute FriendlyName="Email address" Name="email" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" isRequired="false"/>
+ <md:RequestedAttribute FriendlyName="Full name" Name="name" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" isRequired="false"/>
+ <md:RequestedAttribute FriendlyName="Given name" Name="first_name" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" isRequired="false"/>
+ <md:RequestedAttribute FriendlyName="Family name" Name="last_name" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" isRequired="false"/>
+ </md:AttributeConsumingService>
+ </md:SPSSODescriptor>
+</md:EntityDescriptor>
+```
+
+Visiting `https://gitlab-secondary.example.com/users/auth/saml/metadata` may respond with:
+
+```xml
+<md:EntityDescriptor ID="_bf71eb57-7490-4024-bfe2-54cec716d4bf" entityID="https://gitlab-primary.example.com"
+ xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
+ xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
+ <md:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
+ <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat>
+ <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://gitlab-secondary.example.com/users/auth/saml/callback" index="0" isDefault="true"/>
+ <md:AttributeConsumingService index="1" isDefault="true">
+ <md:ServiceName xml:lang="en">Required attributes</md:ServiceName>
+ <md:RequestedAttribute FriendlyName="Email address" Name="email" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" isRequired="false"/>
+ <md:RequestedAttribute FriendlyName="Full name" Name="name" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" isRequired="false"/>
+ <md:RequestedAttribute FriendlyName="Given name" Name="first_name" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" isRequired="false"/>
+ <md:RequestedAttribute FriendlyName="Family name" Name="last_name" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" isRequired="false"/>
+ </md:AttributeConsumingService>
+ </md:SPSSODescriptor>
+</md:EntityDescriptor>
+```
+
+The `Location` attribute of the `md:AssertionConsumerService` field points to `gitlab-secondary.example.com`.
+
+After configuring your SAML IdP to allow the secondary site's SAML callback URL, you should be able to sign in with SAML on your primary site as well as your secondary site.
+
+### SAML with separate URL with proxying disabled
+
+If you have configured SAML on the primary site correctly, then it should work on the secondary site without additional configuration.
+
+## LDAP
+
+If you use LDAP on your **primary** site, you should also set up secondary LDAP servers on each **secondary** site. Otherwise, users cannot perform Git operations over HTTP(s) on the **secondary** site using HTTP basic authentication. However, users can still use Git with SSH and personal access tokens.
+
+NOTE:
+It is possible for all **secondary** sites to share an LDAP server, but additional latency can be an issue. Also, consider what LDAP server is available in a [disaster recovery](../disaster_recovery/index.md) scenario if a **secondary** site is promoted to be a **primary** site.
+
+Check your LDAP service documentation for instructions on how to set up replication in your LDAP service. The process differs depending on the software or service used. For example, OpenLDAP provides this [replication documentation](https://www.openldap.org/doc/admin24/replication.html).
diff --git a/doc/administration/geo/replication/troubleshooting.md b/doc/administration/geo/replication/troubleshooting.md
index f6bb7491ac0..4c9ad1c8bc5 100644
--- a/doc/administration/geo/replication/troubleshooting.md
+++ b/doc/administration/geo/replication/troubleshooting.md
@@ -1100,6 +1100,10 @@ On the **primary** site:
1. Ensure the **URL** field matches the value found in `/etc/gitlab/gitlab.rb`
in `external_url "https://gitlab.example.com"` on the **Rails nodes of the secondary** site.
+### Authenticating with SAML on the secondary site always lands on the primary site
+
+This [problem is usually encountered when upgrading to GitLab 15.1](version_specific_upgrades.md#upgrading-to-151). To fix this problem, see [configuring instance-wide SAML in Geo with Single Sign-On](single_sign_on.md#configuring-instance-wide-saml).
+
## Fixing common errors
This section documents common error messages reported in the Admin Area on the web interface, and how to fix them.
diff --git a/doc/administration/geo/replication/version_specific_upgrades.md b/doc/administration/geo/replication/version_specific_upgrades.md
index d981656f748..b18317cbf5a 100644
--- a/doc/administration/geo/replication/version_specific_upgrades.md
+++ b/doc/administration/geo/replication/version_specific_upgrades.md
@@ -14,7 +14,7 @@ for upgrading Geo sites.
[Geo proxying](../secondary_proxy/index.md) was [enabled by default for different URLs](https://gitlab.com/gitlab-org/gitlab/-/issues/346112) in 15.1. This may be a breaking change. If needed, you may [disable Geo proxying](../secondary_proxy/index.md#disable-geo-proxying).
-If you are using SAML with different URLs, there is a [known issue which requires proxying to be disabled](https://gitlab.com/gitlab-org/gitlab/-/issues/377372).
+If you are using SAML with different URLs, you must modify your SAML configuration and your Identity Provider configuration. For more information, see the [Geo with Single Sign-On (SSO) documentation](single_sign_on.md).
## Upgrading to 14.9
diff --git a/doc/api/linked_epics.md b/doc/api/linked_epics.md
index 77540f37054..c240b3255c6 100644
--- a/doc/api/linked_epics.md
+++ b/doc/api/linked_epics.md
@@ -89,10 +89,10 @@ Example response:
## Create a related epic link
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352840) in GitLab 14.10.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352840) in GitLab 14.10.
+> - Minimum required role for the group [changed](https://gitlab.com/gitlab-org/gitlab/-/issues/381308) from Reporter to Guest in GitLab 15.8.
-Create a two-way relation between two epics. The user must be allowed to
-update both epics to succeed.
+Create a two-way relation between two epics. The user must have at least the Guest role for both groups.
```plaintext
POST /groups/:id/epics/:epic_iid/related_epics
@@ -208,10 +208,10 @@ Example response:
## Delete a related epic link
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352840) in GitLab 14.10.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352840) in GitLab 14.10.
+> - Minimum required role for the group [changed](https://gitlab.com/gitlab-org/gitlab/-/issues/381308) from Reporter to Guest in GitLab 15.8.
-Delete a two-way relation between two epics. The user must be allowed to
-update both epics to succeed.
+Delete a two-way relation between two epics. The user must have at least the Guest role for both groups.
```plaintext
DELETE /groups/:id/epics/:epic_iid/related_epics/:related_epic_link_id
diff --git a/doc/ci/testing/code_quality.md b/doc/ci/testing/code_quality.md
index 8e1c3d72d3d..6b0c6aa4567 100644
--- a/doc/ci/testing/code_quality.md
+++ b/doc/ci/testing/code_quality.md
@@ -687,3 +687,13 @@ For example:
variables:
TIMEOUT_SECONDS: 3600
```
+
+### Using Code Quality with Kubernetes CI executor
+
+Code Quality requires a Docker in Docker setup to work. The Kubernetes executor already [has support for this](https://docs.gitlab.com/runner/executors/kubernetes.md#using-dockerdind).
+
+To ensure Code Quality jobs can run on a Kubernetes executor:
+
+- If you're using TLS to communicate with the Docker daemon, the executor [must be running in privileged mode](https://docs.gitlab.com/runner/executors/kubernetes.html#other-configtoml-settings). Additionally, the certificate directory must be [specified as a volume mount](../docker/using_docker_build.md#docker-in-docker-with-tls-enabled-in-kubernetes).
+- It is possible that the DinD service doesn't start up fully before the Code Quality job starts. This is a limitation documented in
+the [Kubernetes executor for GitLab Runner](https://docs.gitlab.com/runner/executors/kubernetes.html#docker-cannot-connect-to-the-docker-daemon-at-tcpdocker2375-is-the-docker-daemon-running) troubleshooting section.
diff --git a/doc/development/pipelines/index.md b/doc/development/pipelines/index.md
index a46e662827b..105fb7b55bf 100644
--- a/doc/development/pipelines/index.md
+++ b/doc/development/pipelines/index.md
@@ -432,17 +432,21 @@ running every day, updating cache.
The default CI/CD configuration file is also set at `jh/.gitlab-ci.yml` so it
runs exactly like [GitLab JH](https://jihulab.com/gitlab-cn/gitlab/-/blob/main-jh/jh/.gitlab-ci.yml).
-## Ruby 3.0 jobs
+## Ruby 2.7 jobs
-You can add the `pipeline:run-in-ruby3` label to the merge request to switch
-the Ruby version used for running the whole test suite to 3.0. When you do
-this, the test suite will no longer run in Ruby 2.7 (default), and an
-additional job `verify-ruby-2.7` will also run and always fail to remind us to
-remove the label and run in Ruby 2.7 before merging the merge request.
+We're running Ruby 3.0 for the merge requests and the default branch. However,
+we're still running Ruby 2.7 for GitLab.com and there are older versions that
+we need to maintain. We need a way to still try out Ruby 2.7 in merge requests.
+
+You can add the `pipeline:run-in-ruby2` label to the merge request to switch
+the Ruby version used for running the whole test suite to 2.7. When you do
+this, the test suite will no longer run in Ruby 3.0 (default), and an
+additional job `verify-ruby-3.0` will also run and always fail to remind us to
+remove the label and run in Ruby 3.0 before merging the merge request.
This should let us:
-- Test changes for Ruby 3.0
+- Test changes for Ruby 2.7
- Make sure it will not break anything when it's merged into the default branch
## `undercover` RSpec test
@@ -473,9 +477,9 @@ If these commands return `undercover: ✅ No coverage is missing in latest chang
## Ruby versions testing
-Our test suite runs against Ruby 2 in merge requests and default branch pipelines.
+Our test suite runs against Ruby 3 in merge requests and default branch pipelines.
-We also run our test suite against Ruby 3 on another 2-hourly scheduled pipelines, as GitLab.com will soon run on Ruby 3.
+We also run our test suite against Ruby 2.7 on another 2-hourly scheduled pipelines, as GitLab.com still runs on Ruby 2.7.
## PostgreSQL versions testing
@@ -490,26 +494,26 @@ We also run our test suite against PG11 upon specific database library changes i
| Where? | PostgreSQL version | Ruby version |
|------------------------------------------------------------------------------------------------|-------------------------------------------------|--------------|
-| Merge requests | 12 (default version), 11 for DB library changes | 2.7 (default version) |
-| `master` branch commits | 12 (default version), 11 for DB library changes | 2.7 (default version) |
-| `maintenance` scheduled pipelines for the `master` branch (every even-numbered hour) | 12 (default version), 11 for DB library changes | 2.7 (default version) |
-| `maintenance` scheduled pipelines for the `ruby3` branch (every odd-numbered hour), see below. | 12 (default version), 11 for DB library changes | 3.0 (coded in the branch) |
-| `nightly` scheduled pipelines for the `master` branch | 12 (default version), 11, 13 | 2.7 (default version) |
-
-There are 2 pipeline schedules used for testing Ruby 3. One is triggering a
-pipeline in `ruby3-sync` branch, which updates the `ruby3` branch with latest
+| Merge requests | 12 (default version), 11 for DB library changes | 3.0 (default version) |
+| `master` branch commits | 12 (default version), 11 for DB library changes | 3.0 (default version) |
+| `maintenance` scheduled pipelines for the `master` branch (every even-numbered hour) | 12 (default version), 11 for DB library changes | 3.0 (default version) |
+| `maintenance` scheduled pipelines for the `ruby2` branch (every odd-numbered hour), see below. | 12 (default version), 11 for DB library changes | 2.7 |
+| `nightly` scheduled pipelines for the `master` branch | 12 (default version), 11, 13 | 3.0 (default version) |
+
+There are 2 pipeline schedules used for testing Ruby 2.7. One is triggering a
+pipeline in `ruby2-sync` branch, which updates the `ruby2` branch with latest
`master`, and no pipelines will be triggered by this push. The other schedule
-is triggering a pipeline in `ruby3` 5 minutes after it, which is considered
+is triggering a pipeline in `ruby2` 5 minutes after it, which is considered
the maintenance schedule to run test suites and update cache.
-Any changes in `ruby3` are only for running the pipeline. It should
-never be merged back to `master`. Any other Ruby 3 changes should go into
-`master` directly, which should be compatible with Ruby 2.7.
+Any changes in `ruby2` are only for running the pipeline. It should
+never be merged back to `master`. Any other Ruby 2.7 changes should go into
+`master` directly, which should be compatible with Ruby 3.
-Previously, `ruby3-sync` was using a project token stored in `RUBY3_SYNC_TOKEN`
-(now backed up in `RUBY3_SYNC_TOKEN_NOT_USED`), however due to various
+Previously, `ruby2-sync` was using a project token stored in `RUBY2_SYNC_TOKEN`
+(now backed up in `RUBY2_SYNC_TOKEN_NOT_USED`), however due to various
permissions issues, we ended up using an access token from `gitlab-bot` so now
-`RUBY3_SYNC_TOKEN` is actually an access token from `gitlab-bot`.
+`RUBY2_SYNC_TOKEN` is actually an access token from `gitlab-bot`.
### Long-term plan
diff --git a/doc/subscriptions/self_managed/index.md b/doc/subscriptions/self_managed/index.md
index 31684398c4e..e5114b092da 100644
--- a/doc/subscriptions/self_managed/index.md
+++ b/doc/subscriptions/self_managed/index.md
@@ -136,34 +136,32 @@ GitLab has several features which can help you manage the number of users:
users manually.
- View a breakdown of users by role in the [Users statistics](../../user/admin_area/index.md#users-statistics) page.
-## Sync your subscription data with GitLab
+## Subscription data synchronization
> Introduced in GitLab 14.1.
-Prerequisites:
+Subscription data can be automatically synchronized between your self-managed instance and GitLab.
+To enable subscription data synchronization you must have:
-- You must be running GitLab Enterprise Edition (EE).
-- You must have GitLab 14.1 or later.
-- Your instance must be connected to the internet, and not be in an offline environment.
+- GitLab Enterprise Edition (EE), version 14.1 or later.
+- Connection to the internet, and must not have an offline environment.
+- [Activated](../../user/admin_area/license.md) your instance with an activation code.
-To sync subscription data between your self-managed instance and GitLab, you must [activate your instance](../../user/admin_area/license.md) with an
-activation code.
-
-After you activate your instance, the following processes are automated:
+When your instance is activated, and data is synchronized, the following processes are automated:
- [Quarterly subscription reconciliation](../quarterly_reconciliation.md).
- Subscription renewals.
- Subscription updates, such as adding more seats or upgrading a GitLab tier.
-At approximately 03:00 UTC, a daily sync job sends subscription data to the Customers Portal. For this reason, updates and renewals may not
-apply immediately.
+At approximately 03:00 UTC, a daily synchronization job sends subscription data to the Customers
+Portal. For this reason, updates and renewals may not apply immediately.
-The data is sent securely through an encrypted HTTPS connection to `customers.gitlab.com` on port `443`.
-If the job fails, it retries up to 12 times over approximately 17 hours.
+The data is sent securely through an encrypted HTTPS connection to `customers.gitlab.com` on port
+`443`. If the job fails, it retries up to 12 times over approximately 17 hours.
-### Subscription data that GitLab receives
+### Subscription data
-The daily sync job sends **only** the following information to the Customers Portal:
+The daily synchronization job sends **only** the following information to the Customers Portal:
- Date
- Timestamp
@@ -228,14 +226,9 @@ Example of a license sync request:
}
```
-### Troubleshoot automatic subscription sync
-
-If the sync job is not working, ensure you allow network traffic from your GitLab instance
-to IP addresses `172.64.146.11:443` and `104.18.41.245:443` (`customers.gitlab.com`).
-
-## Manually sync your subscription details
+### Manually synchronize your subscription details
-You can manually sync your subscription details at any time.
+You can manually synchronize your subscription details at any time.
1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Subscription**.
@@ -407,7 +400,7 @@ When a subscription is set to auto-renew, it renews automatically on the
expiration date without a gap in available service. Subscriptions purchased through Customers Portal are set to auto-renew by default.
The number of user licenses is adjusted to fit the [number of billable users in your instance](#view-user-totals) at the time of renewal.
Before auto-renewal you should [prepare for the renewal](#prepare-for-renewal-by-reviewing-your-account). To auto-renew your subscription,
-you must have enabled the [synchronization of subscription data](#sync-your-subscription-data-with-gitlab).
+you must have enabled the [synchronization of subscription data](#subscription-data-synchronization).
You can view and download your renewal invoice on the Customers Portal
[View invoices](https://customers.gitlab.com/receipts) page. If your account has a [saved credit card](../index.md#change-your-payment-method), the card is charged for the invoice amount. If we are unable to process a payment or the auto-renewal fails for any other reason, you have 14 days to renew your subscription, after which your GitLab tier is downgraded.
@@ -497,6 +490,11 @@ and for communicating directly with the relevant GitLab team members.
## Troubleshooting
+### Subscription data fails to synchronize
+
+If the synchronization job is not working, ensure you allow network traffic from your GitLab
+instance to IP addresses `172.64.146.11:443` and `104.18.41.245:443` (`customers.gitlab.com`).
+
### Credit card declined
If your credit card is declined when purchasing a GitLab subscription, possible reasons include:
diff --git a/doc/user/application_security/sast/analyzers.md b/doc/user/application_security/sast/analyzers.md
index e83825636bf..efbbf447845 100644
--- a/doc/user/application_security/sast/analyzers.md
+++ b/doc/user/application_security/sast/analyzers.md
@@ -73,7 +73,7 @@ GitLab maintains the analyzer and writes detection rules for it.
If you use the [GitLab-managed CI/CD template](index.md#configuration), the Semgrep-based analyzer operates alongside other language-specific analyzers.
It runs with GitLab-managed detection rules that mimic the other analyzers' detection rules.
-Work to remove language-specific analyzers and replace them with the Semgrep-based analyzer is tracked in [this epic](https://gitlab.com/groups/gitlab-org/-/epics/5245).
+Work to remove language-specific analyzers and replace them with the Semgrep-based analyzer is tracked in [epic 5245](https://gitlab.com/groups/gitlab-org/-/epics/5245). In case of duplicate findings, the [analyzer order](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/reports/security/scanner.rb#L15) determines which analyzer's findings are preferred.
You can choose to disable the other analyzers early and use Semgrep-based scanning for supported languages before the default behavior changes. If you do so:
diff --git a/doc/user/group/epics/linked_epics.md b/doc/user/group/epics/linked_epics.md
index 4049ac2e9a1..63bf1a4471c 100644
--- a/doc/user/group/epics/linked_epics.md
+++ b/doc/user/group/epics/linked_epics.md
@@ -20,9 +20,11 @@ To manage linked epics through our API, visit the [epic links API documentation]
## Add a linked epic
+> Minimum required role for the group [changed](https://gitlab.com/gitlab-org/gitlab/-/issues/381308) from Reporter to Guest in GitLab 15.8.
+
Prerequisites:
-- You must have at least the Reporter role for both groups.
+- You must have at least the Guest role for both groups.
- For GitLab SaaS: the epic that you're editing must be in a group on GitLab Ultimate.
The epics you're linking can be in a group on a lower tier.
@@ -59,9 +61,11 @@ The linked epics are then displayed on the epic grouped by relationship.
## Remove a linked epic
+> Minimum required role for the group [changed](https://gitlab.com/gitlab-org/gitlab/-/issues/381308) from Reporter to Guest in GitLab 15.8.
+
Prerequisites:
-- You must have at least the Reporter role for the epic's group.
+- You must have at least the Guest role for the epic's group.
To remove a linked epic, in the **Linked epics** section of an epic,
select **Remove** (**{close}**) next to
diff --git a/doc/user/read_only_namespaces.md b/doc/user/read_only_namespaces.md
index b11aac9b00c..345a3a87189 100644
--- a/doc/user/read_only_namespaces.md
+++ b/doc/user/read_only_namespaces.md
@@ -2,7 +2,6 @@
stage: Growth
group: Acquisition
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
-noindex: true
---
# Read-only namespaces **(FREE SAAS)**
diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb
index 5aad6f60d44..ceef072a710 100644
--- a/lib/gitlab/gon_helper.rb
+++ b/lib/gitlab/gon_helper.rb
@@ -68,7 +68,6 @@ module Gitlab
push_frontend_feature_flag(:source_editor_toolbar)
push_frontend_feature_flag(:vscode_web_ide, current_user)
push_frontend_feature_flag(:integration_slack_app_notifications)
- push_frontend_feature_flag(:vue_group_select)
push_frontend_feature_flag(:new_fonts, current_user)
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 83e8fcc3af0..9fc32c15371 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -36471,7 +36471,7 @@ msgstr ""
msgid "Runners|Yes, start deleting stale runners"
msgstr ""
-msgid "Runners|You can set up a specific runner to be used by multiple projects but you cannot make this a shared runner."
+msgid "Runners|You can set up a specific runner to be used by multiple projects but you cannot make this a shared or group runner."
msgstr ""
msgid "Runners|You have used %{quotaUsed} out of %{quotaLimit} of your shared Runners pipeline minutes."
diff --git a/qa/Dockerfile b/qa/Dockerfile
index 71fc615ac13..8e79d0a7bad 100644
--- a/qa/Dockerfile
+++ b/qa/Dockerfile
@@ -1,7 +1,7 @@
ARG DOCKER_VERSION=20.10.14
ARG CHROME_VERSION=106
ARG QA_BUILD_TARGET=ee
-ARG RUBY_VERSION=2.7
+ARG RUBY_VERSION=3.0
FROM registry.gitlab.com/gitlab-org/gitlab-build-images/debian-bullseye-ruby-${RUBY_VERSION}:bundler-2.3-git-2.36-lfs-2.9-chrome-${CHROME_VERSION}-docker-${DOCKER_VERSION}-gcloud-383-kubectl-1.23 AS foss
LABEL maintainer="GitLab Quality Department <quality@gitlab.com>"
diff --git a/spec/frontend/ml/experiment_tracking/components/__snapshots__/ml_experiment_spec.js.snap b/spec/frontend/ml/experiment_tracking/components/__snapshots__/ml_experiment_spec.js.snap
index e253a0afc6c..a23dc97b7ed 100644
--- a/spec/frontend/ml/experiment_tracking/components/__snapshots__/ml_experiment_spec.js.snap
+++ b/spec/frontend/ml/experiment_tracking/components/__snapshots__/ml_experiment_spec.js.snap
@@ -96,7 +96,7 @@ exports[`MlExperiment with candidates renders correctly 1`] = `
<table
aria-busy="false"
aria-colcount="6"
- class="table b-table gl-table gl-mt-0!"
+ class="table b-table gl-table gl-mt-0! table-sm"
role="table"
>
<!---->
@@ -275,10 +275,150 @@ exports[`MlExperiment with candidates renders correctly 1`] = `
role="cell"
/>
</tr>
+ <tr
+ class=""
+ role="row"
+ >
+ <td
+ aria-colindex="1"
+ class=""
+ role="cell"
+ >
+ 0.5
+ </td>
+ <td
+ aria-colindex="2"
+ class=""
+ role="cell"
+ />
+ <td
+ aria-colindex="3"
+ class=""
+ role="cell"
+ >
+ 0.3
+ </td>
+ <td
+ aria-colindex="4"
+ class=""
+ role="cell"
+ />
+ <td
+ aria-colindex="5"
+ class=""
+ role="cell"
+ >
+ <a
+ class="gl-link"
+ href="link_to_candidate3"
+ >
+ Details
+ </a>
+ </td>
+ <td
+ aria-colindex="6"
+ class=""
+ role="cell"
+ />
+ </tr>
+ <tr
+ class=""
+ role="row"
+ >
+ <td
+ aria-colindex="1"
+ class=""
+ role="cell"
+ >
+ 0.5
+ </td>
+ <td
+ aria-colindex="2"
+ class=""
+ role="cell"
+ />
+ <td
+ aria-colindex="3"
+ class=""
+ role="cell"
+ >
+ 0.3
+ </td>
+ <td
+ aria-colindex="4"
+ class=""
+ role="cell"
+ />
+ <td
+ aria-colindex="5"
+ class=""
+ role="cell"
+ >
+ <a
+ class="gl-link"
+ href="link_to_candidate4"
+ >
+ Details
+ </a>
+ </td>
+ <td
+ aria-colindex="6"
+ class=""
+ role="cell"
+ />
+ </tr>
+ <tr
+ class=""
+ role="row"
+ >
+ <td
+ aria-colindex="1"
+ class=""
+ role="cell"
+ >
+ 0.5
+ </td>
+ <td
+ aria-colindex="2"
+ class=""
+ role="cell"
+ />
+ <td
+ aria-colindex="3"
+ class=""
+ role="cell"
+ >
+ 0.3
+ </td>
+ <td
+ aria-colindex="4"
+ class=""
+ role="cell"
+ />
+ <td
+ aria-colindex="5"
+ class=""
+ role="cell"
+ >
+ <a
+ class="gl-link"
+ href="link_to_candidate5"
+ >
+ Details
+ </a>
+ </td>
+ <td
+ aria-colindex="6"
+ class=""
+ role="cell"
+ />
+ </tr>
<!---->
<!---->
</tbody>
<!---->
</table>
+
+ <!---->
</div>
`;
diff --git a/spec/frontend/ml/experiment_tracking/components/ml_experiment_spec.js b/spec/frontend/ml/experiment_tracking/components/ml_experiment_spec.js
index 50539440f25..c62e8a7beef 100644
--- a/spec/frontend/ml/experiment_tracking/components/ml_experiment_spec.js
+++ b/spec/frontend/ml/experiment_tracking/components/ml_experiment_spec.js
@@ -1,12 +1,19 @@
-import { GlAlert } from '@gitlab/ui';
+import { GlAlert, GlPagination } from '@gitlab/ui';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import MlExperiment from '~/ml/experiment_tracking/components/ml_experiment.vue';
describe('MlExperiment', () => {
let wrapper;
- const createWrapper = (candidates = [], metricNames = [], paramNames = []) => {
- return mountExtended(MlExperiment, { provide: { candidates, metricNames, paramNames } });
+ const createWrapper = (
+ candidates = [],
+ metricNames = [],
+ paramNames = [],
+ pagination = { page: 1, isLastPage: false, per_page: 2, totalItems: 0 },
+ ) => {
+ return mountExtended(MlExperiment, {
+ provide: { candidates, metricNames, paramNames, pagination },
+ });
};
const findAlert = () => wrapper.findComponent(GlAlert);
@@ -25,20 +32,74 @@ describe('MlExperiment', () => {
expect(findEmptyState().exists()).toBe(true);
});
+
+ it('does not show pagination', () => {
+ wrapper = createWrapper();
+
+ expect(wrapper.findComponent(GlPagination).exists()).toBe(false);
+ });
});
describe('with candidates', () => {
- it('renders correctly', () => {
- wrapper = createWrapper(
+ const defaultPagination = { page: 1, isLastPage: false, per_page: 2, totalItems: 5 };
+
+ const createWrapperWithCandidates = (pagination = defaultPagination) => {
+ return createWrapper(
[
{ rmse: 1, l1_ratio: 0.4, details: 'link_to_candidate1', artifact: 'link_to_artifact' },
{ auc: 0.3, l1_ratio: 0.5, details: 'link_to_candidate2' },
+ { auc: 0.3, l1_ratio: 0.5, details: 'link_to_candidate3' },
+ { auc: 0.3, l1_ratio: 0.5, details: 'link_to_candidate4' },
+ { auc: 0.3, l1_ratio: 0.5, details: 'link_to_candidate5' },
],
['rmse', 'auc', 'mae'],
['l1_ratio'],
+ pagination,
);
+ };
+
+ it('renders correctly', () => {
+ wrapper = createWrapperWithCandidates();
expect(wrapper.element).toMatchSnapshot();
});
+
+ describe('Pagination behaviour', () => {
+ it('should show', () => {
+ wrapper = createWrapperWithCandidates();
+
+ expect(wrapper.findComponent(GlPagination).exists()).toBe(true);
+ });
+
+ it('should get the page number from the URL', () => {
+ wrapper = createWrapperWithCandidates({ ...defaultPagination, page: 2 });
+
+ expect(wrapper.findComponent(GlPagination).props().value).toBe(2);
+ });
+
+ it('should not have a prevPage if the page is 1', () => {
+ wrapper = createWrapperWithCandidates();
+
+ expect(wrapper.findComponent(GlPagination).props().prevPage).toBe(null);
+ });
+
+ it('should set the prevPage to 1 if the page is 2', () => {
+ wrapper = createWrapperWithCandidates({ ...defaultPagination, page: 2 });
+
+ expect(wrapper.findComponent(GlPagination).props().prevPage).toBe(1);
+ });
+
+ it('should not have a nextPage if isLastPage is true', async () => {
+ wrapper = createWrapperWithCandidates({ ...defaultPagination, isLastPage: true });
+
+ expect(wrapper.findComponent(GlPagination).props().nextPage).toBe(null);
+ });
+
+ it('should set the nextPage to 2 if the page is 1', () => {
+ wrapper = createWrapperWithCandidates();
+
+ expect(wrapper.findComponent(GlPagination).props().nextPage).toBe(2);
+ });
+ });
});
});
diff --git a/spec/frontend/projects/settings/components/default_branch_selector_spec.js b/spec/frontend/projects/settings/components/default_branch_selector_spec.js
index 94648d87524..bfbf3e234f4 100644
--- a/spec/frontend/projects/settings/components/default_branch_selector_spec.js
+++ b/spec/frontend/projects/settings/components/default_branch_selector_spec.js
@@ -32,6 +32,7 @@ describe('projects/settings/components/default_branch_selector', () => {
value: persistedDefaultBranch,
enabledRefTypes: [REF_TYPE_BRANCHES],
projectId,
+ refType: null,
state: true,
translations: {
dropdownHeader: expect.any(String),
diff --git a/spec/frontend/ref/components/ref_selector_spec.js b/spec/frontend/ref/components/ref_selector_spec.js
index 96601a729b2..4997c13bbb2 100644
--- a/spec/frontend/ref/components/ref_selector_spec.js
+++ b/spec/frontend/ref/components/ref_selector_spec.js
@@ -18,6 +18,8 @@ import {
REF_TYPE_BRANCHES,
REF_TYPE_TAGS,
REF_TYPE_COMMITS,
+ BRANCH_REF_TYPE,
+ TAG_REF_TYPE,
} from '~/ref/constants';
import createStore from '~/ref/stores/';
@@ -34,7 +36,7 @@ describe('Ref selector component', () => {
let commitApiCallSpy;
let requestSpies;
- const createComponent = (mountOverrides = {}) => {
+ const createComponent = (mountOverrides = {}, propsData = {}) => {
wrapper = mount(
RefSelector,
merge(
@@ -42,6 +44,7 @@ describe('Ref selector component', () => {
propsData: {
projectId,
value: '',
+ ...propsData,
},
listeners: {
// simulate a parent component v-model binding
@@ -338,13 +341,14 @@ describe('Ref selector component', () => {
describe('branches', () => {
describe('when the branches search returns results', () => {
beforeEach(() => {
- createComponent();
+ createComponent({}, { refType: BRANCH_REF_TYPE, useSymbolicRefNames: true });
return waitForRequests();
});
it('renders the branches section in the dropdown', () => {
expect(findBranchesSection().exists()).toBe(true);
+ expect(findBranchesSection().props('shouldShowCheck')).toBe(true);
});
it('renders the "Branches" heading with a total number indicator', () => {
@@ -415,13 +419,14 @@ describe('Ref selector component', () => {
describe('tags', () => {
describe('when the tags search returns results', () => {
beforeEach(() => {
- createComponent();
+ createComponent({}, { refType: TAG_REF_TYPE, useSymbolicRefNames: true });
return waitForRequests();
});
it('renders the tags section in the dropdown', () => {
expect(findTagsSection().exists()).toBe(true);
+ expect(findTagsSection().props('shouldShowCheck')).toBe(true);
});
it('renders the "Tags" heading with a total number indicator', () => {
diff --git a/spec/requests/projects/ml/experiments_controller_spec.rb b/spec/requests/projects/ml/experiments_controller_spec.rb
index b336ec60249..e8b6f806251 100644
--- a/spec/requests/projects/ml/experiments_controller_spec.rb
+++ b/spec/requests/projects/ml/experiments_controller_spec.rb
@@ -68,14 +68,46 @@ RSpec.describe Projects::Ml::ExperimentsController, feature_category: :mlops do
describe 'GET show' do
let(:params) { basic_params.merge(id: experiment.iid) }
- before do
+ it 'renders the template' do
show_experiment
- end
- it 'renders the template' do
expect(response).to render_template('projects/ml/experiments/show')
end
+ describe 'pagination' do
+ let_it_be(:candidates) { create_list(:ml_candidates, 5, experiment: experiment) }
+
+ before do
+ stub_const("Projects::Ml::ExperimentsController::MAX_CANDIDATES_PER_PAGE", 2)
+ candidates
+
+ show_experiment
+ end
+
+ context 'when out of bounds' do
+ let(:params) { basic_params.merge(id: experiment.iid, page: 10000) }
+
+ it 'redirects to last page' do
+ last_page = (experiment.candidates.size + 1) / 2
+
+ expect(response).to redirect_to(project_ml_experiment_path(project, experiment.iid, page: last_page))
+ end
+ end
+
+ context 'when bad page' do
+ let(:params) { basic_params.merge(id: experiment.iid, page: 's') }
+
+ it 'uses first page' do
+ expect(assigns(:pagination)).to include(
+ page: 1,
+ is_last_page: false,
+ per_page: 2,
+ total_items: experiment.candidates&.size
+ )
+ end
+ end
+ end
+
it 'does not perform N+1 sql queries' do
control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) { show_experiment }
@@ -84,7 +116,11 @@ RSpec.describe Projects::Ml::ExperimentsController, feature_category: :mlops do
expect { show_experiment }.not_to exceed_all_query_limit(control_count)
end
- it_behaves_like '404 if feature flag disabled'
+ it_behaves_like '404 if feature flag disabled' do
+ before do
+ show_experiment
+ end
+ end
end
private
diff --git a/spec/services/issue_links/create_service_spec.rb b/spec/services/issue_links/create_service_spec.rb
index 88e8470658d..0629b8b091b 100644
--- a/spec/services/issue_links/create_service_spec.rb
+++ b/spec/services/issue_links/create_service_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe IssueLinks::CreateService do
let_it_be(:project) { create :project, namespace: namespace }
let_it_be(:issuable) { create :issue, project: project }
let_it_be(:issuable2) { create :issue, project: project }
- let_it_be(:guest_issuable) { create :issue }
+ let_it_be(:restricted_issuable) { create :issue }
let_it_be(:another_project) { create :project, namespace: project.namespace }
let_it_be(:issuable3) { create :issue, project: another_project }
let_it_be(:issuable_a) { create :issue, project: project }
@@ -23,7 +23,7 @@ RSpec.describe IssueLinks::CreateService do
before do
project.add_developer(user)
- guest_issuable.project.add_guest(user)
+ restricted_issuable.project.add_guest(user)
another_project.add_developer(user)
end
diff --git a/spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb b/spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb
index 12f2b5d78a5..e47ff2fcd59 100644
--- a/spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb
+++ b/spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb
@@ -30,7 +30,7 @@ RSpec.shared_examples 'issuable link creation' do
context 'when user has no permission to target issuable' do
let(:params) do
- { issuable_references: [guest_issuable.to_reference(issuable_parent)] }
+ { issuable_references: [restricted_issuable.to_reference(issuable_parent)] }
end
it 'returns error' do
diff --git a/spec/support/shared_examples/services/issuable_links/destroyable_issuable_links_shared_examples.rb b/spec/support/shared_examples/services/issuable_links/destroyable_issuable_links_shared_examples.rb
index cc170c6544d..1532e870dcc 100644
--- a/spec/support/shared_examples/services/issuable_links/destroyable_issuable_links_shared_examples.rb
+++ b/spec/support/shared_examples/services/issuable_links/destroyable_issuable_links_shared_examples.rb
@@ -1,10 +1,11 @@
# frozen_string_literal: true
-RSpec.shared_examples 'a destroyable issuable link' do
+RSpec.shared_examples 'a destroyable issuable link' do |required_role: :reporter|
context 'when successfully removes an issuable link' do
before do
- issuable_link.source.resource_parent.add_reporter(user)
- issuable_link.target.resource_parent.add_reporter(user)
+ [issuable_link.target, issuable_link.source].each do |issuable|
+ issuable.resource_parent.try(:"add_#{required_role}", user)
+ end
end
it 'removes related issue' do
diff --git a/spec/tooling/quality/test_level_spec.rb b/spec/tooling/quality/test_level_spec.rb
index 3f46b3e79f4..ffd681b9df9 100644
--- a/spec/tooling/quality/test_level_spec.rb
+++ b/spec/tooling/quality/test_level_spec.rb
@@ -4,9 +4,9 @@ require 'fast_spec_helper'
require_relative '../../../tooling/quality/test_level'
-RSpec.describe Quality::TestLevel do
+RSpec.describe Quality::TestLevel, feature_category: :tooling do
describe 'TEST_LEVEL_FOLDERS constant' do
- it 'all directories it refers to exists', :aggregate_failures do
+ it 'ensures all directories it refers to exists', :aggregate_failures do
ee_only_directories = %w[
lib/ee/gitlab/background_migration
elastic
@@ -228,6 +228,14 @@ RSpec.describe Quality::TestLevel do
.to raise_error(described_class::UnknownTestLevelError,
%r{Test level for spec/unknown/foo_spec.rb couldn't be set. Please rename the file properly or change the test level detection regexes in .+/tooling/quality/test_level.rb.})
end
+
+ it 'ensures all spec/ folders are covered by a test level' do
+ Dir['{,ee/}spec/**/*/'].each do |path|
+ next if path =~ %r{\A(ee/)?spec/(benchmarks|docs_screenshots|fixtures|frontend_integration|support)/}
+
+ expect { subject.level_for(path) }.not_to raise_error
+ end
+ end
end
describe '#background_migration?' do