summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-05-15 18:10:04 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2023-05-15 18:10:04 +0000
commit5ff1f808adf841bca979cb2fac6bdfa9c449d028 (patch)
treec95cfbbcb400684b2bc89fee4fc7b614315ba909
parentf8a5275c45ed2276daf843764113476749e680d2 (diff)
downloadgitlab-ce-5ff1f808adf841bca979cb2fac6bdfa9c449d028.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/merge_request_templates/Stable Branch.md2
-rw-r--r--.rubocop_todo/rspec/file_path.yml1
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--app/assets/javascripts/admin/abuse_reports/components/abuse_report_actions.vue3
-rw-r--r--app/assets/javascripts/admin/abuse_reports/components/abuse_reports_filtered_search_bar.vue6
-rw-r--r--app/assets/javascripts/admin/broadcast_messages/components/base.vue4
-rw-r--r--app/assets/javascripts/admin/broadcast_messages/components/message_form.vue4
-rw-r--r--app/assets/javascripts/analytics/shared/components/metric_tile.vue4
-rw-r--r--app/assets/javascripts/authentication/password/components/password_input.vue6
-rw-r--r--app/assets/javascripts/authentication/password/index.js5
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/keybindings.js9
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js2
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/pipeline_editor_app.vue4
-rw-r--r--app/assets/javascripts/ci/pipeline_new/components/pipeline_new_form.vue4
-rw-r--r--app/assets/javascripts/ci/runner/admin_new_runner/admin_new_runner_app.vue4
-rw-r--r--app/assets/javascripts/ci/runner/admin_runner_show/admin_runner_show_app.vue4
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_update_form.vue4
-rw-r--r--app/assets/javascripts/ci/runner/group_new_runner/group_new_runner_app.vue4
-rw-r--r--app/assets/javascripts/ci/runner/group_runner_show/group_runner_show_app.vue4
-rw-r--r--app/assets/javascripts/ci/runner/project_new_runner/project_new_runner_app.vue4
-rw-r--r--app/assets/javascripts/editor/schema/ci.json8
-rw-r--r--app/assets/javascripts/gitlab_pages/components/pages_pipeline_wizard.vue4
-rw-r--r--app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue2
-rw-r--r--app/assets/javascripts/jobs/components/job/manual_variables_form.vue4
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/sidebar_detail_row.vue22
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/sidebar_job_details_container.vue10
-rw-r--r--app/assets/javascripts/jobs/components/table/cells/actions_cell.vue4
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js26
-rw-r--r--app/assets/javascripts/listbox/redirect_behavior.js4
-rw-r--r--app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue3
-rw-r--r--app/assets/javascripts/milestones/components/delete_milestone_modal.vue4
-rw-r--r--app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row.vue41
-rw-r--r--app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/ml_candidates_show.vue87
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard_actions_menu.vue4
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard_header.vue6
-rw-r--r--app/assets/javascripts/pages/admin/jobs/components/cancel_jobs_modal.vue4
-rw-r--r--app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue4
-rw-r--r--app/assets/javascripts/pages/registrations/new/index.js4
-rw-r--r--app/assets/javascripts/pages/sessions/index.js2
-rw-r--r--app/assets/javascripts/pipelines/components/header_component.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/jobs/failed_jobs_table.vue4
-rw-r--r--app/assets/javascripts/projects/commits/components/author_select.vue6
-rw-r--r--app/assets/javascripts/releases/stores/modules/edit_new/actions.js4
-rw-r--r--app/assets/javascripts/repository/components/blob_content_viewer.vue4
-rw-r--r--app/assets/javascripts/snippets/components/edit.vue4
-rw-r--r--app/assets/javascripts/user_lists/store/edit/actions.js4
-rw-r--r--app/assets/javascripts/user_lists/store/new/actions.js4
-rw-r--r--app/assets/javascripts/vue_shared/security_configuration/components/manage_via_mr.vue4
-rw-r--r--app/assets/stylesheets/page_bundles/login.scss5
-rw-r--r--app/controllers/admin/users_controller.rb17
-rw-r--r--app/models/ci/build.rb19
-rw-r--r--app/models/user.rb7
-rw-r--r--app/serializers/runner_entity.rb16
-rw-r--r--app/services/error_tracking/list_projects_service.rb4
-rw-r--r--app/services/projects/operations/update_service.rb2
-rw-r--r--app/services/users/deactivate_service.rb65
-rw-r--r--app/views/admin/application_settings/_outbound.html.haml2
-rw-r--r--app/views/admin/application_settings/service_usage_data.html.haml2
-rw-r--r--app/views/admin/dashboard/_security_newsletter_callout.html.haml4
-rw-r--r--app/views/admin/dashboard/index.html.haml22
-rw-r--r--app/views/admin/groups/_form.html.haml18
-rw-r--r--app/views/admin/health_check/show.html.haml4
-rw-r--r--app/views/admin/projects/_form.html.haml12
-rw-r--r--app/views/admin/projects/show.html.haml2
-rw-r--r--app/views/admin/runners/edit.html.haml2
-rw-r--r--app/views/admin/sessions/_new_base.html.haml2
-rw-r--r--app/views/admin/sessions/two_factor.html.haml2
-rw-r--r--app/views/admin/system_info/show.html.haml8
-rw-r--r--app/views/admin/users/_form.html.haml2
-rw-r--r--app/views/admin/users/_users.html.haml2
-rw-r--r--app/views/devise/sessions/_new_base.html.haml5
-rw-r--r--app/views/devise/sessions/_new_crowd.html.haml2
-rw-r--r--app/views/devise/sessions/_new_ldap.html.haml2
-rw-r--r--app/views/devise/sessions/two_factor.html.haml2
-rw-r--r--app/views/groups/_group_admin_settings.html.haml4
-rw-r--r--app/views/groups/_import_group_from_another_instance_panel.html.haml4
-rw-r--r--app/views/groups/_import_group_from_file_panel.html.haml2
-rw-r--r--app/views/groups/projects.html.haml4
-rw-r--r--app/views/groups/settings/_export.html.haml4
-rw-r--r--app/views/groups/settings/_remove_button.html.haml2
-rw-r--r--app/views/groups/settings/_transfer.html.haml2
-rw-r--r--app/views/groups/settings/ci_cd/_auto_devops_form.html.haml2
-rw-r--r--app/views/projects/_deletion_failed.html.haml2
-rw-r--r--app/views/projects/_import_project_pane.html.haml2
-rw-r--r--app/views/projects/_last_push.html.haml4
-rw-r--r--app/views/projects/_new_project_fields.html.haml10
-rw-r--r--app/views/projects/blob/edit.html.haml2
-rw-r--r--app/views/projects/branches/_branch_rules_info.haml4
-rw-r--r--app/views/projects/branches/new.html.haml3
-rw-r--r--app/views/projects/commits/_commits.html.haml2
-rw-r--r--app/views/projects/edit.html.haml4
-rw-r--r--app/views/projects/forks/error.html.haml4
-rw-r--r--app/views/projects/imports/new.html.haml2
-rw-r--r--app/views/projects/issues/service_desk/_alert_moved_from_service_desk.html.haml2
-rw-r--r--app/views/projects/mattermosts/new.html.haml2
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml2
-rw-r--r--app/views/projects/merge_requests/_mr_title.html.haml2
-rw-r--r--app/views/projects/merge_requests/invalid.html.haml2
-rw-r--r--app/views/projects/milestones/show.html.haml2
-rw-r--r--app/views/projects/mirrors/_mirror_repos.html.haml2
-rw-r--r--app/views/projects/new.html.haml2
-rw-r--r--app/views/projects/pages_domains/_form.html.haml2
-rw-r--r--app/views/projects/pages_domains/show.html.haml2
-rw-r--r--app/views/projects/pipelines/show.html.haml2
-rw-r--r--app/views/projects/settings/integrations/_form.html.haml2
-rw-r--r--app/views/projects/tags/new.html.haml3
-rw-r--r--app/views/projects/usage_quotas/index.html.haml2
-rw-r--r--app/workers/gitlab/github_import/stage/import_attachments_worker.rb6
-rw-r--r--app/workers/gitlab/github_import/stage/import_collaborators_worker.rb4
-rw-r--r--config/feature_flags/development/adapt_deprecated_approvals.yml (renamed from config/feature_flags/development/remove_deprecated_approvals.yml)2
-rw-r--r--config/feature_flags/development/auto_merge_labels_mr_widget.yml4
-rw-r--r--config/feature_flags/development/sast_reports_in_inline_diff.yml8
-rw-r--r--config/feature_flags/ops/partition_manager_sync_partitions.yml8
-rw-r--r--doc/.vale/gitlab/spelling-exceptions.txt2
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md2
-rw-r--r--doc/api/api_resources.md2
-rw-r--r--doc/api/award_emoji.md60
-rw-r--r--doc/api/graphql/getting_started.md4
-rw-r--r--doc/api/import.md5
-rw-r--r--doc/api/notes.md6
-rw-r--r--doc/ci/caching/index.md41
-rw-r--r--doc/ci/yaml/index.md27
-rw-r--r--doc/development/ai_features.md26
-rw-r--r--doc/development/code_review.md5
-rw-r--r--doc/development/contributing/issue_workflow.md2
-rw-r--r--doc/development/documentation/index.md5
-rw-r--r--doc/development/fe_guide/emojis.md2
-rw-r--r--doc/development/feature_flags/controls.md4
-rw-r--r--doc/development/github_importer.md7
-rw-r--r--doc/development/labels/index.md3
-rw-r--r--doc/operations/incident_management/status_page.md4
-rw-r--r--doc/subscriptions/gitlab_com/index.md2
-rw-r--r--doc/subscriptions/self_managed/index.md2
-rw-r--r--doc/user/admin_area/settings/account_and_limit_settings.md2
-rw-r--r--doc/user/admin_area/settings/sign_in_restrictions.md2
-rw-r--r--doc/user/application_security/api_fuzzing/index.md4
-rw-r--r--doc/user/application_security/api_security/api_discovery/index.md2
-rw-r--r--doc/user/application_security/dast_api/index.md4
-rw-r--r--doc/user/award_emojis.md23
-rw-r--r--doc/user/group/epics/index.md2
-rw-r--r--doc/user/markdown.md2
-rw-r--r--doc/user/profile/account/delete_account.md2
-rw-r--r--doc/user/project/badges.md2
-rw-r--r--doc/user/project/import/github.md6
-rw-r--r--doc/user/project/integrations/mlflow_client.md43
-rw-r--r--doc/user/project/issues/sorting_issue_lists.md2
-rw-r--r--doc/user/project/merge_requests/merge_when_pipeline_succeeds.md6
-rw-r--r--doc/user/project/ml/experiment_tracking/index.md7
-rw-r--r--doc/user/project/quick_actions.md2
-rw-r--r--doc/user/project/settings/import_export.md2
-rw-r--r--doc/user/project/web_ide/index.md3
-rw-r--r--doc/user/shortcuts.md3
-rw-r--r--doc/user/todos.md2
-rw-r--r--doc/user/workspace/index.md13
-rw-r--r--lib/api/entities/ci/job_request/cache.rb2
-rw-r--r--lib/api/users.rb11
-rw-r--r--lib/error_tracking/sentry_client/token.rb13
-rw-r--r--lib/gitlab/ci/config/entry/cache.rb11
-rw-r--r--lib/gitlab/ci/pipeline/seed/build/cache.rb4
-rw-r--r--lib/gitlab/config_checker/external_database_checker.rb6
-rw-r--r--lib/gitlab/database.rb5
-rw-r--r--lib/gitlab/database/partitioning.rb2
-rw-r--r--lib/gitlab/github_import/client.rb4
-rw-r--r--lib/gitlab/github_import/importer/collaborators_importer.rb19
-rw-r--r--lib/gitlab/github_import/settings.rb12
-rw-r--r--lib/gitlab/metrics/subscribers/rails_cache.rb3
-rw-r--r--locale/gitlab.pot14
-rw-r--r--qa/qa/page/component/note.rb2
-rw-r--r--spec/controllers/admin/users_controller_spec.rb6
-rw-r--r--spec/features/projects/user_uses_shortcuts_spec.rb12
-rw-r--r--spec/frontend/admin/abuse_reports/components/abuse_report_actions_spec.js6
-rw-r--r--spec/frontend/admin/abuse_reports/components/abuse_reports_filtered_search_bar_spec.js12
-rw-r--r--spec/frontend/admin/broadcast_messages/components/base_spec.js4
-rw-r--r--spec/frontend/analytics/shared/components/metric_tile_spec.js6
-rw-r--r--spec/frontend/authentication/password/components/password_input_spec.js2
-rw-r--r--spec/frontend/ci/pipeline_editor/pipeline_editor_app_spec.js4
-rw-r--r--spec/frontend/ci/pipeline_new/components/pipeline_new_form_spec.js6
-rw-r--r--spec/frontend/ci/runner/admin_new_runner_app/admin_new_runner_app_spec.js6
-rw-r--r--spec/frontend/ci/runner/admin_runner_show/admin_runner_show_app_spec.js4
-rw-r--r--spec/frontend/ci/runner/components/runner_update_form_spec.js6
-rw-r--r--spec/frontend/ci/runner/group_new_runner_app/group_new_runner_app_spec.js6
-rw-r--r--spec/frontend/ci/runner/group_runner_show/group_runner_show_app_spec.js4
-rw-r--r--spec/frontend/ci/runner/project_new_runner_app/project_new_runner_app_spec.js6
-rw-r--r--spec/frontend/gitlab_pages/new/pages/pages_pipeline_wizard_spec.js4
-rw-r--r--spec/frontend/import_entities/import_projects/components/advanced_settings_spec.js4
-rw-r--r--spec/frontend/import_entities/import_projects/components/import_projects_table_spec.js4
-rw-r--r--spec/frontend/jobs/components/job/manual_variables_form_spec.js6
-rw-r--r--spec/frontend/jobs/components/job/sidebar_detail_row_spec.js27
-rw-r--r--spec/frontend/jobs/components/table/cells/actions_cell_spec.js6
-rw-r--r--spec/frontend/listbox/redirect_behavior_spec.js6
-rw-r--r--spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js9
-rw-r--r--spec/frontend/milestones/components/delete_milestone_modal_spec.js6
-rw-r--r--spec/frontend/ml/experiment_tracking/routes/candidates/show/__snapshots__/ml_candidates_show_spec.js.snap215
-rw-r--r--spec/frontend/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row_spec.js49
-rw-r--r--spec/frontend/ml/experiment_tracking/routes/candidates/show/ml_candidates_show_spec.js139
-rw-r--r--spec/frontend/ml/experiment_tracking/routes/candidates/show/mock_data.js23
-rw-r--r--spec/frontend/monitoring/components/dashboard_actions_menu_spec.js6
-rw-r--r--spec/frontend/monitoring/components/dashboard_header_spec.js4
-rw-r--r--spec/frontend/monitoring/components/dashboard_url_time_spec.js4
-rw-r--r--spec/frontend/pages/admin/jobs/components/cancel_jobs_modal_spec.js6
-rw-r--r--spec/frontend/pages/projects/forks/new/components/fork_form_spec.js6
-rw-r--r--spec/frontend/pipelines/components/jobs/failed_jobs_table_spec.js4
-rw-r--r--spec/frontend/releases/stores/modules/detail/actions_spec.js6
-rw-r--r--spec/frontend/repository/components/blob_content_viewer_spec.js4
-rw-r--r--spec/frontend/snippets/components/edit_spec.js8
-rw-r--r--spec/frontend/user_lists/components/edit_user_list_spec.js4
-rw-r--r--spec/frontend/user_lists/components/new_user_list_spec.js4
-rw-r--r--spec/frontend/user_lists/store/edit/actions_spec.js4
-rw-r--r--spec/frontend/user_lists/store/new/actions_spec.js4
-rw-r--r--spec/frontend/vue_shared/security_reports/components/manage_via_mr_spec.js6
-rw-r--r--spec/lib/error_tracking/sentry_client/token_spec.rb21
-rw-r--r--spec/lib/gitlab/ci/config/entry/cache_spec.rb26
-rw-r--r--spec/lib/gitlab/ci/config/entry/job_spec.rb16
-rw-r--r--spec/lib/gitlab/ci/config/entry/root_spec.rb18
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/build/cache_spec.rb15
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb21
-rw-r--r--spec/lib/gitlab/config_checker/external_database_checker_spec.rb6
-rw-r--r--spec/lib/gitlab/database/partitioning_spec.rb15
-rw-r--r--spec/lib/gitlab/github_import/client_spec.rb10
-rw-r--r--spec/lib/gitlab/github_import/importer/collaborators_importer_spec.rb47
-rw-r--r--spec/lib/gitlab/github_import/settings_spec.rb15
-rw-r--r--spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb4
-rw-r--r--spec/models/ci/build_spec.rb138
-rw-r--r--spec/models/user_spec.rb14
-rw-r--r--spec/requests/api/ci/runner/jobs_request_post_spec.rb13
-rw-r--r--spec/requests/api/users_spec.rb8
-rw-r--r--spec/serializers/runner_entity_spec.rb18
-rw-r--r--spec/services/ci/create_pipeline_service/cache_spec.rb15
-rw-r--r--spec/services/import/github_service_spec.rb3
-rw-r--r--spec/services/users/deactivate_service_spec.rb86
-rw-r--r--spec/workers/gitlab/github_import/stage/import_collaborators_worker_spec.rb19
231 files changed, 1540 insertions, 814 deletions
diff --git a/.gitlab/merge_request_templates/Stable Branch.md b/.gitlab/merge_request_templates/Stable Branch.md
index b9980785d40..f8fc85f5bd2 100644
--- a/.gitlab/merge_request_templates/Stable Branch.md
+++ b/.gitlab/merge_request_templates/Stable Branch.md
@@ -29,7 +29,7 @@ If you have questions about the patch release process, please:
* Ask questions on the [`#releases`] Slack channel (internal only).
[severity label]: https://about.gitlab.com/handbook/engineering/quality/issue-triage/#severity
-[patch release runbook for engineers and maintainers]: https://gitlab.com/gitlab-org/release/docs/-/blob/master/general/patch/process_new.md
+[patch release runbook for engineers and maintainers]: https://gitlab.com/gitlab-org/release/docs/-/blob/master/general/patch/engineers.md
[`#releases`]: https://gitlab.slack.com/archives/C0XM5UU6B
/assign me
diff --git a/.rubocop_todo/rspec/file_path.yml b/.rubocop_todo/rspec/file_path.yml
index 5d543055029..ea193948909 100644
--- a/.rubocop_todo/rspec/file_path.yml
+++ b/.rubocop_todo/rspec/file_path.yml
@@ -12,6 +12,7 @@ RSpec/FilePath:
- 'ee/spec/frontend/fixtures/analytics/value_streams_staging_stage.rb'
- 'ee/spec/frontend/fixtures/analytics/value_streams_test_stage.rb'
- 'ee/spec/frontend/fixtures/dora/metrics.rb'
+ - 'ee/spec/helpers/merge_requests_helper_spec.rb'
- 'ee/spec/models/merge_request/blocking_spec.rb'
- 'ee/spec/requests/api/ci/runner/jobs_put_spec.rb'
- 'ee/spec/requests/api/ci/runner/jobs_trace_spec.rb'
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 7befd5a0d5b..abaf97ff662 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-cf82dc98409a959201de728d2024e80418ea4c2a
+d87e76a602d9a5400c80363c8cfa878a3409c292
diff --git a/app/assets/javascripts/admin/abuse_reports/components/abuse_report_actions.vue b/app/assets/javascripts/admin/abuse_reports/components/abuse_report_actions.vue
index 4f3be0e3a59..5d42caa75ab 100644
--- a/app/assets/javascripts/admin/abuse_reports/components/abuse_report_actions.vue
+++ b/app/assets/javascripts/admin/abuse_reports/components/abuse_report_actions.vue
@@ -2,7 +2,7 @@
import { GlDisclosureDropdown, GlModal } from '@gitlab/ui';
import axios from '~/lib/utils/axios_utils';
import { __, sprintf } from '~/locale';
-import { redirectTo, refreshCurrentPage } from '~/lib/utils/url_utility';
+import { redirectTo, refreshCurrentPage } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { createAlert, VARIANT_SUCCESS } from '~/alert';
import { ACTIONS_I18N } from '../constants';
@@ -126,6 +126,7 @@ export default {
.catch(this.handleError);
},
handleRemoveReportResponse() {
+ // eslint-disable-next-line import/no-deprecated
if (this.report.redirectPath) redirectTo(this.report.redirectPath);
else refreshCurrentPage();
},
diff --git a/app/assets/javascripts/admin/abuse_reports/components/abuse_reports_filtered_search_bar.vue b/app/assets/javascripts/admin/abuse_reports/components/abuse_reports_filtered_search_bar.vue
index e1989cadd86..b1eb5371a35 100644
--- a/app/assets/javascripts/admin/abuse_reports/components/abuse_reports_filtered_search_bar.vue
+++ b/app/assets/javascripts/admin/abuse_reports/components/abuse_reports_filtered_search_bar.vue
@@ -1,5 +1,5 @@
<script>
-import { setUrlParams, redirectTo, queryToObject, updateHistory } from '~/lib/utils/url_utility';
+import { setUrlParams, redirectTo, queryToObject, updateHistory } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
import {
@@ -80,12 +80,12 @@ export default {
params = { ...params, sort };
}
- redirectTo(setUrlParams(params, window.location.href, true));
+ redirectTo(setUrlParams(params, window.location.href, true)); // eslint-disable-line import/no-deprecated
},
handleSort(sort) {
const { page, ...query } = queryToObject(window.location.search);
- redirectTo(setUrlParams({ ...query, sort }, window.location.href, true));
+ redirectTo(setUrlParams({ ...query, sort }, window.location.href, true)); // eslint-disable-line import/no-deprecated
},
},
filteredSearchNamespace: 'abuse_reports',
diff --git a/app/assets/javascripts/admin/broadcast_messages/components/base.vue b/app/assets/javascripts/admin/broadcast_messages/components/base.vue
index c28cd266617..667ab4c34f5 100644
--- a/app/assets/javascripts/admin/broadcast_messages/components/base.vue
+++ b/app/assets/javascripts/admin/broadcast_messages/components/base.vue
@@ -1,6 +1,6 @@
<script>
import { GlPagination } from '@gitlab/ui';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { buildUrlWithCurrentLocation } from '~/lib/utils/common_utils';
import { createAlert, VARIANT_DANGER } from '~/alert';
import { s__ } from '~/locale';
@@ -66,7 +66,7 @@ export default {
// stranded on page 2 when deleting the last message.
// Force a page reload to avoid this edge case.
if (newVal === PER_PAGE && oldVal === PER_PAGE + 1) {
- redirectTo(this.buildPageUrl(1));
+ redirectTo(this.buildPageUrl(1)); // eslint-disable-line import/no-deprecated
}
},
},
diff --git a/app/assets/javascripts/admin/broadcast_messages/components/message_form.vue b/app/assets/javascripts/admin/broadcast_messages/components/message_form.vue
index 789cf77a88a..022f5df9c96 100644
--- a/app/assets/javascripts/admin/broadcast_messages/components/message_form.vue
+++ b/app/assets/javascripts/admin/broadcast_messages/components/message_form.vue
@@ -14,7 +14,7 @@ import {
import axios from '~/lib/utils/axios_utils';
import { s__ } from '~/locale';
import { createAlert, VARIANT_DANGER } from '~/alert';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import SafeHtml from '~/vue_shared/directives/safe_html';
import { THEMES, TYPES, TYPE_BANNER } from '../constants';
@@ -143,7 +143,7 @@ export default {
const success = await this.submitForm();
if (success) {
- redirectTo(this.messagesPath);
+ redirectTo(this.messagesPath); // eslint-disable-line import/no-deprecated
} else {
this.loading = false;
}
diff --git a/app/assets/javascripts/analytics/shared/components/metric_tile.vue b/app/assets/javascripts/analytics/shared/components/metric_tile.vue
index 845a3386f6c..54dbe329c7a 100644
--- a/app/assets/javascripts/analytics/shared/components/metric_tile.vue
+++ b/app/assets/javascripts/analytics/shared/components/metric_tile.vue
@@ -1,6 +1,6 @@
<script>
import { GlSingleStat } from '@gitlab/ui/dist/charts';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import MetricPopover from './metric_popover.vue';
export default {
@@ -27,7 +27,7 @@ export default {
methods: {
clickHandler({ links }) {
if (this.hasLinks) {
- redirectTo(links[0].url);
+ redirectTo(links[0].url); // eslint-disable-line import/no-deprecated
}
},
},
diff --git a/app/assets/javascripts/authentication/password/components/password_input.vue b/app/assets/javascripts/authentication/password/components/password_input.vue
index 9b3c4a692a6..fa9a7782b74 100644
--- a/app/assets/javascripts/authentication/password/components/password_input.vue
+++ b/app/assets/javascripts/authentication/password/components/password_input.vue
@@ -32,6 +32,11 @@ export default {
required: false,
default: '',
},
+ testid: {
+ type: String,
+ required: false,
+ default: '',
+ },
autocomplete: {
type: String,
required: false,
@@ -76,6 +81,7 @@ export default {
:name="name"
:minlength="minimumPasswordLength"
:data-qa-selector="qaSelector"
+ :data-testid="testid"
:title="title"
:type="type"
/>
diff --git a/app/assets/javascripts/authentication/password/index.js b/app/assets/javascripts/authentication/password/index.js
index 4a73e0975ca..a4f2d038cf7 100644
--- a/app/assets/javascripts/authentication/password/index.js
+++ b/app/assets/javascripts/authentication/password/index.js
@@ -2,14 +2,14 @@ import Vue from 'vue';
import GlFieldErrors from '~/gl_field_errors';
import PasswordInput from './components/password_input.vue';
-export const initTogglePasswordVisibility = () => {
+export const initPasswordInput = () => {
document.querySelectorAll('.js-password').forEach((el) => {
if (!el) {
return null;
}
const { form } = el;
- const { title, id, minimumPasswordLength, qaSelector, autocomplete, name } = el.dataset;
+ const { title, id, minimumPasswordLength, qaSelector, testid, autocomplete, name } = el.dataset;
// eslint-disable-next-line no-new
new Vue({
@@ -22,6 +22,7 @@ export const initTogglePasswordVisibility = () => {
id,
minimumPasswordLength,
qaSelector,
+ testid,
autocomplete,
name,
},
diff --git a/app/assets/javascripts/behaviors/shortcuts/keybindings.js b/app/assets/javascripts/behaviors/shortcuts/keybindings.js
index d58e02e7983..a88cc1834ac 100644
--- a/app/assets/javascripts/behaviors/shortcuts/keybindings.js
+++ b/app/assets/javascripts/behaviors/shortcuts/keybindings.js
@@ -240,7 +240,7 @@ export const REPO_GRAPH_SCROLL_BOTTOM = {
export const GO_TO_PROJECT_OVERVIEW = {
id: 'project.goToOverview',
description: __("Go to the project's overview page"),
- defaultKeys: ['g p'], // eslint-disable-line @gitlab/require-i18n-strings
+ defaultKeys: ['g o'], // eslint-disable-line @gitlab/require-i18n-strings
};
export const GO_TO_PROJECT_ACTIVITY_FEED = {
@@ -309,6 +309,12 @@ export const GO_TO_PROJECT_MERGE_REQUESTS = {
defaultKeys: ['g m'], // eslint-disable-line @gitlab/require-i18n-strings
};
+export const GO_TO_PROJECT_PIPELINES = {
+ id: 'project.goToPipelines',
+ description: __('Go to pipelines'),
+ defaultKeys: ['g p'], // eslint-disable-line @gitlab/require-i18n-strings
+};
+
export const GO_TO_PROJECT_JOBS = {
id: 'project.goToJobs',
description: __('Go to jobs'),
@@ -598,6 +604,7 @@ const PROJECT_SHORTCUTS_GROUP = {
NEW_ISSUE,
GO_TO_PROJECT_ISSUE_BOARDS,
GO_TO_PROJECT_MERGE_REQUESTS,
+ GO_TO_PROJECT_PIPELINES,
GO_TO_PROJECT_JOBS,
...(gon.features?.removeMonitorMetrics ? [] : [GO_TO_PROJECT_METRICS]),
GO_TO_PROJECT_ENVIRONMENTS,
diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js
index bd08dc28f7a..9e6c9c2e08e 100644
--- a/app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js
@@ -6,6 +6,7 @@ import {
GO_TO_PROJECT_RELEASES,
GO_TO_PROJECT_FILES,
GO_TO_PROJECT_COMMITS,
+ GO_TO_PROJECT_PIPELINES,
GO_TO_PROJECT_JOBS,
GO_TO_PROJECT_REPO_GRAPH,
GO_TO_PROJECT_REPO_CHARTS,
@@ -32,6 +33,7 @@ export default class ShortcutsNavigation extends Shortcuts {
[GO_TO_PROJECT_RELEASES, () => findAndFollowLink('.shortcuts-deployments-releases')],
[GO_TO_PROJECT_FILES, () => findAndFollowLink('.shortcuts-tree')],
[GO_TO_PROJECT_COMMITS, () => findAndFollowLink('.shortcuts-commits')],
+ [GO_TO_PROJECT_PIPELINES, () => findAndFollowLink('.shortcuts-pipelines')],
[GO_TO_PROJECT_JOBS, () => findAndFollowLink('.shortcuts-builds')],
[GO_TO_PROJECT_REPO_GRAPH, () => findAndFollowLink('.shortcuts-network')],
[GO_TO_PROJECT_REPO_CHARTS, () => findAndFollowLink('.shortcuts-repository-charts')],
diff --git a/app/assets/javascripts/ci/pipeline_editor/pipeline_editor_app.vue b/app/assets/javascripts/ci/pipeline_editor/pipeline_editor_app.vue
index ff848a973e3..de8e5a1a284 100644
--- a/app/assets/javascripts/ci/pipeline_editor/pipeline_editor_app.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/pipeline_editor_app.vue
@@ -1,7 +1,7 @@
<script>
import { GlLoadingIcon, GlModal } from '@gitlab/ui';
import { fetchPolicies } from '~/lib/graphql';
-import { mergeUrlParams, queryToObject, redirectTo } from '~/lib/utils/url_utility';
+import { mergeUrlParams, queryToObject, redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { __, s__ } from '~/locale';
import { unwrapStagesWithNeeds } from '~/pipelines/components/unwrapping_utils';
@@ -325,7 +325,7 @@ export default {
},
this.newMergeRequestPath,
);
- redirectTo(url);
+ redirectTo(url); // eslint-disable-line import/no-deprecated
},
async refetchContent() {
this.$apollo.queries.initialCiFileContent.skip = false;
diff --git a/app/assets/javascripts/ci/pipeline_new/components/pipeline_new_form.vue b/app/assets/javascripts/ci/pipeline_new/components/pipeline_new_form.vue
index 4c4c0ce24f2..6fd5c8130ad 100644
--- a/app/assets/javascripts/ci/pipeline_new/components/pipeline_new_form.vue
+++ b/app/assets/javascripts/ci/pipeline_new/components/pipeline_new_form.vue
@@ -18,7 +18,7 @@ import { uniqueId } from 'lodash';
import Vue from 'vue';
import { fetchPolicies } from '~/lib/graphql';
import SafeHtml from '~/vue_shared/directives/safe_html';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { s__, __, n__ } from '~/locale';
import {
CC_VALIDATION_REQUIRED_ERROR,
@@ -339,7 +339,7 @@ export default {
const { id, errors, totalWarnings, warnings } = data.createPipeline;
if (id) {
- redirectTo(`${this.pipelinesPath}/${id}`);
+ redirectTo(`${this.pipelinesPath}/${id}`); // eslint-disable-line import/no-deprecated
return;
}
diff --git a/app/assets/javascripts/ci/runner/admin_new_runner/admin_new_runner_app.vue b/app/assets/javascripts/ci/runner/admin_new_runner/admin_new_runner_app.vue
index d344fb248cf..e4d47fba464 100644
--- a/app/assets/javascripts/ci/runner/admin_new_runner/admin_new_runner_app.vue
+++ b/app/assets/javascripts/ci/runner/admin_new_runner/admin_new_runner_app.vue
@@ -1,6 +1,6 @@
<script>
import { createAlert, VARIANT_SUCCESS } from '~/alert';
-import { redirectTo, setUrlParams } from '~/lib/utils/url_utility';
+import { redirectTo, setUrlParams } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { s__ } from '~/locale';
import RegistrationCompatibilityAlert from '~/ci/runner/components/registration/registration_compatibility_alert.vue';
@@ -32,7 +32,7 @@ export default {
message: s__('Runners|Runner created.'),
variant: VARIANT_SUCCESS,
});
- redirectTo(ephemeralRegisterUrl);
+ redirectTo(ephemeralRegisterUrl); // eslint-disable-line import/no-deprecated
},
onError(error) {
createAlert({ message: error.message });
diff --git a/app/assets/javascripts/ci/runner/admin_runner_show/admin_runner_show_app.vue b/app/assets/javascripts/ci/runner/admin_runner_show/admin_runner_show_app.vue
index 36fb1cee525..668a55d2437 100644
--- a/app/assets/javascripts/ci/runner/admin_runner_show/admin_runner_show_app.vue
+++ b/app/assets/javascripts/ci/runner/admin_runner_show/admin_runner_show_app.vue
@@ -2,7 +2,7 @@
import { createAlert, VARIANT_SUCCESS } from '~/alert';
import { TYPENAME_CI_RUNNER } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import RunnerDeleteButton from '../components/runner_delete_button.vue';
import RunnerEditButton from '../components/runner_edit_button.vue';
@@ -71,7 +71,7 @@ export default {
},
onDeleted({ message }) {
saveAlertToLocalStorage({ message, variant: VARIANT_SUCCESS });
- redirectTo(this.runnersPath);
+ redirectTo(this.runnersPath); // eslint-disable-line import/no-deprecated
},
},
};
diff --git a/app/assets/javascripts/ci/runner/components/runner_update_form.vue b/app/assets/javascripts/ci/runner/components/runner_update_form.vue
index dd8e965cecd..2d34c551d6d 100644
--- a/app/assets/javascripts/ci/runner/components/runner_update_form.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_update_form.vue
@@ -14,7 +14,7 @@ import {
runnerToModel,
} from 'ee_else_ce/ci/runner/runner_update_form_utils';
import { createAlert, VARIANT_SUCCESS } from '~/alert';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { __ } from '~/locale';
import { captureException } from '~/ci/runner/sentry_utils';
import { ACCESS_LEVEL_NOT_PROTECTED, ACCESS_LEVEL_REF_PROTECTED, PROJECT_TYPE } from '../constants';
@@ -101,7 +101,7 @@ export default {
},
onSuccess() {
saveAlertToLocalStorage({ message: __('Changes saved.'), variant: VARIANT_SUCCESS });
- redirectTo(this.runnerPath);
+ redirectTo(this.runnerPath); // eslint-disable-line import/no-deprecated
},
onError(message) {
this.saving = false;
diff --git a/app/assets/javascripts/ci/runner/group_new_runner/group_new_runner_app.vue b/app/assets/javascripts/ci/runner/group_new_runner/group_new_runner_app.vue
index c2622d720bf..67d29daf66f 100644
--- a/app/assets/javascripts/ci/runner/group_new_runner/group_new_runner_app.vue
+++ b/app/assets/javascripts/ci/runner/group_new_runner/group_new_runner_app.vue
@@ -1,6 +1,6 @@
<script>
import { createAlert, VARIANT_SUCCESS } from '~/alert';
-import { redirectTo, setUrlParams } from '~/lib/utils/url_utility';
+import { redirectTo, setUrlParams } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { s__ } from '~/locale';
import RegistrationCompatibilityAlert from '~/ci/runner/components/registration/registration_compatibility_alert.vue';
@@ -38,7 +38,7 @@ export default {
message: s__('Runners|Runner created.'),
variant: VARIANT_SUCCESS,
});
- redirectTo(ephemeralRegisterUrl);
+ redirectTo(ephemeralRegisterUrl); // eslint-disable-line import/no-deprecated
},
onError(error) {
createAlert({ message: error.message });
diff --git a/app/assets/javascripts/ci/runner/group_runner_show/group_runner_show_app.vue b/app/assets/javascripts/ci/runner/group_runner_show/group_runner_show_app.vue
index 2db3a2f42a7..1318bf5a2e6 100644
--- a/app/assets/javascripts/ci/runner/group_runner_show/group_runner_show_app.vue
+++ b/app/assets/javascripts/ci/runner/group_runner_show/group_runner_show_app.vue
@@ -2,7 +2,7 @@
import { createAlert, VARIANT_SUCCESS } from '~/alert';
import { TYPENAME_CI_RUNNER } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import RunnerDeleteButton from '../components/runner_delete_button.vue';
import RunnerEditButton from '../components/runner_edit_button.vue';
@@ -76,7 +76,7 @@ export default {
},
onDeleted({ message }) {
saveAlertToLocalStorage({ message, variant: VARIANT_SUCCESS });
- redirectTo(this.runnersPath);
+ redirectTo(this.runnersPath); // eslint-disable-line import/no-deprecated
},
},
};
diff --git a/app/assets/javascripts/ci/runner/project_new_runner/project_new_runner_app.vue b/app/assets/javascripts/ci/runner/project_new_runner/project_new_runner_app.vue
index a3661e08497..f0ae54c0232 100644
--- a/app/assets/javascripts/ci/runner/project_new_runner/project_new_runner_app.vue
+++ b/app/assets/javascripts/ci/runner/project_new_runner/project_new_runner_app.vue
@@ -1,6 +1,6 @@
<script>
import { createAlert, VARIANT_SUCCESS } from '~/alert';
-import { redirectTo, setUrlParams } from '~/lib/utils/url_utility';
+import { redirectTo, setUrlParams } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { s__ } from '~/locale';
import RegistrationCompatibilityAlert from '~/ci/runner/components/registration/registration_compatibility_alert.vue';
@@ -38,7 +38,7 @@ export default {
message: s__('Runners|Runner created.'),
variant: VARIANT_SUCCESS,
});
- redirectTo(ephemeralRegisterUrl);
+ redirectTo(ephemeralRegisterUrl); // eslint-disable-line import/no-deprecated
},
onError(error) {
createAlert({ message: error.message });
diff --git a/app/assets/javascripts/editor/schema/ci.json b/app/assets/javascripts/editor/schema/ci.json
index 1fb6f606b6b..d240ad7353a 100644
--- a/app/assets/javascripts/editor/schema/ci.json
+++ b/app/assets/javascripts/editor/schema/ci.json
@@ -1092,6 +1092,14 @@
"on_failure",
"always"
]
+ },
+ "fallback_keys": {
+ "type": "array",
+ "markdownDescription": "List of keys to download cache from if no cache hit occurred for key",
+ "items": {
+ "type": "string"
+ },
+ "maxItems": 5
}
}
},
diff --git a/app/assets/javascripts/gitlab_pages/components/pages_pipeline_wizard.vue b/app/assets/javascripts/gitlab_pages/components/pages_pipeline_wizard.vue
index bf71f682048..f19e047061f 100644
--- a/app/assets/javascripts/gitlab_pages/components/pages_pipeline_wizard.vue
+++ b/app/assets/javascripts/gitlab_pages/components/pages_pipeline_wizard.vue
@@ -5,7 +5,7 @@ import PipelineWizard from '~/pipeline_wizard/pipeline_wizard.vue';
import PagesWizardTemplate from '~/pipeline_wizard/templates/pages.yml?raw';
import { logError } from '~/lib/logger';
import { s__ } from '~/locale';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import pagesMarkOnboardingComplete from '../queries/mark_onboarding_complete.graphql';
export const i18n = {
@@ -57,7 +57,7 @@ export default {
async onDone() {
this.loading = true;
await this.updateOnboardingState();
- redirectTo(this.redirectToWhenDone);
+ redirectTo(this.redirectToWhenDone); // eslint-disable-line import/no-deprecated
},
},
};
diff --git a/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue b/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue
index a867a1695b9..1c830d8c2c5 100644
--- a/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue
+++ b/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue
@@ -58,7 +58,7 @@ export default {
data() {
return {
optionalStagesSelection: Object.fromEntries(
- this.optionalStages.map(({ name }) => [name, false]),
+ this.optionalStages.map(({ name, selected }) => [name, selected]),
),
};
},
diff --git a/app/assets/javascripts/jobs/components/job/manual_variables_form.vue b/app/assets/javascripts/jobs/components/job/manual_variables_form.vue
index 19a75ffaa85..d3b2ddc5422 100644
--- a/app/assets/javascripts/jobs/components/job/manual_variables_form.vue
+++ b/app/assets/javascripts/jobs/components/job/manual_variables_form.vue
@@ -16,7 +16,7 @@ import { TYPENAME_CI_BUILD, TYPENAME_COMMIT_STATUS } from '~/graphql_shared/cons
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { JOB_GRAPHQL_ERRORS } from '~/jobs/constants';
import { helpPagePath } from '~/helpers/help_page_helper';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { s__ } from '~/locale';
import { reportMessageToSentry } from '~/jobs/utils';
import GetJob from './graphql/queries/get_job.query.graphql';
@@ -186,7 +186,7 @@ export default {
return `${this.$options.inputTypes[type]}-${id}`;
},
navigateToJob(path) {
- redirectTo(path);
+ redirectTo(path); // eslint-disable-line import/no-deprecated
},
runJob() {
this.runBtnDisabled = true;
diff --git a/app/assets/javascripts/jobs/components/job/sidebar/sidebar_detail_row.vue b/app/assets/javascripts/jobs/components/job/sidebar/sidebar_detail_row.vue
index 05567328660..0ba34eafa58 100644
--- a/app/assets/javascripts/jobs/components/job/sidebar/sidebar_detail_row.vue
+++ b/app/assets/javascripts/jobs/components/job/sidebar/sidebar_detail_row.vue
@@ -22,6 +22,11 @@ export default {
required: false,
default: '',
},
+ path: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
computed: {
hasTitle() {
@@ -35,10 +40,19 @@ export default {
</script>
<template>
<p class="gl-display-flex gl-justify-content-space-between gl-mb-2">
- <span v-if="hasTitle"
- ><b>{{ title }}:</b> {{ value }}</span
- >
- <gl-link v-if="hasHelpURL" :href="helpUrl" target="_blank">
+ <span v-if="hasTitle">
+ <b>{{ title }}:</b>
+ <gl-link
+ v-if="path"
+ :href="path"
+ class="gl-text-blue-600!"
+ data-testid="job-sidebar-value-link"
+ >
+ {{ value }}
+ </gl-link>
+ <span v-else>{{ value }}</span>
+ </span>
+ <gl-link v-if="hasHelpURL" :href="helpUrl" target="_blank" data-testid="job-sidebar-help-link">
<gl-icon name="question-o" />
</gl-link>
</p>
diff --git a/app/assets/javascripts/jobs/components/job/sidebar/sidebar_job_details_container.vue b/app/assets/javascripts/jobs/components/job/sidebar/sidebar_job_details_container.vue
index 8300a22cb67..3cd90eb3bca 100644
--- a/app/assets/javascripts/jobs/components/job/sidebar/sidebar_job_details_container.vue
+++ b/app/assets/javascripts/jobs/components/job/sidebar/sidebar_job_details_container.vue
@@ -70,6 +70,9 @@ export default {
timeoutSource: this.job.metadata.timeout_source,
});
},
+ runnerAdminPath() {
+ return this.job?.runner?.admin_path || '';
+ },
},
i18n: {
COVERAGE: __('Coverage'),
@@ -104,7 +107,12 @@ export default {
data-testid="job-timeout"
:title="$options.i18n.TIMEOUT"
/>
- <detail-row v-if="job.runner" :value="runnerId" :title="$options.i18n.RUNNER" />
+ <detail-row
+ v-if="job.runner"
+ :value="runnerId"
+ :title="$options.i18n.RUNNER"
+ :path="runnerAdminPath"
+ />
<detail-row v-if="job.coverage" :value="coverage" :title="$options.i18n.COVERAGE" />
<p v-if="hasTags" class="build-detail-row" data-testid="job-tags">
diff --git a/app/assets/javascripts/jobs/components/table/cells/actions_cell.vue b/app/assets/javascripts/jobs/components/table/cells/actions_cell.vue
index f4061f3d375..d97f6f6ff8c 100644
--- a/app/assets/javascripts/jobs/components/table/cells/actions_cell.vue
+++ b/app/assets/javascripts/jobs/components/table/cells/actions_cell.vue
@@ -8,7 +8,7 @@ import {
GlTooltipDirective,
} from '@gitlab/ui';
import GlCountdown from '~/vue_shared/components/gl_countdown.vue';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import {
ACTIONS_DOWNLOAD_ARTIFACTS,
ACTIONS_START_NOW,
@@ -138,7 +138,7 @@ export default {
} else if (redirect) {
// Retry and Play actions redirect to job detail view
// we don't need to refetch with jobActionPerformed event
- redirectTo(job.detailedStatus.detailsPath);
+ redirectTo(job.detailedStatus.detailsPath); // eslint-disable-line import/no-deprecated
} else {
eventHub.$emit('jobActionPerformed');
}
diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js
index 6c1495b8468..f16ff188edb 100644
--- a/app/assets/javascripts/lib/utils/url_utility.js
+++ b/app/assets/javascripts/lib/utils/url_utility.js
@@ -272,6 +272,11 @@ export const setUrlFragment = (url, fragment) => {
return `${rootUrl}#${encodedFragment}`;
};
+/**
+ * Navigates to a URL
+ * @param {*} url - url to navigate to
+ * @param {*} external - if true, open a new page or tab
+ */
export function visitUrl(url, external = false) {
if (external) {
// Simulate `target="_blank" rel="noopener noreferrer"`
@@ -284,6 +289,19 @@ export function visitUrl(url, external = false) {
}
}
+export function refreshCurrentPage() {
+ visitUrl(window.location.href);
+}
+
+/**
+ * Navigates to a URL
+ * @deprecated Use visitUrl from ~/lib/utils/url_utility.js instead
+ * @param {*} url
+ */
+export function redirectTo(url) {
+ return window.location.assign(url);
+}
+
export function updateHistory({ state = {}, title = '', url, replace = false, win = window } = {}) {
if (win.history) {
if (replace) {
@@ -294,14 +312,6 @@ export function updateHistory({ state = {}, title = '', url, replace = false, wi
}
}
-export function refreshCurrentPage() {
- visitUrl(window.location.href);
-}
-
-export function redirectTo(url) {
- return window.location.assign(url);
-}
-
export const escapeFileUrl = (fileUrl) => encodeURIComponent(fileUrl).replace(/%2F/g, '/');
export function webIDEUrl(route = undefined) {
diff --git a/app/assets/javascripts/listbox/redirect_behavior.js b/app/assets/javascripts/listbox/redirect_behavior.js
index 38d9d84f889..28b0892d126 100644
--- a/app/assets/javascripts/listbox/redirect_behavior.js
+++ b/app/assets/javascripts/listbox/redirect_behavior.js
@@ -1,5 +1,5 @@
import { initListbox } from '~/listbox';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
/**
* Instantiates GlCollapsibleListbox components with redirect behavior for tags created
@@ -15,7 +15,7 @@ export function initRedirectListboxBehavior() {
return elements.map((el) =>
initListbox(el, {
onChange({ href }) {
- redirectTo(href);
+ redirectTo(href); // eslint-disable-line import/no-deprecated
},
}),
);
diff --git a/app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue b/app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue
index 685482a76de..8cdaa76e673 100644
--- a/app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue
+++ b/app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue
@@ -5,7 +5,7 @@ import {
getParameterByName,
setUrlParams,
queryToObject,
- redirectTo,
+ redirectTo, // eslint-disable-line import/no-deprecated
} from '~/lib/utils/url_utility';
import {
SORT_QUERY_PARAM_NAME,
@@ -119,6 +119,7 @@ export default {
const sortParamValue = getParameterByName(SORT_QUERY_PARAM_NAME);
const activeTabParamValue = getParameterByName(ACTIVE_TAB_QUERY_PARAM_NAME);
+ // eslint-disable-next-line import/no-deprecated
redirectTo(
setUrlParams(
{
diff --git a/app/assets/javascripts/milestones/components/delete_milestone_modal.vue b/app/assets/javascripts/milestones/components/delete_milestone_modal.vue
index 8e7428089e2..c13bf50eba7 100644
--- a/app/assets/javascripts/milestones/components/delete_milestone_modal.vue
+++ b/app/assets/javascripts/milestones/components/delete_milestone_modal.vue
@@ -3,7 +3,7 @@ import { GlSprintf, GlModal } from '@gitlab/ui';
import { createAlert } from '~/alert';
import axios from '~/lib/utils/axios_utils';
import { HTTP_STATUS_NOT_FOUND } from '~/lib/utils/http_status';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { __, n__, s__, sprintf } from '~/locale';
import eventHub from '../event_hub';
@@ -76,7 +76,7 @@ Once deleted, it cannot be undone or recovered.`),
});
// follow the rediect to milestones overview page
- redirectTo(response.request.responseURL);
+ redirectTo(response.request.responseURL); // eslint-disable-line import/no-deprecated
})
.catch((error) => {
eventHub.$emit('deleteMilestoneModal.requestFinished', {
diff --git a/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row.vue b/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row.vue
new file mode 100644
index 00000000000..20c5248052b
--- /dev/null
+++ b/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row.vue
@@ -0,0 +1,41 @@
+<script>
+import { GlLink } from '@gitlab/ui';
+
+export default {
+ name: 'CandidateDetailRow',
+ components: {
+ GlLink,
+ },
+ props: {
+ label: {
+ type: String,
+ required: true,
+ },
+ text: {
+ type: [String, Number],
+ required: true,
+ },
+ href: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ sectionLabel: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+};
+</script>
+
+<template>
+ <tr>
+ <td class="gl-text-secondary gl-font-weight-bold">{{ sectionLabel }}</td>
+ <td class="gl-font-weight-bold">{{ label }}</td>
+ <td>
+ <gl-link v-if="href" :href="href">{{ text }}</gl-link>
+ <template v-else>{{ text }}</template>
+ </td>
+ </tr>
+</template>
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 cef5da726f3..3ef73e7c874 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,7 +1,8 @@
<script>
-import { GlLink } from '@gitlab/ui';
import ModelExperimentsHeader from '~/ml/experiment_tracking/components/model_experiments_header.vue';
import DeleteButton from '~/ml/experiment_tracking/components/delete_button.vue';
+import DetailRow from './components/candidate_detail_row.vue';
+
import {
TITLE_LABEL,
INFO_LABEL,
@@ -23,7 +24,7 @@ export default {
components: {
ModelExperimentsHeader,
DeleteButton,
- GlLink,
+ DetailRow,
},
props: {
candidate: {
@@ -38,27 +39,27 @@ export default {
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,
},
computed: {
+ info() {
+ return Object.freeze(this.candidate.info);
+ },
sections() {
return [
{
- sectionName: this.$options.i18n.PARAMETERS_LABEL,
+ sectionName: PARAMETERS_LABEL,
sectionValues: this.candidate.params,
},
{
- sectionName: this.$options.i18n.METRICS_LABEL,
+ sectionName: METRICS_LABEL,
sectionValues: this.candidate.metrics,
},
{
- sectionName: this.$options.i18n.METADATA_LABEL,
+ sectionName: METADATA_LABEL,
sectionValues: this.candidate.metadata,
},
];
@@ -71,7 +72,7 @@ export default {
<div>
<model-experiments-header :page-title="$options.i18n.TITLE_LABEL">
<delete-button
- :delete-path="candidate.info.path"
+ :delete-path="info.path"
:delete-confirmation-text="$options.i18n.DELETE_CANDIDATE_CONFIRMATION_MESSAGE"
:action-primary-text="$options.i18n.DELETE_CANDIDATE_PRIMARY_ACTION_LABEL"
:modal-title="$options.i18n.DELETE_CANDIDATE_MODAL_TITLE"
@@ -82,55 +83,39 @@ export default {
<tbody>
<tr class="divider"></tr>
- <tr>
- <td class="gl-text-secondary gl-font-weight-bold">{{ $options.i18n.INFO_LABEL }}</td>
- <td class="gl-font-weight-bold">{{ $options.i18n.ID_LABEL }}</td>
- <td>{{ candidate.info.iid }}</td>
- </tr>
+ <detail-row
+ :label="$options.i18n.ID_LABEL"
+ :section-label="$options.i18n.INFO_LABEL"
+ :text="info.iid"
+ />
- <tr>
- <td></td>
- <td class="gl-font-weight-bold">{{ $options.i18n.MLFLOW_ID_LABEL }}</td>
- <td>{{ candidate.info.eid }}</td>
- </tr>
+ <detail-row :label="$options.i18n.MLFLOW_ID_LABEL" :text="info.eid" />
- <tr>
- <td></td>
- <td class="gl-font-weight-bold">{{ $options.i18n.STATUS_LABEL }}</td>
- <td>{{ candidate.info.status }}</td>
- </tr>
+ <detail-row :label="$options.i18n.STATUS_LABEL" :text="info.status" />
- <tr>
- <td></td>
- <td class="gl-font-weight-bold">{{ $options.i18n.EXPERIMENT_LABEL }}</td>
- <td>
- <gl-link :href="candidate.info.path_to_experiment">{{
- candidate.info.experiment_name
- }}</gl-link>
- </td>
- </tr>
+ <detail-row
+ :label="$options.i18n.EXPERIMENT_LABEL"
+ :text="info.experiment_name"
+ :href="info.path_to_experiment"
+ />
- <tr v-if="candidate.info.path_to_artifact">
- <td></td>
- <td class="gl-font-weight-bold">{{ $options.i18n.ARTIFACTS_LABEL }}</td>
- <td>
- <gl-link :href="candidate.info.path_to_artifact">{{
- $options.i18n.ARTIFACTS_LABEL
- }}</gl-link>
- </td>
- </tr>
+ <detail-row
+ v-if="info.path_to_artifact"
+ :label="$options.i18n.ARTIFACTS_LABEL"
+ :href="info.path_to_artifact"
+ :text="$options.i18n.ARTIFACTS_LABEL"
+ />
<template v-for="{ sectionName, sectionValues } in sections">
- <tr :key="sectionName" class="divider"></tr>
+ <tr v-if="sectionValues" :key="sectionName" class="divider"></tr>
- <tr v-for="(item, index) in sectionValues" :key="item.name">
- <td v-if="index === 0" class="gl-text-secondary gl-font-weight-bold">
- {{ sectionName }}
- </td>
- <td v-else></td>
- <td class="gl-font-weight-bold">{{ item.name }}</td>
- <td>{{ item.value }}</td>
- </tr>
+ <detail-row
+ v-for="(item, index) in sectionValues"
+ :key="item.name"
+ :label="item.name"
+ :section-label="index === 0 ? sectionName : ''"
+ :text="item.value"
+ />
</template>
</tbody>
</table>
diff --git a/app/assets/javascripts/monitoring/components/dashboard_actions_menu.vue b/app/assets/javascripts/monitoring/components/dashboard_actions_menu.vue
index d67154b7697..29ce8572e9a 100644
--- a/app/assets/javascripts/monitoring/components/dashboard_actions_menu.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard_actions_menu.vue
@@ -12,7 +12,7 @@ import {
import { mapState, mapGetters, mapActions } from 'vuex';
import CustomMetricsFormFields from '~/custom_metrics/components/custom_metrics_form_fields.vue';
import invalidUrl from '~/lib/utils/invalid_url';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { s__ } from '~/locale';
import TrackEventDirective from '~/vue_shared/directives/track_event';
import { PANEL_NEW_PAGE } from '../router/constants';
@@ -113,7 +113,7 @@ export default {
const dashboardPath = encodeURIComponent(
dashboard.out_of_the_box_dashboard ? dashboard.path : dashboard.display_name,
);
- redirectTo(`${baseURL}/${dashboardPath}`);
+ redirectTo(`${baseURL}/${dashboardPath}`); // eslint-disable-line import/no-deprecated
},
},
diff --git a/app/assets/javascripts/monitoring/components/dashboard_header.vue b/app/assets/javascripts/monitoring/components/dashboard_header.vue
index 7bb0d3874d1..44dde454983 100644
--- a/app/assets/javascripts/monitoring/components/dashboard_header.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard_header.vue
@@ -13,7 +13,7 @@ import {
import { debounce } from 'lodash';
import { mapActions, mapState, mapGetters } from 'vuex';
import invalidUrl from '~/lib/utils/invalid_url';
-import { mergeUrlParams, redirectTo } from '~/lib/utils/url_utility';
+import { mergeUrlParams, redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { s__ } from '~/locale';
import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue';
@@ -137,13 +137,13 @@ export default {
const dashboardPath = encodeURIComponent(
dashboard.out_of_the_box_dashboard ? dashboard.path : dashboard.display_name,
);
- redirectTo(`${baseURL}/${dashboardPath}`);
+ redirectTo(`${baseURL}/${dashboardPath}`); // eslint-disable-line import/no-deprecated
},
debouncedEnvironmentsSearch: debounce(function environmentsSearchOnInput(searchTerm) {
this.filterEnvironments(searchTerm);
}, 500),
onDateTimePickerInput(timeRange) {
- redirectTo(timeRangeToUrl(timeRange));
+ redirectTo(timeRangeToUrl(timeRange)); // eslint-disable-line import/no-deprecated
},
onDateTimePickerInvalid() {
this.$emit('dateTimePickerInvalid');
diff --git a/app/assets/javascripts/pages/admin/jobs/components/cancel_jobs_modal.vue b/app/assets/javascripts/pages/admin/jobs/components/cancel_jobs_modal.vue
index 3bc785ee1b6..b2c5326fefd 100644
--- a/app/assets/javascripts/pages/admin/jobs/components/cancel_jobs_modal.vue
+++ b/app/assets/javascripts/pages/admin/jobs/components/cancel_jobs_modal.vue
@@ -2,7 +2,7 @@
import { GlModal } from '@gitlab/ui';
import { createAlert } from '~/alert';
import axios from '~/lib/utils/axios_utils';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import {
CANCEL_TEXT,
CANCEL_JOBS_FAILED_TEXT,
@@ -31,7 +31,7 @@ export default {
.post(this.url)
.then((response) => {
// follow the rediect to refresh the page
- redirectTo(response.request.responseURL);
+ redirectTo(response.request.responseURL); // eslint-disable-line import/no-deprecated
})
.catch((error) => {
createAlert({
diff --git a/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue b/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue
index 50e34f4e4f8..9f7a7b436df 100644
--- a/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue
+++ b/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue
@@ -15,7 +15,7 @@ import { buildApiUrl } from '~/api/api_utils';
import { createAlert } from '~/alert';
import axios from '~/lib/utils/axios_utils';
import csrf from '~/lib/utils/csrf';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { s__, __ } from '~/locale';
import validation from '~/vue_shared/directives/validation';
import {
@@ -261,7 +261,7 @@ export default {
try {
const { data } = await axios.post(url, postParams);
- redirectTo(data.web_url);
+ redirectTo(data.web_url); // eslint-disable-line import/no-deprecated
return;
} catch (error) {
createAlert({
diff --git a/app/assets/javascripts/pages/registrations/new/index.js b/app/assets/javascripts/pages/registrations/new/index.js
index 00f7c5d60d1..84050c3cb0f 100644
--- a/app/assets/javascripts/pages/registrations/new/index.js
+++ b/app/assets/javascripts/pages/registrations/new/index.js
@@ -5,7 +5,7 @@ import LengthValidator from '~/validators/length_validator';
import UsernameValidator from '~/pages/sessions/new/username_validator';
import EmailFormatValidator from '~/pages/sessions/new/email_format_validator';
import { initLanguageSwitcher } from '~/language_switcher';
-import { initTogglePasswordVisibility } from '~/authentication/password';
+import { initPasswordInput } from '~/authentication/password';
import Tracking from '~/tracking';
new UsernameValidator(); // eslint-disable-line no-new
@@ -20,4 +20,4 @@ Tracking.enableFormTracking({
});
initLanguageSwitcher();
-initTogglePasswordVisibility();
+initPasswordInput();
diff --git a/app/assets/javascripts/pages/sessions/index.js b/app/assets/javascripts/pages/sessions/index.js
index 8d8534ec556..fdd846a9476 100644
--- a/app/assets/javascripts/pages/sessions/index.js
+++ b/app/assets/javascripts/pages/sessions/index.js
@@ -1,3 +1,5 @@
import { mount2faAuthentication } from '~/authentication/mount_2fa';
+import { initPasswordInput } from '~/authentication/password';
mount2faAuthentication();
+initPasswordInput();
diff --git a/app/assets/javascripts/pipelines/components/header_component.vue b/app/assets/javascripts/pipelines/components/header_component.vue
index 065e40bd3ea..27119419060 100644
--- a/app/assets/javascripts/pipelines/components/header_component.vue
+++ b/app/assets/javascripts/pipelines/components/header_component.vue
@@ -7,7 +7,7 @@ import {
GlModalDirective,
GlTooltipDirective,
} from '@gitlab/ui';
-import { setUrlFragment, redirectTo } from '~/lib/utils/url_utility';
+import { setUrlFragment, redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { __ } from '~/locale';
import CiHeader from '~/vue_shared/components/header_ci_component.vue';
import {
@@ -233,7 +233,7 @@ export default {
this.reportFailure(DELETE_FAILURE, errors);
this.isDeleting = false;
} else {
- redirectTo(setUrlFragment(this.paths.pipelinesPath, 'delete_success'));
+ redirectTo(setUrlFragment(this.paths.pipelinesPath, 'delete_success')); // eslint-disable-line import/no-deprecated
}
} catch {
this.$apollo.queries.pipeline.startPolling(POLL_INTERVAL);
diff --git a/app/assets/javascripts/pipelines/components/jobs/failed_jobs_table.vue b/app/assets/javascripts/pipelines/components/jobs/failed_jobs_table.vue
index 80c08d7c613..ec7000120f1 100644
--- a/app/assets/javascripts/pipelines/components/jobs/failed_jobs_table.vue
+++ b/app/assets/javascripts/pipelines/components/jobs/failed_jobs_table.vue
@@ -3,7 +3,7 @@ import { GlButton, GlLink, GlTableLite } from '@gitlab/ui';
import SafeHtml from '~/vue_shared/directives/safe_html';
import { __, s__ } from '~/locale';
import { createAlert } from '~/alert';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue';
import RetryFailedJobMutation from '../../graphql/mutations/retry_failed_job.mutation.graphql';
import { DEFAULT_FIELDS } from '../../constants';
@@ -40,7 +40,7 @@ export default {
if (errors.length > 0) {
this.showErrorMessage();
} else {
- redirectTo(job.detailedStatus.detailsPath);
+ redirectTo(job.detailedStatus.detailsPath); // eslint-disable-line import/no-deprecated
}
} catch {
this.showErrorMessage();
diff --git a/app/assets/javascripts/projects/commits/components/author_select.vue b/app/assets/javascripts/projects/commits/components/author_select.vue
index f85be67d4b3..2966214e051 100644
--- a/app/assets/javascripts/projects/commits/components/author_select.vue
+++ b/app/assets/javascripts/projects/commits/components/author_select.vue
@@ -9,7 +9,7 @@ import {
} from '@gitlab/ui';
import { debounce } from 'lodash';
import { mapState, mapActions } from 'vuex';
-import { redirectTo, queryToObject } from '~/lib/utils/url_utility';
+import { redirectTo, queryToObject } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { __ } from '~/locale';
const tooltipMessage = __('Searching by both author and message is currently not supported.');
@@ -89,10 +89,10 @@ export default {
commitListElement.style.transition = 'opacity 200ms';
if (!user) {
- return redirectTo(this.commitsPath);
+ return redirectTo(this.commitsPath); // eslint-disable-line import/no-deprecated
}
- return redirectTo(`${this.commitsPath}?author=${user}`);
+ return redirectTo(`${this.commitsPath}?author=${user}`); // eslint-disable-line import/no-deprecated
},
searchAuthors() {
this.fetchAuthors(this.authorInput);
diff --git a/app/assets/javascripts/releases/stores/modules/edit_new/actions.js b/app/assets/javascripts/releases/stores/modules/edit_new/actions.js
index 2ea31518dd0..2e3cf3bf9b8 100644
--- a/app/assets/javascripts/releases/stores/modules/edit_new/actions.js
+++ b/app/assets/javascripts/releases/stores/modules/edit_new/actions.js
@@ -1,6 +1,6 @@
import { getTag } from '~/rest_api';
import { createAlert } from '~/alert';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { s__ } from '~/locale';
import createReleaseMutation from '~/releases/graphql/mutations/create_release.mutation.graphql';
import deleteReleaseMutation from '~/releases/graphql/mutations/delete_release.mutation.graphql';
@@ -95,7 +95,7 @@ export const removeAssetLink = ({ commit }, linkIdToRemove) => {
export const receiveSaveReleaseSuccess = ({ commit }, urlToRedirectTo) => {
commit(types.RECEIVE_SAVE_RELEASE_SUCCESS);
- redirectTo(urlToRedirectTo);
+ redirectTo(urlToRedirectTo); // eslint-disable-line import/no-deprecated
};
export const saveRelease = ({ commit, dispatch, state }) => {
diff --git a/app/assets/javascripts/repository/components/blob_content_viewer.vue b/app/assets/javascripts/repository/components/blob_content_viewer.vue
index 2878d4324c8..e056a822c8b 100644
--- a/app/assets/javascripts/repository/components/blob_content_viewer.vue
+++ b/app/assets/javascripts/repository/components/blob_content_viewer.vue
@@ -8,7 +8,7 @@ import { createAlert } from '~/alert';
import axios from '~/lib/utils/axios_utils';
import { isLoggedIn, handleLocationHash } from '~/lib/utils/common_utils';
import { __ } from '~/locale';
-import { redirectTo, getLocationHash } from '~/lib/utils/url_utility';
+import { redirectTo, getLocationHash } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import WebIdeLink from '~/vue_shared/components/web_ide_link.vue';
import CodeIntelligence from '~/code_navigation/components/app.vue';
@@ -305,7 +305,7 @@ export default {
}
const { ideEditPath, editBlobPath } = this.blobInfo;
- redirectTo(target === 'ide' ? ideEditPath : editBlobPath);
+ redirectTo(target === 'ide' ? ideEditPath : editBlobPath); // eslint-disable-line import/no-deprecated
},
setForkTarget(target) {
this.forkTarget = target;
diff --git a/app/assets/javascripts/snippets/components/edit.vue b/app/assets/javascripts/snippets/components/edit.vue
index 1678e51a29d..5e2f194e133 100644
--- a/app/assets/javascripts/snippets/components/edit.vue
+++ b/app/assets/javascripts/snippets/components/edit.vue
@@ -3,7 +3,7 @@ import { GlButton, GlLoadingIcon, GlFormInput, GlFormGroup } from '@gitlab/ui';
import eventHub from '~/blob/components/eventhub';
import { createAlert } from '~/alert';
-import { redirectTo, joinPaths } from '~/lib/utils/url_utility';
+import { redirectTo, joinPaths } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { __, sprintf } from '~/locale';
import {
SNIPPET_MARK_EDIT_APP_START,
@@ -192,7 +192,7 @@ export default {
if (errors?.length) {
this.alertAPIFailure(errors[0]);
} else {
- redirectTo(baseObj.snippet.webUrl);
+ redirectTo(baseObj.snippet.webUrl); // eslint-disable-line import/no-deprecated
}
})
.catch((e) => {
diff --git a/app/assets/javascripts/user_lists/store/edit/actions.js b/app/assets/javascripts/user_lists/store/edit/actions.js
index 6db2e65cf04..6f5d483a4c7 100644
--- a/app/assets/javascripts/user_lists/store/edit/actions.js
+++ b/app/assets/javascripts/user_lists/store/edit/actions.js
@@ -1,5 +1,5 @@
import Api from '~/api';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { getErrorMessages } from '../utils';
import * as types from './mutation_types';
@@ -17,6 +17,6 @@ export const updateUserList = ({ commit, state }, userList) => {
iid: userList.iid,
name: userList.name,
})
- .then(({ data }) => redirectTo(data.path))
+ .then(({ data }) => redirectTo(data.path)) // eslint-disable-line import/no-deprecated
.catch((response) => commit(types.RECEIVE_USER_LIST_ERROR, getErrorMessages(response)));
};
diff --git a/app/assets/javascripts/user_lists/store/new/actions.js b/app/assets/javascripts/user_lists/store/new/actions.js
index 478fca40142..030f1f59212 100644
--- a/app/assets/javascripts/user_lists/store/new/actions.js
+++ b/app/assets/javascripts/user_lists/store/new/actions.js
@@ -1,5 +1,5 @@
import Api from '~/api';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { getErrorMessages } from '../utils';
import * as types from './mutation_types';
@@ -10,6 +10,6 @@ export const createUserList = ({ commit, state }, userList) => {
...state.userList,
...userList,
})
- .then(({ data }) => redirectTo(data.path))
+ .then(({ data }) => redirectTo(data.path)) // eslint-disable-line import/no-deprecated
.catch((response) => commit(types.RECEIVE_CREATE_USER_LIST_ERROR, getErrorMessages(response)));
};
diff --git a/app/assets/javascripts/vue_shared/security_configuration/components/manage_via_mr.vue b/app/assets/javascripts/vue_shared/security_configuration/components/manage_via_mr.vue
index c2ff2eec9fa..fe408354f66 100644
--- a/app/assets/javascripts/vue_shared/security_configuration/components/manage_via_mr.vue
+++ b/app/assets/javascripts/vue_shared/security_configuration/components/manage_via_mr.vue
@@ -2,7 +2,7 @@
import { GlButton } from '@gitlab/ui';
import { featureToMutationMap } from 'ee_else_ce/security_configuration/components/constants';
import { parseErrorMessage } from '~/lib/utils/error_message';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { sprintf, s__ } from '~/locale';
import apolloProvider from '../provider';
@@ -77,7 +77,7 @@ export default {
);
}
- redirectTo(successPath);
+ redirectTo(successPath); // eslint-disable-line import/no-deprecated
} catch (e) {
this.$emit('error', parseErrorMessage(e, this.$options.i18n.genericErrorText));
this.isLoading = false;
diff --git a/app/assets/stylesheets/page_bundles/login.scss b/app/assets/stylesheets/page_bundles/login.scss
index 6175bba6ba7..98fa45e0e3d 100644
--- a/app/assets/stylesheets/page_bundles/login.scss
+++ b/app/assets/stylesheets/page_bundles/login.scss
@@ -96,11 +96,6 @@
.login-body {
font-size: 13px;
- input + p,
- input ~ p.field-validation {
- margin-top: 5px;
- }
-
.username .validation-success {
color: $green-600;
}
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 7fc534be253..45a7901b2c4 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -96,19 +96,14 @@ class Admin::UsersController < Admin::ApplicationController
end
def deactivate
- if user.blocked?
- return redirect_back_or_admin_user(notice: _("Error occurred. A blocked user cannot be deactivated"))
- end
-
- return redirect_back_or_admin_user(notice: _("Successfully deactivated")) if user.deactivated?
- return redirect_back_or_admin_user(notice: _("Internal users cannot be deactivated")) if user.internal?
+ deactivate_service = Users::DeactivateService.new(current_user, skip_authorization: true)
+ result = deactivate_service.execute(user)
- unless user.can_be_deactivated?
- return redirect_back_or_admin_user(notice: format(_("The user you are trying to deactivate has been active in the past %{minimum_inactive_days} days and cannot be deactivated"), minimum_inactive_days: Gitlab::CurrentSettings.deactivate_dormant_users_period))
+ if result.success?
+ redirect_back_or_admin_user(notice: _("Successfully deactivated"))
+ else
+ redirect_back_or_admin_user(alert: result.message)
end
-
- user.deactivate
- redirect_back_or_admin_user(notice: _("Successfully deactivated"))
end
def block
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 5680cd2181e..61585de4ff7 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -25,7 +25,8 @@ module Ci
refspecs: -> (build) { build.merge_request_ref? },
artifacts_exclude: -> (build) { build.supports_artifacts_exclude? },
multi_build_steps: -> (build) { build.multi_build_steps? },
- return_exit_code: -> (build) { build.exit_codes_defined? }
+ return_exit_code: -> (build) { build.exit_codes_defined? },
+ fallback_cache_keys: -> (build) { build.fallback_cache_keys_defined? }
}.freeze
DEGRADATION_THRESHOLD_VARIABLE_NAME = 'DEGRADATION_THRESHOLD'
@@ -919,9 +920,15 @@ module Ci
def cache
cache = Array.wrap(options[:cache])
+ cache.each do |single_cache|
+ single_cache[:fallback_keys] = [] unless single_cache.key?(:fallback_keys)
+ end
+
if project.jobs_cache_index
cache = cache.map do |single_cache|
- single_cache.merge(key: "#{single_cache[:key]}-#{project.jobs_cache_index}")
+ cache = single_cache.merge(key: "#{single_cache[:key]}-#{project.jobs_cache_index}")
+ fallback = cache.slice(:fallback_keys).transform_values { |keys| keys.map { |key| "#{key}-#{project.jobs_cache_index}" } }
+ cache.merge(fallback.compact)
end
end
@@ -930,10 +937,16 @@ module Ci
cache.map do |entry|
type_suffix = !entry[:unprotect] && pipeline.protected_ref? ? 'protected' : 'non_protected'
- entry.merge(key: "#{entry[:key]}-#{type_suffix}")
+ cache = entry.merge(key: "#{entry[:key]}-#{type_suffix}")
+ fallback = cache.slice(:fallback_keys).transform_values { |keys| keys.map { |key| "#{key}-#{type_suffix}" } }
+ cache.merge(fallback.compact)
end
end
+ def fallback_cache_keys_defined?
+ Array.wrap(options[:cache]).any? { |cache| cache[:fallback_keys].present? }
+ end
+
def credentials
Gitlab::Ci::Build::Credentials::Factory.new(self).create!
end
diff --git a/app/models/user.rb b/app/models/user.rb
index daee2687d2f..dc70ff2e232 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1089,13 +1089,6 @@ class User < ApplicationRecord
update(otp_backup_codes: nil)
end
- # Returns true if the user is allowed to sign in with either otp or recovery codes.
- def sign_in_with_codes_allowed?
- return two_factor_otp_enabled? unless Feature.enabled?(:webauthn_without_totp)
-
- two_factor_enabled?
- end
-
def two_factor_enabled?
two_factor_otp_enabled? || two_factor_webauthn_enabled?
end
diff --git a/app/serializers/runner_entity.rb b/app/serializers/runner_entity.rb
index 6d6ba920a3b..cf0b2e2c3f6 100644
--- a/app/serializers/runner_entity.rb
+++ b/app/serializers/runner_entity.rb
@@ -9,6 +9,10 @@ class RunnerEntity < Grape::Entity
edit_project_runner_path(project, runner)
end
+ expose :admin_path, if: -> (*) { can_admin_runner? } do |runner|
+ Gitlab::Routing.url_helpers.admin_runner_url(runner)
+ end
+
private
alias_method :runner, :object
@@ -17,7 +21,17 @@ class RunnerEntity < Grape::Entity
request.project
end
+ def current_user
+ request.current_user
+ end
+
def can_edit_runner?
- can?(request.current_user, :update_runner, runner) && runner.project_type?
+ can?(current_user, :update_runner, runner) && runner.project_type?
+ end
+
+ # can_admin_all_resources? is used here because the
+ # path exposed is only available to admins
+ def can_admin_runner?
+ current_user&.can_admin_all_resources?
end
end
diff --git a/app/services/error_tracking/list_projects_service.rb b/app/services/error_tracking/list_projects_service.rb
index d52306ef805..35a8179d54d 100644
--- a/app/services/error_tracking/list_projects_service.rb
+++ b/app/services/error_tracking/list_projects_service.rb
@@ -2,8 +2,6 @@
module ErrorTracking
class ListProjectsService < ErrorTracking::BaseService
- MASKED_TOKEN_REGEX = /\A\*+\z/.freeze
-
private
def perform
@@ -46,7 +44,7 @@ module ErrorTracking
end
def masked_token?
- MASKED_TOKEN_REGEX.match?(params[:token])
+ ErrorTracking::SentryClient::Token.masked_token?(params[:token])
end
end
end
diff --git a/app/services/projects/operations/update_service.rb b/app/services/projects/operations/update_service.rb
index b2166dc84c7..d0bef9da329 100644
--- a/app/services/projects/operations/update_service.rb
+++ b/app/services/projects/operations/update_service.rb
@@ -93,7 +93,7 @@ module Projects
sentry_project_id: settings.dig(:project, :sentry_project_id)
}
}
- params[:error_tracking_setting_attributes][:token] = settings[:token] unless /\A\*+\z/.match?(settings[:token]) # Don't update token if we receive masked value
+ params[:error_tracking_setting_attributes][:token] = settings[:token] unless ::ErrorTracking::SentryClient::Token.masked_token?(settings[:token]) # Don't update token if we receive masked value
params[:error_tracking_setting_attributes][:integrated] = settings[:integrated] unless settings[:integrated].nil?
params
diff --git a/app/services/users/deactivate_service.rb b/app/services/users/deactivate_service.rb
new file mode 100644
index 00000000000..e69ce13d3cc
--- /dev/null
+++ b/app/services/users/deactivate_service.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+module Users
+ class DeactivateService < BaseService
+ def initialize(current_user, skip_authorization: false)
+ @current_user = current_user
+ @skip_authorization = skip_authorization
+ end
+
+ def execute(user)
+ unless allowed?
+ return ::ServiceResponse.error(message: _('You are not authorized to perform this action'),
+ reason: :forbidden)
+ end
+
+ if user.blocked?
+ return ::ServiceResponse.error(message: _('Error occurred. A blocked user cannot be deactivated'),
+ reason: :forbidden)
+ end
+
+ if user.internal?
+ return ::ServiceResponse.error(message: _('Internal users cannot be deactivated'),
+ reason: :forbidden)
+ end
+
+ return ::ServiceResponse.success(message: _('User has already been deactivated')) if user.deactivated?
+
+ unless user.can_be_deactivated?
+ message = _(
+ 'The user you are trying to deactivate has been active in the past %{minimum_inactive_days} days ' \
+ 'and cannot be deactivated')
+
+ deactivation_error_message = format(message,
+ minimum_inactive_days: Gitlab::CurrentSettings.deactivate_dormant_users_period)
+ return ::ServiceResponse.error(message: deactivation_error_message, reason: :forbidden)
+ end
+
+ unless user.deactivate
+ return ::ServiceResponse.error(message: user.errors.full_messages.to_sentence,
+ reason: :bad_request)
+ end
+
+ log_event(user)
+
+ ::ServiceResponse.success
+ end
+
+ private
+
+ attr_reader :current_user
+
+ def allowed?
+ return true if @skip_authorization
+
+ can?(current_user, :admin_all_resources)
+ end
+
+ def log_event(user)
+ Gitlab::AppLogger.info(message: 'User deactivated', user: user.username.to_s, email: user.email.to_s,
+ deactivated_by: current_user.username.to_s, ip_address: current_user.current_sign_in_ip.to_s)
+ end
+ end
+end
+
+Users::DeactivateService.prepend_mod_with('Users::DeactivateService')
diff --git a/app/views/admin/application_settings/_outbound.html.haml b/app/views/admin/application_settings/_outbound.html.haml
index 6efafa3c415..8cb25627dfa 100644
--- a/app/views/admin/application_settings/_outbound.html.haml
+++ b/app/views/admin/application_settings/_outbound.html.haml
@@ -11,7 +11,7 @@
= render Pajamas::AlertComponent.new(variant: :warning,
dismissible: false,
alert_options: { class: "gl-mb-3 js-deny-all-requests-warning #{'gl-display-none' unless deny_all_requests}" }) do |c|
- = c.body do
+ - c.with_body do
= s_('OutboundRequests|Webhooks and integrations might not work properly.')
= f.gitlab_ui_checkbox_component :allow_local_requests_from_web_hooks_and_services,
s_('OutboundRequests|Allow requests to the local network from webhooks and integrations'),
diff --git a/app/views/admin/application_settings/service_usage_data.html.haml b/app/views/admin/application_settings/service_usage_data.html.haml
index ead2b2fd666..e42c1091bf2 100644
--- a/app/views/admin/application_settings/service_usage_data.html.haml
+++ b/app/views/admin/application_settings/service_usage_data.html.haml
@@ -22,7 +22,7 @@
dismissible: false,
title: _('Service Ping payload not found in the application cache')) do |c|
- = c.body do
+ - c.with_body do
- enable_service_ping_link_url = help_page_path('user/admin_area/settings/usage_statistics', anchor: 'enable-or-disable-usage-statistics')
- enable_service_ping_link = '<a href="%{url}">'.html_safe % { url: enable_service_ping_link_url }
- generate_manually_link_url = help_page_path('development/service_ping/troubleshooting', anchor: 'generate-service-ping')
diff --git a/app/views/admin/dashboard/_security_newsletter_callout.html.haml b/app/views/admin/dashboard/_security_newsletter_callout.html.haml
index 7495298936d..0bdeef9acea 100644
--- a/app/views/admin/dashboard/_security_newsletter_callout.html.haml
+++ b/app/views/admin/dashboard/_security_newsletter_callout.html.haml
@@ -7,8 +7,8 @@
dismiss_endpoint: callouts_path,
defer_links: 'true' }},
close_button_options: { data: { testid: 'close-security-newsletter-callout' }}) do |c|
- = c.body do
+ - c.with_body do
= s_('AdminArea|Sign up for the GitLab Security Newsletter to get notified for security updates.')
- = c.actions do
+ - c.with_actions do
= render Pajamas::ButtonComponent.new(variant: :confirm, href: 'https://about.gitlab.com/company/preference-center/', target: '_blank', button_options: { class: 'deferred-link gl-alert-action', rel: 'noreferrer noopener' }) do
= s_('AdminArea|Sign up for the GitLab newsletter')
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index 01d47facb5c..4973c0f985c 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -26,7 +26,7 @@
footer_options: { class: 'gl-bg-transparent'} }
.col-md-4.gl-mb-6
= render Pajamas::CardComponent.new(**component_params) do |c|
- = c.body do
+ - c.with_body do
%span
.d-flex.align-items-center
= sprite_icon('project', size: 16, css_class: 'gl-text-gray-700')
@@ -34,13 +34,13 @@
.gl-mt-3.text-uppercase= s_('AdminArea|Projects')
= render Pajamas::ButtonComponent.new(href: new_project_path) do
= s_('AdminArea|New project')
- = c.footer do
+ - c.with_footer do
.d-flex.align-items-center
= link_to(s_('AdminArea|View latest projects'), admin_projects_path(sort: 'created_desc'))
= sprite_icon('chevron-right', size: 12, css_class: 'gl-text-gray-700 gl-ml-2')
.col-md-4.gl-mb-6
= render Pajamas::CardComponent.new(**component_params) do |c|
- = c.body do
+ - c.with_body do
%span
.d-flex.align-items-center
= sprite_icon('users', size: 16, css_class: 'gl-text-gray-700')
@@ -58,13 +58,13 @@
= link_to(s_('AdminArea|Users statistics'), admin_dashboard_stats_path, class: "text-capitalize gl-ml-2")
= render Pajamas::ButtonComponent.new(href: new_admin_user_path) do
= s_('AdminArea|New user')
- = c.footer do
+ - c.with_footer do
.d-flex.align-items-center
= link_to(s_('AdminArea|View latest users'), admin_users_path({ sort: 'created_desc' }))
= sprite_icon('chevron-right', size: 12, css_class: 'gl-text-gray-700 gl-ml-2')
.col-md-4.gl-mb-6
= render Pajamas::CardComponent.new(**component_params) do |c|
- = c.body do
+ - c.with_body do
%span
.d-flex.align-items-center
= sprite_icon('group', size: 16, css_class: 'gl-text-gray-700')
@@ -72,7 +72,7 @@
.gl-mt-3.text-uppercase= s_('AdminArea|Groups')
= render Pajamas::ButtonComponent.new(href: new_admin_group_path) do
= s_('AdminArea|New group')
- = c.footer do
+ - c.with_footer do
.d-flex.align-items-center
= link_to(s_('AdminArea|View latest groups'), admin_groups_path(sort: 'created_desc'))
= sprite_icon('chevron-right', size: 12, css_class: 'gl-text-gray-700 gl-ml-2')
@@ -81,7 +81,7 @@
#js-admin-statistics-container
.col-md-4.gl-mb-6
= render Pajamas::CardComponent.new do |c|
- = c.body do
+ - c.with_body do
%h4= s_('AdminArea|Features')
= feature_entry(_('Sign up'),
href: general_admin_application_settings_path(anchor: 'js-signup-settings'),
@@ -120,7 +120,7 @@
enabled: Gitlab.config.gitlab_ci.shared_runners_enabled)
.col-md-4.gl-mb-6
= render Pajamas::CardComponent.new do |c|
- = c.body do
+ - c.with_body do
%h4
= s_('AdminArea|Components')
- if show_version_check?
@@ -178,7 +178,7 @@
.row
.col-md-4.gl-mb-6
= render Pajamas::CardComponent.new do |c|
- = c.body do
+ - c.with_body do
%h4= s_('AdminArea|Latest projects')
- @projects.each do |project|
.gl-display-flex.gl-py-3
@@ -188,7 +188,7 @@
#{time_ago_with_tooltip(project.created_at)}
.col-md-4.gl-mb-6
= render Pajamas::CardComponent.new do |c|
- = c.body do
+ - c.with_body do
%h4= s_('AdminArea|Latest users')
- @users.each do |user|
.gl-display-flex.gl-py-3
@@ -199,7 +199,7 @@
#{time_ago_with_tooltip(user.created_at)}
.col-md-4.gl-mb-6
= render Pajamas::CardComponent.new do |c|
- = c.body do
+ - c.with_body do
%h4= s_('AdminArea|Latest groups')
- @groups.each do |group|
.gl-display-flex.gl-py-3
diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml
index 20ee8c9f310..b708564e23a 100644
--- a/app/views/admin/groups/_form.html.haml
+++ b/app/views/admin/groups/_form.html.haml
@@ -1,11 +1,11 @@
= gitlab_ui_form_for [:admin, @group] do |f|
= form_errors(@group)
= render ::Layouts::HorizontalSectionComponent.new(options: { class: 'gl-mb-6' }) do |c|
- = c.title { _('Naming, visibility') }
- = c.description do
+ - c.with_title { _('Naming, visibility') }
+ - c.with_description do
= _('Update your group name, description, avatar, and visibility.')
= link_to _('Learn more about groups.'), help_page_path('user/group/index')
- = c.body do
+ - c.with_body do
= render 'shared/groups/group_name_and_path_fields', f: f
= render 'shared/group_form_description', f: f
.form-group.gl-form-group{ role: 'group' }
@@ -14,10 +14,10 @@
= render 'shared/old_visibility_level', f: f, visibility_level: visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group, with_label: false
= render ::Layouts::HorizontalSectionComponent.new(options: { class: 'gl-pb-3 gl-mb-6' }) do |c|
- = c.title { _('Permissions and group features') }
- = c.description do
+ - c.with_title { _('Permissions and group features') }
+ - c.with_description do
= _('Configure advanced permissions, Large File Storage, two-factor authentication, and CI/CD settings.')
- = c.body do
+ - c.with_body do
= render_if_exists 'shared/old_repository_size_limit_setting', form: f, type: :group
= render_if_exists 'admin/namespace_plan', f: f
.form-group.gl-form-group{ role: 'group' }
@@ -26,13 +26,13 @@
= render_if_exists 'namespaces/shared_runners_minutes_settings', group: @group, form: f
= render ::Layouts::HorizontalSectionComponent.new(border: false, options: { class: 'gl-pb-3' }) do |c|
- = c.title { _('Admin notes') }
- = c.body do
+ - c.with_title { _('Admin notes') }
+ - c.with_body do
= render 'shared/admin/admin_note_form', f: f
- if @group.new_record?
= render Pajamas::AlertComponent.new(dismissible: false) do |c|
- = c.body do
+ - c.with_body do
= render 'shared/group_tips'
.gl-mt-5
= f.submit _('Create group'), pajamas_button: true
diff --git a/app/views/admin/health_check/show.html.haml b/app/views/admin/health_check/show.html.haml
index e7aa4f38634..662234bf56a 100644
--- a/app/views/admin/health_check/show.html.haml
+++ b/app/views/admin/health_check/show.html.haml
@@ -23,7 +23,7 @@
= render_if_exists 'admin/health_check/health_check_url'
%hr
= render Pajamas::CardComponent.new do |c|
- = c.header do
+ - c.with_header do
Current Status:
- if no_errors
= sprite_icon('check', css_class: 'cgreen')
@@ -31,7 +31,7 @@
- else
= sprite_icon('warning-solid', css_class: 'cred')
#{ s_('HealthCheck|Unhealthy') }
- = c.body do
+ - c.with_body do
- if no_errors
#{ s_('HealthCheck|No Health Problems Detected') }
- else
diff --git a/app/views/admin/projects/_form.html.haml b/app/views/admin/projects/_form.html.haml
index dbb4f3a63cc..61bf97d8214 100644
--- a/app/views/admin/projects/_form.html.haml
+++ b/app/views/admin/projects/_form.html.haml
@@ -1,10 +1,10 @@
= gitlab_ui_form_for [:admin, @project] do |f|
= form_errors(@project)
= render ::Layouts::HorizontalSectionComponent.new(options: { class: 'gl-pb-3 gl-mb-6' }) do |c|
- = c.title { _('Naming') }
- = c.description do
+ - c.with_title { _('Naming') }
+ - c.with_description do
= _('Update your project name and description.')
- = c.body do
+ - c.with_body do
.form-group.gl-form-group
= f.label :name, _('Project name')
= f.text_field :name, class: 'form-control gl-form-input gl-md-form-input-md'
@@ -18,10 +18,10 @@
= f.text_area :description, class: 'form-control gl-form-input gl-form-textarea gl-lg-form-input-xl', rows: 5
= render ::Layouts::HorizontalSectionComponent.new(options: { class: 'gl-pb-3 gl-mb-6' }) do |c|
- = c.title { _('Permissions and project features') }
- = c.description do
+ - c.with_title { _('Permissions and project features') }
+ - c.with_description do
= _('Configure advanced permissions')
- = c.body do
+ - c.with_body do
- if @project.project_setting.present?
.form-group.gl-form-group
%legend.col-form-label.col-form-label
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index 2ba305ebaea..8eb72fa281e 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -17,7 +17,7 @@
= render Pajamas::AlertComponent.new(variant: :danger,
alert_options: { class: 'gl-mb-5',
data: { testid: 'last-repository-check-failed-alert' }}) do |c|
- = c.body do
+ - c.with_body do
- last_check_message = _("Last repository check (%{last_check_timestamp}) failed. See the 'repocheck.log' file for error messages.")
- last_check_message = last_check_message % { last_check_timestamp: time_ago_with_tooltip(@project.last_repository_check_at) }
= last_check_message.html_safe
diff --git a/app/views/admin/runners/edit.html.haml b/app/views/admin/runners/edit.html.haml
index 05f0c957100..3d245722270 100644
--- a/app/views/admin/runners/edit.html.haml
+++ b/app/views/admin/runners/edit.html.haml
@@ -23,7 +23,7 @@
= render Pajamas::AlertComponent.new(variant: :danger,
dismissible: false,
title: project.full_name) do |c|
- = c.actions do
+ - c.with_actions do
= render Pajamas::ButtonComponent.new(variant: :confirm, href: admin_namespace_project_runner_project_path(project.namespace, project, runner_project), method: :delete) do
= _('Disable')
diff --git a/app/views/admin/sessions/_new_base.html.haml b/app/views/admin/sessions/_new_base.html.haml
index 7256da75d86..13c647cd45f 100644
--- a/app/views/admin/sessions/_new_base.html.haml
+++ b/app/views/admin/sessions/_new_base.html.haml
@@ -1,7 +1,7 @@
= form_tag(admin_session_path, method: :post, class: 'new_user gl-show-field-errors', 'aria-live': 'assertive') do
.form-group
= label_tag :user_password, _('Password'), class: 'label-bold'
- = password_field_tag 'user[password]', nil, class: 'form-control', autocomplete: 'current-password', required: true, title: _('This field is required.'), data: { qa_selector: 'password_field', testid: 'password-field' }
+ = password_field_tag 'user[password]', nil, { class: 'form-control js-password', data: { id: 'user_password', name: 'user[password]', qa_selector: 'password_field', testid: 'password-field' } }
.submit-container.move-submit-down
= submit_tag _('Enter admin mode'), class: 'gl-button btn btn-confirm', data: { qa_selector: 'enter_admin_mode_button' }
diff --git a/app/views/admin/sessions/two_factor.html.haml b/app/views/admin/sessions/two_factor.html.haml
index 3eb2b45c0de..3bbf768d7be 100644
--- a/app/views/admin/sessions/two_factor.html.haml
+++ b/app/views/admin/sessions/two_factor.html.haml
@@ -9,7 +9,7 @@
.tab-content
.login-box.tab-pane.gl-p-5.active{ id: 'login-pane', role: 'tabpanel' }
.login-body
- - if current_user.sign_in_with_codes_allowed?
+ - if current_user.two_factor_enabled?
= render 'admin/sessions/two_factor_otp'
- if current_user.two_factor_webauthn_enabled?
= render 'authentication/authenticate', render_remember_me: false, target_path: admin_session_path
diff --git a/app/views/admin/system_info/show.html.haml b/app/views/admin/system_info/show.html.haml
index 75d23ee3094..d3d2ebb90da 100644
--- a/app/views/admin/system_info/show.html.haml
+++ b/app/views/admin/system_info/show.html.haml
@@ -4,7 +4,7 @@
.row
.col-sm
= render Pajamas::CardComponent.new(card_options: { class: 'gl-mb-3' }) do |c|
- = c.body do
+ - c.with_body do
%h4
= sprite_icon('pod', size: 18, css_class: 'gl-text-gray-700')
= _('CPU')
@@ -16,7 +16,7 @@
= _('Unable to collect CPU info')
= render Pajamas::CardComponent.new(card_options: { class: 'gl-mb-3' }) do |c|
- = c.body do
+ - c.with_body do
%h4
= sprite_icon('status-health', size: 18, css_class: 'gl-text-gray-700')
= _('Memory Usage')
@@ -28,7 +28,7 @@
= _('Unable to collect memory info')
= render Pajamas::CardComponent.new(card_options: { class: 'gl-mb-3' }) do |c|
- = c.body do
+ - c.with_body do
%h4
= sprite_icon('clock', size: 18, css_class: 'gl-text-gray-700')
= _('System started')
@@ -36,7 +36,7 @@
%h2= time_ago_with_tooltip(Rails.application.config.booted_at)
.col-sm
= render Pajamas::CardComponent.new(card_options: { class: 'gl-mb-3' }) do |c|
- = c.body do
+ - c.with_body do
%h4
= sprite_icon('disk', size: 18, css_class: 'gl-text-gray-700')
= _('Disk Usage')
diff --git a/app/views/admin/users/_form.html.haml b/app/views/admin/users/_form.html.haml
index fb4c7a645ca..8822d52c3c0 100644
--- a/app/views/admin/users/_form.html.haml
+++ b/app/views/admin/users/_form.html.haml
@@ -28,7 +28,7 @@
.col-lg-8
- if @user.new_record?
= render Pajamas::AlertComponent.new(variant: :info, dismissible: false, alert_options: { class: 'gl-mb-5' }) do |c|
- = c.body do
+ - c.with_body do
= s_('AdminUsers|Reset link will be generated and sent to the user. User will be forced to set the password on first sign in.')
- else
.form-group.gl-form-group{ role: 'group' }
diff --git a/app/views/admin/users/_users.html.haml b/app/views/admin/users/_users.html.haml
index 96dd16a96da..c9264535a13 100644
--- a/app/views/admin/users/_users.html.haml
+++ b/app/views/admin/users/_users.html.haml
@@ -4,7 +4,7 @@
= render Pajamas::AlertComponent.new(variant: :tip,
alert_options: { class: 'gl-my-5' },
dismissible: false) do |c|
- = c.body do
+ - c.with_body do
= render 'shared/registration_features_discovery_message', feature_title: s_('RegistrationFeatures|send emails to users')
.top-area
diff --git a/app/views/devise/sessions/_new_base.html.haml b/app/views/devise/sessions/_new_base.html.haml
index a5a6c0cff78..698e8c89a08 100644
--- a/app/views/devise/sessions/_new_base.html.haml
+++ b/app/views/devise/sessions/_new_base.html.haml
@@ -4,7 +4,10 @@
= f.text_field :login, value: @invite_email, class: 'form-control gl-form-input top js-username-field', autofocus: 'autofocus', autocapitalize: 'off', autocorrect: 'off', required: true, title: _('This field is required.'), data: { qa_selector: 'login_field', testid: 'username-field' }
.form-group.gl-px-5
= f.label :password, class: "label-bold #{'gl-mb-1' if Feature.enabled?(:restyle_login_page, @project)}"
- = f.password_field :password, class: 'form-control gl-form-input bottom', autocomplete: 'current-password', required: true, title: _('This field is required.'), data: { qa_selector: 'password_field', testid: 'password-field' }
+ = f.password_field :password, class: 'form-control gl-form-input js-password', data: { id: "#{resource_name}_password",
+ qa_selector: 'password_field',
+ testid: 'password-field',
+ name: "#{resource_name}[password]" }
.gl-px-5
.gl-display-inline-block
- if remember_me_enabled?
diff --git a/app/views/devise/sessions/_new_crowd.html.haml b/app/views/devise/sessions/_new_crowd.html.haml
index 14038f3c3c7..293e287371a 100644
--- a/app/views/devise/sessions/_new_crowd.html.haml
+++ b/app/views/devise/sessions/_new_crowd.html.haml
@@ -4,7 +4,7 @@
= text_field_tag :username, nil, { class: "form-control top", title: _("This field is required."), autofocus: "autofocus", required: true }
.form-group.gl-px-5
= label_tag :password
- = password_field_tag :password, nil, { autocomplete: 'current-password', class: "form-control bottom", title: _("This field is required."), required: true }
+ = password_field_tag :password, nil, { class: 'form-control gl-form-input js-password', data: { id: 'password', name: 'password' } }
- if remember_me_enabled?
.remember-me.gl-px-5
%label{ for: "remember_me" }
diff --git a/app/views/devise/sessions/_new_ldap.html.haml b/app/views/devise/sessions/_new_ldap.html.haml
index 8f9d6ee556e..fb5a57b509c 100644
--- a/app/views/devise/sessions/_new_ldap.html.haml
+++ b/app/views/devise/sessions/_new_ldap.html.haml
@@ -8,7 +8,7 @@
= text_field_tag :username, nil, { class: "form-control gl-form-input top", title: _("This field is required."), autofocus: "autofocus", data: { qa_selector: 'username_field' }, required: true }
.form-group.gl-px-5
= label_tag :password
- = password_field_tag :password, nil, { autocomplete: 'current-password', class: "form-control gl-form-input bottom", title: _("This field is required."), data: { qa_selector: 'password_field' }, required: true }
+ = password_field_tag :password, nil, { class: 'form-control gl-form-input js-password', data: { id: 'password', name: 'password', qa_selector: 'password_field' } }
- if render_remember_me
.gl-px-5
= render Pajamas::CheckboxTagComponent.new(name: 'remember_me') do |c|
diff --git a/app/views/devise/sessions/two_factor.html.haml b/app/views/devise/sessions/two_factor.html.haml
index 789f6072b68..06152e3dac5 100644
--- a/app/views/devise/sessions/two_factor.html.haml
+++ b/app/views/devise/sessions/two_factor.html.haml
@@ -2,7 +2,7 @@
= render 'devise/shared/tab_single', tab_title: _('Two-Factor Authentication') if Feature.disabled?(:restyle_login_page, @project)
.login-box.gl-p-5
.login-body
- - if @user.sign_in_with_codes_allowed?
+ - if @user.two_factor_enabled?
= gitlab_ui_form_for(resource, as: resource_name, url: session_path(resource_name), method: :post, html: { class: "edit_user gl-show-field-errors js-2fa-form #{'hidden' if @user.two_factor_webauthn_enabled?}" }) do |f|
- resource_params = params[resource_name].presence || params
- if remember_me_enabled?
diff --git a/app/views/groups/_group_admin_settings.html.haml b/app/views/groups/_group_admin_settings.html.haml
index ca11649e162..7f113a1cfd4 100644
--- a/app/views/groups/_group_admin_settings.html.haml
+++ b/app/views/groups/_group_admin_settings.html.haml
@@ -2,10 +2,10 @@
%legend.col-form-label.col-form-label
= _('Large File Storage')
= f.gitlab_ui_checkbox_component :lfs_enabled, checkbox_options: { checked: @group.lfs_enabled? } do |c|
- = c.label do
+ - c.with_label do
= _('Projects in this group can use Git LFS')
= link_to sprite_icon('question-o'), help_page_path('topics/git/lfs/index'), class: 'gl-ml-2'
- = c.help_text do
+ - c.with_help_text do
= _('This setting can be overridden in each project.')
.form-group.gl-form-group{ role: 'group' }
= f.label s_('ProjectCreationLevel|Allowed to create projects'), class: 'gl-display-block col-form-label'
diff --git a/app/views/groups/_import_group_from_another_instance_panel.html.haml b/app/views/groups/_import_group_from_another_instance_panel.html.haml
index 4a34e124c4c..9fbb7f3c9ed 100644
--- a/app/views/groups/_import_group_from_another_instance_panel.html.haml
+++ b/app/views/groups/_import_group_from_another_instance_panel.html.haml
@@ -9,7 +9,7 @@
- if bulk_imports_disabled
= render Pajamas::AlertComponent.new(dismissible: false, variant: :tip) do |c|
- = c.body do
+ - c.with_body do
= s_('GroupsNew|Importing groups by direct transfer is currently disabled.')
- if current_user.admin?
@@ -24,7 +24,7 @@
- else
= render Pajamas::AlertComponent.new(dismissible: false,
variant: :warning) do |c|
- = c.body do
+ - c.with_body do
- docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/group/import/index.md', anchor: 'migrated-group-items') }
- docs_link_end = '</a>'.html_safe
= s_('GroupsNew|Not all group items are migrated. %{docs_link_start}What items are migrated%{docs_link_end}?').html_safe % { docs_link_start: docs_link_start, docs_link_end: docs_link_end }
diff --git a/app/views/groups/_import_group_from_file_panel.html.haml b/app/views/groups/_import_group_from_file_panel.html.haml
index 775b9c79817..91f7b574dbf 100644
--- a/app/views/groups/_import_group_from_file_panel.html.haml
+++ b/app/views/groups/_import_group_from_file_panel.html.haml
@@ -9,7 +9,7 @@
= render Pajamas::AlertComponent.new(variant: :warning,
alert_options: { class: 'gl-mb-5' },
dismissible: false) do |c|
- = c.body do
+ - c.with_body do
- docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/group/import/index.md', anchor: 'migrate-groups-by-direct-transfer-recommended') }
- link_end = '</a>'.html_safe
= s_('GroupsNew|This feature is deprecated and replaced by group migration by direct transfer. %{docs_link_start}Learn more%{docs_link_end}.').html_safe % { docs_link_start: docs_link_start, docs_link_end: link_end }
diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml
index dda0cb78458..ed078230349 100644
--- a/app/views/groups/projects.html.haml
+++ b/app/views/groups/projects.html.haml
@@ -3,14 +3,14 @@
- @force_desktop_expanded_sidebar = true
= render Pajamas::CardComponent.new(card_options: { class: 'gl-mt-3 js-search-settings-section' }, header_options: { class: 'gl-display-flex' }, body_options: { class: 'gl-py-0' }) do |c|
- - c.header do
+ - c.with_header do
.gl-flex-grow-1
= html_escape(_("%{strong_open}%{group_name}%{strong_close} projects:")) % { strong_open: '<strong>'.html_safe, group_name: @group.name, strong_close: '</strong>'.html_safe }
- if can? current_user, :admin_group, @group
.controls
= render Pajamas::ButtonComponent.new(href: new_project_path(namespace_id: @group.id), size: :small, variant: :confirm) do
= _("New project")
- - c.body do
+ - c.with_body do
%ul.content-list
- @projects.each_with_index do |project, idx|
%li.project-row.gl-align-items-center{ class: 'gl-display-flex!', data: { qa_selector: 'project_row_container', qa_index: idx } }
diff --git a/app/views/groups/settings/_export.html.haml b/app/views/groups/settings/_export.html.haml
index 6b505755727..1e80c1846a4 100644
--- a/app/views/groups/settings/_export.html.haml
+++ b/app/views/groups/settings/_export.html.haml
@@ -4,7 +4,7 @@
%h4= s_('GroupSettings|Export group')
%p= _('Export this group with all related data.')
= render Pajamas::AlertComponent.new(variant: :warning, dismissible: false, alert_options: { class: 'gl-mb-4' }) do |c|
- = c.body do
+ - c.with_body do
- docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/group/import/index', anchor: 'migrate-groups-by-direct-transfer-recommended') }
- docs_link_end = '</a>'.html_safe
= s_('GroupsNew|This feature is deprecated and replaced by group migration by direct transfer. %{docs_link_start}Learn more%{docs_link_end}.').html_safe % { docs_link_start: docs_link_start, docs_link_end: docs_link_end }
@@ -12,7 +12,7 @@
- export_information = _('After the export is complete, download the data file from a notification email or from this page. You can then import the data file from the %{strong_text_start}Create new group%{strong_text_end} page of another GitLab instance.') % { strong_text_start: '<strong>'.html_safe, strong_text_end: '</strong>'.html_safe}
= export_information.html_safe
= render Pajamas::AlertComponent.new(dismissible: false, alert_options: { class: 'gl-mb-5' }) do |c|
- = c.body do
+ - c.with_body do
%p.gl-mb-0
%p= _('The following items will be exported:')
%ul
diff --git a/app/views/groups/settings/_remove_button.html.haml b/app/views/groups/settings/_remove_button.html.haml
index cb05076b39d..acf11fd8858 100644
--- a/app/views/groups/settings/_remove_button.html.haml
+++ b/app/views/groups/settings/_remove_button.html.haml
@@ -2,7 +2,7 @@
- if group.prevent_delete?
= render Pajamas::AlertComponent.new(dismissible: false, alert_options: { class: 'gl-mb-5', data: { testid: 'group-has-linked-subscription-alert' }}) do |c|
- = c.body do
+ - c.with_body do
= html_escape(_("This group can't be removed because it is linked to a subscription. To remove this group, %{linkStart}link the subscription%{linkEnd} with a different group.")) % { linkStart: "<a href=\"#{help_page_path('subscriptions/gitlab_com/index', anchor: 'change-the-linked-namespace')}\">".html_safe, linkEnd: '</a>'.html_safe }
.js-confirm-danger{ data: group_settings_confirm_modal_data(group, remove_form_id) }
diff --git a/app/views/groups/settings/_transfer.html.haml b/app/views/groups/settings/_transfer.html.haml
index 415459f1584..9ebe3a740b3 100644
--- a/app/views/groups/settings/_transfer.html.haml
+++ b/app/views/groups/settings/_transfer.html.haml
@@ -15,6 +15,6 @@
%li= s_("GroupSettings|If the parent group's visibility is lower than the group's current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility.")
- if group.paid?
= render Pajamas::AlertComponent.new(dismissible: false, alert_options: { class: 'gl-mb-5' }) do |c|
- = c.body do
+ - c.with_body do
= html_escape(_("This group can't be transferred because it is linked to a subscription. To transfer this group, %{linkStart}link the subscription%{linkEnd} with a different group.")) % { linkStart: "<a href=\"#{help_page_path('subscriptions/gitlab_com/index', anchor: 'change-the-linked-namespace')}\">".html_safe, linkEnd: '</a>'.html_safe }
.js-transfer-group-form{ data: initial_data }
diff --git a/app/views/groups/settings/ci_cd/_auto_devops_form.html.haml b/app/views/groups/settings/ci_cd/_auto_devops_form.html.haml
index 8c45809261c..b0a5d0bd4fa 100644
--- a/app/views/groups/settings/ci_cd/_auto_devops_form.html.haml
+++ b/app/views/groups/settings/ci_cd/_auto_devops_form.html.haml
@@ -3,7 +3,7 @@
%fieldset
.form-group
= render Pajamas::CardComponent.new(card_options: { class: 'gl-mb-3' }) do |c|
- - c.body do
+ - c.with_body do
- learn_more_link = link_to _('Learn more.'), help_page_path('topics/autodevops/index.md'), target: '_blank', rel: 'noopener noreferrer'
- help_text = s_('GroupSettings|The Auto DevOps pipeline runs if no alternative CI configuration file is found.')
- badge = gl_badge_tag badge_for_auto_devops_scope(group), variant: :info
diff --git a/app/views/projects/_deletion_failed.html.haml b/app/views/projects/_deletion_failed.html.haml
index 489d303c5b9..29551505a7e 100644
--- a/app/views/projects/_deletion_failed.html.haml
+++ b/app/views/projects/_deletion_failed.html.haml
@@ -4,6 +4,6 @@
= render Pajamas::AlertComponent.new(variant: :warning,
dismissible: false,
alert_options: { class: 'project-deletion-failed-message' }) do |c|
- = c.body do
+ - c.with_body do
This project was scheduled for deletion, but failed with the following message:
= project.delete_error
diff --git a/app/views/projects/_import_project_pane.html.haml b/app/views/projects/_import_project_pane.html.haml
index 1d6e47aca49..947a1007fd5 100644
--- a/app/views/projects/_import_project_pane.html.haml
+++ b/app/views/projects/_import_project_pane.html.haml
@@ -11,7 +11,7 @@
= render Pajamas::AlertComponent.new(variant: :tip,
alert_options: { class: 'gl-my-3' },
dismissible: false) do |c|
- = c.body do
+ - c.with_body do
- docs_link_url = help_page_path('user/group/import/index') + '#migrate-groups-by-direct-transfer-recommended'
- docs_link = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: docs_link_url }
= html_escape(_("Importing GitLab projects? Migrating GitLab projects when migrating groups by direct transfer is in Beta. %{link_start}Learn more.%{link_end}")) % { link_start: docs_link, link_end: '</a>'.html_safe }
diff --git a/app/views/projects/_last_push.html.haml b/app/views/projects/_last_push.html.haml
index 5b493772f0a..89c91887d19 100644
--- a/app/views/projects/_last_push.html.haml
+++ b/app/views/projects/_last_push.html.haml
@@ -3,7 +3,7 @@
= render Pajamas::AlertComponent.new(variant: :success,
alert_options: { class: 'gl-mt-3' },
close_button_options: { class: 'js-close-banner' }) do |c|
- = c.body do
+ - c.with_body do
%span= s_("LastPushEvent|You pushed to")
%strong.gl-display-inline-flex.gl-max-w-50p{ data: { toggle: 'tooltip' }, title: event.ref_name }
= link_to event.ref_name, project_commits_path(event.project, event.ref_name), class: 'ref-name gl-text-truncate'
@@ -15,6 +15,6 @@
#{time_ago_with_tooltip(event.created_at)}
- if create_mr_button_from_event?(event)
- = c.actions do
+ - c.with_actions do
= render Pajamas::ButtonComponent.new(variant: :confirm, href: create_mr_path_from_push_event(event), button_options: { data: { qa_selector: 'create_merge_request_button' }}) do
= _('Create merge request')
diff --git a/app/views/projects/_new_project_fields.html.haml b/app/views/projects/_new_project_fields.html.haml
index 27211ffb1e5..70a2476c8e5 100644
--- a/app/views/projects/_new_project_fields.html.haml
+++ b/app/views/projects/_new_project_fields.html.haml
@@ -46,7 +46,7 @@
= render Pajamas::AlertComponent.new(alert_options: { class: "gl-mb-4 gl-display-none js-user-readme-repo" },
dismissible: false,
variant: :success) do |c|
- = c.body do
+ - c.with_body do
- help_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/profile/index', anchor: 'add-details-to-your-profile-with-a-readme') }
= html_escape(_('%{project_path} is a project that you can use to add a README to your GitLab profile. Create a public project and initialize the repository with a README to get started. %{help_link_start}Learn more.%{help_link_end}')) % { project_path: "<strong>#{current_user.username} / #{current_user.username}</strong>".html_safe, help_link_start: help_link_start, help_link_end: '</a>'.html_safe }
@@ -81,17 +81,17 @@
= render Pajamas::CheckboxTagComponent.new(name: 'project[initialize_with_readme]',
checked: true,
checkbox_options: { data: { qa_selector: 'initialize_with_readme_checkbox', track_label: track_label, track_action: 'activate_form_input', track_property: 'init_with_readme' } }) do |c|
- = c.label do
+ - c.with_label do
= s_('ProjectsNew|Initialize repository with a README')
- = c.help_text do
+ - c.with_help_text do
= s_('ProjectsNew|Allows you to immediately clone this project’s repository. Skip this if you plan to push up an existing repository.')
.form-group
= render Pajamas::CheckboxTagComponent.new(name: 'project[initialize_with_sast]',
checkbox_options: { data: { qa_selector: 'initialize_with_sast_checkbox', track_label: track_label, track_action: 'activate_form_input', track_property: 'init_with_sast' } }) do |c|
- = c.label do
+ - c.with_label do
= s_('ProjectsNew|Enable Static Application Security Testing (SAST)')
- = c.help_text do
+ - c.with_help_text do
= s_('ProjectsNew|Analyze your source code for known security vulnerabilities.')
= link_to _('Learn more.'), help_page_path('user/application_security/sast/index'), target: '_blank', rel: 'noopener noreferrer', data: { track_action: 'followed' }
diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml
index 0f37ae8ad41..195dc03632a 100644
--- a/app/views/projects/blob/edit.html.haml
+++ b/app/views/projects/blob/edit.html.haml
@@ -7,7 +7,7 @@
= render Pajamas::AlertComponent.new(alert_options: { class: 'gl-mb-5 gl-mt-5' },
variant: :danger,
dismissible: false) do |c|
- = c.body do
+ - c.with_body do
- blob_link_start = '<a href="%{url}" class="gl-link" target="_blank" rel="noopener noreferrer">'.html_safe
- link_end = '</a>'.html_safe
- external_link_icon = content_tag 'span', { aria: { label: _('Opens new window') }} do
diff --git a/app/views/projects/branches/_branch_rules_info.haml b/app/views/projects/branches/_branch_rules_info.haml
index 15bee31c596..451d0f9928c 100644
--- a/app/views/projects/branches/_branch_rules_info.haml
+++ b/app/views/projects/branches/_branch_rules_info.haml
@@ -2,10 +2,10 @@
= render Pajamas::AlertComponent.new(variant: :info,
title: s_("Branches|See all branch-related settings together with branch rules"),
alert_options: { class: 'js-branch-rules-info-callout gl-mb-6 gl-mt-4', data: { feature_id: Users::CalloutsHelper::BRANCH_RULES_INFO_CALLOUT, dismiss_endpoint: callouts_path, defer_links: 'true' } }) do |c|
- = c.body do
+ - c.with_body do
= s_("Branches|You can now find an overview of settings for protected branches, merge request approvals, status checks, and security approvals conveniently in one spot.")
- = c.actions do
+ - c.with_actions do
= render Pajamas::ButtonComponent.new(variant: :confirm, href: project_settings_repository_path(@project, anchor: 'js-branch-rules'), button_options: { class: 'deferred-link gl-alert-action' }) do
= s_("Branches|View branch rules")
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-close'}) do
diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml
index 86bed956bc4..9fd9943fd26 100644
--- a/app/views/projects/branches/new.html.haml
+++ b/app/views/projects/branches/new.html.haml
@@ -3,7 +3,7 @@
- if @error
= render Pajamas::AlertComponent.new(variant: :danger) do |c|
- = c.body do
+ - c.with_body do
= @error
%h1.page-title.gl-font-size-h-display
= _('New Branch')
@@ -21,4 +21,3 @@
= render Pajamas::ButtonComponent.new(variant: :confirm, button_options: { type: 'submit', class: 'gl-mr-3' }) do
= _('Create branch')
= link_to _('Cancel'), project_branches_path(@project), class: 'gl-button btn btn-default btn-cancel'
-
diff --git a/app/views/projects/commits/_commits.html.haml b/app/views/projects/commits/_commits.html.haml
index a877af20426..9cbabaee774 100644
--- a/app/views/projects/commits/_commits.html.haml
+++ b/app/views/projects/commits/_commits.html.haml
@@ -38,7 +38,7 @@
%li
= render Pajamas::AlertComponent.new(variant: :warning,
dismissible: false) do |c|
- = c.body do
+ - c.with_body do
= n_('%s additional commit has been omitted to prevent performance issues.', '%s additional commits have been omitted to prevent performance issues.', hidden) % number_with_delimiter(hidden)
- if can_update_merge_request && context_commits&.empty? && !(defined?(@next_page) && @next_page)
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 79202dabff9..02a69f25985 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -11,7 +11,7 @@
= render Pajamas::AlertComponent.new(variant: :info,
title: _('GitLab Pages has moved'),
alert_options: { class: 'gl-my-5', data: { feature_id: Users::CalloutsHelper::PAGES_MOVED_CALLOUT, dismiss_endpoint: callouts_path, defer_links: 'true' } }) do |c|
- = c.body do
+ - c.with_body do
= _('To go to GitLab Pages, on the left sidebar, select %{pages_link}.').html_safe % {pages_link: link_to('Deployments > Pages', project_pages_path(@project)).html_safe}
%section.settings.general-settings.no-animate.expanded#js-general-settings
@@ -39,7 +39,7 @@
= render Pajamas::AlertComponent.new(variant: :info,
title: _('Merge requests and approvals settings have moved.'),
alert_options: { class: 'js-merge-request-settings-callout gl-my-5', data: { feature_id: Users::CalloutsHelper::MERGE_REQUEST_SETTINGS_MOVED_CALLOUT, dismiss_endpoint: callouts_path, defer_links: 'true' } }) do |c|
- = c.body do
+ - c.with_body do
= _('On the left sidebar, select %{merge_requests_link} to view them.').html_safe % { merge_requests_link: link_to('Settings > Merge requests', project_settings_merge_requests_path(@project)).html_safe }
= render_if_exists 'projects/settings/analytics', expanded: expanded
diff --git a/app/views/projects/forks/error.html.haml b/app/views/projects/forks/error.html.haml
index 022a96b15a7..cff5899b960 100644
--- a/app/views/projects/forks/error.html.haml
+++ b/app/views/projects/forks/error.html.haml
@@ -4,7 +4,7 @@
variant: :danger,
alert_options: { class: 'gl-mt-5' },
dismissible: false) do |c|
- = c.body do
+ - c.with_body do
%p
= _("You tried to fork %{link_to_the_project} but it failed for the following reason:").html_safe % { link_to_the_project: link_to_project(@project) }
@@ -17,5 +17,5 @@
- else
= error
- = c.actions do
+ - c.with_actions do
= link_to _('Try to fork again'), new_project_fork_path(@project), title: _("Fork"), class: "btn gl-alert-action btn-info btn-md gl-button"
diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml
index efb364bd013..af337082141 100644
--- a/app/views/projects/imports/new.html.haml
+++ b/app/views/projects/imports/new.html.haml
@@ -9,7 +9,7 @@
dismissible: false,
variant: :danger,
alert_options: { class: 'gl-mb-5' }) do |c|
- = c.body do
+ - c.with_body do
= @project.import_state.last_error
= gitlab_ui_form_for @project, url: project_import_path(@project), method: :post, html: { class: 'js-project-import' } do |f|
diff --git a/app/views/projects/issues/service_desk/_alert_moved_from_service_desk.html.haml b/app/views/projects/issues/service_desk/_alert_moved_from_service_desk.html.haml
index cc8d5bdaeec..b087a1d0151 100644
--- a/app/views/projects/issues/service_desk/_alert_moved_from_service_desk.html.haml
+++ b/app/views/projects/issues/service_desk/_alert_moved_from_service_desk.html.haml
@@ -4,5 +4,5 @@
= render Pajamas::AlertComponent.new(variant: :warning,
alert_options: { class: 'hide js-alert-moved-from-service-desk-warning gl-mt-5' }) do |c|
- = c.body do
+ - c.with_body do
= s_('This project does not have %{service_desk_link_start}Service Desk%{service_desk_link_end} enabled, so the user who created the issue will no longer receive email notifications about new activity.').html_safe % { service_desk_link_start: service_desk_link_start, service_desk_link_end: '</a>'.html_safe }
diff --git a/app/views/projects/mattermosts/new.html.haml b/app/views/projects/mattermosts/new.html.haml
index 025ca1e1fd4..c73c256c1c3 100644
--- a/app/views/projects/mattermosts/new.html.haml
+++ b/app/views/projects/mattermosts/new.html.haml
@@ -5,7 +5,7 @@
- if @teams_error_message
= render Pajamas::AlertComponent.new(variant: :danger) do |c|
- = c.body do
+ - c.with_body do
= @teams_error_message
%h3
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index e7510d6c4cb..9142893d400 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -4,7 +4,7 @@
= render Pajamas::CheckboxTagComponent.new(name: dom_id(merge_request, "selected"),
value: nil,
checkbox_options: { 'data-id' => merge_request.id }) do |c|
- = c.label do
+ - c.with_label do
%span.gl-sr-only
= merge_request.title
diff --git a/app/views/projects/merge_requests/_mr_title.html.haml b/app/views/projects/merge_requests/_mr_title.html.haml
index aee746100ea..15339becb74 100644
--- a/app/views/projects/merge_requests/_mr_title.html.haml
+++ b/app/views/projects/merge_requests/_mr_title.html.haml
@@ -9,7 +9,7 @@
= render Pajamas::AlertComponent.new(alert_options: { class: 'gl-mb-5' },
variant: :danger,
dismissible: false) do |c|
- = c.body do
+ - c.with_body do
= _('The source project of this merge request has been removed.')
.detail-page-header.border-bottom-0.gl-display-block.gl-pt-5{ class: "gl-md-display-flex! #{'is-merge-request' if moved_mr_sidebar_enabled? && !fluid_layout}" }
diff --git a/app/views/projects/merge_requests/invalid.html.haml b/app/views/projects/merge_requests/invalid.html.haml
index 4596fcd280d..4ea33cbbd99 100644
--- a/app/views/projects/merge_requests/invalid.html.haml
+++ b/app/views/projects/merge_requests/invalid.html.haml
@@ -11,7 +11,7 @@
= render Pajamas::AlertComponent.new(variant: :danger,
dismissible: false) do |c|
- = c.body do
+ - c.with_body do
- if @merge_request.for_fork? && !@merge_request.source_project
= err_fork_project_removed
- elsif !@merge_request.source_branch_exists?
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index 95ef856daba..36500e157b4 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -16,7 +16,7 @@
= render Pajamas::AlertComponent.new(dismissible: false,
alert_options: { class: 'gl-mt-3 gl-mb-5',
data: { testid: 'no-issues-alert' }}) do |c|
- = c.body do
+ - c.with_body do
= _('Assign some issues to this milestone.')
- else
= render 'shared/milestones/milestone_complete_alert', milestone: @milestone do
diff --git a/app/views/projects/mirrors/_mirror_repos.html.haml b/app/views/projects/mirrors/_mirror_repos.html.haml
index 4cfe463fa38..110bc8d82f8 100644
--- a/app/views/projects/mirrors/_mirror_repos.html.haml
+++ b/app/views/projects/mirrors/_mirror_repos.html.haml
@@ -33,7 +33,7 @@
= f.submit _('Mirror repository'), class: 'js-mirror-submit', name: :update_remote_mirror, pajamas_button: true, data: { qa_selector: 'mirror_repository_button' }
- else
= render Pajamas::AlertComponent.new(dismissible: false) do |c|
- = c.body do
+ - c.with_body do
= _('Mirror settings are only available to GitLab administrators.')
= render 'projects/mirrors/mirror_repos_list'
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 52ac8b58c9a..59a21cecd39 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -27,7 +27,7 @@
#create-from-template-pane.tab-pane
= render Pajamas::CardComponent.new(card_options: { class: 'gl-my-5' }) do |c|
- = c.body do
+ - c.with_body do
%div
- contributing_templates_url = 'https://gitlab.com/gitlab-org/project-templates/contributing'
- link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: contributing_templates_url }
diff --git a/app/views/projects/pages_domains/_form.html.haml b/app/views/projects/pages_domains/_form.html.haml
index 9d9603b0947..bca955dcdae 100644
--- a/app/views/projects/pages_domains/_form.html.haml
+++ b/app/views/projects/pages_domains/_form.html.haml
@@ -1,6 +1,6 @@
- if domain_presenter.errors.any?
= render Pajamas::AlertComponent.new(variant: :danger, dismissible: false) do |c|
- = c.body do
+ - c.with_body do
- domain_presenter.errors.full_messages.each do |msg|
= msg
diff --git a/app/views/projects/pages_domains/show.html.haml b/app/views/projects/pages_domains/show.html.haml
index 5de5188ae6a..89e64d607a6 100644
--- a/app/views/projects/pages_domains/show.html.haml
+++ b/app/views/projects/pages_domains/show.html.haml
@@ -7,7 +7,7 @@
- if verification_enabled && domain_presenter.unverified?
= content_for :flash_message do
= render Pajamas::AlertComponent.new(variant: :warning, dismissible: false) do |c|
- = c.body do
+ - c.with_body do
.container-fluid.container-limited
= _("This domain is not verified. You will need to verify ownership before access is enabled.")
diff --git a/app/views/projects/pipelines/show.html.haml b/app/views/projects/pipelines/show.html.haml
index 15d729c89b9..a7d670f8475 100644
--- a/app/views/projects/pipelines/show.html.haml
+++ b/app/views/projects/pipelines/show.html.haml
@@ -21,7 +21,7 @@
variant: :danger,
dismissible: false,
alert_options: { class: 'gl-mb-5' }) do |c|
- = c.body do
+ - c.with_body do
%ul
- @pipeline.yaml_errors.split("\n").each do |error|
%li= error
diff --git a/app/views/projects/settings/integrations/_form.html.haml b/app/views/projects/settings/integrations/_form.html.haml
index 97d90976f18..39dfd410727 100644
--- a/app/views/projects/settings/integrations/_form.html.haml
+++ b/app/views/projects/settings/integrations/_form.html.haml
@@ -8,7 +8,7 @@
= render Pajamas::AlertComponent.new(title: s_('ExternalIssueIntegration|Another issue tracker is already in use'),
variant: :warning,
dismissible: false) do |c|
- = c.body do
+ - c.with_body do
= s_('ExternalIssueIntegration|Only one issue tracker integration can be active at a time. Please disable the active tracker first and try again.')
- if integration.to_param === 'slack'
diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml
index bd37a6258a2..1df323e7451 100644
--- a/app/views/projects/tags/new.html.haml
+++ b/app/views/projects/tags/new.html.haml
@@ -4,7 +4,7 @@
- if @error
= render Pajamas::AlertComponent.new(variant: :danger, dismissible: true) do |c|
- = c.body do
+ - c.with_body do
= @error
%h1.page-title.gl-font-size-h-display
@@ -38,4 +38,3 @@
= s_('TagsPage|Create tag')
= render Pajamas::ButtonComponent.new(href: project_tags_path(@project)) do
= s_('TagsPage|Cancel')
-
diff --git a/app/views/projects/usage_quotas/index.html.haml b/app/views/projects/usage_quotas/index.html.haml
index 1dd179f7b38..aad96151678 100644
--- a/app/views/projects/usage_quotas/index.html.haml
+++ b/app/views/projects/usage_quotas/index.html.haml
@@ -7,7 +7,7 @@
= render Pajamas::AlertComponent.new(title: _('Repository usage recalculation started'),
variant: :info,
alert_options: { class: 'js-recalculation-started-alert gl-mt-4 gl-mb-5 gl-display-none' }) do |c|
- = c.body do
+ - c.with_body do
= _('To view usage, refresh this page in a few minutes.')
%h1.page-title.gl-font-size-h-display
diff --git a/app/workers/gitlab/github_import/stage/import_attachments_worker.rb b/app/workers/gitlab/github_import/stage/import_attachments_worker.rb
index e4a413b4081..4045852e3f0 100644
--- a/app/workers/gitlab/github_import/stage/import_attachments_worker.rb
+++ b/app/workers/gitlab/github_import/stage/import_attachments_worker.rb
@@ -15,7 +15,7 @@ module Gitlab
# client - An instance of Gitlab::GithubImport::Client.
# project - An instance of Project.
def import(client, project)
- return skip_to_next_stage(project) if feature_disabled?(project)
+ return skip_to_next_stage(project) if import_settings(project).disabled?(:attachments_import)
waiters = importers.each_with_object({}) do |importer, hash|
waiter = start_importer(project, importer, client)
@@ -53,10 +53,6 @@ module Gitlab
:protected_branches
)
end
-
- def feature_disabled?(project)
- import_settings(project).disabled?(:attachments_import)
- end
end
end
end
diff --git a/app/workers/gitlab/github_import/stage/import_collaborators_worker.rb b/app/workers/gitlab/github_import/stage/import_collaborators_worker.rb
index d63d1fd3f5f..8f72cc051b3 100644
--- a/app/workers/gitlab/github_import/stage/import_collaborators_worker.rb
+++ b/app/workers/gitlab/github_import/stage/import_collaborators_worker.rb
@@ -15,8 +15,10 @@ module Gitlab
# client - An instance of Gitlab::GithubImport::Client.
# project - An instance of Project.
def import(client, project)
+ return skip_to_next_stage(project) if import_settings(project).disabled?(:collaborators_import) ||
+ !has_push_access?(client, project.import_source)
+
info(project.id, message: 'starting importer', importer: 'Importer::CollaboratorsImporter')
- return skip_to_next_stage(project) unless has_push_access?(client, project.import_source)
waiter = Importer::CollaboratorsImporter.new(project, client).execute
project.import_state.refresh_jid_expiration
diff --git a/config/feature_flags/development/remove_deprecated_approvals.yml b/config/feature_flags/development/adapt_deprecated_approvals.yml
index 4a3501cc7ff..697f7e21025 100644
--- a/config/feature_flags/development/remove_deprecated_approvals.yml
+++ b/config/feature_flags/development/adapt_deprecated_approvals.yml
@@ -1,5 +1,5 @@
---
-name: remove_deprecated_approvals
+name: adapt_deprecated_approvals
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118036
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/409686
milestone: '16.0'
diff --git a/config/feature_flags/development/auto_merge_labels_mr_widget.yml b/config/feature_flags/development/auto_merge_labels_mr_widget.yml
index 3e84dbd732d..ea689058ee2 100644
--- a/config/feature_flags/development/auto_merge_labels_mr_widget.yml
+++ b/config/feature_flags/development/auto_merge_labels_mr_widget.yml
@@ -1,8 +1,8 @@
---
name: auto_merge_labels_mr_widget
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/115436
-rollout_issue_url:
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/409530
milestone: '15.11'
type: development
group: group::pipeline execution
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/sast_reports_in_inline_diff.yml b/config/feature_flags/development/sast_reports_in_inline_diff.yml
new file mode 100644
index 00000000000..2e320e8e602
--- /dev/null
+++ b/config/feature_flags/development/sast_reports_in_inline_diff.yml
@@ -0,0 +1,8 @@
+---
+name: sast_reports_in_inline_diff
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/119975
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/410191
+milestone: '16.0'
+type: development
+group: group::static analysis
+default_enabled: false
diff --git a/config/feature_flags/ops/partition_manager_sync_partitions.yml b/config/feature_flags/ops/partition_manager_sync_partitions.yml
new file mode 100644
index 00000000000..11de5d02061
--- /dev/null
+++ b/config/feature_flags/ops/partition_manager_sync_partitions.yml
@@ -0,0 +1,8 @@
+---
+name: partition_manager_sync_partitions
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/120378
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/410997
+milestone: '16.0'
+type: ops
+group: group::database
+default_enabled: true
diff --git a/doc/.vale/gitlab/spelling-exceptions.txt b/doc/.vale/gitlab/spelling-exceptions.txt
index b48a9e5dcb3..476b1eebf84 100644
--- a/doc/.vale/gitlab/spelling-exceptions.txt
+++ b/doc/.vale/gitlab/spelling-exceptions.txt
@@ -585,7 +585,7 @@ mitigations
mitmproxy
mixin
mixins
-MLFlow
+MLflow
Mmap
mockup
mockups
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index e5f1bc2bdd6..c0a175c76cd 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -39,7 +39,7 @@ The following metrics are available:
| `gitlab_cache_misses_total` | Counter | 10.2 | Cache read miss | `controller`, `action` |
| `gitlab_cache_operation_duration_seconds` | Histogram | 10.2 | Cache access time | `operation`, `store` |
| `gitlab_cache_operations_total` | Counter | 12.2 | Cache operations by controller or action | `controller`, `action`, `operation`, `store` |
-| `gitlab_cache_read_multikey_count` | Histogram | 15.7 | Count of keys in multi-key cache read operations | `controller`, `action` |
+| `gitlab_cache_read_multikey_count` | Histogram | 15.7 | Count of keys in multi-key cache read operations | `controller`, `action`, `store` |
| `gitlab_ci_pipeline_builder_scoped_variables_duration` | Histogram | 14.5 | Time in seconds it takes to create the scoped variables for a CI/CD job
| `gitlab_ci_pipeline_creation_duration_seconds` | Histogram | 13.0 | Time in seconds it takes to create a CI/CD pipeline | `gitlab` |
| `gitlab_ci_pipeline_size_builds` | Histogram | 13.1 | Total number of builds within a pipeline grouped by a pipeline source | `source` |
diff --git a/doc/api/api_resources.md b/doc/api/api_resources.md
index bc4521c9857..4a70786b6ee 100644
--- a/doc/api/api_resources.md
+++ b/doc/api/api_resources.md
@@ -26,7 +26,6 @@ The following API resources are available in the project context:
| [Access requests](access_requests.md) | `/projects/:id/access_requests` (also available for groups) |
| [Access tokens](project_access_tokens.md) | `/projects/:id/access_tokens` (also available for groups) |
| [Agents](cluster_agents.md) | `/projects/:id/cluster_agents` |
-| [Award emoji](award_emoji.md) | `/projects/:id/issues/.../award_emoji`, `/projects/:id/merge_requests/.../award_emoji`, `/projects/:id/snippets/.../award_emoji` |
| [Branches](branches.md) | `/projects/:id/repository/branches/`, `/projects/:id/repository/merged_branches` |
| [Commits](commits.md) | `/projects/:id/repository/commits`, `/projects/:id/statuses` |
| [Container Registry](container_registry.md) | `/projects/:id/registry/repositories` |
@@ -41,6 +40,7 @@ The following API resources are available in the project context:
| [Deployments](deployments.md) | `/projects/:id/deployments` |
| [Discussions](discussions.md) (threaded comments) | `/projects/:id/issues/.../discussions`, `/projects/:id/snippets/.../discussions`, `/projects/:id/merge_requests/.../discussions`, `/projects/:id/commits/.../discussions` (also available for groups) |
| [Draft Notes](draft_notes.md) (comments) | `/projects/:id/merge_requests/.../draft_notes`
+| [Emoji reactions](award_emoji.md) | `/projects/:id/issues/.../award_emoji`, `/projects/:id/merge_requests/.../award_emoji`, `/projects/:id/snippets/.../award_emoji` |
| [Environments](environments.md) | `/projects/:id/environments` |
| [Error Tracking](error_tracking.md) | `/projects/:id/error_tracking/settings` |
| [Events](events.md) | `/projects/:id/events` (also available for users and standalone) |
diff --git a/doc/api/award_emoji.md b/doc/api/award_emoji.md
index a669c6d00c3..a22c61b3391 100644
--- a/doc/api/award_emoji.md
+++ b/doc/api/award_emoji.md
@@ -4,28 +4,30 @@ group: Project Management
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
---
-# Award emojis API **(FREE)**
+# Emoji reactions API **(FREE)**
-An [awarded emoji](../user/award_emojis.md) tells a thousand words.
+> [Renamed](https://gitlab.com/gitlab-org/gitlab/-/issues/409884) from "award emojis" to "emoji reactions" in GitLab 16.0.
-We call GitLab objects on which you can award an emoji "awardables". You can award emojis on the following:
+An [emoji reaction](../user/award_emojis.md) tells a thousand words.
+
+We call GitLab objects on which you can react with an emoji "awardables".
+You can react with emojis on the following:
- [Epics](../user/group/epics/index.md) ([API](epics.md)). **(PREMIUM)**
- [Issues](../user/project/issues/index.md) ([API](issues.md)).
- [Merge requests](../user/project/merge_requests/index.md) ([API](merge_requests.md)).
- [Snippets](../user/snippets.md) ([API](snippets.md)).
-
-Emojis can also [be awarded](../user/award_emojis.md#award-emojis-for-comments) on comments (also known as notes). See also [Notes API](notes.md).
+- [Comments](../user/award_emojis.md#emoji-reactions-for-comments) ([API](notes.md)).
## Issues, merge requests, and snippets
-See [Award emojis on comments](#award-emojis-on-comments) for information on using these endpoints with comments.
+For information on using these endpoints with comments, see [Add reactions to comments](#add-reactions-to-comments).
-### List an awardable's award emojis
+### List an awardable's emoji reactions
> [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/335068) in GitLab 15.1 to allow unauthenticated access to public awardables.
-Get a list of all award emojis for a specified awardable. This endpoint can
+Get a list of all emoji reactions for a specified awardable. This endpoint can
be accessed without authentication if the awardable is publicly accessible.
```plaintext
@@ -86,11 +88,11 @@ Example response:
]
```
-### Get single award emoji
+### Get single emoji reaction
> [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/335068) in GitLab 15.1 to allow unauthenticated access to public awardables.
-Get a single award emoji from an issue, snippet, or merge request. This endpoint can
+Get a single emoji reaction from an issue, snippet, or merge request. This endpoint can
be accessed without authentication if the awardable is publicly accessible.
```plaintext
@@ -105,7 +107,7 @@ Parameters:
|:---------------|:---------------|:---------|:-----------------------------------------------------------------------------|
| `id` | integer/string | yes | ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding). |
| `issue_iid`/`merge_request_iid`/`snippet_id` | integer | yes | ID (`iid` for merge requests/issues, `id` for snippets) of an awardable. |
-| `award_id` | integer | yes | ID of the award emoji. |
+| `award_id` | integer | yes | ID of the emoji reaction. |
Example request:
@@ -134,9 +136,9 @@ Example response:
}
```
-### Award a new emoji
+### Add a new emoji reaction
-Create an award emoji on the specified awardable.
+Add an emoji reaction on the specified awardable.
```plaintext
POST /projects/:id/issues/:issue_iid/award_emoji
@@ -177,11 +179,11 @@ Example Response:
}
```
-### Delete an award emoji
+### Delete an emoji reaction
-Sometimes it's just not meant to be and you need to remove the award.
+Sometimes it's just not meant to be and you need to remove your reaction.
-Only an administrator or the author of the award can delete an award emoji.
+Only an administrator or the author of the reaction can delete an emoji reaction.
```plaintext
DELETE /projects/:id/issues/:issue_iid/award_emoji/:award_id
@@ -195,26 +197,26 @@ Parameters:
|:---------------|:---------------|:---------|:-----------------------------------------------------------------------------|
| `id` | integer/string | yes | ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding). |
| `issue_iid`/`merge_request_iid`/`snippet_id` | integer | yes | ID (`iid` for merge requests/issues, `id` for snippets) of an awardable. |
-| `award_id` | integer | yes | ID of an award emoji. |
+| `award_id` | integer | yes | ID of an emoji reaction. |
```shell
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/issues/80/award_emoji/344"
```
-## Award emojis on comments
+## Add reactions to comments
Comments (also known as notes) are a sub-resource of issues, merge requests, and snippets.
NOTE:
-The examples below describe working with award emojis on an issue's comments, but can be
+The examples below describe working with emoji reactions on an issue's comments, but can be
adapted to comments on merge requests and snippets. Therefore, you have to replace
`issue_iid` either with `merge_request_iid` or with the `snippet_id`.
-### List a comment's award emojis
+### List a comment's emoji reactions
> [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/335068) in GitLab 15.1 to allow unauthenticated access to public comments.
-Get all award emojis for a comment (note). This endpoint can
+Get all emoji reactions for a comment (note). This endpoint can
be accessed without authentication if the comment is publicly accessible.
```plaintext
@@ -258,11 +260,11 @@ Example response:
]
```
-### Get an award emoji for a comment
+### Get an emoji reaction for a comment
> [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/335068) in GitLab 15.1 to allow unauthenticated access to public comments.
-Get a single award emoji for a comment (note). This endpoint can
+Get a single emoji reaction for a comment (note). This endpoint can
be accessed without authentication if the comment is publicly accessible.
```plaintext
@@ -276,7 +278,7 @@ Parameters:
| `id` | integer/string | yes | ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding). |
| `issue_iid` | integer | yes | Internal ID of an issue. |
| `note_id` | integer | yes | ID of a comment (note). |
-| `award_id` | integer | yes | ID of the award emoji. |
+| `award_id` | integer | yes | ID of the emoji reaction. |
Example request:
@@ -307,7 +309,7 @@ Example response:
### Award a new emoji on a comment
-Create an award emoji on the specified comment (note).
+Create an emoji reaction on the specified comment (note).
```plaintext
POST /projects/:id/issues/:issue_iid/notes/:note_id/award_emoji
@@ -349,11 +351,11 @@ Example response:
}
```
-### Delete an award emoji from a comment
+### Delete an emoji reaction from a comment
-Sometimes it's just not meant to be and you need to remove the award.
+Sometimes it's just not meant to be and you need to remove the reaction.
-Only an administrator or the author of the award can delete an award emoji.
+Only an administrator or the author of the reaction can delete an emoji reaction.
```plaintext
DELETE /projects/:id/issues/:issue_iid/notes/:note_id/award_emoji/:award_id
@@ -366,7 +368,7 @@ Parameters:
| `id` | integer/string | yes | ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding). |
| `issue_iid` | integer | yes | Internal ID of an issue. |
| `note_id` | integer | yes | ID of a comment (note). |
-| `award_id` | integer | yes | ID of an award emoji. |
+| `award_id` | integer | yes | ID of an emoji reaction. |
Example request:
diff --git a/doc/api/graphql/getting_started.md b/doc/api/graphql/getting_started.md
index e5f58c7efa8..237c0cc6934 100644
--- a/doc/api/graphql/getting_started.md
+++ b/doc/api/graphql/getting_started.md
@@ -183,7 +183,7 @@ Mutations generally use InputTypes and variables, neither of which appear here.
Mutations have:
-- Inputs. For example, arguments, such as which emoji you'd like to award,
+- Inputs. For example, arguments, such as which emoji reaction you'd like to add,
and to which object.
- Return statements. That is, what you'd like to get back when it's successful.
- Errors. Always ask for what went wrong, just in case.
@@ -299,7 +299,7 @@ mutation DisableCI_JOB_TOKENscope {
inboundJobTokenScopeEnabled
jobTokenScopeEnabled
}
- errors
+ errors
}
}
```
diff --git a/doc/api/import.md b/doc/api/import.md
index 7f25e9236c3..be70868cca5 100644
--- a/doc/api/import.md
+++ b/doc/api/import.md
@@ -12,6 +12,7 @@ Use the Import API to import repositories from GitHub or Bitbucket Server.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/381902) in GitLab 15.8, GitLab no longer automatically creates namespaces or groups if the namespace or group name specified in `target_namespace` doesn't exist. GitLab also no longer falls back to using the user's personal namespace if the namespace or group name is taken or `target_namespace` is blank.
> - Requirement for Maintainer role instead of Developer role introduced in GitLab 16.0 and backported to GitLab 15.11.1 and GitLab 15.10.5.
+> - `collaborators_import` key in `optional_stages` was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/398154) in GitLab 16.0.
Import your projects from GitHub to GitLab using the API.
@@ -48,7 +49,8 @@ curl --request POST \
"optional_stages": {
"single_endpoint_issue_events_import": true,
"single_endpoint_notes_import": true,
- "attachments_import": true
+ "attachments_import": true,
+ "collaborators_import": true
}
}'
```
@@ -58,6 +60,7 @@ The following keys are available for `optional_stages`:
- `single_endpoint_issue_events_import`, for issue and pull request events import.
- `single_endpoint_notes_import`, for an alternative and more thorough comments import.
- `attachments_import`, for Markdown attachments import.
+- `collaborators_import`, for importing direct repository collaborators who are not outside collaborators.
For more information, see [Select additional items to import](../user/project/import/github.md#select-additional-items-to-import).
diff --git a/doc/api/notes.md b/doc/api/notes.md
index bdcbbdf9728..41b4ab7fd9a 100644
--- a/doc/api/notes.md
+++ b/doc/api/notes.md
@@ -272,7 +272,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
### Create new snippet note
Creates a new note for a single snippet. Snippet notes are user comments on snippets.
-If you create a note where the body only contains an Award Emoji, GitLab returns this object.
+If you create a note where the body only contains an emoji reaction, GitLab returns this object.
```plaintext
POST /projects/:id/snippets/:snippet_id/notes
@@ -408,7 +408,7 @@ lines in a merge request. For other approaches with more granular control, see
and [Create a new thread in the merge request diff](discussions.md#create-a-new-thread-in-the-merge-request-diff)
in the Discussions API.
-If you create a note where the body only contains an award emoji, GitLab returns this object.
+If you create a note where the body only contains an emoji reaction, GitLab returns this object.
```plaintext
POST /projects/:id/merge_requests/:merge_request_iid/notes
@@ -542,7 +542,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
### Create new epic note
Creates a new note for a single epic. Epic notes are comments users can post to an epic.
-If you create a note where the body only contains an Award Emoji, GitLab returns this object.
+If you create a note where the body only contains an emoji reaction, GitLab returns this object.
```plaintext
POST /groups/:id/epics/:epic_id/notes
diff --git a/doc/ci/caching/index.md b/doc/ci/caching/index.md
index b87f34aa633..e9d3dae3837 100644
--- a/doc/ci/caching/index.md
+++ b/doc/ci/caching/index.md
@@ -91,10 +91,37 @@ test-job:
```
If multiple caches are combined with a fallback cache key,
-the fallback cache is fetched every time a cache is not found.
+the global fallback cache is fetched every time a cache is not found.
## Use a fallback cache key
+### Per-cache fallback keys
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/110467) in GitLab 16.0
+
+Each cache entry supports up-to 5 fallback keys:
+
+```yaml
+test-job:
+ stage: build
+ cache:
+ - key: cache-$CI_COMMIT_REF_SLUG
+ fallback_keys:
+ - cache-$CI_DEFAULT_BRANCH
+ - cache-default
+ paths:
+ - vendor/ruby
+ script:
+ - bundle config set --local path 'vendor/ruby'
+ - bundle install
+ - yarn install --cache-folder .yarn-cache
+ - echo Run tests...
+```
+
+Fallback keys follows the same processing logic as `cache:key`, meaning that the fullname may include a `-$index` (based on cache clearance) and `-protected`/`-non_protected` (if cache separation enabled on protected branches) suffixes.
+
+### Global fallback key
+
> [Introduced](https://gitlab.com/gitlab-org/gitlab-runner/-/merge_requests/1534) in GitLab Runner 13.4.
You can use the `$CI_COMMIT_REF_SLUG` [predefined variable](../variables/predefined_variables.md)
@@ -120,6 +147,14 @@ job1:
- binaries/
```
+The order of caches extraction is:
+
+1. Retrieval attempt for `cache:key`
+1. Retrieval attemps for each entry in order in `fallback_keys`
+1. Retrieval attempt for the global fallback key in `CACHE_FALLBACK_KEY`
+
+The cache extraction process stops after the first successful cache is retrieved.
+
## Disable cache for specific jobs
If you define the cache globally, each job uses the
@@ -623,10 +658,10 @@ job B:
```
1. `job A` runs.
-1. `public/` is cached as cache.zip.
+1. `public/` is cached as `cache.zip`.
1. `job B` runs.
1. The previous cache, if any, is unzipped.
-1. `vendor/` is cached as cache.zip and overwrites the previous one.
+1. `vendor/` is cached as `cache.zip` and overwrites the previous one.
1. The next time `job A` runs it uses the cache of `job B` which is different
and thus isn't effective.
diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md
index 49dbd08c90b..ab5226c1c30 100644
--- a/doc/ci/yaml/index.md
+++ b/doc/ci/yaml/index.md
@@ -1480,6 +1480,33 @@ faster-test-job:
- echo "Running tests..."
```
+#### `cache:fallback_keys`
+
+Use `cache:fallback_keys` to specify a list of keys to try to restore cache from
+if there is no cache found for the `cache:key`. Caches are retrieved in the order specified
+in the `fallback_keys` section.
+
+**Keyword type**: Job keyword. You can use it only as part of a job or in the
+[`default` section](#default).
+
+**Possible inputs**:
+
+- An array of cache keys
+
+**Example of `cache:fallback_keys`**:
+
+```yaml
+rspec:
+ script: rspec
+ cache:
+ key: gems-$CI_COMMIT_REF_SLUG
+ paths:
+ - rspec/
+ fallback_keys:
+ - gems
+ when: 'always'
+```
+
### `coverage`
Use `coverage` with a custom regular expression to configure how code coverage
diff --git a/doc/development/ai_features.md b/doc/development/ai_features.md
index d53b2027412..6ed1d59c3e0 100644
--- a/doc/development/ai_features.md
+++ b/doc/development/ai_features.md
@@ -323,7 +323,6 @@ module EE
with_scope :subject
condition(:ai_available) do
::Feature.enabled?(:openai_experimentation) &&
- subject_container.root_ancestor.experiment_features_enabled &&
@subject.send_to_ai?
end
@@ -350,10 +349,33 @@ Some resources already implement `send_to_ai?`. Make sure yours does as well. In
# ee/app/models/concerns/ee
def send_to_ai?
- !try(:confidential) && resource_parent.public? && resource_parent.third_party_ai_features_enabled
+ !try(:confidential) && resource_parent.public?
end
```
+### Check if feature is allowed for this resource based on namespace settings
+
+There are two settings allowed on root namespace level that restrict the use of AI features:
+
+- `experiment_features_enabled`
+- `third_party_ai_features_enabled`.
+
+To check if that feature is allowed for a given namespace, call:
+
+```ruby
+Gitlab::Llm::StageCheck.available?(namespace, :name_of_the_feature)
+```
+
+Add the name of the feature to the `Gitlab::Llm::StageCheck` class. There are arrays there that differentiate
+between experimental and beta features.
+
+This way we are ready for the following different cases:
+
+- If the feature is not in any array, the check will return `true`. For example, the feature was moved to GA and does not use a third-party setting.
+- If feature is in GA, but uses a third-party setting, the class will return a proper answer based on the namespace third-party setting.
+
+To move the feature from the experimental phase to the beta phase, move the name of the feature from the `EXPERIMENTAL_FEATURES` array to the `BETA_FEATURES` array.
+
### Implement calls to AI APIs and the prompts
The `CompletionWorker` will call the `Completions::Factory` which will initialize the Service and execute the actual call to the API.
diff --git a/doc/development/code_review.md b/doc/development/code_review.md
index 341fac1a1b9..f2edc882d91 100644
--- a/doc/development/code_review.md
+++ b/doc/development/code_review.md
@@ -394,9 +394,8 @@ as a reviewer, it is recommended that they are not also picked as the maintainer
Maintainers should check before merging if the merge request is approved by the
required approvers. If still awaiting further approvals from others, remove yourself as a reviewer then `@` mention the author and explain why in a comment. Stay as reviewer if you're merging the code.
-Note that certain merge requests may target a stable branch. These are rare
-events. These types of merge requests cannot be merged by the Maintainer.
-Instead, these should be sent to the [Release Manager](https://about.gitlab.com/community/release-managers/).
+Certain merge requests may target a stable branch. For an overview of how to handle these requests,
+see the [patch release runbook](https://gitlab.com/gitlab-org/release/docs/-/blob/master/general/patch/engineers.md).
After merging, a maintainer should stay as the reviewer listed on the merge request.
diff --git a/doc/development/contributing/issue_workflow.md b/doc/development/contributing/issue_workflow.md
index 583b39ebe56..50e87fc5341 100644
--- a/doc/development/contributing/issue_workflow.md
+++ b/doc/development/contributing/issue_workflow.md
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
**Before you submit an issue, [search the issue tracker](https://gitlab.com/gitlab-org/gitlab/-/issues)**
for similar entries. Someone else might have already had the same bug or feature proposal.
-If you find an existing issue, show your support with an award emoji and add your notes to the discussion.
+If you find an existing issue, show your support with an emoji reaction and add your notes to the discussion.
To submit a bug:
diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md
index 18d962451e4..6811b3d6584 100644
--- a/doc/development/documentation/index.md
+++ b/doc/development/documentation/index.md
@@ -187,9 +187,8 @@ Further needs for what would make the doc even better should be immediately addr
in a follow-up merge request or issue.
If the release version you want to add the documentation to has already been
-frozen or released, use the label `~"Pick into X.Y"` to get it merged into
-the correct release. Avoid picking into a past release as much as you can, as
-it increases the work of the release managers.
+released, follow the [patch release runbook](https://gitlab.com/gitlab-org/release/docs/-/blob/master/general/patch/engineers.md)
+to get it merged into the current version.
## GitLab `/help`
diff --git a/doc/development/fe_guide/emojis.md b/doc/development/fe_guide/emojis.md
index 3296783a616..c93e1bb34c5 100644
--- a/doc/development/fe_guide/emojis.md
+++ b/doc/development/fe_guide/emojis.md
@@ -30,7 +30,7 @@ when your platform does not support it.
- Negative intent should be set to `1.5`.
1. Ensure you see new individual images copied into `app/assets/images/emoji/`
1. Ensure you can see the new emojis and their aliases in the GitLab Flavored Markdown (GLFM) Autocomplete
- 1. Ensure you can see the new emojis and their aliases in the award emoji menu
+ 1. Ensure you can see the new emojis and their aliases in the emoji reactions menu
1. You might need to add new emoji Unicode support checks and rules for platforms
that do not support a certain emoji and we need to fallback to an image.
See `app/assets/javascripts/emoji/support/is_emoji_unicode_supported.js`
diff --git a/doc/development/feature_flags/controls.md b/doc/development/feature_flags/controls.md
index 0e88c68cc99..d341cb3f1ba 100644
--- a/doc/development/feature_flags/controls.md
+++ b/doc/development/feature_flags/controls.md
@@ -410,8 +410,8 @@ take one of the following actions:
To remove a feature flag, open **one merge request** to make the changes. In the MR:
1. Add the ~"feature flag" label so release managers are aware of the removal.
-1. If the merge request has to be picked into a stable branch, add the
- appropriate `~"Pick into X.Y"` label, for example `~"Pick into 13.0"`.
+1. If the merge request has to be backported into the current version, follow the
+ [patch release runbook](https://gitlab.com/gitlab-org/release/docs/-/blob/master/general/patch/engineers.md) process.
See [the feature flag process](https://about.gitlab.com/handbook/product-development-flow/feature-flag-lifecycle/#including-a-feature-behind-feature-flag-in-the-final-release)
for further details.
1. Remove all references to the feature flag from the codebase, including tests.
diff --git a/doc/development/github_importer.md b/doc/development/github_importer.md
index 7fc2ee3a880..73497c22b65 100644
--- a/doc/development/github_importer.md
+++ b/doc/development/github_importer.md
@@ -72,8 +72,11 @@ This worker imports all pull requests. For every pull request a job for the
### 5. Stage::ImportCollaboratorsWorker
-This worker imports all repository collaborators. For every collaborator, we schedule a job
-for the `Gitlab::GithubImport::ImportCollaboratorWorker` worker.
+This worker imports only direct repository collaborators who are not outside collaborators.
+For every collaborator, we schedule a job for the `Gitlab::GithubImport::ImportCollaboratorWorker` worker.
+
+NOTE:
+This stage is optional (controlled by `Gitlab::GithubImport::Settings`) and is selected by default.
### 6. Stage::ImportPullRequestsMergedByWorker
diff --git a/doc/development/labels/index.md b/doc/development/labels/index.md
index af4df4adee2..50b6ec74575 100644
--- a/doc/development/labels/index.md
+++ b/doc/development/labels/index.md
@@ -233,8 +233,7 @@ release. There are three levels of Release Scoping labels:
milestone. If these issues are not done in the current release, they will
strongly be considered for the next release.
- `~"Next Patch Release"`: Issues to put in the next patch release. Work on these
- first, and add the `~"Pick into X.Y"` label to the merge request, along with the
- appropriate milestone.
+ first, and follow the [patch release runbook](https://gitlab.com/gitlab-org/release/docs/-/blob/master/general/patch/engineers.md) to backport the bug fix to the current version.
Each issue scheduled for the current milestone should be labeled `~"Deliverable"~`
or `~"Stretch"`. Any open issue for a previous milestone should be labeled
diff --git a/doc/operations/incident_management/status_page.md b/doc/operations/incident_management/status_page.md
index 5dd690a1c9f..fd37279806f 100644
--- a/doc/operations/incident_management/status_page.md
+++ b/doc/operations/incident_management/status_page.md
@@ -159,13 +159,13 @@ To publish comments to the Status Page Incident:
- Create a comment on the incident issue.
- When you're ready to publish the comment, mark the comment for publication by
- adding a microphone [award emoji](../../user/award_emojis.md)
+ adding a microphone [emoji reaction](../../user/award_emojis.md)
reaction (`:microphone:` 🎤) to the comment.
- Any files attached to the comment (up to 5000 per issue) are also published.
([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/205166) in GitLab 13.1.)
WARNING:
-Anyone with access to view the Issue can add an emoji award to a comment, so
+Anyone with access to view the Issue can add an emoji reaction to a comment, so
consider limiting access to issues to team members only.
### Update the incident status
diff --git a/doc/subscriptions/gitlab_com/index.md b/doc/subscriptions/gitlab_com/index.md
index 84fbca7bfa8..ff93d1c903a 100644
--- a/doc/subscriptions/gitlab_com/index.md
+++ b/doc/subscriptions/gitlab_com/index.md
@@ -335,7 +335,7 @@ expiration date without a gap in available service. Subscriptions purchased thro
To view or change automatic subscription renewal (at the same tier as the
previous period), sign in to the [Customers Portal](https://customers.gitlab.com/customers/sign_in), and:
-- If a **Resume subscription** button is displayed, your subscription was canceled
+- If a **Turn on auto-renew** button is displayed, your subscription was canceled
previously. Select it to resume automatic renewal.
- If a **Cancel subscription** button is displayed, your subscription is set to automatically
renew at the end of the subscription period. Select it to cancel automatic renewal.
diff --git a/doc/subscriptions/self_managed/index.md b/doc/subscriptions/self_managed/index.md
index b5faf3bb0f3..ece1484b107 100644
--- a/doc/subscriptions/self_managed/index.md
+++ b/doc/subscriptions/self_managed/index.md
@@ -391,7 +391,7 @@ You can view and download your renewal invoice on the Customers Portal
To view or change automatic subscription renewal (at the same tier as the
previous period), sign in to the [Customers Portal](https://customers.gitlab.com/customers/sign_in), and:
-- If a **Resume subscription** button is displayed, your subscription was canceled
+- If a **Turn on auto-renew** button is displayed, your subscription was canceled
previously. Select it to resume automatic renewal.
- If a **Cancel subscription** button is displayed, your subscription is set to automatically
renew at the end of the subscription period. Select it to cancel automatic renewal.
diff --git a/doc/user/admin_area/settings/account_and_limit_settings.md b/doc/user/admin_area/settings/account_and_limit_settings.md
index 0abaf32bad0..5c730375f98 100644
--- a/doc/user/admin_area/settings/account_and_limit_settings.md
+++ b/doc/user/admin_area/settings/account_and_limit_settings.md
@@ -332,7 +332,7 @@ By default, newly created users have a public profile. GitLab administrators can
1. On the left sidebar, select **Settings > General**, then expand **Account and limit**.
1. Select the **Make new users' profiles private by default** checkbox.
-## Prevent users from deleting their accounts **(FREE SELF)**
+## Prevent users from deleting their accounts **(PREMIUM SELF)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/26053) in GitLab 16.0 [with a flag](../../../administration/feature_flags.md) named `deleting_account_disabled_for_users`. Disabled by default.
diff --git a/doc/user/admin_area/settings/sign_in_restrictions.md b/doc/user/admin_area/settings/sign_in_restrictions.md
index a3558991e36..951e0784c88 100644
--- a/doc/user/admin_area/settings/sign_in_restrictions.md
+++ b/doc/user/admin_area/settings/sign_in_restrictions.md
@@ -103,6 +103,8 @@ To turn off Admin Mode for your current session, on the top bar, select **Main m
### Limitations of Admin Mode
+Admin Mode times out after six hours, and you cannot change this timeout limit.
+
The following access methods are **not** protected by Admin Mode:
- Git client access (SSH using public keys or HTTPS using Personal Access Tokens).
diff --git a/doc/user/application_security/api_fuzzing/index.md b/doc/user/application_security/api_fuzzing/index.md
index c0ba764e706..b613b0cc33e 100644
--- a/doc/user/application_security/api_fuzzing/index.md
+++ b/doc/user/application_security/api_fuzzing/index.md
@@ -2762,7 +2762,7 @@ The following example uses the [statically defined credentials](../../../ci/dock
Starting service registry.example.com/my-target-app:latest ...
Authenticating with credentials from $DOCKER_AUTH_CONFIG
Pulling docker image registry.example.com/my-target-app:latest ...
- Using docker image sha256:139c39668e5e4417f7d0eb0eeb74145ba862f4f3c24f7c6594ecb2f82dc4ad06 for registry.example.com/my-target-app:latest with digest registry.example.com/my-target-
+ Using docker image sha256:139c39668e5e4417f7d0eb0eeb74145ba862f4f3c24f7c6594ecb2f82dc4ad06 for registry.example.com/my-target-app:latest with digest registry.example.com/my-target-
app@sha256:2b69fc7c3627dbd0ebaa17674c264fcd2f2ba21ed9552a472acf8b065d39039c ...
Waiting for services to be up and running (timeout 30 seconds)...
```
@@ -2774,7 +2774,7 @@ To get support for your particular problem use the [getting help channels](https
The [GitLab issue tracker on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues) is the right place for bugs and feature proposals about API Security and API Fuzzing.
Use `~"Category:API Security"` [label](../../../development/labels/index.md) when opening a new issue regarding API fuzzing to ensure it is quickly reviewed by the right people. Refer to our [review response SLO](https://about.gitlab.com/handbook/engineering/workflow/code-review/#review-response-slo) to understand when you should receive a response.
-[Search the issue tracker](https://gitlab.com/gitlab-org/gitlab/-/issues) for similar entries before submitting your own, there's a good chance somebody else had the same issue or feature proposal. Show your support with an award emoji and or join the discussion.
+[Search the issue tracker](https://gitlab.com/gitlab-org/gitlab/-/issues) for similar entries before submitting your own, there's a good chance somebody else had the same issue or feature proposal. Show your support with an emoji reaction or join the discussion.
When experiencing a behavior not working as expected, consider providing contextual information:
diff --git a/doc/user/application_security/api_security/api_discovery/index.md b/doc/user/application_security/api_security/api_discovery/index.md
index b87c7f40663..95b53249653 100644
--- a/doc/user/application_security/api_security/api_discovery/index.md
+++ b/doc/user/application_security/api_security/api_discovery/index.md
@@ -166,7 +166,7 @@ To get support for your particular problem, use the [getting help channels](http
The [GitLab issue tracker on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues) is the right place for bugs and feature proposals about API Discovery.
Use `~"Category:API Security"` [label](../../../../development/labels/index.md) when opening a new issue regarding API Discovery to ensure it is quickly reviewed by the right people. Refer to our [review response SLO](https://about.gitlab.com/handbook/engineering/workflow/code-review/#review-response-slo) to understand when you should receive a response.
-[Search the issue tracker](https://gitlab.com/gitlab-org/gitlab/-/issues) for similar entries before submitting your own, there's a good chance somebody else had the same issue or feature proposal. Show your support with an award emoji and or join the discussion.
+[Search the issue tracker](https://gitlab.com/gitlab-org/gitlab/-/issues) for similar entries before submitting your own, there's a good chance somebody else had the same issue or feature proposal. Show your support with an emoji reaction or join the discussion.
When experiencing a behavior not working as expected, consider providing contextual information:
diff --git a/doc/user/application_security/dast_api/index.md b/doc/user/application_security/dast_api/index.md
index 0daa44aec16..d494259ecc4 100644
--- a/doc/user/application_security/dast_api/index.md
+++ b/doc/user/application_security/dast_api/index.md
@@ -2630,7 +2630,7 @@ The following example uses the [statically defined credentials](../../../ci/dock
Starting service registry.example.com/my-target-app:latest ...
Authenticating with credentials from $DOCKER_AUTH_CONFIG
Pulling docker image registry.example.com/my-target-app:latest ...
- Using docker image sha256:139c39668e5e4417f7d0eb0eeb74145ba862f4f3c24f7c6594ecb2f82dc4ad06 for registry.example.com/my-target-app:latest with digest registry.example.com/my-target-
+ Using docker image sha256:139c39668e5e4417f7d0eb0eeb74145ba862f4f3c24f7c6594ecb2f82dc4ad06 for registry.example.com/my-target-app:latest with digest registry.example.com/my-target-
app@sha256:2b69fc7c3627dbd0ebaa17674c264fcd2f2ba21ed9552a472acf8b065d39039c ...
Waiting for services to be up and running (timeout 30 seconds)...
```
@@ -2642,7 +2642,7 @@ To get support for your particular problem, use the [getting help channels](http
The [GitLab issue tracker on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues) is the right place for bugs and feature proposals about API Security and DAST API.
Use `~"Category:API Security"` [label](../../../development/labels/index.md) when opening a new issue regarding DAST API to ensure it is quickly reviewed by the right people. Refer to our [review response SLO](https://about.gitlab.com/handbook/engineering/workflow/code-review/#review-response-slo) to understand when you should receive a response.
-[Search the issue tracker](https://gitlab.com/gitlab-org/gitlab/-/issues) for similar entries before submitting your own, there's a good chance somebody else had the same issue or feature proposal. Show your support with an award emoji and or join the discussion.
+[Search the issue tracker](https://gitlab.com/gitlab-org/gitlab/-/issues) for similar entries before submitting your own, there's a good chance somebody else had the same issue or feature proposal. Show your support with an emoji reaction or join the discussion.
When experiencing a behavior not working as expected, consider providing contextual information:
diff --git a/doc/user/award_emojis.md b/doc/user/award_emojis.md
index 29ca6fe8d98..c3aad86461b 100644
--- a/doc/user/award_emojis.md
+++ b/doc/user/award_emojis.md
@@ -4,12 +4,13 @@ group: Project Management
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
---
-# Award emojis **(FREE)**
+# Emoji reactions **(FREE)**
-> Awarding emojis to work items (such as tasks, objectives, and key results) [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/393599) in GitLab 16.0.
+> - [Renamed](https://gitlab.com/gitlab-org/gitlab/-/issues/409884) from "award emojis" to "emoji reactions" in GitLab 16.0.
+> - Reacting with emojis on work items (such as tasks, objectives, and key results) [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/393599) in GitLab 16.0.
When you're collaborating online, you get fewer opportunities for high-fives
-and thumbs-ups. Add award emojis to:
+and thumbs-ups. React with emojis on:
- [Issues](project/issues/index.md).
- [Tasks](tasks.md).
@@ -19,12 +20,12 @@ and thumbs-ups. Add award emojis to:
- [Objectives and key results](okrs.md).
- Anywhere else you can have a comment thread.
-![Award emoji](img/award_emoji_select_v14_6.png)
+![Emoji reactions](img/award_emoji_select_v14_6.png)
-Award emojis make it much easier to give and receive feedback without a long
+Emoji reactions make it much easier to give and receive feedback without a long
comment thread.
-For information on the relevant API, see [Award Emoji API](../api/award_emoji.md).
+For information on the relevant API, see [Emoji reactions API](../api/award_emoji.md).
## Sort issues and merge requests on vote count
@@ -38,17 +39,17 @@ The total number of votes is not summed up. An issue with 18 upvotes and 5
downvotes is considered more popular than an issue with 17 upvotes and no
downvotes.
-## Award emojis for comments
+## Emoji reactions for comments
-Award emojis can also be applied to individual comments when you want to
+Emoji reactions can also be applied to individual comments when you want to
celebrate an accomplishment or agree with an opinion.
-To add an award emoji:
+To add an emoji reaction:
1. In the upper-right corner of the comment, select the smile (**{slight-smile}**).
-1. Select an emoji from the dropdown list.
+1. Select an emoji from the emoji picker.
-To remove an award emoji, select the emoji again.
+To remove an emoji reaction, select the emoji again.
## Custom emojis
diff --git a/doc/user/group/epics/index.md b/doc/user/group/epics/index.md
index 21c95f37aeb..32454693d71 100644
--- a/doc/user/group/epics/index.md
+++ b/doc/user/group/epics/index.md
@@ -59,7 +59,7 @@ have a start or due date, a visual
- Link [related epics](linked_epics.md) based on a type of relationship.
- Create workflows with [epic boards](epic_boards.md).
- [Turn on notifications](../../profile/notifications.md) for about epic events.
-- [Award an emoji](../../award_emojis.md) to an epic or its comments.
+- [Add an emoji reaction](../../award_emojis.md) to an epic or its comments.
- Collaborate on an epic by posting comments in a [thread](../../discussions/index.md).
- Use [health status](../../project/issues/managing_issues.md#health-status) to track your progress.
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index 104c633229a..b8ed1c06324 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -266,7 +266,7 @@ this font installed by default.
<!-- vale gitlab.Spelling = YES -->
-To learn more about adding custom emojis, see [Custom emoji](award_emojis.md#custom-emojis).
+To learn more about adding custom emojis, see [Custom emojis](award_emojis.md#custom-emojis).
### Front matter
diff --git a/doc/user/profile/account/delete_account.md b/doc/user/profile/account/delete_account.md
index 56aa545408f..a2c82fdeadf 100644
--- a/doc/user/profile/account/delete_account.md
+++ b/doc/user/profile/account/delete_account.md
@@ -51,7 +51,7 @@ When deleting users, you can either:
The user's personal projects are deleted, not moved to the Ghost User.
- Delete the user and their contributions, including:
- Abuse reports.
- - Award emojis.
+ - Emoji reactions.
- Epics.
- Groups of which the user is the only user with the Owner role.
- Issues.
diff --git a/doc/user/project/badges.md b/doc/user/project/badges.md
index 76933255820..26708dece50 100644
--- a/doc/user/project/badges.md
+++ b/doc/user/project/badges.md
@@ -107,7 +107,7 @@ If there is no release, it shows `none`.
You can access a latest release badge image by using the following link:
```plaintext
-https://gitlab.example.com/<namespace>/<project>/badges/<branch>/release.svg
+https://gitlab.example.com/<namespace>/<project>/-/badges/release.svg
```
By default, the badge fetches the release sorted using the [`released_at`](../../api/releases/index.md#create-a-release)
diff --git a/doc/user/project/import/github.md b/doc/user/project/import/github.md
index b956993f8c4..1c0dfc377b4 100644
--- a/doc/user/project/import/github.md
+++ b/doc/user/project/import/github.md
@@ -140,6 +140,9 @@ You can choose to import these items, but this could significantly increase impo
- **Import issue and pull request events**.
- **Use alternative comments import method**.
- **Import Markdown attachments**.
+- **Import collaborators** (selected by default). Leaving it selected might result in new users using a seat in the group or namespace,
+ and being granted permissions [as high as project owner](#collaborators-members). Only direct collaborators are imported.
+ Outside collaborators are never imported.
### Select which repositories to import
@@ -209,7 +212,8 @@ The following items of a project are imported:
- Repository description.
- Git repository data.
- Branch protection rules. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22650) in GitLab 15.4.
-- Collaborators (members). [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/388716) in GitLab 15.10.
+- Collaborators (members). [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/388716) in GitLab 15.10. From GitLab 16.0, can
+ be imported [as an additional item](#select-additional-items-to-import).
- Issues.
- Pull requests.
- Wiki pages.
diff --git a/doc/user/project/integrations/mlflow_client.md b/doc/user/project/integrations/mlflow_client.md
index c5931928626..75fae4647d0 100644
--- a/doc/user/project/integrations/mlflow_client.md
+++ b/doc/user/project/integrations/mlflow_client.md
@@ -4,7 +4,7 @@ group: Incubation
info: Machine Learning Experiment Tracking is a GitLab Incubation Engineering program. No technical writer assigned to this group.
---
-# MLFlow client integration **(FREE)**
+# MLflow client integration **(FREE)**
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/8560) in GitLab 15.11 as an [Experiment](../../../policy/alpha-beta-support.md#experiment) release [with a flag](../../../administration/feature_flags.md) named `ml_experiment_tracking`. Disabled by default.
@@ -12,21 +12,20 @@ NOTE:
Model experiment tracking is an [experimental feature](../../../policy/alpha-beta-support.md).
Refer to <https://gitlab.com/gitlab-org/gitlab/-/issues/381660> for feedback and feature requests.
-[MLFlow](https://mlflow.org/) is one of the most popular open source tools for Machine Learning Experiment Tracking.
-GitLab works as a backend to the MLFlow Client, [logging experiments](../ml/experiment_tracking/index.md).
+[MLflow](https://mlflow.org/) is a popular open source tool for Machine Learning Experiment Tracking.
+GitLab works as a backend to the MLflow Client, [logging experiments](../ml/experiment_tracking/index.md).
Setting up your integrations requires minimal changes to existing code.
-GitLab plays the role of proxy server, both for artifact storage and tracking data. It reflects the
-MLFlow [Scenario 5](https://www.mlflow.org/docs/latest/tracking.html#scenario-5-mlflow-tracking-server-enabled-with-proxied-artifact-storage-access).
+GitLab plays the role of a MLflow server. Running `mlflow server` is not necessary.
-## Enable MLFlow client integration
+## Enable MLflow client integration
Prerequisites:
- A [personal access token](../../../user/profile/personal_access_tokens.md) for the project, with minimum access level of `api`.
- The project ID. To find the project ID, on the top bar, select **Main menu > Projects** and find your project. On the left sidebar, select **Settings > General**.
-To enable MLFlow client integration:
+To enable MLflow client integration:
1. Set the tracking URI and token environment variables on the host that runs the code.
This can be your local environment, CI pipeline, or remote host. For example:
@@ -38,7 +37,7 @@ To enable MLFlow client integration:
1. If your training code contains the call to `mlflow.set_tracking_uri()`, remove it.
-When running the training code, MLFlow creates experiments, runs, log parameters, metrics, metadata
+When running the training code, MLflow creates experiments, runs, log parameters, metrics, metadata
and artifacts on GitLab.
After experiments are logged, they are listed under `/<your project>/-/ml/experiments`.
@@ -47,10 +46,28 @@ Runs are registered as:
- Model Candidates, which can be explored by selecting an experiment.
- Tags, which are registered as metadata.
-## Supported MlFlow client methods and caveats
+## Associating a candidate to a CI/CD job
-GitLab supports these methods from the MLFlow client. Other methods might be supported but were not
-tested. More information can be found in the [MLFlow Documentation](https://www.mlflow.org/docs/1.28.0/python_api/mlflow.html).
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/119454) in GitLab 16.1.
+
+If your training code is being run from a CI/CD job, GitLab can use that information to enhance
+candidate metadata. To do so, add the following snippet to your training code within the run
+execution context:
+
+```python
+with mlflow.start_run(run_name=f"Candidate {index}"):
+ # Your training code
+
+ # Start of snippet to be included
+ if os.getenv('GITLAB_CI'):
+ mlflow.set_tag('gitlab.CI_JOB_ID', os.getenv('CI_JOB_ID'))
+ # End of snippet to be included
+```
+
+## Supported MLflow client methods and caveats
+
+GitLab supports these methods from the MLflow client. Other methods might be supported but were not
+tested. More information can be found in the [MLflow Documentation](https://www.mlflow.org/docs/1.28.0/python_api/mlflow.html).
| Method | Supported | Version Added | Comments |
|--------------------------|------------------|----------------|----------|
@@ -79,7 +96,7 @@ tested. More information can be found in the [MLFlow Documentation](https://www.
## Limitations
-- The API GitLab supports is the one defined at MLFlow version 1.28.0.
+- The API GitLab supports is the one defined at MLflow version 1.28.0.
- API endpoints not listed above are not supported.
- During creation of experiments and runs, ExperimentTags are stored, even though they are not displayed.
-- MLFlow Model Registry is not supported.
+- MLflow Model Registry is not supported.
diff --git a/doc/user/project/issues/sorting_issue_lists.md b/doc/user/project/issues/sorting_issue_lists.md
index a2f90d5c444..829e44eae9a 100644
--- a/doc/user/project/issues/sorting_issue_lists.md
+++ b/doc/user/project/issues/sorting_issue_lists.md
@@ -73,7 +73,7 @@ then issues with a milestone without a due date.
## Sorting by popularity
When you sort by **Popularity**, the issue order changes to sort descending by the
-number of upvotes ([awarded](../../award_emojis.md) a "thumbs up" emoji)
+number of upvotes ([emoji reactions](../../award_emojis.md) with the "thumbs up")
on each issue. You can use this to identify issues that are in high demand.
## Sorting by priority
diff --git a/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md b/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md
index c6fd38e3317..7588af70bd4 100644
--- a/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md
+++ b/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md
@@ -5,10 +5,12 @@ info: To determine the technical writer assigned to the Stage/Group associated w
type: reference, concepts
---
-# Merge when pipeline succeeds **(FREE)**
+# Auto-merge **(FREE)**
+
+> **Merge when pipeline succeeds** and **Add to merge train when pipeline succeeds** [renamed](https://gitlab.com/gitlab-org/gitlab/-/issues/409530) to **Auto-merge** in GitLab 16.0 [with a flag](../../../administration/feature_flags.md) named `auto_merge_labels_mr_widget`. Enabled by default.
NOTE:
-[In GitLab 16.0 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/359057), the **Merge when pipeline succeeds** and **Add to merge train when pipeline succeeds** buttons become **Set to auto-merge**.
+[In GitLab 16.0 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/359057), **Merge when pipeline succeeds** and **Add to merge train when pipeline succeeds** become **Set to auto-merge**.
If you review a merge request and it's ready to merge, but the pipeline hasn't
completed yet, you can set it to merge when the pipeline succeeds (MWPS). You don't
diff --git a/doc/user/project/ml/experiment_tracking/index.md b/doc/user/project/ml/experiment_tracking/index.md
index 114ccea7c09..a2c2ab0cc40 100644
--- a/doc/user/project/ml/experiment_tracking/index.md
+++ b/doc/user/project/ml/experiment_tracking/index.md
@@ -23,7 +23,6 @@ These features have been proposed:
- Searching experiments.
- Visual comparison of candidates.
-- Creating, deleting, and updating experiments through the GitLab UI.
- Creating, deleting, and updating candidates through the GitLab UI.
For feature requests, see [epic 9341](https://gitlab.com/groups/gitlab-org/-/epics/9341).
@@ -59,9 +58,9 @@ Some example parameters:
## Track new experiments and candidates
Experiment and trials can only be tracked through the
-[MLFlow](https://www.mlflow.org/docs/latest/tracking.html) client integration.
-See [MLFlow client integration](../../integrations/mlflow_client.md) for more information
-on how to use GitLab as a backend for the MLFlow Client.
+[MLflow](https://www.mlflow.org/docs/latest/tracking.html) client integration.
+See [MLflow client integration](../../integrations/mlflow_client.md) for more information
+on how to use GitLab as a backend for the MLflow Client.
## Explore model candidates
diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md
index 3273e1c0be8..2c52a5d743a 100644
--- a/doc/user/project/quick_actions.md
+++ b/doc/user/project/quick_actions.md
@@ -59,7 +59,7 @@ threads. Some quick actions might not be available to all subscription tiers.
| `/assign me` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Assign yourself.
| `/assign_reviewer @user1 @user2` or `/reviewer @user1 @user2` or `/request_review @user1 @user2` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Assign one or more users as reviewers.
| `/assign_reviewer me` or `/reviewer me` or `/request_review me` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Assign yourself as a reviewer.
-| `/award :emoji:` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Toggle emoji award.
+| `/award :emoji:` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Toggle an emoji reaction.
| `/cc @user` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Mention a user. In GitLab 15.0 and later, this command performs no action. You can instead type `CC @user` or only `@user`. [In GitLab 14.9 and earlier](https://gitlab.com/gitlab-org/gitlab/-/issues/31200), mentioning a user at the start of a line created a specific type of to-do item notification. |
| `/child_epic <epic>` | **{dotted-circle}** No | **{dotted-circle}** No | **{check-circle}** Yes | Add child epic to `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic`, or a URL to an epic.
| `/clear_health_status` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Clear [health status](issues/managing_issues.md#health-status) ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213814) in GitLab 14.7).
diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md
index 3ac864c1351..958246908bc 100644
--- a/doc/user/project/settings/import_export.md
+++ b/doc/user/project/settings/import_export.md
@@ -160,7 +160,7 @@ For a quick overview, items that are exported include:
- Issue boards
- Pipelines history
- Push rules
-- Awards
+- Emoji reactions
- Group members as long as the user has the Maintainer role in the
exported project's group or is an administrator
diff --git a/doc/user/project/web_ide/index.md b/doc/user/project/web_ide/index.md
index 4fb6fe4efc7..140008f8b93 100644
--- a/doc/user/project/web_ide/index.md
+++ b/doc/user/project/web_ide/index.md
@@ -10,6 +10,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/371084) in GitLab 15.7.
> - [Enabled on self-managed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/115741) in GitLab 15.11.
+FLAG:
+On self-managed GitLab, by default this feature is available. To hide the feature, ask an administrator to [disable the feature flag](../../../administration/feature_flags.md) named `vscode_web_ide`. On GitLab.com, this feature is available.
+
The Web IDE is an advanced editor with commit staging.
You can use the Web IDE to make changes to multiple files directly from the GitLab UI.
For a more basic implementation, see [Web Editor](../repository/web_editor.md).
diff --git a/doc/user/shortcuts.md b/doc/user/shortcuts.md
index 89de52c33ea..3b6c6105315 100644
--- a/doc/user/shortcuts.md
+++ b/doc/user/shortcuts.md
@@ -68,7 +68,7 @@ relatively quickly to work, and they take you to another page in the project.
| Keyboard shortcut | Description |
|-----------------------------|-------------|
-| <kbd>g</kbd> + <kbd>p</kbd> | Go to the project home page (**Project > Details**). |
+| <kbd>g</kbd> + <kbd>o</kbd> | Go to the project overview page (**Project > Details**). |
| <kbd>g</kbd> + <kbd>v</kbd> | Go to the project activity feed (**Project > Activity**). |
| <kbd>g</kbd> + <kbd>r</kbd> | Go to the project releases list (**Project > Releases**). |
| <kbd>g</kbd> + <kbd>f</kbd> | Go to the [project files](#project-files) list (**Repository > Files**). |
@@ -80,6 +80,7 @@ relatively quickly to work, and they take you to another page in the project.
| <kbd>i</kbd> | Go to the New Issue page (**Issues**, select **New Issue** ). |
| <kbd>g</kbd> + <kbd>b</kbd> | Go to the project issue boards list (**Issues > Boards**). |
| <kbd>g</kbd> + <kbd>m</kbd> | Go to the project [merge requests](project/merge_requests/index.md) list (**Merge Requests**). |
+| <kbd>g</kbd> + <kbd>p</kbd> | Go to the CI/CD pipelines list (**CI/CD > Pipelines**). |
| <kbd>g</kbd> + <kbd>j</kbd> | Go to the CI/CD jobs list (**CI/CD > Jobs**). |
| <kbd>g</kbd> + <kbd>e</kbd> | Go to the project environments (**Deployments > Environments**). |
| <kbd>g</kbd> + <kbd>k</kbd> | Go to the project Kubernetes cluster integration page (**Infrastructure > Kubernetes clusters**). You must have at least [`maintainer` permissions](permissions.md) to access this page. |
diff --git a/doc/user/todos.md b/doc/user/todos.md
index b05e968dd11..8f67311b559 100644
--- a/doc/user/todos.md
+++ b/doc/user/todos.md
@@ -128,7 +128,7 @@ corresponding to-do item as done.
To-do items are marked as done if you:
-- Add an award emoji to the description or comment.
+- Add an emoji reaction to the description or comment.
- Add or remove a label.
- Change the assignee.
- Change the milestone.
diff --git a/doc/user/workspace/index.md b/doc/user/workspace/index.md
index 0d540023c2c..922ce8e1354 100644
--- a/doc/user/workspace/index.md
+++ b/doc/user/workspace/index.md
@@ -6,21 +6,22 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Workspaces (Beta) **(PREMIUM)**
-> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/10122) in GitLab 16.0 [with a flag](../../administration/feature_flags.md) named `remote_development_feature_flag`. Enabled by default.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/112397) in GitLab 15.11 [with a flag](../../administration/feature_flags.md) named `remote_development_feature_flag`. Disabled by default.
+> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/391543) in GitLab 16.0.
FLAG:
-On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](../../administration/feature_flags.md) named `remote_development_feature_flag`. On GitLab.com, this feature is not available. The feature is not ready for production use.
+On self-managed GitLab, by default this feature is available. To hide the feature, ask an administrator to [disable the feature flag](../../administration/feature_flags.md) named `remote_development_feature_flag`. On GitLab.com, this feature is available. The feature is not ready for production use.
WARNING:
-This feature is in [Beta](../../policy/alpha-beta-support.md#beta) and subject to change without notice. To leave your feedback, see the [feedback issue](https://gitlab.com/gitlab-org/gitlab/-/issues/410031).
+This feature is in [Beta](../../policy/alpha-beta-support.md#beta) and subject to change without notice. To leave feedback, see the [feedback issue](https://gitlab.com/gitlab-org/gitlab/-/issues/410031).
A workspace is a virtual sandbox environment for your code in GitLab. You can use workspaces to create and manage isolated development environments for your GitLab projects. These environments ensure that different projects don't interfere with each other.
Each workspace includes its own set of dependencies, libraries, and tools, which you can customize to meet the specific needs of each project. Workspaces use the AMD64 architecture.
-## Create a workspace
+## Set up a workspace
-Prerequisites:
+### Prerequisites
- Set up a Kubernetes cluster that the GitLab agent for Kubernetes supports. See the [supported Kubernetes versions](../clusters/agent/index.md#gitlab-agent-for-kubernetes-supported-cluster-versions).
- Ensure autoscaling for the Kubernetes cluster is enabled.
@@ -40,6 +41,8 @@ Prerequisites:
- In each public project you want to use this feature for, define a [devfile](#devfile). Ensure the container images used in the devfile support [arbitrary user IDs](#arbitrary-user-ids).
+### Create a workspace
+
To create a workspace in GitLab:
1. On the top bar, select **Main menu > Projects** and find your project.
diff --git a/lib/api/entities/ci/job_request/cache.rb b/lib/api/entities/ci/job_request/cache.rb
index 9820719b4f0..9be2b4c34ce 100644
--- a/lib/api/entities/ci/job_request/cache.rb
+++ b/lib/api/entities/ci/job_request/cache.rb
@@ -5,7 +5,7 @@ module API
module Ci
module JobRequest
class Cache < Grape::Entity
- expose :key, :untracked, :paths, :policy, :when
+ expose :key, :untracked, :paths, :policy, :when, :fallback_keys
end
end
end
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 505fcb2b38e..3d9af536c3c 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -758,16 +758,11 @@ module API
break if user.deactivated?
- unless user.can_be_deactivated?
- forbidden!('A blocked user cannot be deactivated by the API') if user.blocked?
- forbidden!('An internal user cannot be deactivated by the API') if user.internal?
- forbidden!("The user you are trying to deactivate has been active in the past #{Gitlab::CurrentSettings.deactivate_dormant_users_period} days and cannot be deactivated")
- end
-
- if user.deactivate
+ result = ::Users::DeactivateService.new(current_user, skip_authorization: true).execute(user)
+ if result[:status] == :success
true
else
- render_api_error!(user.errors.full_messages, 400)
+ render_api_error!(result[:message], result[:reason] || :bad_request)
end
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/lib/error_tracking/sentry_client/token.rb b/lib/error_tracking/sentry_client/token.rb
new file mode 100644
index 00000000000..ce7db5f2800
--- /dev/null
+++ b/lib/error_tracking/sentry_client/token.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module ErrorTracking
+ class SentryClient
+ class Token
+ MASKED_TOKEN_REGEX = /\A\*+\z/
+
+ def self.masked_token?(token)
+ MASKED_TOKEN_REGEX.match?(token)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/entry/cache.rb b/lib/gitlab/ci/config/entry/cache.rb
index a635f409109..b3ff74c14da 100644
--- a/lib/gitlab/ci/config/entry/cache.rb
+++ b/lib/gitlab/ci/config/entry/cache.rb
@@ -9,11 +9,12 @@ module Gitlab
include ::Gitlab::Config::Entry::Validatable
include ::Gitlab::Config::Entry::Attributable
- ALLOWED_KEYS = %i[key untracked paths when policy unprotect].freeze
+ ALLOWED_KEYS = %i[key untracked paths when policy unprotect fallback_keys].freeze
ALLOWED_POLICY = %w[pull-push push pull].freeze
DEFAULT_POLICY = 'pull-push'
ALLOWED_WHEN = %w[on_success on_failure always].freeze
DEFAULT_WHEN = 'on_success'
+ DEFAULT_FALLBACK_KEYS = [].freeze
validations do
validates :config, type: Hash, allowed_keys: ALLOWED_KEYS
@@ -27,6 +28,8 @@ module Gitlab
in: ALLOWED_WHEN,
message: "should be one of: #{ALLOWED_WHEN.join(', ')}"
}
+
+ validates :fallback_keys, length: { maximum: 5, too_long: "has to many entries (maximum %{count})" }
end
end
@@ -42,7 +45,10 @@ module Gitlab
entry :paths, Entry::Paths,
description: 'Specify which paths should be cached across builds.'
- attributes :policy, :when, :unprotect
+ entry :fallback_keys, ::Gitlab::Config::Entry::ArrayOfStrings,
+ description: 'List of keys to download cache from if no cache hit occurred for key'
+
+ attributes :policy, :when, :unprotect, :fallback_keys
def value
result = super
@@ -52,6 +58,7 @@ module Gitlab
result[:policy] = policy || DEFAULT_POLICY
# Use self.when to avoid conflict with reserved word
result[:when] = self.when || DEFAULT_WHEN
+ result[:fallback_keys] = fallback_keys || DEFAULT_FALLBACK_KEYS
result
end
diff --git a/lib/gitlab/ci/pipeline/seed/build/cache.rb b/lib/gitlab/ci/pipeline/seed/build/cache.rb
index 409b6658cc0..936344b9ae8 100644
--- a/lib/gitlab/ci/pipeline/seed/build/cache.rb
+++ b/lib/gitlab/ci/pipeline/seed/build/cache.rb
@@ -16,6 +16,7 @@ module Gitlab
@when = local_cache.delete(:when)
@unprotect = local_cache.delete(:unprotect)
@custom_key_prefix = custom_key_prefix
+ @fallback_keys = local_cache.delete(:fallback_keys)
raise ArgumentError, "unknown cache keys: #{local_cache.keys}" if local_cache.any?
end
@@ -27,7 +28,8 @@ module Gitlab
policy: @policy,
untracked: @untracked,
when: @when,
- unprotect: @unprotect
+ unprotect: @unprotect,
+ fallback_keys: @fallback_keys
}.compact
end
diff --git a/lib/gitlab/config_checker/external_database_checker.rb b/lib/gitlab/config_checker/external_database_checker.rb
index ff20833b5be..c95e19940c3 100644
--- a/lib/gitlab/config_checker/external_database_checker.rb
+++ b/lib/gitlab/config_checker/external_database_checker.rb
@@ -21,9 +21,9 @@ module Gitlab
{
type: 'warning',
message: _('Database \'%{database_name}\' is using PostgreSQL %{pg_version_current}, ' \
- 'but PostgreSQL %{pg_version_minimum} is required for this version of GitLab. ' \
- 'Please upgrade your environment to a supported PostgreSQL version, ' \
- 'see %{pg_requirements_url} for details.') % \
+ 'but this version of GitLab requires PostgreSQL %{pg_version_minimum}. ' \
+ 'Please upgrade your environment to a supported PostgreSQL version. ' \
+ 'See %{pg_requirements_url} for details.') % \
{
database_name: database_name,
pg_version_current: database.version,
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index 093667dc624..4197c87f51f 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -193,13 +193,12 @@ module Gitlab
 ███ ███  ██  ██ ██  ██ ██   ████ ██ ██   ████  ██████  
******************************************************************************
- You are using PostgreSQL #{database.version} for the #{name} database, but PostgreSQL >= <%= Gitlab::Database::MINIMUM_POSTGRES_VERSION %>
- is required for this version of GitLab.
+ You are using PostgreSQL #{database.version} for the #{name} database, but this version of GitLab requires PostgreSQL >= <%= Gitlab::Database::MINIMUM_POSTGRES_VERSION %>.
<% if Rails.env.development? || Rails.env.test? %>
If using gitlab-development-kit, please find the relevant steps here:
https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/main/doc/howto/postgresql.md#upgrade-postgresql
<% end %>
- Please upgrade your environment to a supported PostgreSQL version, see
+ Please upgrade your environment to a supported PostgreSQL version. See
https://docs.gitlab.com/ee/install/requirements.html#database for details.
******************************************************************************
EOS
diff --git a/lib/gitlab/database/partitioning.rb b/lib/gitlab/database/partitioning.rb
index 4a9e002a1a2..7222f148b3f 100644
--- a/lib/gitlab/database/partitioning.rb
+++ b/lib/gitlab/database/partitioning.rb
@@ -27,6 +27,8 @@ module Gitlab
end
def sync_partitions(models_to_sync = registered_for_sync, only_on: nil)
+ return unless Feature.enabled?(:partition_manager_sync_partitions, type: :ops)
+
Gitlab::AppLogger.info(message: 'Syncing dynamic postgres partitions')
Gitlab::Database::EachDatabase.each_model_connection(models_to_sync, only_on: only_on) do |model|
diff --git a/lib/gitlab/github_import/client.rb b/lib/gitlab/github_import/client.rb
index 1c9ca9f43a8..886563a6f69 100644
--- a/lib/gitlab/github_import/client.rb
+++ b/lib/gitlab/github_import/client.rb
@@ -112,6 +112,10 @@ module Gitlab
each_object(:branches, *args)
end
+ def collaborators(*args)
+ each_object(:collaborators, *args)
+ end
+
def branch_protection(repo_name, branch_name)
with_rate_limit { octokit.branch_protection(repo_name, branch_name).to_h }
end
diff --git a/lib/gitlab/github_import/importer/collaborators_importer.rb b/lib/gitlab/github_import/importer/collaborators_importer.rb
index dd947632d01..7b18d3dba2a 100644
--- a/lib/gitlab/github_import/importer/collaborators_importer.rb
+++ b/lib/gitlab/github_import/importer/collaborators_importer.rb
@@ -6,6 +6,25 @@ module Gitlab
class CollaboratorsImporter
include ParallelScheduling
+ # The method that will be called for traversing through all the objects to
+ # import, yielding them to the supplied block.
+ def each_object_to_import
+ repo = project.import_source
+
+ direct_collaborators = client.collaborators(repo, affiliation: 'direct')
+ outside_collaborators = client.collaborators(repo, affiliation: 'outside')
+ collaborators_to_import = direct_collaborators.to_a - outside_collaborators.to_a
+
+ collaborators_to_import.each do |collaborator|
+ next if already_imported?(collaborator)
+
+ yield collaborator
+
+ Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched)
+ mark_as_imported(collaborator)
+ end
+ end
+
def importer_class
CollaboratorImporter
end
diff --git a/lib/gitlab/github_import/settings.rb b/lib/gitlab/github_import/settings.rb
index 22ab99df107..0b883de8ed0 100644
--- a/lib/gitlab/github_import/settings.rb
+++ b/lib/gitlab/github_import/settings.rb
@@ -6,6 +6,7 @@ module Gitlab
OPTIONAL_STAGES = {
single_endpoint_issue_events_import: {
label: 'Import issue and pull request events',
+ selected: false,
details: <<-TEXT.split("\n").map(&:strip).join(' ')
For example, opened or closed, renamed, and labeled or unlabeled.
Time required to import these events depends on how many issues or pull requests your project has.
@@ -13,17 +14,27 @@ module Gitlab
},
single_endpoint_notes_import: {
label: 'Use alternative comments import method',
+ selected: false,
details: <<-TEXT.split("\n").map(&:strip).join(' ')
The default method can skip some comments in large projects because of limitations of the GitHub API.
TEXT
},
attachments_import: {
label: 'Import Markdown attachments (links)',
+ selected: false,
details: <<-TEXT.split("\n").map(&:strip).join(' ')
Import Markdown attachments (links) from repository comments, release posts, issue descriptions,
and pull request descriptions. These can include images, text, or binary attachments.
If not imported, links in Markdown to attachments break after you remove the attachments from GitHub.
TEXT
+ },
+ collaborators_import: {
+ label: 'Import collaborators',
+ selected: true,
+ details: <<-TEXT.split("\n").map(&:strip).join(' ')
+ Import direct repository collaborators who are not outside collaborators.
+ Imported collaborators who aren't members of the group you imported the project into consume seats on your GitLab instance.
+ TEXT
}
}.freeze
@@ -32,6 +43,7 @@ module Gitlab
{
name: stage_name.to_s,
label: s_(format("GitHubImport|%{text}", text: data[:label])),
+ selected: data[:selected],
details: s_(format("GitHubImport|%{text}", text: data[:details]))
}
end
diff --git a/lib/gitlab/metrics/subscribers/rails_cache.rb b/lib/gitlab/metrics/subscribers/rails_cache.rb
index 1759c0544b1..b4e9e85a012 100644
--- a/lib/gitlab/metrics/subscribers/rails_cache.rb
+++ b/lib/gitlab/metrics/subscribers/rails_cache.rb
@@ -13,7 +13,8 @@ module Gitlab
return unless current_transaction
- current_transaction.observe(:gitlab_cache_read_multikey_count, event.payload[:key].size) do
+ labels = { store: event.payload[:store].split('::').last }
+ current_transaction.observe(:gitlab_cache_read_multikey_count, event.payload[:key].size, labels) do
buckets [10, 50, 100, 1000]
docstring 'Number of keys for mget in read_multi/fetch_multi'
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index ca8143c5e3f..b2d1169af7b 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -1850,6 +1850,9 @@ msgstr ""
msgid "AI actions"
msgstr ""
+msgid "AI generated this test"
+msgstr ""
+
msgid "AI-generated test file"
msgstr ""
@@ -14096,7 +14099,7 @@ msgstr ""
msgid "Data type"
msgstr ""
-msgid "Database '%{database_name}' is using PostgreSQL %{pg_version_current}, but PostgreSQL %{pg_version_minimum} is required for this version of GitLab. Please upgrade your environment to a supported PostgreSQL version, see %{pg_requirements_url} for details."
+msgid "Database '%{database_name}' is using PostgreSQL %{pg_version_current}, but this version of GitLab requires PostgreSQL %{pg_version_minimum}. Please upgrade your environment to a supported PostgreSQL version. See %{pg_requirements_url} for details."
msgstr ""
msgid "Database update failed"
@@ -20594,6 +20597,9 @@ msgstr ""
msgid "Go to parent directory"
msgstr ""
+msgid "Go to pipelines"
+msgstr ""
+
msgid "Go to previous page"
msgstr ""
@@ -26418,6 +26424,9 @@ msgstr ""
msgid "Leave edit mode? All unsaved changes will be lost."
msgstr ""
+msgid "Leave feedback"
+msgstr ""
+
msgid "Leave feedback."
msgstr ""
@@ -48592,6 +48601,9 @@ msgstr ""
msgid "User does not have permission to create a Security Policy project."
msgstr ""
+msgid "User has already been deactivated"
+msgstr ""
+
msgid "User identity was successfully created."
msgstr ""
diff --git a/qa/qa/page/component/note.rb b/qa/qa/page/component/note.rb
index 37c833e77c2..db9492ea516 100644
--- a/qa/qa/page/component/note.rb
+++ b/qa/qa/page/component/note.rb
@@ -28,7 +28,7 @@ module QA
end
base.view 'app/assets/javascripts/notes/components/discussion_filter.vue' do
- element :discussion_preferences_dropdown, required: true
+ element :discussion_preferences_dropdown
element :filter_menu_item
end
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb
index ec2559550c3..9b00451de30 100644
--- a/spec/controllers/admin/users_controller_spec.rb
+++ b/spec/controllers/admin/users_controller_spec.rb
@@ -388,7 +388,7 @@ RSpec.describe Admin::UsersController do
put :deactivate, params: { id: user.username }
user.reload
expect(user.deactivated?).to be_falsey
- expect(flash[:notice]).to eq("The user you are trying to deactivate has been active in the past #{Gitlab::CurrentSettings.deactivate_dormant_users_period} days and cannot be deactivated")
+ expect(flash[:alert]).to eq("The user you are trying to deactivate has been active in the past #{Gitlab::CurrentSettings.deactivate_dormant_users_period} days and cannot be deactivated")
end
end
end
@@ -410,7 +410,7 @@ RSpec.describe Admin::UsersController do
put :deactivate, params: { id: user.username }
user.reload
expect(user.deactivated?).to be_falsey
- expect(flash[:notice]).to eq('Error occurred. A blocked user cannot be deactivated')
+ expect(flash[:alert]).to eq('Error occurred. A blocked user cannot be deactivated')
end
end
@@ -421,7 +421,7 @@ RSpec.describe Admin::UsersController do
put :deactivate, params: { id: internal_user.username }
expect(internal_user.reload.deactivated?).to be_falsey
- expect(flash[:notice]).to eq('Internal users cannot be deactivated')
+ expect(flash[:alert]).to eq('Internal users cannot be deactivated')
end
end
end
diff --git a/spec/features/projects/user_uses_shortcuts_spec.rb b/spec/features/projects/user_uses_shortcuts_spec.rb
index 1bad04382f4..1d4ab242308 100644
--- a/spec/features/projects/user_uses_shortcuts_spec.rb
+++ b/spec/features/projects/user_uses_shortcuts_spec.rb
@@ -69,11 +69,11 @@ RSpec.describe 'User uses shortcuts', :js, feature_category: :projects do
end
context 'when navigating to the Project pages' do
- it 'redirects to the project page' do
+ it 'redirects to the project overview page' do
visit project_issues_path(project)
find('body').native.send_key('g')
- find('body').native.send_key('p')
+ find('body').native.send_key('o')
expect(page).to have_active_navigation(project.name)
end
@@ -156,6 +156,14 @@ RSpec.describe 'User uses shortcuts', :js, feature_category: :projects do
end
context 'when navigating to the CI/CD pages' do
+ it 'redirects to the Pipelines page' do
+ find('body').native.send_key('g')
+ find('body').native.send_key('p')
+
+ expect(page).to have_active_navigation('CI/CD')
+ expect(page).to have_active_sub_navigation('Pipelines')
+ end
+
it 'redirects to the Jobs page' do
find('body').native.send_key('g')
find('body').native.send_key('j')
diff --git a/spec/frontend/admin/abuse_reports/components/abuse_report_actions_spec.js b/spec/frontend/admin/abuse_reports/components/abuse_report_actions_spec.js
index 571d01a2fb5..09b6b1edc44 100644
--- a/spec/frontend/admin/abuse_reports/components/abuse_report_actions_spec.js
+++ b/spec/frontend/admin/abuse_reports/components/abuse_report_actions_spec.js
@@ -5,7 +5,7 @@ import { GlDisclosureDropdown, GlDisclosureDropdownItem, GlModal } from '@gitlab
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import AbuseReportActions from '~/admin/abuse_reports/components/abuse_report_actions.vue';
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
-import { redirectTo, refreshCurrentPage } from '~/lib/utils/url_utility';
+import { redirectTo, refreshCurrentPage } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { createAlert, VARIANT_SUCCESS } from '~/alert';
import { sprintf } from '~/locale';
import { ACTIONS_I18N } from '~/admin/abuse_reports/constants';
@@ -115,7 +115,7 @@ describe('AbuseReportActions', () => {
findConfirmationModal().vm.$emit('primary');
await axios.waitForAll();
- expect(redirectTo).toHaveBeenCalledWith('/redirect_path');
+ expect(redirectTo).toHaveBeenCalledWith('/redirect_path'); // eslint-disable-line import/no-deprecated
});
});
});
@@ -194,7 +194,7 @@ describe('AbuseReportActions', () => {
await axios.waitForAll();
- expect(redirectTo).toHaveBeenCalledWith('/redirect_path');
+ expect(redirectTo).toHaveBeenCalledWith('/redirect_path'); // eslint-disable-line import/no-deprecated
});
});
});
diff --git a/spec/frontend/admin/abuse_reports/components/abuse_reports_filtered_search_bar_spec.js b/spec/frontend/admin/abuse_reports/components/abuse_reports_filtered_search_bar_spec.js
index 990503c453d..1f3f2caa995 100644
--- a/spec/frontend/admin/abuse_reports/components/abuse_reports_filtered_search_bar_spec.js
+++ b/spec/frontend/admin/abuse_reports/components/abuse_reports_filtered_search_bar_spec.js
@@ -1,6 +1,6 @@
import { shallowMount } from '@vue/test-utils';
import setWindowLocation from 'helpers/set_window_location_helper';
-import { redirectTo, updateHistory } from '~/lib/utils/url_utility';
+import { redirectTo, updateHistory } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import AbuseReportsFilteredSearchBar from '~/admin/abuse_reports/components/abuse_reports_filtered_search_bar.vue';
import {
FILTERED_SEARCH_TOKENS,
@@ -161,23 +161,24 @@ describe('AbuseReportsFilteredSearchBar', () => {
(filterToken) => {
createComponentAndFilter([filterToken]);
const { type, value } = filterToken;
- expect(redirectTo).toHaveBeenCalledWith(`https://localhost/?${type}=${value.data}`);
+ expect(redirectTo).toHaveBeenCalledWith(`https://localhost/?${type}=${value.data}`); // eslint-disable-line import/no-deprecated
},
);
it('ignores search query param', () => {
const searchFilterToken = { type: FILTERED_SEARCH_TERM, value: { data: 'ignored' } };
createComponentAndFilter([USER_FILTER_TOKEN, searchFilterToken]);
- expect(redirectTo).toHaveBeenCalledWith('https://localhost/?user=mr_abuser');
+ expect(redirectTo).toHaveBeenCalledWith('https://localhost/?user=mr_abuser'); // eslint-disable-line import/no-deprecated
});
it('redirects without page query param', () => {
createComponentAndFilter([USER_FILTER_TOKEN], '?page=2');
- expect(redirectTo).toHaveBeenCalledWith('https://localhost/?user=mr_abuser');
+ expect(redirectTo).toHaveBeenCalledWith('https://localhost/?user=mr_abuser'); // eslint-disable-line import/no-deprecated
});
it('redirects with existing sort query param', () => {
createComponentAndFilter([USER_FILTER_TOKEN], `?sort=${DEFAULT_SORT}`);
+ // eslint-disable-next-line import/no-deprecated
expect(redirectTo).toHaveBeenCalledWith(
`https://localhost/?user=mr_abuser&sort=${DEFAULT_SORT}`,
);
@@ -197,6 +198,7 @@ describe('AbuseReportsFilteredSearchBar', () => {
it('redirects to URL with existing query params and the sort query param', () => {
createComponentAndSort(`?${EXISTING_QUERY}`);
+ // eslint-disable-next-line import/no-deprecated
expect(redirectTo).toHaveBeenCalledWith(
`https://localhost/?${EXISTING_QUERY}&sort=${SORT_VALUE}`,
);
@@ -205,6 +207,7 @@ describe('AbuseReportsFilteredSearchBar', () => {
it('redirects without page query param', () => {
createComponentAndSort(`?${EXISTING_QUERY}&page=2`);
+ // eslint-disable-next-line import/no-deprecated
expect(redirectTo).toHaveBeenCalledWith(
`https://localhost/?${EXISTING_QUERY}&sort=${SORT_VALUE}`,
);
@@ -213,6 +216,7 @@ describe('AbuseReportsFilteredSearchBar', () => {
it('redirects with existing sort query param replaced with the new one', () => {
createComponentAndSort(`?${EXISTING_QUERY}&sort=created_at_desc`);
+ // eslint-disable-next-line import/no-deprecated
expect(redirectTo).toHaveBeenCalledWith(
`https://localhost/?${EXISTING_QUERY}&sort=${SORT_VALUE}`,
);
diff --git a/spec/frontend/admin/broadcast_messages/components/base_spec.js b/spec/frontend/admin/broadcast_messages/components/base_spec.js
index 50d8eeb563d..80577f86e3e 100644
--- a/spec/frontend/admin/broadcast_messages/components/base_spec.js
+++ b/spec/frontend/admin/broadcast_messages/components/base_spec.js
@@ -7,7 +7,7 @@ import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
import { createAlert } from '~/alert';
import axios from '~/lib/utils/axios_utils';
import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import BroadcastMessagesBase from '~/admin/broadcast_messages/components/base.vue';
import MessagesTable from '~/admin/broadcast_messages/components/messages_table.vue';
import { generateMockMessages, MOCK_MESSAGES } from '../mock_data';
@@ -107,6 +107,6 @@ describe('BroadcastMessagesBase', () => {
findTable().vm.$emit('delete-message', id);
await waitForPromises();
- expect(redirectTo).toHaveBeenCalledWith(`${TEST_HOST}/admin/broadcast_messages?page=1`);
+ expect(redirectTo).toHaveBeenCalledWith(`${TEST_HOST}/admin/broadcast_messages?page=1`); // eslint-disable-line import/no-deprecated
});
});
diff --git a/spec/frontend/analytics/shared/components/metric_tile_spec.js b/spec/frontend/analytics/shared/components/metric_tile_spec.js
index 00e82cff0f0..9da5ed0fb07 100644
--- a/spec/frontend/analytics/shared/components/metric_tile_spec.js
+++ b/spec/frontend/analytics/shared/components/metric_tile_spec.js
@@ -2,7 +2,7 @@ import { GlSingleStat } from '@gitlab/ui/dist/charts';
import { shallowMount } from '@vue/test-utils';
import MetricTile from '~/analytics/shared/components/metric_tile.vue';
import MetricPopover from '~/analytics/shared/components/metric_popover.vue';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
jest.mock('~/lib/utils/url_utility');
@@ -34,7 +34,7 @@ describe('MetricTile', () => {
const singleStat = findSingleStat();
singleStat.vm.$emit('click');
- expect(redirectTo).toHaveBeenCalledWith('foo/bar');
+ expect(redirectTo).toHaveBeenCalledWith('foo/bar'); // eslint-disable-line import/no-deprecated
});
it("when the metric doesn't have links, it won't the user on click", () => {
@@ -43,7 +43,7 @@ describe('MetricTile', () => {
const singleStat = findSingleStat();
singleStat.vm.$emit('click');
- expect(redirectTo).not.toHaveBeenCalled();
+ expect(redirectTo).not.toHaveBeenCalled(); // eslint-disable-line import/no-deprecated
});
});
diff --git a/spec/frontend/authentication/password/components/password_input_spec.js b/spec/frontend/authentication/password/components/password_input_spec.js
index 9960539af10..5b2a9da993b 100644
--- a/spec/frontend/authentication/password/components/password_input_spec.js
+++ b/spec/frontend/authentication/password/components/password_input_spec.js
@@ -10,6 +10,7 @@ describe('PasswordInput', () => {
id: 'new_user_password',
minimumPasswordLength: '8',
qaSelector: 'new_user_password_field',
+ testid: 'new_user_password',
autocomplete: 'new-password',
name: 'new_user',
};
@@ -33,6 +34,7 @@ describe('PasswordInput', () => {
expect(findPasswordInput().attributes('name')).toBe(propsData.name);
expect(findPasswordInput().attributes('minlength')).toBe(propsData.minimumPasswordLength);
expect(findPasswordInput().attributes('data-qa-selector')).toBe(propsData.qaSelector);
+ expect(findPasswordInput().attributes('data-testid')).toBe(propsData.testid);
expect(findPasswordInput().attributes('title')).toBe(propsData.title);
});
diff --git a/spec/frontend/ci/pipeline_editor/pipeline_editor_app_spec.js b/spec/frontend/ci/pipeline_editor/pipeline_editor_app_spec.js
index 8bac46a3e9c..cc4a022c2df 100644
--- a/spec/frontend/ci/pipeline_editor/pipeline_editor_app_spec.js
+++ b/spec/frontend/ci/pipeline_editor/pipeline_editor_app_spec.js
@@ -6,7 +6,7 @@ import setWindowLocation from 'helpers/set_window_location_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { HTTP_STATUS_INTERNAL_SERVER_ERROR } from '~/lib/utils/http_status';
-import { objectToQuery, redirectTo } from '~/lib/utils/url_utility';
+import { objectToQuery, redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { resolvers } from '~/ci/pipeline_editor/graphql/resolvers';
import PipelineEditorTabs from '~/ci/pipeline_editor/components/pipeline_editor_tabs.vue';
import PipelineEditorEmptyState from '~/ci/pipeline_editor/components/ui/pipeline_editor_empty_state.vue';
@@ -434,7 +434,7 @@ describe('Pipeline editor app component', () => {
'merge_request[target_branch]': mockDefaultBranch,
});
- expect(redirectTo).toHaveBeenCalledWith(`${mockNewMergeRequestPath}?${branchesQuery}`);
+ expect(redirectTo).toHaveBeenCalledWith(`${mockNewMergeRequestPath}?${branchesQuery}`); // eslint-disable-line import/no-deprecated
});
});
diff --git a/spec/frontend/ci/pipeline_new/components/pipeline_new_form_spec.js b/spec/frontend/ci/pipeline_new/components/pipeline_new_form_spec.js
index a08a01009e2..1d4ae33c667 100644
--- a/spec/frontend/ci/pipeline_new/components/pipeline_new_form_spec.js
+++ b/spec/frontend/ci/pipeline_new/components/pipeline_new_form_spec.js
@@ -13,7 +13,7 @@ import {
HTTP_STATUS_INTERNAL_SERVER_ERROR,
HTTP_STATUS_OK,
} from '~/lib/utils/http_status';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import PipelineNewForm, {
POLLING_INTERVAL,
} from '~/ci/pipeline_new/components/pipeline_new_form.vue';
@@ -212,7 +212,7 @@ describe('Pipeline New Form', () => {
await waitForPromises();
expect(getFormPostParams().ref).toEqual(`refs/heads/${defaultBranch}`);
- expect(redirectTo).toHaveBeenCalledWith(`${pipelinesPath}/${newPipelinePostResponse.id}`);
+ expect(redirectTo).toHaveBeenCalledWith(`${pipelinesPath}/${newPipelinePostResponse.id}`); // eslint-disable-line import/no-deprecated
});
it('creates a pipeline with short ref and variables from the query params', async () => {
@@ -225,7 +225,7 @@ describe('Pipeline New Form', () => {
await waitForPromises();
expect(getFormPostParams()).toEqual(mockPostParams);
- expect(redirectTo).toHaveBeenCalledWith(`${pipelinesPath}/${newPipelinePostResponse.id}`);
+ expect(redirectTo).toHaveBeenCalledWith(`${pipelinesPath}/${newPipelinePostResponse.id}`); // eslint-disable-line import/no-deprecated
});
});
diff --git a/spec/frontend/ci/runner/admin_new_runner_app/admin_new_runner_app_spec.js b/spec/frontend/ci/runner/admin_new_runner_app/admin_new_runner_app_spec.js
index 62aba4e4be6..4c56dd74f1a 100644
--- a/spec/frontend/ci/runner/admin_new_runner_app/admin_new_runner_app_spec.js
+++ b/spec/frontend/ci/runner/admin_new_runner_app/admin_new_runner_app_spec.js
@@ -16,7 +16,7 @@ import {
WINDOWS_PLATFORM,
} from '~/ci/runner/constants';
import RunnerCreateForm from '~/ci/runner/components/runner_create_form.vue';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { runnerCreateResult } from '../mock_data';
jest.mock('~/ci/runner/local_storage_alert/save_alert_to_local_storage');
@@ -87,7 +87,7 @@ describe('AdminNewRunnerApp', () => {
it('redirects to the registration page', () => {
const url = `${mockCreatedRunner.ephemeralRegisterUrl}?${PARAM_KEY_PLATFORM}=${DEFAULT_PLATFORM}`;
- expect(redirectTo).toHaveBeenCalledWith(url);
+ expect(redirectTo).toHaveBeenCalledWith(url); // eslint-disable-line import/no-deprecated
});
});
@@ -100,7 +100,7 @@ describe('AdminNewRunnerApp', () => {
it('redirects to the registration page with the platform', () => {
const url = `${mockCreatedRunner.ephemeralRegisterUrl}?${PARAM_KEY_PLATFORM}=${WINDOWS_PLATFORM}`;
- expect(redirectTo).toHaveBeenCalledWith(url);
+ expect(redirectTo).toHaveBeenCalledWith(url); // eslint-disable-line import/no-deprecated
});
});
diff --git a/spec/frontend/ci/runner/admin_runner_show/admin_runner_show_app_spec.js b/spec/frontend/ci/runner/admin_runner_show/admin_runner_show_app_spec.js
index d1f95aef349..9787b1ef83f 100644
--- a/spec/frontend/ci/runner/admin_runner_show/admin_runner_show_app_spec.js
+++ b/spec/frontend/ci/runner/admin_runner_show/admin_runner_show_app_spec.js
@@ -5,7 +5,7 @@ import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_help
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { createAlert, VARIANT_SUCCESS } from '~/alert';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import RunnerHeader from '~/ci/runner/components/runner_header.vue';
@@ -180,7 +180,7 @@ describe('AdminRunnerShowApp', () => {
message: 'Runner deleted',
variant: VARIANT_SUCCESS,
});
- expect(redirectTo).toHaveBeenCalledWith(mockRunnersPath);
+ expect(redirectTo).toHaveBeenCalledWith(mockRunnersPath); // eslint-disable-line import/no-deprecated
});
});
diff --git a/spec/frontend/ci/runner/components/runner_update_form_spec.js b/spec/frontend/ci/runner/components/runner_update_form_spec.js
index 620e2f85890..db4c236bfff 100644
--- a/spec/frontend/ci/runner/components/runner_update_form_spec.js
+++ b/spec/frontend/ci/runner/components/runner_update_form_spec.js
@@ -6,7 +6,7 @@ import createMockApollo from 'helpers/mock_apollo_helper';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { createAlert, VARIANT_SUCCESS } from '~/alert';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import RunnerUpdateForm from '~/ci/runner/components/runner_update_form.vue';
import {
INSTANCE_TYPE,
@@ -86,7 +86,7 @@ describe('RunnerUpdateForm', () => {
variant: VARIANT_SUCCESS,
}),
);
- expect(redirectTo).toHaveBeenCalledWith(mockRunnerPath);
+ expect(redirectTo).toHaveBeenCalledWith(mockRunnerPath); // eslint-disable-line import/no-deprecated
};
beforeEach(() => {
@@ -278,7 +278,7 @@ describe('RunnerUpdateForm', () => {
expect(captureException).not.toHaveBeenCalled();
expect(saveAlertToLocalStorage).not.toHaveBeenCalled();
- expect(redirectTo).not.toHaveBeenCalled();
+ expect(redirectTo).not.toHaveBeenCalled(); // eslint-disable-line import/no-deprecated
});
});
});
diff --git a/spec/frontend/ci/runner/group_new_runner_app/group_new_runner_app_spec.js b/spec/frontend/ci/runner/group_new_runner_app/group_new_runner_app_spec.js
index e2cf46023b1..1c052b00fc3 100644
--- a/spec/frontend/ci/runner/group_new_runner_app/group_new_runner_app_spec.js
+++ b/spec/frontend/ci/runner/group_new_runner_app/group_new_runner_app_spec.js
@@ -16,7 +16,7 @@ import {
WINDOWS_PLATFORM,
} from '~/ci/runner/constants';
import RunnerCreateForm from '~/ci/runner/components/runner_create_form.vue';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { runnerCreateResult } from '../mock_data';
const mockGroupId = 'gid://gitlab/Group/72';
@@ -92,7 +92,7 @@ describe('GroupRunnerRunnerApp', () => {
it('redirects to the registration page', () => {
const url = `${mockCreatedRunner.ephemeralRegisterUrl}?${PARAM_KEY_PLATFORM}=${DEFAULT_PLATFORM}`;
- expect(redirectTo).toHaveBeenCalledWith(url);
+ expect(redirectTo).toHaveBeenCalledWith(url); // eslint-disable-line import/no-deprecated
});
});
@@ -105,7 +105,7 @@ describe('GroupRunnerRunnerApp', () => {
it('redirects to the registration page with the platform', () => {
const url = `${mockCreatedRunner.ephemeralRegisterUrl}?${PARAM_KEY_PLATFORM}=${WINDOWS_PLATFORM}`;
- expect(redirectTo).toHaveBeenCalledWith(url);
+ expect(redirectTo).toHaveBeenCalledWith(url); // eslint-disable-line import/no-deprecated
});
});
diff --git a/spec/frontend/ci/runner/group_runner_show/group_runner_show_app_spec.js b/spec/frontend/ci/runner/group_runner_show/group_runner_show_app_spec.js
index 60f51704c0e..0c594e8005c 100644
--- a/spec/frontend/ci/runner/group_runner_show/group_runner_show_app_spec.js
+++ b/spec/frontend/ci/runner/group_runner_show/group_runner_show_app_spec.js
@@ -5,7 +5,7 @@ import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_help
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { createAlert, VARIANT_SUCCESS } from '~/alert';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import RunnerHeader from '~/ci/runner/components/runner_header.vue';
@@ -185,7 +185,7 @@ describe('GroupRunnerShowApp', () => {
message: 'Runner deleted',
variant: VARIANT_SUCCESS,
});
- expect(redirectTo).toHaveBeenCalledWith(mockRunnersPath);
+ expect(redirectTo).toHaveBeenCalledWith(mockRunnersPath); // eslint-disable-line import/no-deprecated
});
});
});
diff --git a/spec/frontend/ci/runner/project_new_runner_app/project_new_runner_app_spec.js b/spec/frontend/ci/runner/project_new_runner_app/project_new_runner_app_spec.js
index 4bb5b25fe15..5bfbbfdc074 100644
--- a/spec/frontend/ci/runner/project_new_runner_app/project_new_runner_app_spec.js
+++ b/spec/frontend/ci/runner/project_new_runner_app/project_new_runner_app_spec.js
@@ -16,7 +16,7 @@ import {
WINDOWS_PLATFORM,
} from '~/ci/runner/constants';
import RunnerCreateForm from '~/ci/runner/components/runner_create_form.vue';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { runnerCreateResult, mockRegistrationToken } from '../mock_data';
const mockProjectId = 'gid://gitlab/Project/72';
@@ -93,7 +93,7 @@ describe('ProjectRunnerRunnerApp', () => {
it('redirects to the registration page', () => {
const url = `${mockCreatedRunner.ephemeralRegisterUrl}?${PARAM_KEY_PLATFORM}=${DEFAULT_PLATFORM}`;
- expect(redirectTo).toHaveBeenCalledWith(url);
+ expect(redirectTo).toHaveBeenCalledWith(url); // eslint-disable-line import/no-deprecated
});
});
@@ -106,7 +106,7 @@ describe('ProjectRunnerRunnerApp', () => {
it('redirects to the registration page with the platform', () => {
const url = `${mockCreatedRunner.ephemeralRegisterUrl}?${PARAM_KEY_PLATFORM}=${WINDOWS_PLATFORM}`;
- expect(redirectTo).toHaveBeenCalledWith(url);
+ expect(redirectTo).toHaveBeenCalledWith(url); // eslint-disable-line import/no-deprecated
});
});
diff --git a/spec/frontend/gitlab_pages/new/pages/pages_pipeline_wizard_spec.js b/spec/frontend/gitlab_pages/new/pages/pages_pipeline_wizard_spec.js
index b1adc3f794a..289702a4263 100644
--- a/spec/frontend/gitlab_pages/new/pages/pages_pipeline_wizard_spec.js
+++ b/spec/frontend/gitlab_pages/new/pages/pages_pipeline_wizard_spec.js
@@ -7,7 +7,7 @@ import PagesPipelineWizard, { i18n } from '~/gitlab_pages/components/pages_pipel
import PipelineWizard from '~/pipeline_wizard/pipeline_wizard.vue';
import pagesTemplate from '~/pipeline_wizard/templates/pages.yml';
import pagesMarkOnboardingComplete from '~/gitlab_pages/queries/mark_onboarding_complete.graphql';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
Vue.use(VueApollo);
@@ -92,7 +92,7 @@ describe('PagesPipelineWizard', () => {
await waitForPromises();
- expect(redirectTo).toHaveBeenCalledWith(props.redirectToWhenDone);
+ expect(redirectTo).toHaveBeenCalledWith(props.redirectToWhenDone); // eslint-disable-line import/no-deprecated
});
});
});
diff --git a/spec/frontend/import_entities/import_projects/components/advanced_settings_spec.js b/spec/frontend/import_entities/import_projects/components/advanced_settings_spec.js
index 2294d236e8b..29af6dc946f 100644
--- a/spec/frontend/import_entities/import_projects/components/advanced_settings_spec.js
+++ b/spec/frontend/import_entities/import_projects/components/advanced_settings_spec.js
@@ -5,8 +5,8 @@ import AdvancedSettingsPanel from '~/import_entities/import_projects/components/
describe('Import Advanced Settings', () => {
let wrapper;
const OPTIONAL_STAGES = [
- { name: 'stage1', label: 'Stage 1' },
- { name: 'stage2', label: 'Stage 2', details: 'Extra details' },
+ { name: 'stage1', label: 'Stage 1', selected: false },
+ { name: 'stage2', label: 'Stage 2', details: 'Extra details', selected: false },
];
const createComponent = () => {
diff --git a/spec/frontend/import_entities/import_projects/components/import_projects_table_spec.js b/spec/frontend/import_entities/import_projects/components/import_projects_table_spec.js
index f7bc0e4a0e8..351bbe5ea28 100644
--- a/spec/frontend/import_entities/import_projects/components/import_projects_table_spec.js
+++ b/spec/frontend/import_entities/import_projects/components/import_projects_table_spec.js
@@ -285,7 +285,7 @@ describe('ImportProjectsTable', () => {
});
it('should render advanced settings panel when no optional steps are passed', () => {
- const OPTIONAL_STAGES = [{ name: 'step1', label: 'Step 1' }];
+ const OPTIONAL_STAGES = [{ name: 'step1', label: 'Step 1', selected: true }];
createComponent({ state: { providerRepos: [providerRepo] }, optionalStages: OPTIONAL_STAGES });
expect(wrapper.findComponent(AdvancedSettingsPanel).exists()).toBe(true);
@@ -293,7 +293,7 @@ describe('ImportProjectsTable', () => {
OPTIONAL_STAGES,
);
expect(wrapper.findComponent(AdvancedSettingsPanel).props('value')).toStrictEqual({
- step1: false,
+ step1: true,
});
});
});
diff --git a/spec/frontend/jobs/components/job/manual_variables_form_spec.js b/spec/frontend/jobs/components/job/manual_variables_form_spec.js
index c8c865dd28e..a48155d93ac 100644
--- a/spec/frontend/jobs/components/job/manual_variables_form_spec.js
+++ b/spec/frontend/jobs/components/job/manual_variables_form_spec.js
@@ -9,7 +9,7 @@ import { TYPENAME_CI_BUILD } from '~/graphql_shared/constants';
import { JOB_GRAPHQL_ERRORS } from '~/jobs/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import waitForPromises from 'helpers/wait_for_promises';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import ManualVariablesForm from '~/jobs/components/job/manual_variables_form.vue';
import getJobQuery from '~/jobs/components/job/graphql/queries/get_job.query.graphql';
import playJobMutation from '~/jobs/components/job/graphql/mutations/job_play_with_variables.mutation.graphql';
@@ -192,7 +192,7 @@ describe('Manual Variables Form', () => {
await waitForPromises();
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledTimes(1);
- expect(redirectTo).toHaveBeenCalledWith(mockJobPlayMutationData.data.jobPlay.job.webPath);
+ expect(redirectTo).toHaveBeenCalledWith(mockJobPlayMutationData.data.jobPlay.job.webPath); // eslint-disable-line import/no-deprecated
});
});
@@ -227,7 +227,7 @@ describe('Manual Variables Form', () => {
await waitForPromises();
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledTimes(1);
- expect(redirectTo).toHaveBeenCalledWith(mockJobRetryMutationData.data.jobRetry.job.webPath);
+ expect(redirectTo).toHaveBeenCalledWith(mockJobRetryMutationData.data.jobRetry.job.webPath); // eslint-disable-line import/no-deprecated
});
});
diff --git a/spec/frontend/jobs/components/job/sidebar_detail_row_spec.js b/spec/frontend/jobs/components/job/sidebar_detail_row_spec.js
index dd5a9e3491d..fd27004816a 100644
--- a/spec/frontend/jobs/components/job/sidebar_detail_row_spec.js
+++ b/spec/frontend/jobs/components/job/sidebar_detail_row_spec.js
@@ -1,5 +1,4 @@
-import { GlLink } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import SidebarDetailRow from '~/jobs/components/job/sidebar/sidebar_detail_row.vue';
describe('Sidebar detail row', () => {
@@ -8,18 +7,20 @@ describe('Sidebar detail row', () => {
const title = 'this is the title';
const value = 'this is the value';
const helpUrl = 'https://docs.gitlab.com/runner/register/index.html';
+ const path = 'path/to/value';
- const findHelpLink = () => wrapper.findComponent(GlLink);
+ const findHelpLink = () => wrapper.findByTestId('job-sidebar-help-link');
+ const findValueLink = () => wrapper.findByTestId('job-sidebar-value-link');
const createComponent = (props) => {
- wrapper = shallowMount(SidebarDetailRow, {
+ wrapper = shallowMountExtended(SidebarDetailRow, {
propsData: {
...props,
},
});
};
- describe('with title/value and without helpUrl', () => {
+ describe('with title/value and without helpUrl/path', () => {
beforeEach(() => {
createComponent({ title, value });
});
@@ -31,6 +32,10 @@ describe('Sidebar detail row', () => {
it('should not render the help link', () => {
expect(findHelpLink().exists()).toBe(false);
});
+
+ it('should not render the value link', () => {
+ expect(findValueLink().exists()).toBe(false);
+ });
});
describe('when helpUrl provided', () => {
@@ -47,4 +52,16 @@ describe('Sidebar detail row', () => {
expect(findHelpLink().attributes('href')).toBe(helpUrl);
});
});
+
+ describe('when path is provided', () => {
+ it('should render link to value', () => {
+ createComponent({
+ path,
+ title,
+ value,
+ });
+
+ expect(findValueLink().attributes('href')).toBe(path);
+ });
+ });
});
diff --git a/spec/frontend/jobs/components/table/cells/actions_cell_spec.js b/spec/frontend/jobs/components/table/cells/actions_cell_spec.js
index 79bc765f181..f2d249b6014 100644
--- a/spec/frontend/jobs/components/table/cells/actions_cell_spec.js
+++ b/spec/frontend/jobs/components/table/cells/actions_cell_spec.js
@@ -4,7 +4,7 @@ import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import ActionsCell from '~/jobs/components/table/cells/actions_cell.vue';
import eventHub from '~/jobs/components/table/event_hub';
import JobPlayMutation from '~/jobs/components/table/graphql/mutations/job_play.mutation.graphql';
@@ -146,7 +146,7 @@ describe('Job actions cell', () => {
await waitForPromises();
expect(eventHub.$emit).toHaveBeenCalledWith('jobActionPerformed');
- expect(redirectTo).not.toHaveBeenCalled();
+ expect(redirectTo).not.toHaveBeenCalled(); // eslint-disable-line import/no-deprecated
},
);
@@ -165,7 +165,7 @@ describe('Job actions cell', () => {
await waitForPromises();
- expect(redirectTo).toHaveBeenCalledWith(redirectLink);
+ expect(redirectTo).toHaveBeenCalledWith(redirectLink); // eslint-disable-line import/no-deprecated
expect(eventHub.$emit).not.toHaveBeenCalled();
},
);
diff --git a/spec/frontend/listbox/redirect_behavior_spec.js b/spec/frontend/listbox/redirect_behavior_spec.js
index 7b2a40b65ce..c2479e71e4a 100644
--- a/spec/frontend/listbox/redirect_behavior_spec.js
+++ b/spec/frontend/listbox/redirect_behavior_spec.js
@@ -1,6 +1,6 @@
import { initListbox } from '~/listbox';
import { initRedirectListboxBehavior } from '~/listbox/redirect_behavior';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { getFixture, setHTMLFixture } from 'helpers/fixtures';
jest.mock('~/lib/utils/url_utility');
@@ -42,10 +42,10 @@ describe('initRedirectListboxBehavior', () => {
const { onChange } = firstCallArgs[1];
const mockItem = { href: '/foo' };
- expect(redirectTo).not.toHaveBeenCalled();
+ expect(redirectTo).not.toHaveBeenCalled(); // eslint-disable-line import/no-deprecated
onChange(mockItem);
- expect(redirectTo).toHaveBeenCalledWith(mockItem.href);
+ expect(redirectTo).toHaveBeenCalledWith(mockItem.href); // eslint-disable-line import/no-deprecated
});
});
diff --git a/spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js b/spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js
index f346967121c..29b7ceae0e3 100644
--- a/spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js
+++ b/spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js
@@ -2,7 +2,7 @@ import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
import setWindowLocation from 'helpers/set_window_location_helper';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import MembersFilteredSearchBar from '~/members/components/filter_sort/members_filtered_search_bar.vue';
import {
MEMBER_TYPES,
@@ -167,7 +167,7 @@ describe('MembersFilteredSearchBar', () => {
{ type: FILTERED_SEARCH_TOKEN_TWO_FACTOR.type, value: { data: 'enabled', operator: '=' } },
]);
- expect(redirectTo).toHaveBeenCalledWith('https://localhost/?two_factor=enabled');
+ expect(redirectTo).toHaveBeenCalledWith('https://localhost/?two_factor=enabled'); // eslint-disable-line import/no-deprecated
});
it('adds search query param', () => {
@@ -178,6 +178,7 @@ describe('MembersFilteredSearchBar', () => {
{ type: FILTERED_SEARCH_TERM, value: { data: 'foobar' } },
]);
+ // eslint-disable-next-line import/no-deprecated
expect(redirectTo).toHaveBeenCalledWith(
'https://localhost/?two_factor=enabled&search=foobar',
);
@@ -191,6 +192,7 @@ describe('MembersFilteredSearchBar', () => {
{ type: FILTERED_SEARCH_TERM, value: { data: 'foo bar baz' } },
]);
+ // eslint-disable-next-line import/no-deprecated
expect(redirectTo).toHaveBeenCalledWith(
'https://localhost/?two_factor=enabled&search=foo+bar+baz',
);
@@ -206,6 +208,7 @@ describe('MembersFilteredSearchBar', () => {
{ type: FILTERED_SEARCH_TERM, value: { data: 'foobar' } },
]);
+ // eslint-disable-next-line import/no-deprecated
expect(redirectTo).toHaveBeenCalledWith(
'https://localhost/?two_factor=enabled&search=foobar&sort=name_asc',
);
@@ -220,7 +223,7 @@ describe('MembersFilteredSearchBar', () => {
{ type: FILTERED_SEARCH_TERM, value: { data: 'foobar' } },
]);
- expect(redirectTo).toHaveBeenCalledWith('https://localhost/?search=foobar&tab=invited');
+ expect(redirectTo).toHaveBeenCalledWith('https://localhost/?search=foobar&tab=invited'); // eslint-disable-line import/no-deprecated
});
});
});
diff --git a/spec/frontend/milestones/components/delete_milestone_modal_spec.js b/spec/frontend/milestones/components/delete_milestone_modal_spec.js
index f8730fd93a3..ad6aedaa8ff 100644
--- a/spec/frontend/milestones/components/delete_milestone_modal_spec.js
+++ b/spec/frontend/milestones/components/delete_milestone_modal_spec.js
@@ -5,7 +5,7 @@ import axios from '~/lib/utils/axios_utils';
import DeleteMilestoneModal from '~/milestones/components/delete_milestone_modal.vue';
import eventHub from '~/milestones/event_hub';
import { HTTP_STATUS_IM_A_TEAPOT, HTTP_STATUS_NOT_FOUND } from '~/lib/utils/http_status';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { createAlert } from '~/alert';
jest.mock('~/lib/utils/url_utility');
@@ -60,7 +60,7 @@ describe('Delete milestone modal', () => {
});
});
await findModal().vm.$emit('primary');
- expect(redirectTo).toHaveBeenCalledWith(responseURL);
+ expect(redirectTo).toHaveBeenCalledWith(responseURL); // eslint-disable-line import/no-deprecated
expect(eventHub.$emit).toHaveBeenCalledWith('deleteMilestoneModal.requestFinished', {
milestoneUrl: mockProps.milestoneUrl,
successful: true,
@@ -90,7 +90,7 @@ describe('Delete milestone modal', () => {
expect(createAlert).toHaveBeenCalledWith({
message: alertMessage,
});
- expect(redirectTo).not.toHaveBeenCalled();
+ expect(redirectTo).not.toHaveBeenCalled(); // eslint-disable-line import/no-deprecated
expect(eventHub.$emit).toHaveBeenCalledWith('deleteMilestoneModal.requestFinished', {
milestoneUrl: mockProps.milestoneUrl,
successful: false,
diff --git a/spec/frontend/ml/experiment_tracking/routes/candidates/show/__snapshots__/ml_candidates_show_spec.js.snap b/spec/frontend/ml/experiment_tracking/routes/candidates/show/__snapshots__/ml_candidates_show_spec.js.snap
deleted file mode 100644
index 0d2615e3b80..00000000000
--- a/spec/frontend/ml/experiment_tracking/routes/candidates/show/__snapshots__/ml_candidates_show_spec.js.snap
+++ /dev/null
@@ -1,215 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`MlCandidatesShow renders correctly 1`] = `
-<div>
- <model-experiments-header-stub
- pagetitle="Model candidate details"
- >
- <delete-button-stub
- actionprimarytext="Delete candidate"
- deleteconfirmationtext="Deleting this candidate will delete the associated parameters, metrics, and metadata."
- deletepath="path_to_candidate"
- modaltitle="Delete candidate?"
- />
- </model-experiments-header-stub>
-
- <table
- class="candidate-details gl-w-full"
- >
- <tbody>
- <tr
- class="divider"
- />
-
- <tr>
- <td
- class="gl-text-secondary gl-font-weight-bold"
- >
- Info
- </td>
-
- <td
- class="gl-font-weight-bold"
- >
- ID
- </td>
-
- <td>
- candidate_iid
- </td>
- </tr>
-
- <tr>
- <td />
-
- <td
- class="gl-font-weight-bold"
- >
- MLflow run ID
- </td>
-
- <td>
- abcdefg
- </td>
- </tr>
-
- <tr>
- <td />
-
- <td
- class="gl-font-weight-bold"
- >
- Status
- </td>
-
- <td>
- SUCCESS
- </td>
- </tr>
-
- <tr>
- <td />
-
- <td
- class="gl-font-weight-bold"
- >
- Experiment
- </td>
-
- <td>
- <gl-link-stub>
- The Experiment
- </gl-link-stub>
- </td>
- </tr>
-
- <tr>
- <td />
-
- <td
- class="gl-font-weight-bold"
- >
- Artifacts
- </td>
-
- <td>
- <gl-link-stub
- href="path_to_artifact"
- >
- Artifacts
- </gl-link-stub>
- </td>
- </tr>
-
- <tr
- class="divider"
- />
-
- <tr>
- <td
- class="gl-text-secondary gl-font-weight-bold"
- >
-
- Parameters
-
- </td>
-
- <td
- class="gl-font-weight-bold"
- >
- Algorithm
- </td>
-
- <td>
- Decision Tree
- </td>
- </tr>
- <tr>
- <td />
-
- <td
- class="gl-font-weight-bold"
- >
- MaxDepth
- </td>
-
- <td>
- 3
- </td>
- </tr>
- <tr
- class="divider"
- />
-
- <tr>
- <td
- class="gl-text-secondary gl-font-weight-bold"
- >
-
- Metrics
-
- </td>
-
- <td
- class="gl-font-weight-bold"
- >
- AUC
- </td>
-
- <td>
- .55
- </td>
- </tr>
- <tr>
- <td />
-
- <td
- class="gl-font-weight-bold"
- >
- Accuracy
- </td>
-
- <td>
- .99
- </td>
- </tr>
- <tr
- class="divider"
- />
-
- <tr>
- <td
- class="gl-text-secondary gl-font-weight-bold"
- >
-
- Metadata
-
- </td>
-
- <td
- class="gl-font-weight-bold"
- >
- FileName
- </td>
-
- <td>
- test.py
- </td>
- </tr>
- <tr>
- <td />
-
- <td
- class="gl-font-weight-bold"
- >
- ExecutionTime
- </td>
-
- <td>
- .0856
- </td>
- </tr>
- </tbody>
- </table>
-</div>
-`;
diff --git a/spec/frontend/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row_spec.js b/spec/frontend/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row_spec.js
new file mode 100644
index 00000000000..8a39c5de2b3
--- /dev/null
+++ b/spec/frontend/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row_spec.js
@@ -0,0 +1,49 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlLink } from '@gitlab/ui';
+import DetailRow from '~/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row.vue';
+
+describe('CandidateDetailRow', () => {
+ const SECTION_LABEL_CELL = 0;
+ const ROW_LABEL_CELL = 1;
+ const ROW_VALUE_CELL = 2;
+
+ let wrapper;
+
+ const createWrapper = (href = '') => {
+ wrapper = shallowMount(DetailRow, {
+ propsData: { sectionLabel: 'Section', label: 'Item', text: 'Text', href },
+ });
+ };
+
+ const findCellAt = (index) => wrapper.findAll('td').at(index);
+ const findLink = () => findCellAt(ROW_VALUE_CELL).findComponent(GlLink);
+
+ beforeEach(() => createWrapper());
+
+ it('renders section label', () => {
+ expect(findCellAt(SECTION_LABEL_CELL).text()).toBe('Section');
+ });
+
+ it('renders row label', () => {
+ expect(findCellAt(ROW_LABEL_CELL).text()).toBe('Item');
+ });
+
+ describe('No href', () => {
+ it('Renders text', () => {
+ expect(findCellAt(ROW_VALUE_CELL).text()).toBe('Text');
+ });
+
+ it('Does not render as link', () => {
+ expect(findLink().exists()).toBe(false);
+ });
+ });
+
+ describe('With href', () => {
+ beforeEach(() => createWrapper('LINK'));
+
+ it('Renders link', () => {
+ expect(findLink().attributes().href).toBe('LINK');
+ expect(findLink().text()).toBe('Text');
+ });
+ });
+});
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 d7044cbcd9b..9d1c22faa8f 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,58 +1,119 @@
-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 } from '~/ml/experiment_tracking/routes/candidates/show/translations';
import DeleteButton from '~/ml/experiment_tracking/components/delete_button.vue';
import ModelExperimentsHeader from '~/ml/experiment_tracking/components/model_experiments_header.vue';
+import { newCandidate } from './mock_data';
describe('MlCandidatesShow', () => {
let wrapper;
+ const CANDIDATE = newCandidate();
- const createWrapper = () => {
- const candidate = {
- params: [
- { name: 'Algorithm', value: 'Decision Tree' },
- { name: 'MaxDepth', value: '3' },
- ],
- metrics: [
- { name: 'AUC', value: '.55' },
- { name: 'Accuracy', value: '.99' },
- ],
- 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',
- experiment_path: 'path/to/experiment',
- status: 'SUCCESS',
- path: 'path_to_candidate',
- },
- };
-
- wrapper = shallowMountExtended(MlCandidatesShow, { propsData: { candidate } });
+ const createWrapper = (createCandidate = () => CANDIDATE) => {
+ wrapper = shallowMount(MlCandidatesShow, {
+ propsData: { candidate: createCandidate() },
+ });
};
- beforeEach(createWrapper);
-
const findDeleteButton = () => wrapper.findComponent(DeleteButton);
const findHeader = () => wrapper.findComponent(ModelExperimentsHeader);
+ const findNthDetailRow = (index) => wrapper.findAllComponents(DetailRow).at(index);
+ const findSectionLabel = (label) => wrapper.find(`[sectionLabel='${label}']`);
+ const findLabel = (label) => wrapper.find(`[label='${label}']`);
- it('shows delete button', () => {
- expect(findDeleteButton().exists()).toBe(true);
- });
+ describe('Header', () => {
+ beforeEach(() => createWrapper());
- it('passes the delete path to delete button', () => {
- expect(findDeleteButton().props('deletePath')).toBe('path_to_candidate');
- });
+ it('shows delete button', () => {
+ expect(findDeleteButton().exists()).toBe(true);
+ });
+
+ it('passes the delete path to delete button', () => {
+ expect(findDeleteButton().props('deletePath')).toBe('path_to_candidate');
+ });
- it('passes the right title', () => {
- expect(findHeader().props('pageTitle')).toBe(TITLE_LABEL);
+ it('passes the right title', () => {
+ expect(findHeader().props('pageTitle')).toBe(TITLE_LABEL);
+ });
});
- it('renders correctly', () => {
- expect(wrapper.element).toMatchSnapshot();
+ describe('Detail Table', () => {
+ describe('All info available', () => {
+ beforeEach(() => createWrapper());
+
+ const expectedTable = [
+ ['Info', 'ID', CANDIDATE.info.iid, ''],
+ ['', 'MLflow run ID', CANDIDATE.info.eid, ''],
+ ['', 'Status', CANDIDATE.info.status, ''],
+ ['', 'Experiment', CANDIDATE.info.experiment_name, CANDIDATE.info.path_to_experiment],
+ ['', 'Artifacts', 'Artifacts', CANDIDATE.info.path_to_artifact],
+ ['Parameters', CANDIDATE.params[0].name, CANDIDATE.params[0].value, ''],
+ ['', CANDIDATE.params[1].name, CANDIDATE.params[1].value, ''],
+ ['Metrics', CANDIDATE.metrics[0].name, CANDIDATE.metrics[0].value, ''],
+ ['', CANDIDATE.metrics[1].name, CANDIDATE.metrics[1].value, ''],
+ ['Metadata', CANDIDATE.metadata[0].name, CANDIDATE.metadata[0].value, ''],
+ ['', CANDIDATE.metadata[1].name, CANDIDATE.metadata[1].value, ''],
+ ].map((row, index) => [index, ...row]);
+
+ it.each(expectedTable)(
+ 'row %s is created correctly',
+ (index, sectionLabel, label, text, href) => {
+ const row = findNthDetailRow(index);
+
+ expect(row.props()).toMatchObject({ sectionLabel, label, text, href });
+ },
+ );
+ it('does not render params', () => {
+ expect(findSectionLabel('Parameters').exists()).toBe(true);
+ });
+
+ it('renders all conditional rows', () => {
+ // This is a bit of a duplicated test from the above table test, but having this makes sure that the
+ // tests that test the negatives are implemented correctly
+ expect(findLabel('Artifacts').exists()).toBe(true);
+ expect(findSectionLabel('Parameters').exists()).toBe(true);
+ expect(findSectionLabel('Metadata').exists()).toBe(true);
+ expect(findSectionLabel('Metrics').exists()).toBe(true);
+ });
+ });
+
+ 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;
+ return candidate;
+ }),
+ );
+
+ it('does not render params', () => {
+ expect(findSectionLabel('Parameters').exists()).toBe(false);
+ });
+
+ it('does not render metadata', () => {
+ expect(findSectionLabel('Metadata').exists()).toBe(false);
+ });
+
+ it('does not render metrics', () => {
+ expect(findSectionLabel('Metrics').exists()).toBe(false);
+ });
+ });
});
});
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
new file mode 100644
index 00000000000..cad2c03fc93
--- /dev/null
+++ b/spec/frontend/ml/experiment_tracking/routes/candidates/show/mock_data.js
@@ -0,0 +1,23 @@
+export const newCandidate = () => ({
+ params: [
+ { name: 'Algorithm', value: 'Decision Tree' },
+ { name: 'MaxDepth', value: '3' },
+ ],
+ metrics: [
+ { name: 'AUC', value: '.55' },
+ { name: 'Accuracy', value: '.99' },
+ ],
+ 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',
+ },
+});
diff --git a/spec/frontend/monitoring/components/dashboard_actions_menu_spec.js b/spec/frontend/monitoring/components/dashboard_actions_menu_spec.js
index c54acf3cbee..4d290922707 100644
--- a/spec/frontend/monitoring/components/dashboard_actions_menu_spec.js
+++ b/spec/frontend/monitoring/components/dashboard_actions_menu_spec.js
@@ -2,7 +2,7 @@ import { GlDropdownItem, GlModal } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import CustomMetricsFormFields from '~/custom_metrics/components/custom_metrics_form_fields.vue';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import ActionsMenu from '~/monitoring/components/dashboard_actions_menu.vue';
import { DASHBOARD_PAGE, PANEL_NEW_PAGE } from '~/monitoring/router/constants';
import { createStore } from '~/monitoring/stores';
@@ -292,8 +292,8 @@ describe('Actions menu', () => {
findDuplicateDashboardModal().vm.$emit('dashboardDuplicated', newDashboard);
await nextTick();
- expect(redirectTo).toHaveBeenCalled();
- expect(redirectTo).toHaveBeenCalledWith(newDashboardUrl);
+ expect(redirectTo).toHaveBeenCalled(); // eslint-disable-line import/no-deprecated
+ expect(redirectTo).toHaveBeenCalledWith(newDashboardUrl); // eslint-disable-line import/no-deprecated
});
});
});
diff --git a/spec/frontend/monitoring/components/dashboard_header_spec.js b/spec/frontend/monitoring/components/dashboard_header_spec.js
index e54b87c307c..091e05ab271 100644
--- a/spec/frontend/monitoring/components/dashboard_header_spec.js
+++ b/spec/frontend/monitoring/components/dashboard_header_spec.js
@@ -1,7 +1,7 @@
import { GlDropdownItem, GlSearchBoxByType, GlLoadingIcon, GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import ActionsMenu from '~/monitoring/components/dashboard_actions_menu.vue';
import DashboardHeader from '~/monitoring/components/dashboard_header.vue';
import DashboardsDropdown from '~/monitoring/components/dashboards_dropdown.vue';
@@ -74,6 +74,7 @@ describe('Dashboard header', () => {
display_name: 'A display name',
});
+ // eslint-disable-next-line import/no-deprecated
expect(redirectTo).toHaveBeenCalledWith(
`${mockProjectPath}/-/metrics/.gitlab%2Fdashboards%2Fdashboard%26copy.yml`,
);
@@ -85,6 +86,7 @@ describe('Dashboard header', () => {
display_name: 'dashboard&copy.yml',
});
+ // eslint-disable-next-line import/no-deprecated
expect(redirectTo).toHaveBeenCalledWith(`${mockProjectPath}/-/metrics/dashboard%26copy.yml`);
});
});
diff --git a/spec/frontend/monitoring/components/dashboard_url_time_spec.js b/spec/frontend/monitoring/components/dashboard_url_time_spec.js
index c43f6446b99..b123d1e7d79 100644
--- a/spec/frontend/monitoring/components/dashboard_url_time_spec.js
+++ b/spec/frontend/monitoring/components/dashboard_url_time_spec.js
@@ -5,7 +5,7 @@ import { createAlert } from '~/alert';
import axios from '~/lib/utils/axios_utils';
import {
queryToObject,
- redirectTo,
+ redirectTo, // eslint-disable-line import/no-deprecated
removeParams,
mergeUrlParams,
updateHistory,
@@ -136,7 +136,7 @@ describe('dashboard invalid url parameters', () => {
// redirect to with new parameters
expect(mergeUrlParams).toHaveBeenCalledWith({ duration_seconds: '120' }, toUrl);
- expect(redirectTo).toHaveBeenCalledTimes(1);
+ expect(redirectTo).toHaveBeenCalledTimes(1); // eslint-disable-line import/no-deprecated
});
it('changes the url when a panel moves the time slider', async () => {
diff --git a/spec/frontend/pages/admin/jobs/components/cancel_jobs_modal_spec.js b/spec/frontend/pages/admin/jobs/components/cancel_jobs_modal_spec.js
index afd7ee09ae8..b1d2e443d54 100644
--- a/spec/frontend/pages/admin/jobs/components/cancel_jobs_modal_spec.js
+++ b/spec/frontend/pages/admin/jobs/components/cancel_jobs_modal_spec.js
@@ -3,7 +3,7 @@ import { mount } from '@vue/test-utils';
import { GlModal } from '@gitlab/ui';
import { TEST_HOST } from 'helpers/test_constants';
import axios from '~/lib/utils/axios_utils';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import CancelJobsModal from '~/pages/admin/jobs/components/cancel_jobs_modal.vue';
jest.mock('~/lib/utils/url_utility', () => ({
@@ -41,7 +41,7 @@ describe('Cancel jobs modal', () => {
wrapper.findComponent(GlModal).vm.$emit('primary');
await nextTick();
- expect(redirectTo).toHaveBeenCalledWith(responseURL);
+ expect(redirectTo).toHaveBeenCalledWith(responseURL); // eslint-disable-line import/no-deprecated
});
it('displays error if canceling jobs failed', async () => {
@@ -60,7 +60,7 @@ describe('Cancel jobs modal', () => {
wrapper.findComponent(GlModal).vm.$emit('primary');
await nextTick();
- expect(redirectTo).not.toHaveBeenCalled();
+ expect(redirectTo).not.toHaveBeenCalled(); // eslint-disable-line import/no-deprecated
});
});
});
diff --git a/spec/frontend/pages/projects/forks/new/components/fork_form_spec.js b/spec/frontend/pages/projects/forks/new/components/fork_form_spec.js
index 9dce6fde6f6..722857a1420 100644
--- a/spec/frontend/pages/projects/forks/new/components/fork_form_spec.js
+++ b/spec/frontend/pages/projects/forks/new/components/fork_form_spec.js
@@ -461,7 +461,7 @@ describe('ForkForm component', () => {
await submitForm();
- expect(urlUtility.redirectTo).not.toHaveBeenCalled();
+ expect(urlUtility.redirectTo).not.toHaveBeenCalled(); // eslint-disable-line import/no-deprecated
});
it('does not make POST request if no visibility is checked', async () => {
@@ -549,7 +549,7 @@ describe('ForkForm component', () => {
setupComponent();
await submitForm();
- expect(urlUtility.redirectTo).toHaveBeenCalledWith(webUrl);
+ expect(urlUtility.redirectTo).toHaveBeenCalledWith(webUrl); // eslint-disable-line import/no-deprecated
});
it('displays an alert when POST is unsuccessful', async () => {
@@ -560,7 +560,7 @@ describe('ForkForm component', () => {
setupComponent();
await submitForm();
- expect(urlUtility.redirectTo).not.toHaveBeenCalled();
+ expect(urlUtility.redirectTo).not.toHaveBeenCalled(); // eslint-disable-line import/no-deprecated
expect(createAlert).toHaveBeenCalledWith({
message: 'An error occurred while forking the project. Please try again.',
});
diff --git a/spec/frontend/pipelines/components/jobs/failed_jobs_table_spec.js b/spec/frontend/pipelines/components/jobs/failed_jobs_table_spec.js
index 071977c9481..d5307b87a11 100644
--- a/spec/frontend/pipelines/components/jobs/failed_jobs_table_spec.js
+++ b/spec/frontend/pipelines/components/jobs/failed_jobs_table_spec.js
@@ -5,7 +5,7 @@ import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import { createAlert } from '~/alert';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import FailedJobsTable from '~/pipelines/components/jobs/failed_jobs_table.vue';
import RetryFailedJobMutation from '~/pipelines/graphql/mutations/retry_failed_job.mutation.graphql';
import {
@@ -94,7 +94,7 @@ describe('Failed Jobs Table', () => {
await waitForPromises();
- expect(redirectTo).toHaveBeenCalledWith(job.detailedStatus.detailsPath);
+ expect(redirectTo).toHaveBeenCalledWith(job.detailedStatus.detailsPath); // eslint-disable-line import/no-deprecated
});
it('shows error message if the retry failed job mutation fails', async () => {
diff --git a/spec/frontend/releases/stores/modules/detail/actions_spec.js b/spec/frontend/releases/stores/modules/detail/actions_spec.js
index 464fd3cb203..1d164b9f5c1 100644
--- a/spec/frontend/releases/stores/modules/detail/actions_spec.js
+++ b/spec/frontend/releases/stores/modules/detail/actions_spec.js
@@ -3,7 +3,7 @@ import originalOneReleaseForEditingQueryResponse from 'test_fixtures/graphql/rel
import testAction from 'helpers/vuex_action_helper';
import { getTag } from '~/api/tags_api';
import { createAlert } from '~/alert';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { s__ } from '~/locale';
import { ASSET_LINK_TYPE } from '~/releases/constants';
import createReleaseAssetLinkMutation from '~/releases/graphql/mutations/create_release_link.mutation.graphql';
@@ -306,8 +306,8 @@ describe('Release edit/new actions', () => {
it("redirects to the release's dedicated page", () => {
const { selfUrl } = releaseResponse.data.project.release.links;
actions.receiveSaveReleaseSuccess({ commit: jest.fn(), state }, selfUrl);
- expect(redirectTo).toHaveBeenCalledTimes(1);
- expect(redirectTo).toHaveBeenCalledWith(selfUrl);
+ expect(redirectTo).toHaveBeenCalledTimes(1); // eslint-disable-line import/no-deprecated
+ expect(redirectTo).toHaveBeenCalledWith(selfUrl); // eslint-disable-line import/no-deprecated
});
});
diff --git a/spec/frontend/repository/components/blob_content_viewer_spec.js b/spec/frontend/repository/components/blob_content_viewer_spec.js
index a588251c4bd..7e14d292946 100644
--- a/spec/frontend/repository/components/blob_content_viewer_spec.js
+++ b/spec/frontend/repository/components/blob_content_viewer_spec.js
@@ -551,12 +551,12 @@ describe('Blob content viewer component', () => {
it('simple edit redirects to the simple editor', () => {
findWebIdeLink().vm.$emit('edit', 'simple');
- expect(urlUtility.redirectTo).toHaveBeenCalledWith(simpleViewerMock.editBlobPath);
+ expect(urlUtility.redirectTo).toHaveBeenCalledWith(simpleViewerMock.editBlobPath); // eslint-disable-line import/no-deprecated
});
it('IDE edit redirects to the IDE editor', () => {
findWebIdeLink().vm.$emit('edit', 'ide');
- expect(urlUtility.redirectTo).toHaveBeenCalledWith(simpleViewerMock.ideEditPath);
+ expect(urlUtility.redirectTo).toHaveBeenCalledWith(simpleViewerMock.ideEditPath); // eslint-disable-line import/no-deprecated
});
it.each`
diff --git a/spec/frontend/snippets/components/edit_spec.js b/spec/frontend/snippets/components/edit_spec.js
index 957cfc0bc22..d17e20ac227 100644
--- a/spec/frontend/snippets/components/edit_spec.js
+++ b/spec/frontend/snippets/components/edit_spec.js
@@ -328,7 +328,7 @@ describe('Snippet Edit app', () => {
it('should redirect to snippet view on successful mutation', async () => {
await createComponentAndSubmit();
- expect(urlUtils.redirectTo).toHaveBeenCalledWith(TEST_WEB_URL);
+ expect(urlUtils.redirectTo).toHaveBeenCalledWith(TEST_WEB_URL); // eslint-disable-line import/no-deprecated
});
describe('when there are errors after creating a new snippet', () => {
@@ -349,7 +349,7 @@ describe('Snippet Edit app', () => {
await waitForPromises();
- expect(urlUtils.redirectTo).not.toHaveBeenCalled();
+ expect(urlUtils.redirectTo).not.toHaveBeenCalled(); // eslint-disable-line import/no-deprecated
expect(createAlert).toHaveBeenCalledWith({
message: `Can't create snippet: ${TEST_MUTATION_ERROR}`,
});
@@ -373,7 +373,7 @@ describe('Snippet Edit app', () => {
},
});
- expect(urlUtils.redirectTo).not.toHaveBeenCalled();
+ expect(urlUtils.redirectTo).not.toHaveBeenCalled(); // eslint-disable-line import/no-deprecated
expect(createAlert).toHaveBeenCalledWith({
message: `Can't update snippet: ${TEST_MUTATION_ERROR}`,
});
@@ -391,7 +391,7 @@ describe('Snippet Edit app', () => {
});
it('should not redirect', () => {
- expect(urlUtils.redirectTo).not.toHaveBeenCalled();
+ expect(urlUtils.redirectTo).not.toHaveBeenCalled(); // eslint-disable-line import/no-deprecated
});
it('should alert', () => {
diff --git a/spec/frontend/user_lists/components/edit_user_list_spec.js b/spec/frontend/user_lists/components/edit_user_list_spec.js
index b5eb6313bed..21a883aefe0 100644
--- a/spec/frontend/user_lists/components/edit_user_list_spec.js
+++ b/spec/frontend/user_lists/components/edit_user_list_spec.js
@@ -4,7 +4,7 @@ import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
import waitForPromises from 'helpers/wait_for_promises';
import Api from '~/api';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import EditUserList from '~/user_lists/components/edit_user_list.vue';
import UserListForm from '~/user_lists/components/user_list_form.vue';
import createStore from '~/user_lists/store/edit';
@@ -114,7 +114,7 @@ describe('user_lists/components/edit_user_list', () => {
});
it('should redirect to the feature flag details page', () => {
- expect(redirectTo).toHaveBeenCalledWith(userList.path);
+ expect(redirectTo).toHaveBeenCalledWith(userList.path); // eslint-disable-line import/no-deprecated
});
});
diff --git a/spec/frontend/user_lists/components/new_user_list_spec.js b/spec/frontend/user_lists/components/new_user_list_spec.js
index 8683cf2463c..004cfb6ca07 100644
--- a/spec/frontend/user_lists/components/new_user_list_spec.js
+++ b/spec/frontend/user_lists/components/new_user_list_spec.js
@@ -4,7 +4,7 @@ import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
import waitForPromises from 'helpers/wait_for_promises';
import Api from '~/api';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import NewUserList from '~/user_lists/components/new_user_list.vue';
import createStore from '~/user_lists/store/new';
import { userList } from 'jest/feature_flags/mock_data';
@@ -58,7 +58,7 @@ describe('user_lists/components/new_user_list', () => {
});
it('should redirect to the feature flag details page', () => {
- expect(redirectTo).toHaveBeenCalledWith(userList.path);
+ expect(redirectTo).toHaveBeenCalledWith(userList.path); // eslint-disable-line import/no-deprecated
});
});
diff --git a/spec/frontend/user_lists/store/edit/actions_spec.js b/spec/frontend/user_lists/store/edit/actions_spec.js
index ca56c935ea5..0fd08c1c052 100644
--- a/spec/frontend/user_lists/store/edit/actions_spec.js
+++ b/spec/frontend/user_lists/store/edit/actions_spec.js
@@ -1,6 +1,6 @@
import testAction from 'helpers/vuex_action_helper';
import Api from '~/api';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import * as actions from '~/user_lists/store/edit/actions';
import * as types from '~/user_lists/store/edit/mutation_types';
import createState from '~/user_lists/store/edit/state';
@@ -89,7 +89,7 @@ describe('User Lists Edit Actions', () => {
name: updatedList.name,
iid: updatedList.iid,
});
- expect(redirectTo).toHaveBeenCalledWith(userList.path);
+ expect(redirectTo).toHaveBeenCalledWith(userList.path); // eslint-disable-line import/no-deprecated
});
});
});
diff --git a/spec/frontend/user_lists/store/new/actions_spec.js b/spec/frontend/user_lists/store/new/actions_spec.js
index fa69fa7fa66..7ecf05e380a 100644
--- a/spec/frontend/user_lists/store/new/actions_spec.js
+++ b/spec/frontend/user_lists/store/new/actions_spec.js
@@ -1,6 +1,6 @@
import testAction from 'helpers/vuex_action_helper';
import Api from '~/api';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import * as actions from '~/user_lists/store/new/actions';
import * as types from '~/user_lists/store/new/mutation_types';
import createState from '~/user_lists/store/new/state';
@@ -41,7 +41,7 @@ describe('User Lists Edit Actions', () => {
it('should redirect to the user list page', () => {
return testAction(actions.createUserList, createdList, state, [], [], () => {
expect(Api.createFeatureFlagUserList).toHaveBeenCalledWith('1', createdList);
- expect(redirectTo).toHaveBeenCalledWith(userList.path);
+ expect(redirectTo).toHaveBeenCalledWith(userList.path); // eslint-disable-line import/no-deprecated
});
});
});
diff --git a/spec/frontend/vue_shared/security_reports/components/manage_via_mr_spec.js b/spec/frontend/vue_shared/security_reports/components/manage_via_mr_spec.js
index 5cdb6612487..f3d0d66cdd1 100644
--- a/spec/frontend/vue_shared/security_reports/components/manage_via_mr_spec.js
+++ b/spec/frontend/vue_shared/security_reports/components/manage_via_mr_spec.js
@@ -7,7 +7,7 @@ import createMockApollo from 'helpers/mock_apollo_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { humanize } from '~/lib/utils/text_utility';
-import { redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import ManageViaMr, {
i18n,
} from '~/vue_shared/security_configuration/components/manage_via_mr.vue';
@@ -146,8 +146,8 @@ describe('ManageViaMr component', () => {
it('should call redirect helper with correct value', async () => {
await wrapper.trigger('click');
await waitForPromises();
- expect(redirectTo).toHaveBeenCalledTimes(1);
- expect(redirectTo).toHaveBeenCalledWith('testSuccessPath');
+ expect(redirectTo).toHaveBeenCalledTimes(1); // eslint-disable-line import/no-deprecated
+ expect(redirectTo).toHaveBeenCalledWith('testSuccessPath'); // eslint-disable-line import/no-deprecated
// This is done for UX reasons. If the loading prop is set to false
// on success, then there's a period where the button is clickable
// again. Instead, we want the button to display a loading indicator
diff --git a/spec/lib/error_tracking/sentry_client/token_spec.rb b/spec/lib/error_tracking/sentry_client/token_spec.rb
new file mode 100644
index 00000000000..c50ec42ed67
--- /dev/null
+++ b/spec/lib/error_tracking/sentry_client/token_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ErrorTracking::SentryClient::Token, feature_category: :error_tracking do
+ describe '.masked_token?' do
+ subject { described_class.masked_token?(token) }
+
+ context 'with masked token' do
+ let(:token) { '*********' }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'without masked token' do
+ let(:token) { 'token' }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/entry/cache_spec.rb b/spec/lib/gitlab/ci/config/entry/cache_spec.rb
index 67252eed938..82db116fa0d 100644
--- a/spec/lib/gitlab/ci/config/entry/cache_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/cache_spec.rb
@@ -17,6 +17,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Cache do
let(:key) { 'some key' }
let(:when_config) { nil }
let(:unprotect) { false }
+ let(:fallback_keys) { [] }
let(:config) do
{
@@ -27,13 +28,22 @@ RSpec.describe Gitlab::Ci::Config::Entry::Cache do
}.tap do |config|
config[:policy] = policy if policy
config[:when] = when_config if when_config
+ config[:fallback_keys] = fallback_keys if fallback_keys
end
end
describe '#value' do
shared_examples 'hash key value' do
it 'returns hash value' do
- expect(entry.value).to eq(key: key, untracked: true, paths: ['some/path/'], policy: 'pull-push', when: 'on_success', unprotect: false)
+ expect(entry.value).to eq(
+ key: key,
+ untracked: true,
+ paths: ['some/path/'],
+ policy: 'pull-push',
+ when: 'on_success',
+ unprotect: false,
+ fallback_keys: []
+ )
end
end
@@ -104,6 +114,20 @@ RSpec.describe Gitlab::Ci::Config::Entry::Cache do
expect(entry.value).to include(when: 'on_success')
end
end
+
+ context 'with `fallback_keys`' do
+ let(:fallback_keys) { %w[key-1 key-2] }
+
+ it 'matches the list of fallback keys' do
+ expect(entry.value).to match(a_hash_including(fallback_keys: %w[key-1 key-2]))
+ end
+ end
+
+ context 'without `fallback_keys`' do
+ it 'assigns an empty list' do
+ expect(entry.value).to match(a_hash_including(fallback_keys: []))
+ end
+ end
end
describe '#valid?' do
diff --git a/spec/lib/gitlab/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb
index a06fc2d86c7..4be7c11fab0 100644
--- a/spec/lib/gitlab/ci/config/entry/job_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb
@@ -664,7 +664,13 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job, feature_category: :pipeline_compo
it 'overrides default config' do
expect(entry[:image].value).to eq(name: 'some_image')
- expect(entry[:cache].value).to eq([key: 'test', policy: 'pull-push', when: 'on_success', unprotect: false])
+ expect(entry[:cache].value).to match_array([
+ key: 'test',
+ policy: 'pull-push',
+ when: 'on_success',
+ unprotect: false,
+ fallback_keys: []
+ ])
end
end
@@ -679,7 +685,13 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job, feature_category: :pipeline_compo
it 'uses config from default entry' do
expect(entry[:image].value).to eq 'specified'
- expect(entry[:cache].value).to eq([key: 'test', policy: 'pull-push', when: 'on_success', unprotect: false])
+ expect(entry[:cache].value).to match_array([
+ key: 'test',
+ policy: 'pull-push',
+ when: 'on_success',
+ unprotect: false,
+ fallback_keys: []
+ ])
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/root_spec.rb b/spec/lib/gitlab/ci/config/entry/root_spec.rb
index 9722609aef6..5fac5298e8e 100644
--- a/spec/lib/gitlab/ci/config/entry/root_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/root_spec.rb
@@ -128,7 +128,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
stage: 'test',
cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success',
- unprotect: false }],
+ unprotect: false, fallback_keys: [] }],
job_variables: {},
root_variables_inheritance: true,
ignore: false,
@@ -144,7 +144,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
stage: 'test',
cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success',
- unprotect: false }],
+ unprotect: false, fallback_keys: [] }],
job_variables: {},
root_variables_inheritance: true,
ignore: false,
@@ -161,7 +161,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
image: { name: "image:1.0" },
services: [{ name: "postgres:9.1" }, { name: "mysql:5.5" }],
cache: [{ key: "k", untracked: true, paths: ["public/"], policy: "pull-push", when: 'on_success',
- unprotect: false }],
+ unprotect: false, fallback_keys: [] }],
only: { refs: %w(branches tags) },
job_variables: { 'VAR' => { value: 'job' } },
root_variables_inheritance: true,
@@ -209,7 +209,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
image: { name: 'image:1.0' },
services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
stage: 'test',
- cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success', unprotect: false }],
+ cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success', unprotect: false, fallback_keys: [] }],
job_variables: {},
root_variables_inheritance: true,
ignore: false,
@@ -222,7 +222,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
image: { name: 'image:1.0' },
services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
stage: 'test',
- cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success', unprotect: false }],
+ cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success', unprotect: false, fallback_keys: [] }],
job_variables: { 'VAR' => { value: 'job' } },
root_variables_inheritance: true,
ignore: false,
@@ -277,7 +277,13 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
describe '#cache_value' do
it 'returns correct cache definition' do
- expect(root.cache_value).to eq([key: 'a', policy: 'pull-push', when: 'on_success', unprotect: false])
+ expect(root.cache_value).to match_array([
+ key: 'a',
+ policy: 'pull-push',
+ when: 'on_success',
+ unprotect: false,
+ fallback_keys: []
+ ])
end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/seed/build/cache_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build/cache_spec.rb
index 49511e14db6..07e2d6960bf 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/build/cache_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/build/cache_spec.rb
@@ -220,6 +220,18 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build::Cache do
end
end
+ context 'with cache:fallback_keys' do
+ let(:config) do
+ {
+ key: 'ruby-branch-key',
+ paths: ['vendor/ruby'],
+ fallback_keys: ['ruby-default']
+ }
+ end
+
+ it { is_expected.to include(config) }
+ end
+
context 'with all cache option keys' do
let(:config) do
{
@@ -228,7 +240,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build::Cache do
untracked: true,
policy: 'push',
unprotect: true,
- when: 'on_success'
+ when: 'on_success',
+ fallback_keys: ['default-ruby']
}
end
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index f8c2889798f..2c020e76cb6 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -1870,7 +1870,8 @@ module Gitlab
key: 'key',
policy: 'pull-push',
when: 'on_success',
- unprotect: false
+ unprotect: false,
+ fallback_keys: []
])
end
@@ -1895,7 +1896,8 @@ module Gitlab
key: { files: ['file'] },
policy: 'pull-push',
when: 'on_success',
- unprotect: false
+ unprotect: false,
+ fallback_keys: []
])
end
@@ -1922,7 +1924,8 @@ module Gitlab
key: 'keya',
policy: 'pull-push',
when: 'on_success',
- unprotect: false
+ unprotect: false,
+ fallback_keys: []
},
{
paths: ['logs/', 'binaries/'],
@@ -1930,7 +1933,8 @@ module Gitlab
key: 'key',
policy: 'pull-push',
when: 'on_success',
- unprotect: false
+ unprotect: false,
+ fallback_keys: []
}
]
)
@@ -1958,7 +1962,8 @@ module Gitlab
key: { files: ['file'] },
policy: 'pull-push',
when: 'on_success',
- unprotect: false
+ unprotect: false,
+ fallback_keys: []
])
end
@@ -1984,7 +1989,8 @@ module Gitlab
key: { files: ['file'], prefix: 'prefix' },
policy: 'pull-push',
when: 'on_success',
- unprotect: false
+ unprotect: false,
+ fallback_keys: []
])
end
@@ -2008,7 +2014,8 @@ module Gitlab
key: 'local',
policy: 'pull-push',
when: 'on_success',
- unprotect: false
+ unprotect: false,
+ fallback_keys: []
])
end
end
diff --git a/spec/lib/gitlab/config_checker/external_database_checker_spec.rb b/spec/lib/gitlab/config_checker/external_database_checker_spec.rb
index c962b9ad393..6379a5edb90 100644
--- a/spec/lib/gitlab/config_checker/external_database_checker_spec.rb
+++ b/spec/lib/gitlab/config_checker/external_database_checker_spec.rb
@@ -89,9 +89,9 @@ RSpec.describe Gitlab::ConfigChecker::ExternalDatabaseChecker do
{
type: 'warning',
message: _('Database \'%{database_name}\' is using PostgreSQL %{pg_version_current}, ' \
- 'but PostgreSQL %{pg_version_minimum} is required for this version of GitLab. ' \
- 'Please upgrade your environment to a supported PostgreSQL version, ' \
- 'see %{pg_requirements_url} for details.') % \
+ 'but this version of GitLab requires PostgreSQL %{pg_version_minimum}. ' \
+ 'Please upgrade your environment to a supported PostgreSQL version. ' \
+ 'See %{pg_requirements_url} for details.') % \
{
database_name: database_name,
pg_version_current: database_version,
diff --git a/spec/lib/gitlab/database/partitioning_spec.rb b/spec/lib/gitlab/database/partitioning_spec.rb
index 4a82cd43fbb..9df238a0024 100644
--- a/spec/lib/gitlab/database/partitioning_spec.rb
+++ b/spec/lib/gitlab/database/partitioning_spec.rb
@@ -171,6 +171,21 @@ RSpec.describe Gitlab::Database::Partitioning, feature_category: :database do
expect(find_partitions(ci_model.table_name, conn: main_connection).size).to eq(0)
end
end
+
+ context 'when partition_manager_sync_partitions feature flag is disabled' do
+ before do
+ described_class.register_models(models)
+ stub_feature_flags(partition_manager_sync_partitions: false)
+ end
+
+ it 'skips sync_partitions' do
+ expect(described_class::PartitionManager).not_to receive(:new)
+ expect(described_class).to receive(:sync_partitions)
+ .and_call_original
+
+ described_class.sync_partitions(models)
+ end
+ end
end
describe '.report_metrics' do
diff --git a/spec/lib/gitlab/github_import/client_spec.rb b/spec/lib/gitlab/github_import/client_spec.rb
index aa1205663e1..c9f7fd4f748 100644
--- a/spec/lib/gitlab/github_import/client_spec.rb
+++ b/spec/lib/gitlab/github_import/client_spec.rb
@@ -131,6 +131,16 @@ RSpec.describe Gitlab::GithubImport::Client, feature_category: :importers do
end
end
+ describe '#collaborators' do
+ it 'returns the collaborators' do
+ expect(client)
+ .to receive(:each_object)
+ .with(:collaborators, 'foo/bar')
+
+ client.collaborators('foo/bar')
+ end
+ end
+
describe '#branch_protection' do
it 'returns the protection details for the given branch' do
expect(client.octokit)
diff --git a/spec/lib/gitlab/github_import/importer/collaborators_importer_spec.rb b/spec/lib/gitlab/github_import/importer/collaborators_importer_spec.rb
index e48b562279e..dcb02f32a28 100644
--- a/spec/lib/gitlab/github_import/importer/collaborators_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/collaborators_importer_spec.rb
@@ -73,12 +73,11 @@ RSpec.describe Gitlab::GithubImport::Importer::CollaboratorsImporter, feature_ca
end
describe '#parallel_import', :clean_gitlab_redis_cache do
- let(:page_struct) { Struct.new(:objects, :number, keyword_init: true) }
-
before do
- allow(client).to receive(:each_page)
- .with(:collaborators, project.import_source, { page: 1 })
- .and_yield(page_struct.new(number: 1, objects: [github_collaborator]))
+ allow(client).to receive(:collaborators).with(project.import_source, affiliation: 'direct')
+ .and_return([github_collaborator])
+ allow(client).to receive(:collaborators).with(project.import_source, affiliation: 'outside')
+ .and_return([])
end
it 'imports each collaborator in parallel' do
@@ -110,6 +109,44 @@ RSpec.describe Gitlab::GithubImport::Importer::CollaboratorsImporter, feature_ca
end
end
+ describe '#each_object_to_import', :clean_gitlab_redis_cache do
+ let(:github_collaborator_2) { { id: 100501, login: 'alice', role_name: 'owner' } }
+ let(:github_collaborator_3) { { id: 100502, login: 'tom', role_name: 'guest' } }
+
+ before do
+ allow(client).to receive(:collaborators).with(project.import_source, affiliation: 'direct')
+ .and_return([github_collaborator, github_collaborator_2, github_collaborator_3])
+ allow(client).to receive(:collaborators).with(project.import_source, affiliation: 'outside')
+ .and_return([github_collaborator_3])
+ allow(Gitlab::GithubImport::ObjectCounter).to receive(:increment)
+ .with(project, :collaborator, :fetched)
+ end
+
+ it 'yields every direct collaborator who is not an outside collaborator to the supplied block' do
+ expect { |b| importer.each_object_to_import(&b) }
+ .to yield_successive_args(github_collaborator, github_collaborator_2)
+
+ expect(Gitlab::GithubImport::ObjectCounter).to have_received(:increment).twice
+ end
+
+ context 'when a collaborator has been already imported' do
+ before do
+ allow(importer).to receive(:already_imported?).and_return(true)
+ end
+
+ it 'does not yield anything' do
+ expect(Gitlab::GithubImport::ObjectCounter)
+ .not_to receive(:increment)
+
+ expect(importer)
+ .not_to receive(:mark_as_imported)
+
+ expect { |b| importer.each_object_to_import(&b) }
+ .not_to yield_control
+ end
+ end
+ end
+
describe '#id_for_already_imported_cache' do
it 'returns the ID of the given note' do
expect(importer.id_for_already_imported_cache(github_collaborator))
diff --git a/spec/lib/gitlab/github_import/settings_spec.rb b/spec/lib/gitlab/github_import/settings_spec.rb
index ad0c47e8e8a..43e096863b8 100644
--- a/spec/lib/gitlab/github_import/settings_spec.rb
+++ b/spec/lib/gitlab/github_import/settings_spec.rb
@@ -11,7 +11,8 @@ RSpec.describe Gitlab::GithubImport::Settings do
{
single_endpoint_issue_events_import: true,
single_endpoint_notes_import: false,
- attachments_import: false
+ attachments_import: false,
+ collaborators_import: false
}
end
@@ -22,17 +23,26 @@ RSpec.describe Gitlab::GithubImport::Settings do
{
name: 'single_endpoint_issue_events_import',
label: stages[:single_endpoint_issue_events_import][:label],
+ selected: false,
details: stages[:single_endpoint_issue_events_import][:details]
},
{
name: 'single_endpoint_notes_import',
label: stages[:single_endpoint_notes_import][:label],
+ selected: false,
details: stages[:single_endpoint_notes_import][:details]
},
{
name: 'attachments_import',
label: stages[:attachments_import][:label].strip,
+ selected: false,
details: stages[:attachments_import][:details]
+ },
+ {
+ name: 'collaborators_import',
+ label: stages[:collaborators_import][:label].strip,
+ selected: true,
+ details: stages[:collaborators_import][:details]
}
]
end
@@ -48,6 +58,7 @@ RSpec.describe Gitlab::GithubImport::Settings do
single_endpoint_issue_events_import: true,
single_endpoint_notes_import: 'false',
attachments_import: nil,
+ collaborators_import: false,
foo: :bar
}.stringify_keys
end
@@ -67,6 +78,7 @@ RSpec.describe Gitlab::GithubImport::Settings do
expect(settings.enabled?(:single_endpoint_issue_events_import)).to eq true
expect(settings.enabled?(:single_endpoint_notes_import)).to eq false
expect(settings.enabled?(:attachments_import)).to eq false
+ expect(settings.enabled?(:collaborators_import)).to eq false
end
end
@@ -77,6 +89,7 @@ RSpec.describe Gitlab::GithubImport::Settings do
expect(settings.disabled?(:single_endpoint_issue_events_import)).to eq false
expect(settings.disabled?(:single_endpoint_notes_import)).to eq true
expect(settings.disabled?(:attachments_import)).to eq true
+ expect(settings.disabled?(:collaborators_import)).to eq true
end
end
end
diff --git a/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb b/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
index 1731da9b752..2d4c6d1cc56 100644
--- a/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
@@ -76,7 +76,9 @@ RSpec.describe Gitlab::Metrics::Subscribers::RailsCache do
it 'observes multi-key count' do
expect(transaction).to receive(:observe)
- .with(:gitlab_cache_read_multikey_count, event.payload[:key].size)
+ .with(:gitlab_cache_read_multikey_count,
+ event.payload[:key].size,
+ { store: store_label })
subject
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 7c32c6d74c8..e3e78acb7e5 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -1152,6 +1152,12 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
{ cache: [{ key: "key", paths: ["public"], policy: "pull-push" }] }
end
+ let(:options_with_fallback_keys) do
+ { cache: [
+ { key: "key", paths: ["public"], policy: "pull-push", fallback_keys: %w(key1 key2) }
+ ] }
+ end
+
subject { build.cache }
context 'when build has cache' do
@@ -1167,6 +1173,13 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
] }
end
+ let(:options_with_fallback_keys) do
+ { cache: [
+ { key: "key", paths: ["public"], policy: "pull-push", fallback_keys: %w(key3 key4) },
+ { key: "key2", paths: ["public"], policy: "pull-push", fallback_keys: %w(key5 key6) }
+ ] }
+ end
+
before do
allow_any_instance_of(Project).to receive(:jobs_cache_index).and_return(1)
end
@@ -1178,8 +1191,21 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
allow(build.pipeline).to receive(:protected_ref?).and_return(true)
end
- it do
- is_expected.to all(a_hash_including(key: a_string_matching(/-protected$/)))
+ context 'without the `unprotect` option' do
+ it do
+ is_expected.to all(a_hash_including(key: a_string_matching(/-protected$/)))
+ end
+
+ context 'and the caches have fallback keys' do
+ let(:options) { options_with_fallback_keys }
+
+ it do
+ is_expected.to all(a_hash_including({
+ key: a_string_matching(/-protected$/),
+ fallback_keys: array_including(a_string_matching(/-protected$/))
+ }))
+ end
+ end
end
context 'and the cache has the `unprotect` option' do
@@ -1193,6 +1219,20 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
it do
is_expected.to all(a_hash_including(key: a_string_matching(/-non_protected$/)))
end
+
+ context 'and the caches have fallback keys' do
+ let(:options) do
+ options_with_fallback_keys[:cache].each { |entry| entry[:unprotect] = true }
+ options_with_fallback_keys
+ end
+
+ it do
+ is_expected.to all(a_hash_including({
+ key: a_string_matching(/-non_protected$/),
+ fallback_keys: array_including(a_string_matching(/-non_protected$/))
+ }))
+ end
+ end
end
end
@@ -1204,6 +1244,17 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
it do
is_expected.to all(a_hash_including(key: a_string_matching(/-non_protected$/)))
end
+
+ context 'and the caches have fallback keys' do
+ let(:options) { options_with_fallback_keys }
+
+ it do
+ is_expected.to all(a_hash_including({
+ key: a_string_matching(/-non_protected$/),
+ fallback_keys: array_including(a_string_matching(/-non_protected$/))
+ }))
+ end
+ end
end
context 'when separated caches are disabled' do
@@ -1219,6 +1270,23 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
it 'is expected to have no type suffix' do
is_expected.to match([a_hash_including(key: 'key-1'), a_hash_including(key: 'key2-1')])
end
+
+ context 'and the caches have fallback keys' do
+ let(:options) { options_with_fallback_keys }
+
+ it do
+ is_expected.to match([
+ a_hash_including({
+ key: 'key-1',
+ fallback_keys: %w(key3-1 key4-1)
+ }),
+ a_hash_including({
+ key: 'key2-1',
+ fallback_keys: %w(key5-1 key6-1)
+ })
+ ])
+ end
+ end
end
context 'running on not protected ref' do
@@ -1229,6 +1297,23 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
it 'is expected to have no type suffix' do
is_expected.to match([a_hash_including(key: 'key-1'), a_hash_including(key: 'key2-1')])
end
+
+ context 'and the caches have fallback keys' do
+ let(:options) { options_with_fallback_keys }
+
+ it do
+ is_expected.to match([
+ a_hash_including({
+ key: 'key-1',
+ fallback_keys: %w(key3-1 key4-1)
+ }),
+ a_hash_including({
+ key: 'key2-1',
+ fallback_keys: %w(key5-1 key6-1)
+ })
+ ])
+ end
+ end
end
end
end
@@ -1239,6 +1324,17 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
end
it { is_expected.to be_an(Array).and all(include(key: a_string_matching(/^key-1-(?>protected|non_protected)/))) }
+
+ context 'and the cache have fallback keys' do
+ let(:options) { options_with_fallback_keys }
+
+ it do
+ is_expected.to be_an(Array).and all(include({
+ key: a_string_matching(/^key-1-(?>protected|non_protected)/),
+ fallback_keys: array_including(a_string_matching(/^key\d-1-(?>protected|non_protected)/))
+ }))
+ end
+ end
end
context 'when project does not have jobs_cache_index' do
@@ -1249,6 +1345,21 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
it do
is_expected.to eq(options[:cache].map { |entry| entry.merge(key: "#{entry[:key]}-non_protected") })
end
+
+ context 'and the cache have fallback keys' do
+ let(:options) { options_with_fallback_keys }
+
+ it do
+ is_expected.to eq(
+ options[:cache].map do |entry|
+ entry[:key] = "#{entry[:key]}-non_protected"
+ entry[:fallback_keys].map! { |key| "#{key}-non_protected" }
+
+ entry
+ end
+ )
+ end
+ end
end
end
@@ -1261,6 +1372,29 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
end
end
+ describe '#fallback_cache_keys_defined?' do
+ subject { build }
+
+ it 'returns false when fallback keys are not defined' do
+ expect(subject.fallback_cache_keys_defined?).to be false
+ end
+
+ context "with fallbacks keys" do
+ before do
+ allow(build).to receive(:options).and_return({
+ cache: [{
+ key: "key1",
+ fallback_keys: %w(key2)
+ }]
+ })
+ end
+
+ it 'returns true when fallback keys are defined' do
+ expect(subject.fallback_cache_keys_defined?).to be true
+ end
+ end
+ end
+
describe '#triggered_by?' do
subject { build.triggered_by?(user) }
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index eaf4ecda635..c73dac7251e 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -2224,20 +2224,6 @@ RSpec.describe User, feature_category: :user_profile do
end
end
- describe '#sign_in_with_codes_allowed?' do
- let_it_be(:user) { create(:user, :two_factor_via_webauthn) }
-
- context 'when `webauthn_without_totp` disabled' do
- before do
- stub_feature_flags(webauthn_without_totp: false)
- end
-
- it { expect(user.sign_in_with_codes_allowed?).to eq(false) }
- end
-
- it { expect(user.sign_in_with_codes_allowed?).to eq(true) }
- end
-
describe '#two_factor_otp_enabled?' do
let_it_be(:user) { create(:user) }
diff --git a/spec/requests/api/ci/runner/jobs_request_post_spec.rb b/spec/requests/api/ci/runner/jobs_request_post_spec.rb
index 39b8b489019..0164eda7680 100644
--- a/spec/requests/api/ci/runner/jobs_request_post_spec.rb
+++ b/spec/requests/api/ci/runner/jobs_request_post_spec.rb
@@ -230,11 +230,14 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state, feature_catego
end
let(:expected_cache) do
- [{ 'key' => a_string_matching(/^cache_key-(?>protected|non_protected)$/),
- 'untracked' => false,
- 'paths' => ['vendor/*'],
- 'policy' => 'pull-push',
- 'when' => 'on_success' }]
+ [{
+ 'key' => a_string_matching(/^cache_key-(?>protected|non_protected)$/),
+ 'untracked' => false,
+ 'paths' => ['vendor/*'],
+ 'policy' => 'pull-push',
+ 'when' => 'on_success',
+ 'fallback_keys' => []
+ }]
end
let(:expected_features) do
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 3ecd012be12..cc8be312c71 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -3558,7 +3558,7 @@ RSpec.describe API::Users, :aggregate_failures, feature_category: :user_profile
deactivate
expect(response).to have_gitlab_http_status(:forbidden)
- expect(json_response['message']).to eq("403 Forbidden - The user you are trying to deactivate has been active in the past #{Gitlab::CurrentSettings.deactivate_dormant_users_period} days and cannot be deactivated")
+ expect(json_response['message']).to eq("The user you are trying to deactivate has been active in the past #{Gitlab::CurrentSettings.deactivate_dormant_users_period} days and cannot be deactivated")
expect(user.reload.state).to eq('active')
end
end
@@ -3582,7 +3582,7 @@ RSpec.describe API::Users, :aggregate_failures, feature_category: :user_profile
deactivate
expect(response).to have_gitlab_http_status(:forbidden)
- expect(json_response['message']).to eq('403 Forbidden - A blocked user cannot be deactivated by the API')
+ expect(json_response['message']).to eq('Error occurred. A blocked user cannot be deactivated')
expect(blocked_user.reload.state).to eq('blocked')
end
end
@@ -3596,7 +3596,7 @@ RSpec.describe API::Users, :aggregate_failures, feature_category: :user_profile
deactivate
expect(response).to have_gitlab_http_status(:forbidden)
- expect(json_response['message']).to eq('403 Forbidden - A blocked user cannot be deactivated by the API')
+ expect(json_response['message']).to eq('Error occurred. A blocked user cannot be deactivated')
expect(user.reload.state).to eq('ldap_blocked')
end
end
@@ -3608,7 +3608,7 @@ RSpec.describe API::Users, :aggregate_failures, feature_category: :user_profile
deactivate
expect(response).to have_gitlab_http_status(:forbidden)
- expect(json_response['message']).to eq('403 Forbidden - An internal user cannot be deactivated by the API')
+ expect(json_response['message']).to eq('Internal users cannot be deactivated')
end
end
diff --git a/spec/serializers/runner_entity_spec.rb b/spec/serializers/runner_entity_spec.rb
index f34cb794834..b94e1e225ed 100644
--- a/spec/serializers/runner_entity_spec.rb
+++ b/spec/serializers/runner_entity_spec.rb
@@ -22,5 +22,23 @@ RSpec.describe RunnerEntity do
expect(subject).to include(:edit_path)
expect(subject).to include(:short_sha)
end
+
+ context 'without admin permissions' do
+ it 'does not contain admin_path field' do
+ expect(subject).not_to include(:admin_path)
+ end
+ end
+
+ context 'with admin permissions' do
+ let_it_be(:user) { create(:user, :admin) }
+
+ before do
+ allow(user).to receive(:can_admin_all_resources?).and_return(true)
+ end
+
+ it 'contains admin_path field' do
+ expect(subject).to include(:admin_path)
+ end
+ end
end
end
diff --git a/spec/services/ci/create_pipeline_service/cache_spec.rb b/spec/services/ci/create_pipeline_service/cache_spec.rb
index e8d9cec6695..2a65f92bfd6 100644
--- a/spec/services/ci/create_pipeline_service/cache_spec.rb
+++ b/spec/services/ci/create_pipeline_service/cache_spec.rb
@@ -39,7 +39,8 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
policy: 'pull-push',
untracked: true,
unprotect: false,
- when: 'on_success'
+ when: 'on_success',
+ fallback_keys: []
}
expect(pipeline).to be_persisted
@@ -72,7 +73,8 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
paths: ['logs/'],
policy: 'pull-push',
when: 'on_success',
- unprotect: false
+ unprotect: false,
+ fallback_keys: []
}
expect(pipeline).to be_persisted
@@ -89,7 +91,8 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
paths: ['logs/'],
policy: 'pull-push',
when: 'on_success',
- unprotect: false
+ unprotect: false,
+ fallback_keys: []
}
expect(pipeline).to be_persisted
@@ -123,7 +126,8 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
paths: ['logs/'],
policy: 'pull-push',
when: 'on_success',
- unprotect: false
+ unprotect: false,
+ fallback_keys: []
}
expect(pipeline).to be_persisted
@@ -140,7 +144,8 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
paths: ['logs/'],
policy: 'pull-push',
when: 'on_success',
- unprotect: false
+ unprotect: false,
+ fallback_keys: []
}
expect(pipeline).to be_persisted
diff --git a/spec/services/import/github_service_spec.rb b/spec/services/import/github_service_spec.rb
index a8928fb5c09..fa8b2489599 100644
--- a/spec/services/import/github_service_spec.rb
+++ b/spec/services/import/github_service_spec.rb
@@ -152,7 +152,8 @@ RSpec.describe Import::GithubService, feature_category: :importers do
{
single_endpoint_issue_events_import: true,
single_endpoint_notes_import: 'false',
- attachments_import: false
+ attachments_import: false,
+ collaborators_import: true
}
end
diff --git a/spec/services/users/deactivate_service_spec.rb b/spec/services/users/deactivate_service_spec.rb
new file mode 100644
index 00000000000..0bb6e51a3b1
--- /dev/null
+++ b/spec/services/users/deactivate_service_spec.rb
@@ -0,0 +1,86 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Users::DeactivateService, feature_category: :user_management do
+ let_it_be(:current_user) { build(:admin) }
+ let_it_be(:user) { build(:user) }
+
+ subject(:service) { described_class.new(current_user) }
+
+ describe '#execute' do
+ subject(:operation) { service.execute(user) }
+
+ context 'when successful', :enable_admin_mode do
+ let(:user) { create(:user) }
+
+ it 'returns success status' do
+ expect(operation[:status]).to eq(:success)
+ end
+
+ it "changes the user's state" do
+ expect { operation }.to change { user.state }.to('deactivated')
+ end
+
+ it 'creates a log entry' do
+ expect(Gitlab::AppLogger).to receive(:info).with(message: "User deactivated", user: user.username,
+ email: user.email, deactivated_by: current_user.username, ip_address: current_user.current_sign_in_ip.to_s)
+
+ operation
+ end
+ end
+
+ context 'when the user is already deactivated', :enable_admin_mode do
+ let(:user) { create(:user, :deactivated) }
+
+ it 'returns error result' do
+ aggregate_failures 'error result' do
+ expect(operation[:status]).to eq(:success)
+ expect(operation[:message]).to eq('User has already been deactivated')
+ end
+ end
+
+ it "does not change the user's state" do
+ expect { operation }.not_to change { user.state }
+ end
+ end
+
+ context 'when internal user', :enable_admin_mode do
+ let(:user) { create(:user, :bot) }
+
+ it 'returns an error message' do
+ expect(operation[:status]).to eq(:error)
+ expect(operation[:message]).to eq('Internal users cannot be deactivated')
+ expect(operation.reason).to eq :forbidden
+ end
+ end
+
+ context 'when user is blocked', :enable_admin_mode do
+ let(:user) { create(:user, :blocked) }
+
+ it 'returns an error message' do
+ expect(operation[:status]).to eq(:error)
+ expect(operation[:message]).to eq('Error occurred. A blocked user cannot be deactivated')
+ expect(operation.reason).to eq :forbidden
+ end
+ end
+
+ context 'when user is not an admin' do
+ it 'returns permissions error message' do
+ expect(operation[:status]).to eq(:error)
+ expect(operation[:message]).to eq("You are not authorized to perform this action")
+ expect(operation.reason).to eq :forbidden
+ end
+ end
+
+ context 'when skip_authorization is true' do
+ let(:non_admin_user) { create(:user) }
+ let(:user_to_deactivate) { create(:user) }
+ let(:skip_authorization_service) { described_class.new(non_admin_user, skip_authorization: true) }
+
+ it 'deactivates the user even if the current user is not an admin' do
+ expect(skip_authorization_service.execute(user_to_deactivate)[:status]).to eq(:success)
+ end
+ end
+ end
+end
diff --git a/spec/workers/gitlab/github_import/stage/import_collaborators_worker_spec.rb b/spec/workers/gitlab/github_import/stage/import_collaborators_worker_spec.rb
index 0eac9f21b77..33ecf848997 100644
--- a/spec/workers/gitlab/github_import/stage/import_collaborators_worker_spec.rb
+++ b/spec/workers/gitlab/github_import/stage/import_collaborators_worker_spec.rb
@@ -5,6 +5,8 @@ require 'spec_helper'
RSpec.describe Gitlab::GithubImport::Stage::ImportCollaboratorsWorker, feature_category: :importers do
let_it_be(:project) { create(:project) }
let_it_be(:import_state) { create(:import_state, project: project) }
+ let(:settings) { Gitlab::GithubImport::Settings.new(project) }
+ let(:stage_enabled) { true }
let(:worker) { described_class.new }
let(:importer) { instance_double(Gitlab::GithubImport::Importer::CollaboratorsImporter) }
@@ -14,12 +16,13 @@ RSpec.describe Gitlab::GithubImport::Stage::ImportCollaboratorsWorker, feature_c
let(:push_rights_granted) { true }
before do
+ settings.write({ collaborators_import: stage_enabled })
allow(client).to receive(:repository).with(project.import_source)
.and_return({ permissions: { push: push_rights_granted } })
end
context 'when user has push access for this repo' do
- it 'imports all the pull requests' do
+ it 'imports all collaborators' do
waiter = Gitlab::JobWaiter.new(2, '123')
expect(Gitlab::GithubImport::Importer::CollaboratorsImporter)
@@ -52,6 +55,20 @@ RSpec.describe Gitlab::GithubImport::Stage::ImportCollaboratorsWorker, feature_c
end
end
+ context 'when stage is disabled' do
+ let(:stage_enabled) { false }
+
+ it 'skips collaborators import and calls next stage' do
+ expect(Gitlab::GithubImport::Importer::CollaboratorsImporter).not_to receive(:new)
+
+ expect(Gitlab::GithubImport::AdvanceStageWorker)
+ .to receive(:perform_async)
+ .with(project.id, {}, :pull_requests_merged_by)
+
+ worker.import(client, project)
+ end
+ end
+
it 'raises an error' do
exception = StandardError.new('_some_error_')