summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/ci/dev-fixtures.gitlab-ci.yml4
-rw-r--r--.gitlab/ci/frontend.gitlab-ci.yml3
-rw-r--r--.gitlab/ci/package-and-test/rules.gitlab-ci.yml5
-rw-r--r--.gitlab/ci/rails.gitlab-ci.yml8
-rw-r--r--.gitlab/ci/rails/shared.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/setup.gitlab-ci.yml2
-rw-r--r--.rubocop_todo/layout/argument_alignment.yml9
-rw-r--r--app/assets/javascripts/analytics/cycle_analytics/components/base.vue13
-rw-r--r--app/assets/javascripts/analytics/cycle_analytics/components/stage_table.vue4
-rw-r--r--app/assets/javascripts/analytics/cycle_analytics/components/total_time.vue4
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_projects.vue1
-rw-r--r--app/assets/javascripts/ci/runner/graphql/show/runner_projects.query.graphql10
-rw-r--r--app/assets/javascripts/grafana_integration/index.js3
-rw-r--r--app/assets/javascripts/issuable/components/issue_assignees.vue1
-rw-r--r--app/assets/javascripts/jobs/components/table/jobs_table_tabs.vue51
-rw-r--r--app/assets/javascripts/milestones/index.js6
-rw-r--r--app/assets/javascripts/operation_settings/index.js2
-rw-r--r--app/assets/javascripts/pages/admin/jobs/components/table/admin_jobs_table_app.vue12
-rw-r--r--app/assets/javascripts/pages/admin/jobs/components/table/graphql/queries/get_cancelable_jobs_count.query.graphql5
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue5
-rw-r--r--app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue6
-rw-r--r--app/assets/stylesheets/page_bundles/issues_list.scss5
-rw-r--r--app/controllers/concerns/send_file_upload.rb7
-rw-r--r--app/controllers/projects_controller.rb1
-rw-r--r--app/graphql/resolvers/ci/runner_projects_resolver.rb11
-rw-r--r--app/graphql/resolvers/concerns/project_search_arguments.rb6
-rw-r--r--app/graphql/resolvers/projects_resolver.rb5
-rw-r--r--app/models/abuse_report.rb30
-rw-r--r--app/models/achievements/achievement.rb6
-rw-r--r--app/models/achievements/user_achievement.rb12
-rw-r--r--app/models/active_session.rb4
-rw-r--r--app/models/analytics/cycle_analytics/project_level.rb8
-rw-r--r--app/models/analytics/cycle_analytics/stage.rb2
-rw-r--r--app/models/analytics/cycle_analytics/value_stream.rb7
-rw-r--r--app/models/appearance.rb27
-rw-r--r--app/models/atlassian/identity.rb20
-rw-r--r--app/models/work_item.rb2
-rw-r--r--app/services/spam/spam_action_service.rb2
-rw-r--r--app/views/devise/mailer/user_admin_approval.text.erb4
-rw-r--r--app/views/projects/runners/edit.html.haml2
-rw-r--r--app/views/projects/settings/integrations/_form.html.haml5
-rw-r--r--app/views/projects/settings/operations/show.html.haml9
-rw-r--r--app/views/shared/integrations/prometheus/_metrics.html.haml2
-rw-r--r--app/views/shared/runners/_runner_details.html.haml2
-rw-r--r--config/feature_flags/development/remove_monitor_metrics.yml8
-rw-r--r--config/feature_flags/ops/dynamic_image_resizing.yml8
-rw-r--r--doc/api/graphql/reference/index.md2
-rw-r--r--doc/development/contributing/merge_request_workflow.md9
-rw-r--r--doc/development/documentation/topic_types/tutorial.md7
-rw-r--r--doc/gitlab-basics/add-file.md2
-rw-r--r--doc/gitlab-basics/start-using-git.md2
-rw-r--r--doc/topics/autodevops/requirements.md4
-rw-r--r--doc/topics/git/getting_started.md4
-rw-r--r--doc/topics/git/index.md2
-rw-r--r--doc/tutorials/agile_sprint.md104
-rw-r--r--doc/tutorials/agile_sprint/index.md101
-rw-r--r--doc/tutorials/compliance_pipeline/index.md177
-rw-r--r--doc/tutorials/convert_personal_namespace_into_group.md98
-rw-r--r--doc/tutorials/convert_personal_namespace_to_group/index.md95
-rw-r--r--doc/tutorials/create_compliance_pipeline.md180
-rw-r--r--doc/tutorials/fuzz_testing/index.md243
-rw-r--r--doc/tutorials/fuzz_testing_tutorial.md246
-rw-r--r--doc/tutorials/learn_git.md2
-rw-r--r--doc/tutorials/make_first_git_commit/img/branches_dropdown_v14_10.png (renamed from doc/tutorials/img/branches_dropdown_v14_10.png)bin20781 -> 20781 bytes
-rw-r--r--doc/tutorials/make_first_git_commit/img/clone_project_v14_9.png (renamed from doc/tutorials/img/clone_project_v14_9.png)bin30075 -> 30075 bytes
-rw-r--r--doc/tutorials/make_first_git_commit/img/commit_message_v14_10.png (renamed from doc/tutorials/img/commit_message_v14_10.png)bin4616 -> 4616 bytes
-rw-r--r--doc/tutorials/make_first_git_commit/index.md271
-rw-r--r--doc/tutorials/make_your_first_git_commit.md274
-rw-r--r--doc/tutorials/move_personal_project_to_a_group.md92
-rw-r--r--doc/tutorials/move_personal_project_to_group/index.md89
-rw-r--r--doc/tutorials/plan_and_track.md2
-rw-r--r--doc/tutorials/scan_result_policy.md128
-rw-r--r--doc/tutorials/scan_result_policy/index.md125
-rw-r--r--doc/tutorials/secure_application.md4
-rw-r--r--doc/user/group/compliance_frameworks.md2
-rw-r--r--doc/user/project/merge_requests/reviews/index.md1
-rw-r--r--doc/user/project/repository/branches/index.md9
-rw-r--r--lib/gitlab/ci/reports/codequality_mr_diff.rb13
-rw-r--r--lib/gitlab/metrics/subscribers/action_cable.rb64
-rw-r--r--lib/sidebars/projects/menus/monitor_menu.rb2
-rw-r--r--qa/qa/page/project/sub_menus/super_sidebar/monitor.rb4
-rw-r--r--scripts/rspec_helpers.sh10
-rw-r--r--scripts/utils.sh35
-rw-r--r--spec/controllers/concerns/send_file_upload_spec.rb22
-rw-r--r--spec/factories/packages/debian/distribution.rb6
-rw-r--r--spec/factories/projects.rb6
-rw-r--r--spec/features/alerts_settings/user_views_alerts_settings_spec.rb1
-rw-r--r--spec/features/markdown/sandboxed_mermaid_spec.rb24
-rw-r--r--spec/features/monitor_sidebar_link_spec.rb1
-rw-r--r--spec/features/projects/navbar_spec.rb1
-rw-r--r--spec/features/projects/settings/monitor_settings_spec.rb1
-rw-r--r--spec/features/projects/user_uses_shortcuts_spec.rb1
-rw-r--r--spec/fixtures/pipeline_artifacts/code_quality_mr_diff.json63
-rw-r--r--spec/frontend/analytics/cycle_analytics/components/__snapshots__/total_time_spec.js.snap (renamed from spec/frontend/analytics/cycle_analytics/__snapshots__/total_time_spec.js.snap)0
-rw-r--r--spec/frontend/analytics/cycle_analytics/components/base_spec.js (renamed from spec/frontend/analytics/cycle_analytics/base_spec.js)2
-rw-r--r--spec/frontend/analytics/cycle_analytics/components/filter_bar_spec.js (renamed from spec/frontend/analytics/cycle_analytics/filter_bar_spec.js)0
-rw-r--r--spec/frontend/analytics/cycle_analytics/components/formatted_stage_count_spec.js (renamed from spec/frontend/analytics/cycle_analytics/formatted_stage_count_spec.js)0
-rw-r--r--spec/frontend/analytics/cycle_analytics/components/path_navigation_spec.js (renamed from spec/frontend/analytics/cycle_analytics/path_navigation_spec.js)2
-rw-r--r--spec/frontend/analytics/cycle_analytics/components/stage_table_spec.js (renamed from spec/frontend/analytics/cycle_analytics/stage_table_spec.js)2
-rw-r--r--spec/frontend/analytics/cycle_analytics/components/total_time_spec.js (renamed from spec/frontend/analytics/cycle_analytics/total_time_spec.js)0
-rw-r--r--spec/frontend/analytics/cycle_analytics/components/value_stream_filters_spec.js (renamed from spec/frontend/analytics/cycle_analytics/value_stream_filters_spec.js)2
-rw-r--r--spec/frontend/analytics/cycle_analytics/components/value_stream_metrics_spec.js (renamed from spec/frontend/analytics/cycle_analytics/value_stream_metrics_spec.js)2
-rw-r--r--spec/frontend/analytics/product_analytics/components/activity_chart_spec.js (renamed from spec/frontend/analytics/components/activity_chart_spec.js)0
-rw-r--r--spec/frontend/ci/runner/components/runner_projects_spec.js6
-rw-r--r--spec/frontend/fixtures/jobs.rb5
-rw-r--r--spec/frontend/fixtures/prometheus_integration.rb1
-rw-r--r--spec/frontend/jobs/components/table/jobs_table_tabs_spec.js19
-rw-r--r--spec/frontend/jobs/mock_data.js2
-rw-r--r--spec/frontend/milestones/index_spec.js38
-rw-r--r--spec/frontend/pages/admin/jobs/components/table/admin_job_table_app_spec.js39
-rw-r--r--spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap1
-rw-r--r--spec/graphql/resolvers/ci/runner_projects_resolver_spec.rb8
-rw-r--r--spec/lib/gitlab/ci/reports/codequality_mr_diff_spec.rb40
-rw-r--r--spec/lib/gitlab/metrics/subscribers/action_cable_spec.rb95
-rw-r--r--spec/lib/sidebars/projects/menus/monitor_menu_spec.rb4
-rw-r--r--spec/models/packages/debian/group_distribution_spec.rb7
-rw-r--r--spec/models/packages/debian/project_distribution_spec.rb7
-rw-r--r--spec/presenters/ci/pipeline_artifacts/code_quality_mr_diff_presenter_spec.rb51
-rw-r--r--spec/requests/api/badges_spec.rb4
-rw-r--r--spec/requests/api/graphql/ci/runner_spec.rb4
-rw-r--r--spec/requests/api/graphql/project/work_items_spec.rb59
-rw-r--r--spec/requests/api/project_import_spec.rb2
-rw-r--r--spec/serializers/ci/codequality_mr_diff_entity_spec.rb15
-rw-r--r--spec/services/spam/spam_action_service_spec.rb30
-rw-r--r--spec/support/shared_contexts/models/distribution_shared_context.rb22
-rw-r--r--spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb265
-rw-r--r--spec/tooling/lib/tooling/parallel_rspec_runner_spec.rb96
-rw-r--r--spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb4
-rw-r--r--tooling/lib/tooling/parallel_rspec_runner.rb99
129 files changed, 2210 insertions, 1603 deletions
diff --git a/.gitlab/ci/dev-fixtures.gitlab-ci.yml b/.gitlab/ci/dev-fixtures.gitlab-ci.yml
index f89456a18fb..891a5dd79ad 100644
--- a/.gitlab/ci/dev-fixtures.gitlab-ci.yml
+++ b/.gitlab/ci/dev-fixtures.gitlab-ci.yml
@@ -15,8 +15,8 @@
# SEED_NESTED_GROUPS: "false" # requires network connection
.run-dev-fixtures-script: &run-dev-fixtures-script
- - run_timed_command "scripts/gitaly-test-spawn"
- - run_timed_command "bundle exec rake db:seed_fu"
+ - section_start "gitaly-test-spawn" "Spawning Gitaly"; scripts/gitaly-test-spawn; section_end "gitaly-test-spawn"; # Do not use 'bundle exec' here
+ - section_start "seeding-db" "Seeding DB"; bundle exec rake db:seed_fu; section_end "seeding-db";
run-dev-fixtures:
extends:
diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml
index 4d28bc772b7..be983b177b2 100644
--- a/.gitlab/ci/frontend.gitlab-ci.yml
+++ b/.gitlab/ci/frontend.gitlab-ci.yml
@@ -138,7 +138,6 @@ retrieve-frontend-fixtures:
# More information in https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74003.
WEBPACK_VENDOR_DLL: "true"
script:
- - source scripts/utils.sh
- source scripts/gitlab_component_helpers.sh
- |
if [[ -d "tmp/tests/frontend" ]]; then
@@ -153,7 +152,7 @@ retrieve-frontend-fixtures:
echo "No frontend fixtures directory, generating frontend fixtures."
fi
- run_timed_command "gem install knapsack --no-document"
- - run_timed_command "scripts/gitaly-test-spawn"
+ - section_start "gitaly-test-spawn" "Spawning Gitaly"; scripts/gitaly-test-spawn; section_end "gitaly-test-spawn"; # Do not use 'bundle exec' here
- source ./scripts/rspec_helpers.sh
- rspec_paralellized_job
artifacts:
diff --git a/.gitlab/ci/package-and-test/rules.gitlab-ci.yml b/.gitlab/ci/package-and-test/rules.gitlab-ci.yml
index 8f63dbc38c3..bb22ea41b47 100644
--- a/.gitlab/ci/package-and-test/rules.gitlab-ci.yml
+++ b/.gitlab/ci/package-and-test/rules.gitlab-ci.yml
@@ -132,6 +132,11 @@
# these jobs need gitlab version because we can't reliably detect it from just the image
- if: $GITLAB_SEMVER_VERSION !~ /^\d+\.\d+\.\d+/
when: never
+ # update type tests are used to check if gitlab upgrade can be performed correctly (mainly migrations)
+ # there isn't much benefit in running tests after update with new sidebar enabled and there
+ # is also an issue to properly pass feature toggle to this job due to how gitlab-qa parses cli args
+ - if: $QA_SUPER_SIDEBAR_ENABLED == "true"
+ when: never
- !reference [.rules:test:ee-only, rules]
- !reference [.rules:test:qa, rules]
diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml
index 6e055d5b3ec..2c31a6f6775 100644
--- a/.gitlab/ci/rails.gitlab-ci.yml
+++ b/.gitlab/ci/rails.gitlab-ci.yml
@@ -15,11 +15,9 @@ setup-test-env:
- echo $CI_MERGE_REQUEST_APPROVED
- source scripts/gitlab_component_helpers.sh
- run_timed_command "download_and_extract_gitlab_workhorse_package" || true
- - run_timed_command "scripts/setup-test-env"
- - run_timed_command "select_gitlab_workhorse_essentials"
- - echo -e "\e[0Ksection_start:`date +%s`:gitaly-test-build[collapsed=true]\r\e[0KCompiling Gitaly binaries"
- - run_timed_command "scripts/gitaly-test-build" # Do not use 'bundle exec' here
- - echo -e "\e[0Ksection_end:`date +%s`:gitaly-test-build\r\e[0K"
+ - section_start "setup-test-env" "Setting up testing environment"; scripts/setup-test-env; section_end "setup-test-env";
+ - select_gitlab_workhorse_essentials
+ - section_start "gitaly-test-build" "Compiling Gitaly binaries"; scripts/gitaly-test-build; section_end "gitaly-test-build"; # Do not use 'bundle exec' here
artifacts:
expire_in: 7d
paths:
diff --git a/.gitlab/ci/rails/shared.gitlab-ci.yml b/.gitlab/ci/rails/shared.gitlab-ci.yml
index adcfcd2010f..33aef166afd 100644
--- a/.gitlab/ci/rails/shared.gitlab-ci.yml
+++ b/.gitlab/ci/rails/shared.gitlab-ci.yml
@@ -25,7 +25,7 @@ include:
# gems could not be found under some circumstance. No idea why, hours wasted.
- run_timed_command "gem install knapsack --no-document"
- echo -e "\e[0Ksection_start:`date +%s`:gitaly-test-spawn[collapsed=true]\r\e[0KStarting Gitaly"
- - run_timed_command "scripts/gitaly-test-spawn" # Do not use 'bundle exec' here
+ - section_start "gitaly-test-spawn" "Spawning Gitaly"; scripts/gitaly-test-spawn; section_end "gitaly-test-spawn" # Do not use 'bundle exec' here
- echo -e "\e[0Ksection_end:`date +%s`:gitaly-test-spawn\r\e[0K"
.single-db:
diff --git a/.gitlab/ci/setup.gitlab-ci.yml b/.gitlab/ci/setup.gitlab-ci.yml
index 0b5860b3ddb..f2d9bbc6ffa 100644
--- a/.gitlab/ci/setup.gitlab-ci.yml
+++ b/.gitlab/ci/setup.gitlab-ci.yml
@@ -90,7 +90,7 @@ generate-frontend-fixtures-mapping:
before_script:
- !reference [.default-before_script, before_script]
- source ./scripts/rspec_helpers.sh
- - run_timed_command "scripts/gitaly-test-spawn"
+ - section_start "gitaly-test-spawn" "Spawning Gitaly"; scripts/gitaly-test-spawn; section_end "gitaly-test-spawn"; # Do not use 'bundle exec' here
script:
- generate_frontend_fixtures_mapping
artifacts:
diff --git a/.rubocop_todo/layout/argument_alignment.yml b/.rubocop_todo/layout/argument_alignment.yml
index bc8007ca33e..6666a375fa2 100644
--- a/.rubocop_todo/layout/argument_alignment.yml
+++ b/.rubocop_todo/layout/argument_alignment.yml
@@ -506,16 +506,7 @@ Layout/ArgumentAlignment:
- 'app/graphql/types/work_items/widgets/start_and_due_date_update_input_type.rb'
- 'app/graphql/types/x509_certificate_type.rb'
- 'app/graphql/types/x509_issuer_type.rb'
- - 'app/models/abuse_report.rb'
- - 'app/models/achievements/achievement.rb'
- - 'app/models/achievements/user_achievement.rb'
- - 'app/models/active_session.rb'
- - 'app/models/analytics/cycle_analytics/project_level.rb'
- - 'app/models/analytics/cycle_analytics/stage.rb'
- - 'app/models/analytics/cycle_analytics/value_stream.rb'
- - 'app/models/appearance.rb'
- 'app/models/application_setting.rb'
- - 'app/models/atlassian/identity.rb'
- 'app/models/bulk_imports/configuration.rb'
- 'app/models/bulk_imports/entity.rb'
- 'app/models/clusters/kubernetes_namespace.rb'
diff --git a/app/assets/javascripts/analytics/cycle_analytics/components/base.vue b/app/assets/javascripts/analytics/cycle_analytics/components/base.vue
index 365cbeaf6a2..cc97a77e925 100644
--- a/app/assets/javascripts/analytics/cycle_analytics/components/base.vue
+++ b/app/assets/javascripts/analytics/cycle_analytics/components/base.vue
@@ -91,11 +91,7 @@ export default {
},
selectedStageCount() {
if (this.selectedStage) {
- const {
- stageCounts,
- selectedStage: { id },
- } = this;
- return stageCounts[id];
+ return this.stageCounts[this.selectedStage.id];
}
return 0;
},
@@ -113,12 +109,9 @@ export default {
);
},
dashboardsPath() {
- const {
- namespace: { fullPath },
- groupPath,
- } = this;
+ const { fullPath } = this.namespace;
return this.showLinkToDashboard
- ? generateValueStreamsDashboardLink(groupPath, [fullPath])
+ ? generateValueStreamsDashboardLink(this.groupPath, [fullPath])
: null;
},
query() {
diff --git a/app/assets/javascripts/analytics/cycle_analytics/components/stage_table.vue b/app/assets/javascripts/analytics/cycle_analytics/components/stage_table.vue
index 78ac29426d9..6842373fb61 100644
--- a/app/assets/javascripts/analytics/cycle_analytics/components/stage_table.vue
+++ b/app/assets/javascripts/analytics/cycle_analytics/components/stage_table.vue
@@ -102,9 +102,7 @@ export default {
},
data() {
if (this.pagination) {
- const {
- pagination: { sort, direction },
- } = this;
+ const { sort, direction } = this.pagination;
return {
sort,
direction,
diff --git a/app/assets/javascripts/analytics/cycle_analytics/components/total_time.vue b/app/assets/javascripts/analytics/cycle_analytics/components/total_time.vue
index 725952c3518..662c5c64bba 100644
--- a/app/assets/javascripts/analytics/cycle_analytics/components/total_time.vue
+++ b/app/assets/javascripts/analytics/cycle_analytics/components/total_time.vue
@@ -14,9 +14,7 @@ export default {
return Object.keys(this.time).length;
},
calculatedTime() {
- const {
- time: { days = null, mins = null, hours = null, seconds = null },
- } = this;
+ const { days = null, mins = null, hours = null, seconds = null } = this.time;
if (days) {
return {
diff --git a/app/assets/javascripts/ci/runner/components/runner_projects.vue b/app/assets/javascripts/ci/runner/components/runner_projects.vue
index 4cfc57340f5..ee67196e8e2 100644
--- a/app/assets/javascripts/ci/runner/components/runner_projects.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_projects.vue
@@ -71,6 +71,7 @@ export default {
return {
id: runner.id,
search: search.length >= SHORT_SEARCH_LENGTH ? search : '',
+ sort: 'ID_ASC',
...getPaginationVariables(this.pagination, RUNNER_DETAILS_PROJECTS_PAGE_SIZE),
};
},
diff --git a/app/assets/javascripts/ci/runner/graphql/show/runner_projects.query.graphql b/app/assets/javascripts/ci/runner/graphql/show/runner_projects.query.graphql
index e42648b3079..589a549c52e 100644
--- a/app/assets/javascripts/ci/runner/graphql/show/runner_projects.query.graphql
+++ b/app/assets/javascripts/ci/runner/graphql/show/runner_projects.query.graphql
@@ -3,6 +3,7 @@
query getRunnerProjects(
$id: CiRunnerID!
$search: String
+ $sort: String
$first: Int
$last: Int
$before: String
@@ -14,7 +15,14 @@ query getRunnerProjects(
id
}
projectCount
- projects(search: $search, first: $first, last: $last, before: $before, after: $after) {
+ projects(
+ search: $search
+ sort: $sort
+ first: $first
+ last: $last
+ before: $before
+ after: $after
+ ) {
nodes {
id
avatarUrl
diff --git a/app/assets/javascripts/grafana_integration/index.js b/app/assets/javascripts/grafana_integration/index.js
index 208a92c97c7..9ade29dae69 100644
--- a/app/assets/javascripts/grafana_integration/index.js
+++ b/app/assets/javascripts/grafana_integration/index.js
@@ -4,6 +4,9 @@ import store from './store';
export default () => {
const el = document.querySelector('.js-grafana-integration');
+
+ if (!el) return false;
+
return new Vue({
el,
store: store(el.dataset),
diff --git a/app/assets/javascripts/issuable/components/issue_assignees.vue b/app/assets/javascripts/issuable/components/issue_assignees.vue
index 21f35690f6d..d8cbc45684b 100644
--- a/app/assets/javascripts/issuable/components/issue_assignees.vue
+++ b/app/assets/javascripts/issuable/components/issue_assignees.vue
@@ -84,6 +84,7 @@ export default {
:link-href="webUrl(assignee)"
:img-alt="avatarUrlTitle(assignee)"
:img-css-classes="imgCssClasses"
+ img-css-wrapper-classes="gl-display-inline-flex"
:img-src="avatarUrl(assignee)"
:img-size="iconSize"
class="js-no-trigger author-link"
diff --git a/app/assets/javascripts/jobs/components/table/jobs_table_tabs.vue b/app/assets/javascripts/jobs/components/table/jobs_table_tabs.vue
index 68c6c669a1a..797facb1eb8 100644
--- a/app/assets/javascripts/jobs/components/table/jobs_table_tabs.vue
+++ b/app/assets/javascripts/jobs/components/table/jobs_table_tabs.vue
@@ -1,6 +1,7 @@
<script>
import { GlBadge, GlTab, GlTabs, GlLoadingIcon } from '@gitlab/ui';
import { s__ } from '~/locale';
+import CancelJobs from '~/pages/admin/jobs/components/cancel_jobs.vue';
import { limitedCounterWithDelimiter } from '~/lib/utils/text_utility';
export default {
@@ -9,11 +10,16 @@ export default {
GlTab,
GlTabs,
GlLoadingIcon,
+ CancelJobs,
},
inject: {
jobStatuses: {
default: {},
},
+ url: {
+ type: String,
+ default: '',
+ },
},
props: {
allJobsCount: {
@@ -24,6 +30,11 @@ export default {
type: Boolean,
required: true,
},
+ showCancelAllJobsButton: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
computed: {
tabs() {
@@ -51,23 +62,27 @@ export default {
</script>
<template>
- <gl-tabs content-class="gl-py-0">
- <gl-tab
- v-for="tab in tabs"
- :key="tab.text"
- :title-link-attributes="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ {
- 'data-testid': tab.testId,
- } /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
- @click="$emit('fetchJobsByStatus', tab.scope)"
- >
- <template #title>
- <span>{{ tab.text }}</span>
- <gl-loading-icon v-if="showLoadingIcon && tab.showBadge" class="gl-ml-2" />
+ <div class="gl-display-flex align-items-lg-center">
+ <gl-tabs content-class="gl-py-0">
+ <gl-tab
+ v-for="tab in tabs"
+ :key="tab.text"
+ :title-link-attributes="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ {
+ 'data-testid': tab.testId,
+ } /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
+ @click="$emit('fetchJobsByStatus', tab.scope)"
+ >
+ <template #title>
+ <span>{{ tab.text }}</span>
+ <gl-loading-icon v-if="showLoadingIcon && tab.showBadge" class="gl-ml-2" />
- <gl-badge v-else-if="tab.showBadge" size="sm" class="gl-tab-counter-badge">
- {{ tab.count }}
- </gl-badge>
- </template>
- </gl-tab>
- </gl-tabs>
+ <gl-badge v-else-if="tab.showBadge" size="sm" class="gl-tab-counter-badge">
+ {{ tab.count }}
+ </gl-badge>
+ </template>
+ </gl-tab>
+ </gl-tabs>
+ <div class="gl-flex-grow-1"></div>
+ <cancel-jobs v-if="showCancelAllJobsButton" :url="url" />
+ </div>
</template>
diff --git a/app/assets/javascripts/milestones/index.js b/app/assets/javascripts/milestones/index.js
index 9d210f7a6ec..8780d931588 100644
--- a/app/assets/javascripts/milestones/index.js
+++ b/app/assets/javascripts/milestones/index.js
@@ -4,6 +4,7 @@ import initDatePicker from '~/behaviors/date_picker';
import GLForm from '~/gl_form';
import { BV_SHOW_MODAL } from '~/lib/utils/constants';
import Milestone from '~/milestones/milestone';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
import Sidebar from '~/right_sidebar';
import MountMilestoneSidebar from '~/sidebar/mount_milestone_sidebar';
import Translate from '~/vue_shared/translate';
@@ -12,6 +13,9 @@ import DeleteMilestoneModal from './components/delete_milestone_modal.vue';
import PromoteMilestoneModal from './components/promote_milestone_modal.vue';
import eventHub from './event_hub';
+// See app/views/shared/milestones/_description.html.haml
+export const MILESTONE_DESCRIPTION_ELEMENT = '.milestone-detail .description';
+
export function initForm(initGFM = true) {
new ZenMode(); // eslint-disable-line no-new
initDatePicker();
@@ -34,6 +38,8 @@ export function initShow() {
new Milestone(); // eslint-disable-line no-new
new Sidebar(); // eslint-disable-line no-new
new MountMilestoneSidebar(); // eslint-disable-line no-new
+
+ renderGFM(document.querySelector(MILESTONE_DESCRIPTION_ELEMENT));
}
export function initPromoteMilestoneModal() {
diff --git a/app/assets/javascripts/operation_settings/index.js b/app/assets/javascripts/operation_settings/index.js
index faddf9c7b81..e56583963ad 100644
--- a/app/assets/javascripts/operation_settings/index.js
+++ b/app/assets/javascripts/operation_settings/index.js
@@ -5,6 +5,8 @@ import store from './store';
export default () => {
const el = document.querySelector('.js-operation-settings');
+ if (!el) return false;
+
return new Vue({
el,
store: store(el.dataset),
diff --git a/app/assets/javascripts/pages/admin/jobs/components/table/admin_jobs_table_app.vue b/app/assets/javascripts/pages/admin/jobs/components/table/admin_jobs_table_app.vue
index 4d1f3355bb9..9f93e9583d0 100644
--- a/app/assets/javascripts/pages/admin/jobs/components/table/admin_jobs_table_app.vue
+++ b/app/assets/javascripts/pages/admin/jobs/components/table/admin_jobs_table_app.vue
@@ -9,6 +9,7 @@ import JobsTableEmptyState from '~/jobs/components/table/jobs_table_empty_state.
import { DEFAULT_FIELDS_ADMIN } from '../constants';
import JobsSkeletonLoader from '../jobs_skeleton_loader.vue';
import GetAllJobs from './graphql/queries/get_all_jobs.query.graphql';
+import CancelableJobs from './graphql/queries/get_cancelable_jobs_count.query.graphql';
export default {
i18n: {
@@ -24,12 +25,15 @@ export default {
inject: {
jobStatuses: {
default: null,
+ required: false,
},
url: {
default: '',
+ required: false,
},
emptyStateSvgPath: {
default: '',
+ required: false,
},
},
apollo: {
@@ -50,6 +54,12 @@ export default {
this.error = this.$options.i18n.jobsFetchErrorMsg;
},
},
+ cancelable: {
+ query: CancelableJobs,
+ update(data) {
+ this.isCancelable = data.cancelable.count !== 0;
+ },
+ },
},
data() {
return {
@@ -62,6 +72,7 @@ export default {
infiniteScrollingTriggered: false,
filterSearchTriggered: false,
DEFAULT_FIELDS_ADMIN,
+ isCancelable: false,
};
},
computed: {
@@ -124,6 +135,7 @@ export default {
<jobs-table-tabs
:all-jobs-count="count"
:loading="loading"
+ :show-cancel-all-jobs-button="isCancelable"
@fetchJobsByStatus="fetchJobsByStatus"
/>
diff --git a/app/assets/javascripts/pages/admin/jobs/components/table/graphql/queries/get_cancelable_jobs_count.query.graphql b/app/assets/javascripts/pages/admin/jobs/components/table/graphql/queries/get_cancelable_jobs_count.query.graphql
new file mode 100644
index 00000000000..9b90abebbf7
--- /dev/null
+++ b/app/assets/javascripts/pages/admin/jobs/components/table/graphql/queries/get_cancelable_jobs_count.query.graphql
@@ -0,0 +1,5 @@
+query canelableJobs {
+ cancelable: jobs(statuses: [PENDING, RUNNING]) {
+ count
+ }
+}
diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
index 2f29d96d85e..64c363dd721 100644
--- a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
+++ b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
@@ -930,7 +930,10 @@ export default {
name="project[project_feature_attributes][monitor_access_level]"
/>
</project-setting-row>
- <div class="project-feature-setting-group gl-pl-7 gl-sm-pl-5">
+ <div
+ v-if="!glFeatures.removeMonitorMetrics"
+ class="project-feature-setting-group gl-pl-7 gl-sm-pl-5"
+ >
<project-setting-row
ref="metrics-visibility-settings"
:label="__('Metrics Dashboard')"
diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue
index ab308d11a79..00720f27934 100644
--- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue
+++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue
@@ -55,6 +55,11 @@ export default {
required: false,
default: '',
},
+ imgCssWrapperClasses: {
+ type: String,
+ required: false,
+ default: '',
+ },
imgSize: {
type: [Number, Object],
required: true,
@@ -89,6 +94,7 @@ export default {
<template>
<gl-avatar-link :href="linkHref" class="user-avatar-link">
<user-avatar-image
+ :class="imgCssWrapperClasses"
:img-src="imgSrc"
:img-alt="imgAlt"
:css-classes="imgCssClasses"
diff --git a/app/assets/stylesheets/page_bundles/issues_list.scss b/app/assets/stylesheets/page_bundles/issues_list.scss
index 41515a98e0a..f39dee12126 100644
--- a/app/assets/stylesheets/page_bundles/issues_list.scss
+++ b/app/assets/stylesheets/page_bundles/issues_list.scss
@@ -23,11 +23,6 @@
margin-bottom: 2px;
}
- .issue-labels,
- .author-link {
- display: inline-block;
- }
-
.icon-merge-request-unmerged {
height: 13px;
margin-bottom: 3px;
diff --git a/app/controllers/concerns/send_file_upload.rb b/app/controllers/concerns/send_file_upload.rb
index 2141b257b40..c8b987da58c 100644
--- a/app/controllers/concerns/send_file_upload.rb
+++ b/app/controllers/concerns/send_file_upload.rb
@@ -63,8 +63,7 @@ module SendFileUpload
private
def image_scaling_request?(file_upload)
- (avatar_safe_for_scaling?(file_upload) || pwa_icon_safe_for_scaling?(file_upload)) &&
- scaling_allowed_by_feature_flags?(file_upload)
+ avatar_safe_for_scaling?(file_upload) || pwa_icon_safe_for_scaling?(file_upload)
end
def pwa_icon_safe_for_scaling?(file_upload)
@@ -90,8 +89,4 @@ module SendFileUpload
def valid_image_scaling_width?(allowed_scalar_widths)
allowed_scalar_widths.include?(params[:width]&.to_i)
end
-
- def scaling_allowed_by_feature_flags?(file_upload)
- Feature.enabled?(:dynamic_image_resizing, type: :ops)
- end
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index a6bc754d09e..0a6ac2a25a4 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -39,6 +39,7 @@ class ProjectsController < Projects::ApplicationController
before_action do
push_frontend_feature_flag(:highlight_js, @project)
push_frontend_feature_flag(:synchronize_fork, @project&.fork_source)
+ push_frontend_feature_flag(:remove_monitor_metrics, @project)
push_licensed_feature(:file_locks) if @project.present? && @project.licensed_feature_available?(:file_locks)
push_licensed_feature(:security_orchestration_policies) if @project.present? && @project.licensed_feature_available?(:security_orchestration_policies)
push_force_frontend_feature_flag(:work_items, @project&.work_items_feature_flag_enabled?)
diff --git a/app/graphql/resolvers/ci/runner_projects_resolver.rb b/app/graphql/resolvers/ci/runner_projects_resolver.rb
index 625efc615c8..a9c26acf7b2 100644
--- a/app/graphql/resolvers/ci/runner_projects_resolver.rb
+++ b/app/graphql/resolvers/ci/runner_projects_resolver.rb
@@ -13,17 +13,6 @@ module Resolvers
alias_method :runner, :object
- argument :sort, GraphQL::Types::String,
- required: false,
- default_value: 'id_asc', # TODO: Remove in %16.0 and move :sort to ProjectSearchArguments, see https://gitlab.com/gitlab-org/gitlab/-/issues/372117
- deprecated: {
- reason: 'Default sort order will change in 16.0. ' \
- 'Specify `"id_asc"` if query results\' order is important',
- milestone: '15.4'
- },
- description: "Sort order of results. Format: `<field_name>_<sort_direction>`, " \
- "for example: `id_desc` or `name_asc`"
-
def resolve_with_lookahead(**args)
return unless runner.project_type?
diff --git a/app/graphql/resolvers/concerns/project_search_arguments.rb b/app/graphql/resolvers/concerns/project_search_arguments.rb
index faf3b85fc14..09cb6c2d0f3 100644
--- a/app/graphql/resolvers/concerns/project_search_arguments.rb
+++ b/app/graphql/resolvers/concerns/project_search_arguments.rb
@@ -19,6 +19,12 @@ module ProjectSearchArguments
argument :topics, type: [GraphQL::Types::String],
required: false,
description: 'Filter projects by topics.'
+
+ argument :sort, GraphQL::Types::String,
+ required: false,
+ default_value: 'id_desc',
+ description: "Sort order of results. Format: `<field_name>_<sort_direction>`, " \
+ "for example: `id_desc` or `name_asc`"
end
private
diff --git a/app/graphql/resolvers/projects_resolver.rb b/app/graphql/resolvers/projects_resolver.rb
index 08981f2c441..04a7e05544a 100644
--- a/app/graphql/resolvers/projects_resolver.rb
+++ b/app/graphql/resolvers/projects_resolver.rb
@@ -10,11 +10,6 @@ module Resolvers
required: false,
description: 'Filter projects by IDs.'
- argument :sort, GraphQL::Types::String,
- required: false,
- description: "Sort order of results. Format: `<field_name>_<sort_direction>`, " \
- "for example: `id_desc` or `name_asc`"
-
argument :with_issues_enabled, GraphQL::Types::Boolean,
required: false,
description: "Return only projects with issues enabled."
diff --git a/app/models/abuse_report.rb b/app/models/abuse_report.rb
index 716738e87c9..fa0ff5bc3fd 100644
--- a/app/models/abuse_report.rb
+++ b/app/models/abuse_report.rb
@@ -27,20 +27,20 @@ class AbuseReport < ApplicationRecord
}
validates :reported_from_url,
- allow_blank: true,
- length: { maximum: MAX_CHAR_LIMIT_URL },
- addressable_url: {
- dns_rebind_protection: true,
- blocked_message: 'is an invalid URL. You can try reporting the abuse again, ' \
- 'or contact a GitLab administrator for help.'
- }
+ allow_blank: true,
+ length: { maximum: MAX_CHAR_LIMIT_URL },
+ addressable_url: {
+ dns_rebind_protection: true,
+ blocked_message: 'is an invalid URL. You can try reporting the abuse again, ' \
+ 'or contact a GitLab administrator for help.'
+ }
validates :links_to_spam,
- allow_blank: true,
- length: {
- maximum: 20,
- message: N_("exceeds the limit of %{count} links")
- }
+ allow_blank: true,
+ length: {
+ maximum: 20,
+ message: N_("exceeds the limit of %{count} links")
+ }
before_validation :filter_empty_strings_from_links_to_spam
validate :links_to_spam_contains_valid_urls
@@ -119,9 +119,9 @@ class AbuseReport < ApplicationRecord
links_to_spam.each do |link|
Gitlab::UrlBlocker.validate!(
link,
- schemes: %w[http https],
- allow_localhost: true,
- dns_rebind_protection: true
+ schemes: %w[http https],
+ allow_localhost: true,
+ dns_rebind_protection: true
)
next unless link.length > MAX_CHAR_LIMIT_URL
diff --git a/app/models/achievements/achievement.rb b/app/models/achievements/achievement.rb
index a436e32b35b..834c12fee5a 100644
--- a/app/models/achievements/achievement.rb
+++ b/app/models/achievements/achievement.rb
@@ -13,9 +13,9 @@ module Achievements
strip_attributes! :name, :description
validates :name,
- presence: true,
- length: { maximum: 255 },
- uniqueness: { case_sensitive: false, scope: [:namespace_id] }
+ presence: true,
+ length: { maximum: 255 },
+ uniqueness: { case_sensitive: false, scope: [:namespace_id] }
validates :description, length: { maximum: 1024 }
end
end
diff --git a/app/models/achievements/user_achievement.rb b/app/models/achievements/user_achievement.rb
index bc5d10923d7..844780c6164 100644
--- a/app/models/achievements/user_achievement.rb
+++ b/app/models/achievements/user_achievement.rb
@@ -6,13 +6,13 @@ module Achievements
belongs_to :user, inverse_of: :user_achievements, optional: false
belongs_to :awarded_by_user,
- class_name: 'User',
- inverse_of: :awarded_user_achievements,
- optional: false
+ class_name: 'User',
+ inverse_of: :awarded_user_achievements,
+ optional: false
belongs_to :revoked_by_user,
- class_name: 'User',
- inverse_of: :revoked_user_achievements,
- optional: true
+ class_name: 'User',
+ inverse_of: :revoked_user_achievements,
+ optional: true
scope :not_revoked, -> { where(revoked_by_user_id: nil) }
diff --git a/app/models/active_session.rb b/app/models/active_session.rb
index 133466e93e3..7d025fb7738 100644
--- a/app/models/active_session.rb
+++ b/app/models/active_session.rb
@@ -26,8 +26,8 @@ class ActiveSession
ALLOWED_NUMBER_OF_ACTIVE_SESSIONS = 100
attr_accessor :ip_address, :browser, :os,
- :device_name, :device_type,
- :is_impersonated, :session_id, :session_private_id
+ :device_name, :device_type,
+ :is_impersonated, :session_id, :session_private_id
attr_reader :created_at, :updated_at
diff --git a/app/models/analytics/cycle_analytics/project_level.rb b/app/models/analytics/cycle_analytics/project_level.rb
index 813263fe833..a423ea35261 100644
--- a/app/models/analytics/cycle_analytics/project_level.rb
+++ b/app/models/analytics/cycle_analytics/project_level.rb
@@ -11,9 +11,11 @@ module Analytics
end
def summary
- @summary ||= ::Gitlab::CycleAnalytics::StageSummary.new(project,
- options: options,
- current_user: options[:current_user]).data
+ @summary ||= ::Gitlab::CycleAnalytics::StageSummary.new(
+ project,
+ options: options,
+ current_user: options[:current_user]
+ ).data
end
def permissions(user:)
diff --git a/app/models/analytics/cycle_analytics/stage.rb b/app/models/analytics/cycle_analytics/stage.rb
index 7e9a89975a3..c7bff7c8d7f 100644
--- a/app/models/analytics/cycle_analytics/stage.rb
+++ b/app/models/analytics/cycle_analytics/stage.rb
@@ -11,7 +11,7 @@ module Analytics
validates :name, uniqueness: { scope: [:group_id, :group_value_stream_id] }
belongs_to :value_stream, class_name: 'Analytics::CycleAnalytics::ValueStream',
-foreign_key: :group_value_stream_id, inverse_of: :stages
+ foreign_key: :group_value_stream_id, inverse_of: :stages
alias_attribute :parent, :namespace
alias_attribute :parent_id, :group_id
diff --git a/app/models/analytics/cycle_analytics/value_stream.rb b/app/models/analytics/cycle_analytics/value_stream.rb
index 3d8a0a53f5e..59c68393d74 100644
--- a/app/models/analytics/cycle_analytics/value_stream.rb
+++ b/app/models/analytics/cycle_analytics/value_stream.rb
@@ -19,12 +19,7 @@ module Analytics
accepts_nested_attributes_for :stages, allow_destroy: true
scope :preload_associated_models, -> {
- includes(:namespace,
- stages: [
- :namespace,
- :end_event_label,
- :start_event_label
- ])
+ includes(:namespace, stages: [:namespace, :end_event_label, :start_event_label])
}
after_save :ensure_aggregation_record_presence
diff --git a/app/models/appearance.rb b/app/models/appearance.rb
index 8d6048d45d5..4d2baf13f52 100644
--- a/app/models/appearance.rb
+++ b/app/models/appearance.rb
@@ -27,22 +27,25 @@ class Appearance < MainClusterwide::ApplicationRecord
cache_markdown_field :footer_message, pipeline: :broadcast_message
validates :pwa_name,
- length: { maximum: 255, too_long: ->(object, data) {
- N_("is too long (maximum is %{count} characters)")
- } },
- allow_blank: true
+ length: {
+ maximum: 255,
+ too_long: ->(object, data) { N_("is too long (maximum is %{count} characters)") }
+ },
+ allow_blank: true
validates :pwa_short_name,
- length: { maximum: 255, too_long: ->(object, data) {
- N_("is too long (maximum is %{count} characters)")
- } },
- allow_blank: true
+ length: {
+ maximum: 255,
+ too_long: ->(object, data) { N_("is too long (maximum is %{count} characters)") }
+ },
+ allow_blank: true
validates :pwa_description,
- length: { maximum: 2048, too_long: ->(object, data) {
- N_("is too long (maximum is %{count} characters)")
- } },
- allow_blank: true
+ length: {
+ maximum: 2048,
+ too_long: ->(object, data) { N_("is too long (maximum is %{count} characters)") }
+ },
+ allow_blank: true
validates :logo, file_size: { maximum: 1.megabyte }
validates :pwa_icon, file_size: { maximum: 1.megabyte }
diff --git a/app/models/atlassian/identity.rb b/app/models/atlassian/identity.rb
index 02bbe007e1b..1ad7f657db1 100644
--- a/app/models/atlassian/identity.rb
+++ b/app/models/atlassian/identity.rb
@@ -10,17 +10,17 @@ module Atlassian
validates :user, presence: true, uniqueness: true
attr_encrypted :token,
- mode: :per_attribute_iv,
- key: Settings.attr_encrypted_db_key_base_32,
- algorithm: 'aes-256-gcm',
- encode: false,
- encode_iv: false
+ mode: :per_attribute_iv,
+ key: Settings.attr_encrypted_db_key_base_32,
+ algorithm: 'aes-256-gcm',
+ encode: false,
+ encode_iv: false
attr_encrypted :refresh_token,
- mode: :per_attribute_iv,
- key: Settings.attr_encrypted_db_key_base_32,
- algorithm: 'aes-256-gcm',
- encode: false,
- encode_iv: false
+ mode: :per_attribute_iv,
+ key: Settings.attr_encrypted_db_key_base_32,
+ algorithm: 'aes-256-gcm',
+ encode: false,
+ encode_iv: false
end
end
diff --git a/app/models/work_item.rb b/app/models/work_item.rb
index 10476339ca9..36de891815a 100644
--- a/app/models/work_item.rb
+++ b/app/models/work_item.rb
@@ -51,7 +51,7 @@ class WorkItem < Issue
)
])
- includes(:child_links).order(keyset_order)
+ includes(:parent_link).order(keyset_order)
end
end
diff --git a/app/services/spam/spam_action_service.rb b/app/services/spam/spam_action_service.rb
index 9c52e9f0cd3..7c96f003e46 100644
--- a/app/services/spam/spam_action_service.rb
+++ b/app/services/spam/spam_action_service.rb
@@ -53,7 +53,7 @@ module Spam
end
def allowlisted?(user)
- user.try(:gitlab_employee?) || user.try(:gitlab_bot?) || user.try(:gitlab_service_user?)
+ user.try(:gitlab_bot?) || user.try(:gitlab_service_user?)
end
##
diff --git a/app/views/devise/mailer/user_admin_approval.text.erb b/app/views/devise/mailer/user_admin_approval.text.erb
index 5242981e514..bce56d59c68 100644
--- a/app/views/devise/mailer/user_admin_approval.text.erb
+++ b/app/views/devise/mailer/user_admin_approval.text.erb
@@ -2,6 +2,6 @@
<%= _('Your GitLab account request has been approved!') %>
-<%= _('Your username is %{username}.' % { username: @resource.username }) %>
+<%= _('Your username is %{username}.') % { username: @resource.username } %>
-<%= _('Your sign-in page is %{url}.' % { url: Gitlab.config.gitlab.url }) %>
+<%= _('Your sign-in page is %{url}.') % { url: Gitlab.config.gitlab.url } %>
diff --git a/app/views/projects/runners/edit.html.haml b/app/views/projects/runners/edit.html.haml
index ce56b160187..50c46402b53 100644
--- a/app/views/projects/runners/edit.html.haml
+++ b/app/views/projects/runners/edit.html.haml
@@ -4,7 +4,7 @@
- add_to_breadcrumbs "#{@runner.short_sha}", project_runner_path(@project, @runner)
%h1.page-title.gl-font-size-h-display
- = s_('Runners|Runner #%{runner_id}' % { runner_id: @runner.id })
+ = s_('Runners|Runner #%{runner_id}') % { runner_id: @runner.id }
= render 'shared/runners/runner_type_badge', runner: @runner
= render 'shared/runners/runner_type_alert', runner: @runner
diff --git a/app/views/projects/settings/integrations/_form.html.haml b/app/views/projects/settings/integrations/_form.html.haml
index 97d90976f18..de5c63cc7fd 100644
--- a/app/views/projects/settings/integrations/_form.html.haml
+++ b/app/views/projects/settings/integrations/_form.html.haml
@@ -21,5 +21,6 @@
= render 'shared/integration_settings', integration: integration
- if lookup_context.template_exists?('show', "shared/integrations/#{integration.to_param}", true)
- %hr
- = render "shared/integrations/#{integration.to_param}/show", integration: integration
+ - if !(integration.to_param == 'prometheus' && Feature.enabled?(:remove_monitor_metrics))
+ %hr
+ = render "shared/integrations/#{integration.to_param}/show", integration: integration
diff --git a/app/views/projects/settings/operations/show.html.haml b/app/views/projects/settings/operations/show.html.haml
index 2aae408b88f..bfa637ee35e 100644
--- a/app/views/projects/settings/operations/show.html.haml
+++ b/app/views/projects/settings/operations/show.html.haml
@@ -1,9 +1,14 @@
- page_title _('Monitor Settings')
- breadcrumb_title _('Monitor Settings')
-= render 'projects/settings/operations/metrics_dashboard'
+- if Feature.disabled?(:remove_monitor_metrics)
+ = render 'projects/settings/operations/metrics_dashboard'
+
= render 'projects/settings/operations/error_tracking'
= render 'projects/settings/operations/alert_management'
= render 'projects/settings/operations/incidents'
-= render 'projects/settings/operations/grafana_integration'
+
+- if Feature.disabled?(:remove_monitor_metrics)
+ = render 'projects/settings/operations/grafana_integration'
+
= render_if_exists 'projects/settings/operations/status_page'
diff --git a/app/views/shared/integrations/prometheus/_metrics.html.haml b/app/views/shared/integrations/prometheus/_metrics.html.haml
index a8125c3e3ec..1c54e4bd1de 100644
--- a/app/views/shared/integrations/prometheus/_metrics.html.haml
+++ b/app/views/shared/integrations/prometheus/_metrics.html.haml
@@ -33,6 +33,6 @@
.flash-container
.flash-notice
.flash-text
- = s_("PrometheusService|To set up automatic monitoring, add the environment variable %{variable} to exporter's queries." % { variable: "<code>$CI_ENVIRONMENT_SLUG</code>" }).html_safe
+ = html_escape(s_("PrometheusService|To set up automatic monitoring, add the environment variable %{variable} to exporter's queries.")) % { variable: "<code>$CI_ENVIRONMENT_SLUG</code>".html_safe }
= link_to s_('PrometheusService|More information'), help_page_path('operations/metrics/dashboards/variables.md', anchor: 'query-variables')
%ul.list-unstyled.metrics-list.js-missing-var-metrics-list
diff --git a/app/views/shared/runners/_runner_details.html.haml b/app/views/shared/runners/_runner_details.html.haml
index f6396168cb3..64ee4b38a73 100644
--- a/app/views/shared/runners/_runner_details.html.haml
+++ b/app/views/shared/runners/_runner_details.html.haml
@@ -2,7 +2,7 @@
- page_title "##{runner.id} (#{runner.short_sha})"
%h1.page-title.gl-font-size-h-display
- = s_('Runners|Runner #%{runner_id}' % { runner_id: runner.id })
+ = s_('Runners|Runner #%{runner_id}') % { runner_id: runner.id }
= render 'shared/runners/runner_type_badge', runner: runner
.table-holder
diff --git a/config/feature_flags/development/remove_monitor_metrics.yml b/config/feature_flags/development/remove_monitor_metrics.yml
new file mode 100644
index 00000000000..a2f082ac977
--- /dev/null
+++ b/config/feature_flags/development/remove_monitor_metrics.yml
@@ -0,0 +1,8 @@
+---
+name: remove_monitor_metrics
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/115714
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/399248
+milestone: '15.11'
+type: development
+group: group::respond
+default_enabled: false
diff --git a/config/feature_flags/ops/dynamic_image_resizing.yml b/config/feature_flags/ops/dynamic_image_resizing.yml
deleted file mode 100644
index 7ecf97d34a5..00000000000
--- a/config/feature_flags/ops/dynamic_image_resizing.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: dynamic_image_resizing
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45050
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/266986
-milestone: '13.6'
-type: ops
-group: group::tenant scale
-default_enabled: true
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index cb184b1e7a8..57868455803 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -12238,7 +12238,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="cirunnerprojectsmembership"></a>`membership` | [`Boolean`](#boolean) | Return only projects that the current user is a member of. |
| <a id="cirunnerprojectssearch"></a>`search` | [`String`](#string) | Search query, which can be for the project name, a path, or a description. |
| <a id="cirunnerprojectssearchnamespaces"></a>`searchNamespaces` | [`Boolean`](#boolean) | Include namespace in project search. |
-| <a id="cirunnerprojectssort"></a>`sort` **{warning-solid}** | [`String`](#string) | **Deprecated** in 15.4. Default sort order will change in 16.0. Specify `"id_asc"` if query results' order is important. |
+| <a id="cirunnerprojectssort"></a>`sort` | [`String`](#string) | Sort order of results. Format: `<field_name>_<sort_direction>`, for example: `id_desc` or `name_asc`. |
| <a id="cirunnerprojectstopics"></a>`topics` | [`[String!]`](#string) | Filter projects by topics. |
##### `CiRunner.status`
diff --git a/doc/development/contributing/merge_request_workflow.md b/doc/development/contributing/merge_request_workflow.md
index a67459e02ed..7a0269e551d 100644
--- a/doc/development/contributing/merge_request_workflow.md
+++ b/doc/development/contributing/merge_request_workflow.md
@@ -242,6 +242,15 @@ requirements.
There isn't a way to know anything about our customers' data on their
[self-managed instances](../../subscriptions/self_managed/index.md), so keep
that in mind for any data implications with your merge request.
+1. Consider self-managed functionality and upgrade paths. The change should consider both:
+
+ - If additional work needs to be done for self-managed availability, and
+ - If the change requires a [required stop](../database/required_stops.md) when upgrading GitLab versions.
+
+ Upgrade stops are sometimes requested when a GitLab code change is dependent
+ upon a background migration being already complete. Ideally, changes causing required
+ upgrade stops should be held for the next major release, or
+ [at least a 3 milestones notice in advance if unavoidable](../../update/index.md#upgrade-paths).
### Testing
diff --git a/doc/development/documentation/topic_types/tutorial.md b/doc/development/documentation/topic_types/tutorial.md
index b75b1e6b629..52f715cfcf3 100644
--- a/doc/development/documentation/topic_types/tutorial.md
+++ b/doc/development/documentation/topic_types/tutorial.md
@@ -25,6 +25,11 @@ In general, you might consider using a tutorial when:
ideal to duplicate content that is available elsewhere, it's worse to force the reader to
leave the page to find what they need.
+## Tutorial file name and location
+
+For tutorial Markdown files, create a subfolder under `doc/tutorials`.
+The tutorial file should be `index.md`.
+
## Tutorial format
Tutorials should be in this format:
@@ -67,7 +72,7 @@ To do step 2:
```
An example of a tutorial that follows this format is
-[Tutorial: Make your first Git commit](../../../tutorials/make_your_first_git_commit.md).
+[Tutorial: Make your first Git commit](../../../tutorials/make_first_git_commit/index.md).
## Tutorial page title
diff --git a/doc/gitlab-basics/add-file.md b/doc/gitlab-basics/add-file.md
index 0915b9b8e21..425b8927520 100644
--- a/doc/gitlab-basics/add-file.md
+++ b/doc/gitlab-basics/add-file.md
@@ -39,7 +39,7 @@ To add a new file from the command line:
1. Use the "change directory" (`cd`) command to go to your GitLab project's folder.
Run the `cd DESTINATION` command, changing `DESTINATION` to the location of your folder.
1. Choose a Git branch to work in. You can either:
- - [Create a new branch](../tutorials/make_your_first_git_commit.md#create-a-branch-and-make-changes)
+ - [Create a new branch](../tutorials/make_first_git_commit/index.md#create-a-branch-and-make-changes)
to add your file into. Don't submit changes directly to the default branch of your
repository unless your project is very small and you're the only person working on it.
- [Switch to an existing branch](start-using-git.md#switch-to-a-branch).
diff --git a/doc/gitlab-basics/start-using-git.md b/doc/gitlab-basics/start-using-git.md
index 5fcaa68656f..cdcff148505 100644
--- a/doc/gitlab-basics/start-using-git.md
+++ b/doc/gitlab-basics/start-using-git.md
@@ -15,7 +15,7 @@ You can do many Git operations directly in GitLab. However, the command line is
like fixing complex merge conflicts or rolling back commits.
If you're new to Git and want to learn by working in your own project,
-[learn how to make your first commit](../tutorials/make_your_first_git_commit.md).
+[learn how to make your first commit](../tutorials/make_first_git_commit/index.md).
For a quick reference of Git commands, download a [Git Cheat Sheet](https://about.gitlab.com/images/press/git-cheat-sheet.pdf).
diff --git a/doc/topics/autodevops/requirements.md b/doc/topics/autodevops/requirements.md
index be242a89c2e..7d008b67639 100644
--- a/doc/topics/autodevops/requirements.md
+++ b/doc/topics/autodevops/requirements.md
@@ -63,8 +63,8 @@ To define the base domain, either:
- In the project or group level: add it as an environment variable: `KUBE_INGRESS_BASE_DOMAIN`.
- In the instance level: go to **Main menu > Admin > Settings > CI/CD > Continuous Integration and Delivery** and add it there.
-The base domain variable `KUBE_INGRESS_BASE_DOMAIN` follows the same order of precedence
-as other environment [variables](../../ci/variables/index.md#cicd-variable-precedence).
+The base domain variable `KUBE_INGRESS_BASE_DOMAIN` follows the same order of
+[precedence as other environment variables](../../ci/variables/index.md#cicd-variable-precedence).
If you don't specify the base domain in your projects and groups, Auto DevOps uses the instance-wide **Auto DevOps domain**.
diff --git a/doc/topics/git/getting_started.md b/doc/topics/git/getting_started.md
index 790fd3aa6c0..b57fcc9d395 100644
--- a/doc/topics/git/getting_started.md
+++ b/doc/topics/git/getting_started.md
@@ -1,9 +1,9 @@
---
-redirect_to: '../../tutorials/make_your_first_git_commit.md'
+redirect_to: '../../tutorials/make_first_git_commit/index.md'
remove_date: '2023-04-23'
---
-This document was moved to [another location](../../tutorials/make_your_first_git_commit.md).
+This document was moved to [another location](../../tutorials/make_first_git_commit/index.md).
<!-- This redirect file can be deleted after <2023-04-23>. -->
<!-- Redirects that point to other docs in the same project expire in three months. -->
diff --git a/doc/topics/git/index.md b/doc/topics/git/index.md
index dbd046fc162..065bb9821ee 100644
--- a/doc/topics/git/index.md
+++ b/doc/topics/git/index.md
@@ -26,7 +26,7 @@ The following resources can help you get started with Git:
- [Git-ing started with Git](https://www.youtube.com/watch?v=Ce5nz5n41z4),
a video introduction to Git.
-- [Make your first Git commit](../../tutorials/make_your_first_git_commit.md)
+- [Make your first Git commit](../../tutorials/make_first_git_commit/index.md)
- [Git Basics](https://git-scm.com/book/en/v2/Getting-Started-Git-Basics)
- [Git on the Server - GitLab](https://git-scm.com/book/en/v2/Git-on-the-Server-GitLab)
- [How to install Git](how_to_install_git/index.md)
diff --git a/doc/tutorials/agile_sprint.md b/doc/tutorials/agile_sprint.md
index ff0b17d7eb0..84927fe0a66 100644
--- a/doc/tutorials/agile_sprint.md
+++ b/doc/tutorials/agile_sprint.md
@@ -1,101 +1,11 @@
---
-stage: none
-group: Tutorials
-info: For assistance with this tutorial, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments-to-other-projects-and-subjects.
+redirect_to: 'agile_sprint/index.md'
+remove_date: '2023-07-21'
---
-# Tutorial: Use GitLab to run an Agile iteration
+This document was moved to [another location](agile_sprint/index.md).
-To run an Agile development iteration in GitLab, you use multiple GitLab features
-that work together.
-
-To run an Agile iteration from GitLab:
-
-1. Create a group.
-1. Create a project.
-1. Set up an iteration cadence.
-1. Create scoped labels.
-1. Create your epics and issues.
-1. Create an issue board.
-
-After you've created these core components, you can begin running your iterations.
-
-## Create a group
-
-Iteration cadences are created at the group level, so start by
-[creating one](../user/group/manage.md#create-a-group) if you don't have one already.
-
-You use groups to manage one or more related projects at the same time.
-You add your users as members in the group, and assign them a role. Roles determine
-the [level of permissions](../user/permissions.md) each user has on the projects in the group.
-Membership automatically cascades down to all subgroups and projects.
-
-## Create a project
-
-Now [create one or more projects](../user/project/index.md#create-a-project) in your group.
-There are several different ways to create a project. A project contains
-your code and pipelines, but also the issues that are used for planning your upcoming code changes.
-
-## Set up an iteration cadence
-
-Before you start creating epics or issues, create an
-[iteration cadence](../user/group/iterations/index.md#iteration-cadences).
-Iteration cadences contain the individual, sequential iteration timeboxes for planning and reporting
-on your issues.
-
-When creating an iteration cadence, you can decide whether to automatically manage the iterations or
-disable the automated scheduling to
-[manually manage the iterations](../user/group/iterations/index.md#manual-iteration-management).
-
-Similar to membership, iterations cascade down your group, subgroup, and project hierarchy. If your
-team works across many groups, subgroups, and projects, create the iteration cadence in the top-most
-group shared by all projects that contain the team's issues as illustrated by the diagram below.
-
-```mermaid
-graph TD
- Group --> SubgroupA --> Project1
- Group --> SubgroupB --> Project2
- Group --> IterationCadence
-```
-
-## Create scoped labels
-
-You should also [create scoped labels](../user/project/labels.md) in the same group where you created
-your iteration cadence. Labels help you
-organize your epics, issues, and merge requests, as well as help you
-to visualize the flow of issues in boards. For example, you can use scoped labels like
-`workflow::planning`, `workflow::ready for development`, `workflow::in development`, and `workflow::complete`
-to indicate the status of an issue. You can also leverage scoped labels to denote the type of issue
-or epic such as `type::feature`, `type::defect`, and `type::maintenance`.
-
-## Create your epics and issues
-
-Now you can get started planning your iterations. Start by creating [epics](../user/group/epics/index.md)
-in the group where you created your iteration cadence,
-then create child [issues](../user/project/issues/index.md) in one or more of your projects.
-Add labels to each as needed.
-
-## Create an issue board
-
-[Issue boards](../user/project/issue_board.md) help you plan your upcoming iterations or visualize
-the workflow of the iteration currently in progress. List columns can be created based on label,
-assignee, iteration, or milestone. You can also filter the board by multiple attributes and group
-issues by their epic.
-
-In the group where you created your iteration cadence and labels,
-[create an issue board](../user/project/issue_board.md#create-an-issue-board) and name it
-"Iteration Planning." Then, create lists for each of your iterations. You can then drag issues from
-the "Open" list into iteration lists to schedule them for upcoming iterations.
-
-To visualize the workflow for issues in the current iteration, create another issue board called
-"Current Iteration." When you're creating the board:
-
-1. Select **Edit board**.
-1. Next to **Iteration**, select **Edit**.
-1. From the dropdown list, select **Current iteration**.
-1. Select **Save changes**.
-
-Your board will now only ever show issues that are in the current iteration.
-You can start adding lists for each of the `workflow::...` labels you created previously.
-
-Now you're ready to start development.
+<!-- This redirect file can be deleted after 2023-07-21. -->
+<!-- Redirects that point to other docs in the same project expire in three months. -->
+<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> \ No newline at end of file
diff --git a/doc/tutorials/agile_sprint/index.md b/doc/tutorials/agile_sprint/index.md
new file mode 100644
index 00000000000..0ce100df65e
--- /dev/null
+++ b/doc/tutorials/agile_sprint/index.md
@@ -0,0 +1,101 @@
+---
+stage: none
+group: Tutorials
+info: For assistance with this tutorial, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments-to-other-projects-and-subjects.
+---
+
+# Tutorial: Use GitLab to run an Agile iteration
+
+To run an Agile development iteration in GitLab, you use multiple GitLab features
+that work together.
+
+To run an Agile iteration from GitLab:
+
+1. Create a group.
+1. Create a project.
+1. Set up an iteration cadence.
+1. Create scoped labels.
+1. Create your epics and issues.
+1. Create an issue board.
+
+After you've created these core components, you can begin running your iterations.
+
+## Create a group
+
+Iteration cadences are created at the group level, so start by
+[creating one](../../user/group/manage.md#create-a-group) if you don't have one already.
+
+You use groups to manage one or more related projects at the same time.
+You add your users as members in the group, and assign them a role. Roles determine
+the [level of permissions](../../user/permissions.md) each user has on the projects in the group.
+Membership automatically cascades down to all subgroups and projects.
+
+## Create a project
+
+Now [create one or more projects](../../user/project/index.md#create-a-project) in your group.
+There are several different ways to create a project. A project contains
+your code and pipelines, but also the issues that are used for planning your upcoming code changes.
+
+## Set up an iteration cadence
+
+Before you start creating epics or issues, create an
+[iteration cadence](../../user/group/iterations/index.md#iteration-cadences).
+Iteration cadences contain the individual, sequential iteration timeboxes for planning and reporting
+on your issues.
+
+When creating an iteration cadence, you can decide whether to automatically manage the iterations or
+disable the automated scheduling to
+[manually manage the iterations](../../user/group/iterations/index.md#manual-iteration-management).
+
+Similar to membership, iterations cascade down your group, subgroup, and project hierarchy. If your
+team works across many groups, subgroups, and projects, create the iteration cadence in the top-most
+group shared by all projects that contain the team's issues as illustrated by the diagram below.
+
+```mermaid
+graph TD
+ Group --> SubgroupA --> Project1
+ Group --> SubgroupB --> Project2
+ Group --> IterationCadence
+```
+
+## Create scoped labels
+
+You should also [create scoped labels](../../user/project/labels.md) in the same group where you created
+your iteration cadence. Labels help you
+organize your epics, issues, and merge requests, as well as help you
+to visualize the flow of issues in boards. For example, you can use scoped labels like
+`workflow::planning`, `workflow::ready for development`, `workflow::in development`, and `workflow::complete`
+to indicate the status of an issue. You can also leverage scoped labels to denote the type of issue
+or epic such as `type::feature`, `type::defect`, and `type::maintenance`.
+
+## Create your epics and issues
+
+Now you can get started planning your iterations. Start by creating [epics](../../user/group/epics/index.md)
+in the group where you created your iteration cadence,
+then create child [issues](../../user/project/issues/index.md) in one or more of your projects.
+Add labels to each as needed.
+
+## Create an issue board
+
+[Issue boards](../../user/project/issue_board.md) help you plan your upcoming iterations or visualize
+the workflow of the iteration currently in progress. List columns can be created based on label,
+assignee, iteration, or milestone. You can also filter the board by multiple attributes and group
+issues by their epic.
+
+In the group where you created your iteration cadence and labels,
+[create an issue board](../../user/project/issue_board.md#create-an-issue-board) and name it
+"Iteration Planning." Then, create lists for each of your iterations. You can then drag issues from
+the "Open" list into iteration lists to schedule them for upcoming iterations.
+
+To visualize the workflow for issues in the current iteration, create another issue board called
+"Current Iteration." When you're creating the board:
+
+1. Select **Edit board**.
+1. Next to **Iteration**, select **Edit**.
+1. From the dropdown list, select **Current iteration**.
+1. Select **Save changes**.
+
+Your board will now only ever show issues that are in the current iteration.
+You can start adding lists for each of the `workflow::...` labels you created previously.
+
+Now you're ready to start development.
diff --git a/doc/tutorials/compliance_pipeline/index.md b/doc/tutorials/compliance_pipeline/index.md
new file mode 100644
index 00000000000..2dab7a7ecb1
--- /dev/null
+++ b/doc/tutorials/compliance_pipeline/index.md
@@ -0,0 +1,177 @@
+---
+stage: Govern
+group: Compliance
+info: For assistance with this tutorial, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments-to-other-projects-and-subjects.
+---
+
+# Tutorial: Create a compliance pipeline **(ULTIMATE)**
+
+You can use [compliance pipelines](../../user/group/compliance_frameworks.md#compliance-pipelines) to ensure specific
+compliance-related jobs are run on pipelines for all projects in a group. Compliance pipelines are applied
+to projects through [compliance frameworks](../../user/group/compliance_frameworks.md).
+
+In this tutorial, you:
+
+1. Create a [new group](#create-a-new-group).
+1. Create a [new project for the compliance pipeline configuration](#create-a-new-compliance-pipeline-project).
+1. Configure a [compliance framework](#configure-compliance-framework) to apply to other projects.
+1. Create a [new project and apply the compliance framework](#create-a-new-project-and-apply-the-compliance-framework) to it.
+1. Combine [compliance pipeline configuration and regular pipeline configuration](#combine-pipeline-configurations).
+
+Prerequisites:
+
+- Permission to create new top-level groups.
+
+## Create a new group
+
+Compliance frameworks are configured in top-level groups. In this tutorial, you create a top-level group that:
+
+- Contains two projects:
+ - The compliance pipeline project to store the compliance pipeline configuration.
+ - Another project that must run a job in its pipeline that is defined by the compliance pipeline configuration.
+- Has the compliance framework to apply to projects.
+
+To create the new group:
+
+1. On the top bar, select **Create new... > New group**.
+1. Select **Create group**.
+1. In the **Group name** field, enter `Tutorial group`.
+1. Select **Create group**.
+
+## Create a new compliance pipeline project
+
+Now you're ready to create a compliance pipeline project. This project contains the
+[compliance pipeline configuration](../../user/group/compliance_frameworks.md#example-configuration) to apply to all
+projects with the compliance framework applied.
+
+To create the compliance pipeline project:
+
+1. On the top bar, select **Main menu > Groups** and find the `Tutorial group` group.
+1. Select **New project**.
+1. Select **Create blank project**.
+1. In the **Project name** field, enter `Tutorial compliance project`.
+1. Select **Create project**.
+
+To add compliance pipeline configuration to `Tutorial compliance project`:
+
+1. On the top bar, select **Main menu > Projects** and find the `Tutorial compliance project` project.
+1. On the left sidebar, select **CI/CD > Editor**.
+1. Select **Configure pipeline**.
+1. In the pipeline editor, replace the default configuration with:
+
+ ```yaml
+ ---
+ compliance-job:
+ script:
+ - echo "Running compliance job required for every project in this group..."
+ ```
+
+1. Select **Commit changes**.
+
+## Configure compliance framework
+
+The compliance framework is configured in the [new group](#create-a-new-group).
+
+To configure the compliance framework:
+
+1. On the top bar, select **Main menu > Groups** and find the `Tutorial group` group.
+1. On the left sidebar, select **Settings > General**.
+1. Expand **Compliance frameworks**.
+1. Select **Add framework**.
+1. In the **Name** field, enter `Tutorial compliance framework`.
+1. In the **Description** field, enter `Compliance framework for tutorial`.
+1. In the **Compliance pipeline configuration (optional)** field, enter
+ `.gitlab-ci.yml@tutorial-group/tutorial-compliance-project`.
+1. In the **Background color** field, select a color of your choice.
+1. Select **Add framework**.
+
+For convenience, make the new compliance framework the default for all new projects in the group:
+
+1. On the top bar, select **Main menu > Groups** and find the `Tutorial group` group.
+1. On the left sidebar, select **Settings > General**.
+1. Expand **Compliance frameworks**.
+1. In the row for `Tutorial compliance framework`, select **Options** (**{ellipsis_v}**).
+1. Select **Set default**.
+
+## Create a new project and apply the compliance framework
+
+Your compliance framework is ready, so you can now create projects in the group and they automatically run the
+compliance pipeline configuration in their pipelines.
+
+To create a new project for running the compliance pipeline configuration:
+
+1. On the top bar, select **Main menu > Groups** and find the `Tutorial group` group.
+1. Select **New project**.
+1. Select **Create blank project**.
+1. In the **Project name** field, enter `Tutorial project`.
+1. Select **Create project**.
+
+On the project page, notice the `Tutorial compliance framework` label appears because that was set as the default
+compliance framework for the group.
+
+Without any other pipeline configuration, `Tutorial project` can run the jobs defined in the compliance
+pipeline configuration in `Tutorial compliance project`.
+
+To run the compliance pipeline configuration in `Tutorial project`:
+
+1. On the top bar, select **Main menu > Projects** and find the `Tutorial project` project.
+1. Select **CI/CD Pipelines**.
+1. Select **Run pipeline**.
+1. On the **Run pipeline** page, select **Run pipeline**.
+
+Notice the pipeline runs a job called `compliance-job` in a **test** stage. Nice work, you've run your first compliance
+job!
+
+## Combine pipeline configurations
+
+If you want projects to run their own jobs as well as the compliance pipeline jobs, you must combine the compliance
+pipeline configuration and the regular pipeline configuration of the project.
+
+To combine the pipeline configurations, you must define the regular pipeline configuration and then update the
+compliance pipeline configuration to refer to it.
+
+To create the regular pipeline configuration:
+
+1. On the top bar, select **Main menu > Projects** and find the `Tutorial project` project.
+1. On the left sidebar, select **CI/CD > Editor**.
+1. Select **Configure pipeline**.
+1. In the pipeline editor, replace the default configuration with:
+
+ ```yaml
+ ---
+ project-job:
+ script:
+ - echo "Running project job..."
+ ```
+
+1. Select **Commit changes**.
+
+To combine the new project pipeline configuration with the compliance pipeline configuration:
+
+1. On the top bar, select **Main menu > Projects** and find the `Tutorial compliance project` project.
+1. On the left sidebar, select **CI/CD > Editor**.
+1. In the existing configuration, add:
+
+ ```yaml
+ include:
+ - project: 'tutorial-group/tutorial-project'
+ file: '.gitlab-ci.yml'
+ ```
+
+1. Select **Commit changes**.
+
+To confirm the regular pipeline configuration is combined with the compliance pipeline configuration:
+
+1. On the top bar, select **Main menu > Projects** and find the `Tutorial project` project.
+1. Select **CI/CD Pipelines**.
+1. Select **Run pipeline**.
+1. On the **Run pipeline** page, select **Run pipeline**.
+
+Notice the pipeline runs two jobs in a **test** stage:
+
+- `compliance-job`.
+- `project-job`.
+
+Congratulations, you've created and configured a compliance pipeline!
+
+See more [example compliance pipeline configurations](../../user/group/compliance_frameworks.md#example-configuration).
diff --git a/doc/tutorials/convert_personal_namespace_into_group.md b/doc/tutorials/convert_personal_namespace_into_group.md
index 6018d85a466..c1b3b58efb8 100644
--- a/doc/tutorials/convert_personal_namespace_into_group.md
+++ b/doc/tutorials/convert_personal_namespace_into_group.md
@@ -1,95 +1,11 @@
---
-stage: Data Stores
-group: Tenant Scale
-info: For assistance with this tutorial, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments-to-other-projects-and-subjects.
+redirect_to: 'convert_personal_namespace_to_group/index.md'
+remove_date: '2023-07-21'
---
-# Tutorial: Convert a personal namespace into a group **(FREE SAAS)**
+This document was moved to [another location](convert_personal_namespace_to_group/index.md).
-If you've started out on GitLab with a personal [namespace](../user/namespace/index.md), but now find
-that you've outgrown its capabilities and its limitations hinder the collaboration on your projects,
-you might want to switch to a group namespace instead.
-A group namespace allows you to create multiple subgroups, and manage their members and permissions.
-
-You don't have to start from scratch - you can create a new group
-and move your existing projects to the group to get the added benefits.
-To find out how, see [Tutorial: Move your personal project to a group](move_personal_project_to_a_group.md).
-
-But you can go one step further and convert your personal namespace into a group namespace,
-so you get to keep the existing username and URL. For example, if your username is `alex`,
-you can continue using the `https://gitlab.example.com/alex` URL for your group.
-
-This tutorial shows you how to convert your personal namespace into a group namespace
-using the following steps:
-
-1. [Create a group](#create-a-group).
-1. [Transfer projects from the personal namespace to the group](#transfer-projects-from-the-personal-namespace-to-the-group).
-1. [Rename the original username](#rename-the-original-username).
-1. [Rename the new group namespace to the original username](#rename-the-new-group-namespace-to-the-original-username).
-
-For example, if your username for a personal namespace is `alex`, first create a group namespace named `alex-group`.
-Then, move all projects from the `alex` to the `alex-group` namespace. Finally,
-rename the `alex` namespace to `alex-user`, and `alex-group` namespace to the now available `alex` username.
-
-## Create a group
-
-1. On the top bar, select **Main menu > Groups > View all groups**.
-1. On the right of the page, select **New group**.
-1. In **Group name**, enter a name for the group.
-1. In **Group URL**, enter a path for the group, which is used as the namespace.
- Don't worry about the actual path, this is only temporary. You'll change this URL to the username of the personal namespace in the [final step](#rename-the-new-group-namespace-to-the-original-username).
-1. Choose the [visibility level](../user/public_access.md).
-1. Optional. Fill in information to personalize your experience.
-1. Select **Create group**.
-
-## Transfer projects from the personal namespace to the group
-
-Next, you must transfer your projects from the personal namespace to the new group.
-You can transfer only one project at a time, so if you want to transfer multiple projects,
-you must perform the steps below for each project.
-
-Before you start the transfer process, make sure you:
-
-- Have the Owner role for the project.
-- Remove [container images](../user/packages/container_registry/index.md#move-or-rename-container-registry-repositories).
- You can't transfer a project that contains container images.
-- Remove npm packages. You can't update the root namespace of a project that contains npm packages.
-
-To transfer a project to a group:
-
-1. On the top bar, select **Main menu > Projects** and find your project.
-1. On the left sidebar, select **Settings > General**.
-1. Expand **Advanced**.
-1. Under **Transfer project**, choose the group to transfer the project to.
-1. Select **Transfer project**.
-1. Enter the project's name and select **Confirm**.
-
-## Rename the original username
-
-Next, rename the original username of the personal namespace, so that the username becomes available for the new group namespace.
-You can keep on using the personal namespace for other personal projects, or [delete that user account](../user/profile/account/delete_account.md)
-
-From the moment you rename the personal namespace, the username becomes available, so it's possible that someone else registers an account with it. To avoid this, you should [rename the new group](#rename-the-new-group-namespace-to-the-original-username) as soon as possible.
-
-To [change a user's username](../user/profile/index.md#change-your-username):
-
-1. On the top bar, in the top-right corner, select your avatar.
-1. Select **Edit profile**.
-1. On the left sidebar, select **Account**.
-1. In the **Change username** section, enter a new username as the path.
-1. Select **Update username**.
-
-## Rename the new group namespace to the original username
-
-Finally, rename the new group's URL to the username of the original personal namespace.
-
-To [change your group path](../user/group/manage.md#change-a-groups-path) (group URL):
-
-1. On the top bar, select **Main menu > Groups** and find your group.
-1. On the left sidebar, select **Settings > General page**.
-1. Expand the **Advanced** section.
-1. Under **Change group URL**, enter the user's original username.
-1. Select **Change group URL**.
-
-That's it! You have now converted a personal namespace into a group, which opens up new possibilities of
-working on projects and collaborating with more members.
+<!-- This redirect file can be deleted after 2023-07-21. -->
+<!-- Redirects that point to other docs in the same project expire in three months. -->
+<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> \ No newline at end of file
diff --git a/doc/tutorials/convert_personal_namespace_to_group/index.md b/doc/tutorials/convert_personal_namespace_to_group/index.md
new file mode 100644
index 00000000000..53ad46effd9
--- /dev/null
+++ b/doc/tutorials/convert_personal_namespace_to_group/index.md
@@ -0,0 +1,95 @@
+---
+stage: Data Stores
+group: Tenant Scale
+info: For assistance with this tutorial, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments-to-other-projects-and-subjects.
+---
+
+# Tutorial: Convert a personal namespace into a group **(FREE SAAS)**
+
+If you've started out on GitLab with a personal [namespace](../../user/namespace/index.md), but now find
+that you've outgrown its capabilities and its limitations hinder the collaboration on your projects,
+you might want to switch to a group namespace instead.
+A group namespace allows you to create multiple subgroups, and manage their members and permissions.
+
+You don't have to start from scratch - you can create a new group
+and move your existing projects to the group to get the added benefits.
+To find out how, see [Tutorial: Move your personal project to a group](../move_personal_project_to_group/index.md).
+
+But you can go one step further and convert your personal namespace into a group namespace,
+so you get to keep the existing username and URL. For example, if your username is `alex`,
+you can continue using the `https://gitlab.example.com/alex` URL for your group.
+
+This tutorial shows you how to convert your personal namespace into a group namespace
+using the following steps:
+
+1. [Create a group](#create-a-group).
+1. [Transfer projects from the personal namespace to the group](#transfer-projects-from-the-personal-namespace-to-the-group).
+1. [Rename the original username](#rename-the-original-username).
+1. [Rename the new group namespace to the original username](#rename-the-new-group-namespace-to-the-original-username).
+
+For example, if your username for a personal namespace is `alex`, first create a group namespace named `alex-group`.
+Then, move all projects from the `alex` to the `alex-group` namespace. Finally,
+rename the `alex` namespace to `alex-user`, and `alex-group` namespace to the now available `alex` username.
+
+## Create a group
+
+1. On the top bar, select **Main menu > Groups > View all groups**.
+1. On the right of the page, select **New group**.
+1. In **Group name**, enter a name for the group.
+1. In **Group URL**, enter a path for the group, which is used as the namespace.
+ Don't worry about the actual path, this is only temporary. You'll change this URL to the username of the personal namespace in the [final step](#rename-the-new-group-namespace-to-the-original-username).
+1. Choose the [visibility level](../../user/public_access.md).
+1. Optional. Fill in information to personalize your experience.
+1. Select **Create group**.
+
+## Transfer projects from the personal namespace to the group
+
+Next, you must transfer your projects from the personal namespace to the new group.
+You can transfer only one project at a time, so if you want to transfer multiple projects,
+you must perform the steps below for each project.
+
+Before you start the transfer process, make sure you:
+
+- Have the Owner role for the project.
+- Remove [container images](../../user/packages/container_registry/index.md#move-or-rename-container-registry-repositories).
+ You can't transfer a project that contains container images.
+- Remove npm packages. You can't update the root namespace of a project that contains npm packages.
+
+To transfer a project to a group:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Settings > General**.
+1. Expand **Advanced**.
+1. Under **Transfer project**, choose the group to transfer the project to.
+1. Select **Transfer project**.
+1. Enter the project's name and select **Confirm**.
+
+## Rename the original username
+
+Next, rename the original username of the personal namespace, so that the username becomes available for the new group namespace.
+You can keep on using the personal namespace for other personal projects, or [delete that user account](../../user/profile/account/delete_account.md)
+
+From the moment you rename the personal namespace, the username becomes available, so it's possible that someone else registers an account with it. To avoid this, you should [rename the new group](#rename-the-new-group-namespace-to-the-original-username) as soon as possible.
+
+To [change a user's username](../../user/profile/index.md#change-your-username):
+
+1. On the top bar, in the top-right corner, select your avatar.
+1. Select **Edit profile**.
+1. On the left sidebar, select **Account**.
+1. In the **Change username** section, enter a new username as the path.
+1. Select **Update username**.
+
+## Rename the new group namespace to the original username
+
+Finally, rename the new group's URL to the username of the original personal namespace.
+
+To [change your group path](../../user/group/manage.md#change-a-groups-path) (group URL):
+
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Settings > General page**.
+1. Expand the **Advanced** section.
+1. Under **Change group URL**, enter the user's original username.
+1. Select **Change group URL**.
+
+That's it! You have now converted a personal namespace into a group, which opens up new possibilities of
+working on projects and collaborating with more members.
diff --git a/doc/tutorials/create_compliance_pipeline.md b/doc/tutorials/create_compliance_pipeline.md
index 382a7f3fa57..ac5550ad6a4 100644
--- a/doc/tutorials/create_compliance_pipeline.md
+++ b/doc/tutorials/create_compliance_pipeline.md
@@ -1,177 +1,11 @@
---
-stage: Govern
-group: Compliance
-info: For assistance with this tutorial, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments-to-other-projects-and-subjects.
+redirect_to: 'compliance_pipeline/index.md'
+remove_date: '2023-07-21'
---
-# Tutorial: Create a compliance pipeline **(ULTIMATE)**
+This document was moved to [another location](compliance_pipeline/index.md).
-You can use [compliance pipelines](../user/group/compliance_frameworks.md#compliance-pipelines) to ensure specific
-compliance-related jobs are run on pipelines for all projects in a group. Compliance pipelines are applied
-to projects through [compliance frameworks](../user/group/compliance_frameworks.md).
-
-In this tutorial, you:
-
-1. Create a [new group](#create-a-new-group).
-1. Create a [new project for the compliance pipeline configuration](#create-a-new-compliance-pipeline-project).
-1. Configure a [compliance framework](#configure-compliance-framework) to apply to other projects.
-1. Create a [new project and apply the compliance framework](#create-a-new-project-and-apply-the-compliance-framework) to it.
-1. Combine [compliance pipeline configuration and regular pipeline configuration](#combine-pipeline-configurations).
-
-Prerequisites:
-
-- Permission to create new top-level groups.
-
-## Create a new group
-
-Compliance frameworks are configured in top-level groups. In this tutorial, you create a top-level group that:
-
-- Contains two projects:
- - The compliance pipeline project to store the compliance pipeline configuration.
- - Another project that must run a job in its pipeline that is defined by the compliance pipeline configuration.
-- Has the compliance framework to apply to projects.
-
-To create the new group:
-
-1. On the top bar, select **Create new... > New group**.
-1. Select **Create group**.
-1. In the **Group name** field, enter `Tutorial group`.
-1. Select **Create group**.
-
-## Create a new compliance pipeline project
-
-Now you're ready to create a compliance pipeline project. This project contains the
-[compliance pipeline configuration](../user/group/compliance_frameworks.md#example-configuration) to apply to all
-projects with the compliance framework applied.
-
-To create the compliance pipeline project:
-
-1. On the top bar, select **Main menu > Groups** and find the `Tutorial group` group.
-1. Select **New project**.
-1. Select **Create blank project**.
-1. In the **Project name** field, enter `Tutorial compliance project`.
-1. Select **Create project**.
-
-To add compliance pipeline configuration to `Tutorial compliance project`:
-
-1. On the top bar, select **Main menu > Projects** and find the `Tutorial compliance project` project.
-1. On the left sidebar, select **CI/CD > Editor**.
-1. Select **Configure pipeline**.
-1. In the pipeline editor, replace the default configuration with:
-
- ```yaml
- ---
- compliance-job:
- script:
- - echo "Running compliance job required for every project in this group..."
- ```
-
-1. Select **Commit changes**.
-
-## Configure compliance framework
-
-The compliance framework is configured in the [new group](#create-a-new-group).
-
-To configure the compliance framework:
-
-1. On the top bar, select **Main menu > Groups** and find the `Tutorial group` group.
-1. On the left sidebar, select **Settings > General**.
-1. Expand **Compliance frameworks**.
-1. Select **Add framework**.
-1. In the **Name** field, enter `Tutorial compliance framework`.
-1. In the **Description** field, enter `Compliance framework for tutorial`.
-1. In the **Compliance pipeline configuration (optional)** field, enter
- `.gitlab-ci.yml@tutorial-group/tutorial-compliance-project`.
-1. In the **Background color** field, select a color of your choice.
-1. Select **Add framework**.
-
-For convenience, make the new compliance framework the default for all new projects in the group:
-
-1. On the top bar, select **Main menu > Groups** and find the `Tutorial group` group.
-1. On the left sidebar, select **Settings > General**.
-1. Expand **Compliance frameworks**.
-1. In the row for `Tutorial compliance framework`, select **Options** (**{ellipsis_v}**).
-1. Select **Set default**.
-
-## Create a new project and apply the compliance framework
-
-Your compliance framework is ready, so you can now create projects in the group and they automatically run the
-compliance pipeline configuration in their pipelines.
-
-To create a new project for running the compliance pipeline configuration:
-
-1. On the top bar, select **Main menu > Groups** and find the `Tutorial group` group.
-1. Select **New project**.
-1. Select **Create blank project**.
-1. In the **Project name** field, enter `Tutorial project`.
-1. Select **Create project**.
-
-On the project page, notice the `Tutorial compliance framework` label appears because that was set as the default
-compliance framework for the group.
-
-Without any other pipeline configuration, `Tutorial project` can run the jobs defined in the compliance
-pipeline configuration in `Tutorial compliance project`.
-
-To run the compliance pipeline configuration in `Tutorial project`:
-
-1. On the top bar, select **Main menu > Projects** and find the `Tutorial project` project.
-1. Select **CI/CD Pipelines**.
-1. Select **Run pipeline**.
-1. On the **Run pipeline** page, select **Run pipeline**.
-
-Notice the pipeline runs a job called `compliance-job` in a **test** stage. Nice work, you've run your first compliance
-job!
-
-## Combine pipeline configurations
-
-If you want projects to run their own jobs as well as the compliance pipeline jobs, you must combine the compliance
-pipeline configuration and the regular pipeline configuration of the project.
-
-To combine the pipeline configurations, you must define the regular pipeline configuration and then update the
-compliance pipeline configuration to refer to it.
-
-To create the regular pipeline configuration:
-
-1. On the top bar, select **Main menu > Projects** and find the `Tutorial project` project.
-1. On the left sidebar, select **CI/CD > Editor**.
-1. Select **Configure pipeline**.
-1. In the pipeline editor, replace the default configuration with:
-
- ```yaml
- ---
- project-job:
- script:
- - echo "Running project job..."
- ```
-
-1. Select **Commit changes**.
-
-To combine the new project pipeline configuration with the compliance pipeline configuration:
-
-1. On the top bar, select **Main menu > Projects** and find the `Tutorial compliance project` project.
-1. On the left sidebar, select **CI/CD > Editor**.
-1. In the existing configuration, add:
-
- ```yaml
- include:
- - project: 'tutorial-group/tutorial-project'
- file: '.gitlab-ci.yml'
- ```
-
-1. Select **Commit changes**.
-
-To confirm the regular pipeline configuration is combined with the compliance pipeline configuration:
-
-1. On the top bar, select **Main menu > Projects** and find the `Tutorial project` project.
-1. Select **CI/CD Pipelines**.
-1. Select **Run pipeline**.
-1. On the **Run pipeline** page, select **Run pipeline**.
-
-Notice the pipeline runs two jobs in a **test** stage:
-
-- `compliance-job`.
-- `project-job`.
-
-Congratulations, you've created and configured a compliance pipeline!
-
-See more [example compliance pipeline configurations](../user/group/compliance_frameworks.md#example-configuration).
+<!-- This redirect file can be deleted after 2023-07-21. -->
+<!-- Redirects that point to other docs in the same project expire in three months. -->
+<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> \ No newline at end of file
diff --git a/doc/tutorials/fuzz_testing/index.md b/doc/tutorials/fuzz_testing/index.md
new file mode 100644
index 00000000000..1d4985f9003
--- /dev/null
+++ b/doc/tutorials/fuzz_testing/index.md
@@ -0,0 +1,243 @@
+---
+stage: Secure
+group: Dynamic Analysis
+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
+---
+
+# Tutorial: Perform fuzz testing in GitLab **(ULTIMATE)**
+
+[Coverage-guided fuzz testing](../../user/application_security/coverage_fuzzing/index.md#coverage-guided-fuzz-testing-process) sends unexpected, malformed, or random data to your application, and then monitors
+your application for unstable behaviors and crashes.
+
+This helps you discover bugs and potential security issues that other QA processes may miss.
+
+You should use fuzz testing in addition to other security scanners and your own test processes.
+If you're using GitLab CI/CD, you can run fuzz tests as part your CI/CD workflow.
+
+To set up, configure, and perform coverage-guided fuzz testing
+using JavaScript in this tutorial, you:
+
+1. [Fork the project template](#fork-the-project-template) to create a project
+ to run the fuzz tests in.
+1. [Create the fuzz targets](#create-the-fuzz-targets).
+1. [Enable coverage-guided fuzz testing](#enable-coverage-guided-fuzz-testing)
+ in your forked project.
+1. [Run the fuzz test](#run-the-fuzz-test) to identify security vulnerabilities.
+1. [Fix any vulnerabilities](#fix-the-vulnerabilities) identified by the fuzz test.
+
+## Fork the project template
+
+First, to create a project to try out fuzz testing in, you must fork the `fuzz-testing`
+project template:
+
+1. Open the [`fuzz-testing` project template](https://gitlab.com/gitlab-org/tutorial-project-templates/fuzz-testing).
+1. [Fork the project template](../../user/project/repository/forking_workflow.md).
+1. When forking the project template:
+ - Name the forked project `fuzz-testing-demo`.
+ - Select an appropriate [namespace](../../user/namespace/index.md).
+ - Set [project visibility](../../user/public_access.md) to **Private**.
+
+You have successfully forked the `fuzz-testing` project template. Before you can
+start fuzz testing, remove the relationship between the project template and the fork:
+
+1. On the left sidebar, select **Settings > General**.
+1. Expand **Advanced**.
+1. In the **Remove fork relationship** section, select **Remove fork relationship**.
+ Enter the name of the project when prompted.
+
+Your project is ready and you can now create the fuzz test. Next you will create
+the fuzz targets.
+
+## Create the fuzz targets
+
+Now you have a project for fuzz testing, you create the fuzz targets. A fuzz target
+is a function or program that, given an input, makes a call to the application
+being tested.
+
+In this tutorial, the fuzz targets call a function of the `my-tools.js` file using
+a random buffer as a parameter.
+
+To create the two fuzz target files:
+
+1. On the top bar, select **Main menu > Projects** and select the `fuzz-testing-demo` project.
+1. Create a file in the root directory of the project.
+1. Name the file `fuzz-sayhello.js` and add the following code:
+
+ ```javascript
+ let tools = require('./my-tools')
+
+ function fuzz(buf) {
+ const text = buf.toString()
+ tools.sayHello(text)
+ }
+
+ module.exports = {
+ fuzz
+ }
+ ```
+
+ You can also copy this code from the `fuzz-testing-demo/fuzzers/fuzz-sayhello.js`
+ project file.
+
+1. Name the **Target Branch** `add-fuzz-test` and write a descriptive commit message.
+ - Do not select the **Start a new merge request with these changes** checkbox yet.
+1. Select **Commit changes**.
+1. Return to the root directory of the project.
+1. Make sure you are in the `add-fuzz-test` branch.
+1. Create the second file named `fuzz-readme.js` and add the following code:
+
+ ```javascript
+ let tools = require('./my-tools')
+ function fuzz(buf) {
+ const text = buf.toString()
+ tools.readmeContent(text)
+ }
+ module.exports = {
+ fuzz
+ }
+ ```
+
+ You can also copy this code from the `fuzz-testing-demo/fuzzers/fuzz-readme.js`
+ project file.
+
+1. Write a descriptive commit message.
+1. Make sure the **Target Branch** is `add-fuzz-test`.
+1. Select **Commit changes**.
+
+You now have two fuzz targets that can make calls to the application being tested.
+Next you will enable the fuzz testing.
+
+## Enable coverage-guided fuzz testing
+
+To enable coverage-guided fuzz testing, create a CI/CD pipeline running
+the `gitlab-cov-fuzz` CLI to execute the fuzz test on the two fuzz targets.
+
+To create the pipeline file:
+
+1. Make sure you are in the `add-fuzz-test` branch.
+1. In the root directory of the `fuzz-testing-demo` project, create a new file.
+1. Name the file `.gitlab-ci.yml` and add the following code:
+
+ ```yaml
+ image: node:18
+
+ stages:
+ - fuzz
+
+ include:
+ - template: Coverage-Fuzzing.gitlab-ci.yml
+
+ readme_fuzz_target:
+ extends: .fuzz_base
+ tags: [saas-linux-large-amd64] # Optional
+ variables:
+ COVFUZZ_ADDITIONAL_ARGS: '--fuzzTime=60'
+ script:
+ - npm config set @gitlab-org:registry https://gitlab.com/api/v4/packages/npm/ && npm i -g @gitlab-org/jsfuzz
+ - ./gitlab-cov-fuzz run --engine jsfuzz -- fuzz-readme.js
+
+ hello_fuzzing_target:
+ extends: .fuzz_base
+ tags: [saas-linux-large-amd64] # Optional
+ variables:
+ COVFUZZ_ADDITIONAL_ARGS: '--fuzzTime=60'
+ script:
+ - npm config set @gitlab-org:registry https://gitlab.com/api/v4/packages/npm/ && npm i -g @gitlab-org/jsfuzz
+ - ./gitlab-cov-fuzz run --engine jsfuzz -- fuzz-sayhello.js
+ ```
+
+ This step adds the following to your pipeline:
+ - A `fuzz` stage using a template.
+ - Two jobs, `readme_fuzz_target` and `hello_fuzzing_target`. Each job runs using
+ the `jsfuzz` engine, which reports unhandled exceptions as crashes.
+
+ You can also copy this code from the `fuzz-testing-demo/fuzzers/fuzzers.yml`
+ project file.
+
+1. Write a descriptive commit message.
+1. Make sure the **Target Branch** is `add-fuzz-test`.
+1. Select **Commit changes**.
+
+You have successfully enabled coverage-guided fuzz testing. Next you will run the
+fuzz test using the pipeline you've just created.
+
+## Run the fuzz test
+
+To run the fuzz test:
+
+1. On the left sidebar, select **Merge requests**.
+1. Select **New merge request**.
+1. In the **Source branch** section, select the `add-fuzz-test` branch.
+1. In the **Target branch** section, make sure that your namespace and the `main` branch are selected.
+1. Select **Compare branches and continue**.
+1. [Create the merge request](../../user/project/merge_requests/creating_merge_requests.md).
+
+Creating the merge request triggers a new pipeline, which runs the fuzz test.
+When the pipeline is finished running, you should see a security vulnerability
+alert on the merge request page.
+
+To see more information on each vulnerability, select the individual **Uncaught-exception** links.
+
+You have successfully run the fuzz test and identified vulnerabilities to fix.
+
+## Fix the vulnerabilities
+
+The fuzz test identified two security vulnerabilities. To fix those
+vulnerabilities, you use the `my-tools.js` library.
+
+To create the `my-tools.js` file:
+
+1. Make sure you are in the `add-fuzz-test` branch of the project.
+1. Go to the root directory of your project and open the `my-tools.js` file.
+1. Replace the contents of this file with the following code:
+
+ ```javascript
+ const fs = require('fs')
+
+ function sayHello(name) {
+ if(name.includes("z")) {
+ //throw new Error("😡 error name: " + name)
+ console.log("😡 error name: " + name)
+ } else {
+ return "😀 hello " + name
+ }
+ }
+
+ function readmeContent(name) {
+
+ let fileName = name => {
+ if(name.includes("w")) {
+ return "./README.txt"
+ } else {
+ return "./README.md"
+ }
+ }
+
+ //const data = fs.readFileSync(fileName(name), 'utf8')
+ try {
+ const data = fs.readFileSync(fileName(name), 'utf8')
+ return data
+ } catch (err) {
+ console.error(err.message)
+ return ""
+ }
+
+ }
+
+ module.exports = {
+ sayHello, readmeContent
+ }
+ ```
+
+ You can also copy the code from the `fuzz-testing-demo/javascript/my-tools.js`
+ project file.
+
+1. Select **Commit changes**. This triggers another pipeline to run another fuzz test.
+1. When the pipeline is finished, check the merge request **Overview** page. You
+ should see that the security scan detected no new potential vulnerabilities.
+1. Merge your changes.
+
+Congratulations, you've successfully run a fuzz test and fixed the identified
+security vulnerabilities!
+
+For more information, see [coverage-guided fuzz testing](../../user/application_security/coverage_fuzzing/index.md).
diff --git a/doc/tutorials/fuzz_testing_tutorial.md b/doc/tutorials/fuzz_testing_tutorial.md
index 5a8209d9716..74b24005077 100644
--- a/doc/tutorials/fuzz_testing_tutorial.md
+++ b/doc/tutorials/fuzz_testing_tutorial.md
@@ -1,243 +1,11 @@
---
-stage: Secure
-group: Dynamic Analysis
-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
+redirect_to: 'fuzz_testing/index.md'
+remove_date: '2023-07-21'
---
-# Tutorial: Perform fuzz testing in GitLab **(ULTIMATE)**
+This document was moved to [another location](fuzz_testing/index.md).
-[Coverage-guided fuzz testing](../user/application_security/coverage_fuzzing/index.md#coverage-guided-fuzz-testing-process) sends unexpected, malformed, or random data to your application, and then monitors
-your application for unstable behaviors and crashes.
-
-This helps you discover bugs and potential security issues that other QA processes may miss.
-
-You should use fuzz testing in addition to other security scanners and your own test processes.
-If you're using GitLab CI/CD, you can run fuzz tests as part your CI/CD workflow.
-
-To set up, configure, and perform coverage-guided fuzz testing
-using JavaScript in this tutorial, you:
-
-1. [Fork the project template](#fork-the-project-template) to create a project
- to run the fuzz tests in.
-1. [Create the fuzz targets](#create-the-fuzz-targets).
-1. [Enable coverage-guided fuzz testing](#enable-coverage-guided-fuzz-testing)
- in your forked project.
-1. [Run the fuzz test](#run-the-fuzz-test) to identify security vulnerabilities.
-1. [Fix any vulnerabilities](#fix-the-vulnerabilities) identified by the fuzz test.
-
-## Fork the project template
-
-First, to create a project to try out fuzz testing in, you must fork the `fuzz-testing`
-project template:
-
-1. Open the [`fuzz-testing` project template](https://gitlab.com/gitlab-org/tutorial-project-templates/fuzz-testing).
-1. [Fork the project template](../user/project/repository/forking_workflow.md).
-1. When forking the project template:
- - Name the forked project `fuzz-testing-demo`.
- - Select an appropriate [namespace](../user/namespace/index.md).
- - Set [project visibility](../user/public_access.md) to **Private**.
-
-You have successfully forked the `fuzz-testing` project template. Before you can
-start fuzz testing, remove the relationship between the project template and the fork:
-
-1. On the left sidebar, select **Settings > General**.
-1. Expand **Advanced**.
-1. In the **Remove fork relationship** section, select **Remove fork relationship**.
- Enter the name of the project when prompted.
-
-Your project is ready and you can now create the fuzz test. Next you will create
-the fuzz targets.
-
-## Create the fuzz targets
-
-Now you have a project for fuzz testing, you create the fuzz targets. A fuzz target
-is a function or program that, given an input, makes a call to the application
-being tested.
-
-In this tutorial, the fuzz targets call a function of the `my-tools.js` file using
-a random buffer as a parameter.
-
-To create the two fuzz target files:
-
-1. On the top bar, select **Main menu > Projects** and select the `fuzz-testing-demo` project.
-1. Create a file in the root directory of the project.
-1. Name the file `fuzz-sayhello.js` and add the following code:
-
- ```javascript
- let tools = require('./my-tools')
-
- function fuzz(buf) {
- const text = buf.toString()
- tools.sayHello(text)
- }
-
- module.exports = {
- fuzz
- }
- ```
-
- You can also copy this code from the `fuzz-testing-demo/fuzzers/fuzz-sayhello.js`
- project file.
-
-1. Name the **Target Branch** `add-fuzz-test` and write a descriptive commit message.
- - Do not select the **Start a new merge request with these changes** checkbox yet.
-1. Select **Commit changes**.
-1. Return to the root directory of the project.
-1. Make sure you are in the `add-fuzz-test` branch.
-1. Create the second file named `fuzz-readme.js` and add the following code:
-
- ```javascript
- let tools = require('./my-tools')
- function fuzz(buf) {
- const text = buf.toString()
- tools.readmeContent(text)
- }
- module.exports = {
- fuzz
- }
- ```
-
- You can also copy this code from the `fuzz-testing-demo/fuzzers/fuzz-readme.js`
- project file.
-
-1. Write a descriptive commit message.
-1. Make sure the **Target Branch** is `add-fuzz-test`.
-1. Select **Commit changes**.
-
-You now have two fuzz targets that can make calls to the application being tested.
-Next you will enable the fuzz testing.
-
-## Enable coverage-guided fuzz testing
-
-To enable coverage-guided fuzz testing, create a CI/CD pipeline running
-the `gitlab-cov-fuzz` CLI to execute the fuzz test on the two fuzz targets.
-
-To create the pipeline file:
-
-1. Make sure you are in the `add-fuzz-test` branch.
-1. In the root directory of the `fuzz-testing-demo` project, create a new file.
-1. Name the file `.gitlab-ci.yml` and add the following code:
-
- ```yaml
- image: node:18
-
- stages:
- - fuzz
-
- include:
- - template: Coverage-Fuzzing.gitlab-ci.yml
-
- readme_fuzz_target:
- extends: .fuzz_base
- tags: [saas-linux-large-amd64] # Optional
- variables:
- COVFUZZ_ADDITIONAL_ARGS: '--fuzzTime=60'
- script:
- - npm config set @gitlab-org:registry https://gitlab.com/api/v4/packages/npm/ && npm i -g @gitlab-org/jsfuzz
- - ./gitlab-cov-fuzz run --engine jsfuzz -- fuzz-readme.js
-
- hello_fuzzing_target:
- extends: .fuzz_base
- tags: [saas-linux-large-amd64] # Optional
- variables:
- COVFUZZ_ADDITIONAL_ARGS: '--fuzzTime=60'
- script:
- - npm config set @gitlab-org:registry https://gitlab.com/api/v4/packages/npm/ && npm i -g @gitlab-org/jsfuzz
- - ./gitlab-cov-fuzz run --engine jsfuzz -- fuzz-sayhello.js
- ```
-
- This step adds the following to your pipeline:
- - A `fuzz` stage using a template.
- - Two jobs, `readme_fuzz_target` and `hello_fuzzing_target`. Each job runs using
- the `jsfuzz` engine, which reports unhandled exceptions as crashes.
-
- You can also copy this code from the `fuzz-testing-demo/fuzzers/fuzzers.yml`
- project file.
-
-1. Write a descriptive commit message.
-1. Make sure the **Target Branch** is `add-fuzz-test`.
-1. Select **Commit changes**.
-
-You have successfully enabled coverage-guided fuzz testing. Next you will run the
-fuzz test using the pipeline you've just created.
-
-## Run the fuzz test
-
-To run the fuzz test:
-
-1. On the left sidebar, select **Merge requests**.
-1. Select **New merge request**.
-1. In the **Source branch** section, select the `add-fuzz-test` branch.
-1. In the **Target branch** section, make sure that your namespace and the `main` branch are selected.
-1. Select **Compare branches and continue**.
-1. [Create the merge request](../user/project/merge_requests/creating_merge_requests.md).
-
-Creating the merge request triggers a new pipeline, which runs the fuzz test.
-When the pipeline is finished running, you should see a security vulnerability
-alert on the merge request page.
-
-To see more information on each vulnerability, select the individual **Uncaught-exception** links.
-
-You have successfully run the fuzz test and identified vulnerabilities to fix.
-
-## Fix the vulnerabilities
-
-The fuzz test identified two security vulnerabilities. To fix those
-vulnerabilities, you use the `my-tools.js` library.
-
-To create the `my-tools.js` file:
-
-1. Make sure you are in the `add-fuzz-test` branch of the project.
-1. Go to the root directory of your project and open the `my-tools.js` file.
-1. Replace the contents of this file with the following code:
-
- ```javascript
- const fs = require('fs')
-
- function sayHello(name) {
- if(name.includes("z")) {
- //throw new Error("😡 error name: " + name)
- console.log("😡 error name: " + name)
- } else {
- return "😀 hello " + name
- }
- }
-
- function readmeContent(name) {
-
- let fileName = name => {
- if(name.includes("w")) {
- return "./README.txt"
- } else {
- return "./README.md"
- }
- }
-
- //const data = fs.readFileSync(fileName(name), 'utf8')
- try {
- const data = fs.readFileSync(fileName(name), 'utf8')
- return data
- } catch (err) {
- console.error(err.message)
- return ""
- }
-
- }
-
- module.exports = {
- sayHello, readmeContent
- }
- ```
-
- You can also copy the code from the `fuzz-testing-demo/javascript/my-tools.js`
- project file.
-
-1. Select **Commit changes**. This triggers another pipeline to run another fuzz test.
-1. When the pipeline is finished, check the merge request **Overview** page. You
- should see that the security scan detected no new potential vulnerabilities.
-1. Merge your changes.
-
-Congratulations, you've successfully run a fuzz test and fixed the identified
-security vulnerabilities!
-
-For more information, see [coverage-guided fuzz testing](../user/application_security/coverage_fuzzing/index.md).
+<!-- This redirect file can be deleted after 2023-07-21. -->
+<!-- Redirects that point to other docs in the same project expire in three months. -->
+<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> \ No newline at end of file
diff --git a/doc/tutorials/learn_git.md b/doc/tutorials/learn_git.md
index 484ab2b50b2..a5b52ef73e9 100644
--- a/doc/tutorials/learn_git.md
+++ b/doc/tutorials/learn_git.md
@@ -11,7 +11,7 @@ the most out of GitLab.
| Topic | Description | Good for beginners |
|-------|-------------|--------------------|
-| [Make your first Git commit](make_your_first_git_commit.md) | Create a project, edit a file, and commit changes to a Git repository from the command line. | **{star}** |
+| [Make your first Git commit](make_first_git_commit/index.md) | Create a project, edit a file, and commit changes to a Git repository from the command line. | **{star}** |
| [Start using Git on the command line](../gitlab-basics/start-using-git.md) | Learn how to set up Git, clone repositories, and work with branches. | **{star}** |
| [Take advantage of Git rebase](https://about.gitlab.com/blog/2022/10/06/take-advantage-of-git-rebase/)| Learn how to use the `rebase` command in your workflow. | |
| [Git cheat sheet](https://about.gitlab.com/images/press/git-cheat-sheet.pdf) | Download a PDF of common Git commands. | |
diff --git a/doc/tutorials/img/branches_dropdown_v14_10.png b/doc/tutorials/make_first_git_commit/img/branches_dropdown_v14_10.png
index 926baff0ff8..926baff0ff8 100644
--- a/doc/tutorials/img/branches_dropdown_v14_10.png
+++ b/doc/tutorials/make_first_git_commit/img/branches_dropdown_v14_10.png
Binary files differ
diff --git a/doc/tutorials/img/clone_project_v14_9.png b/doc/tutorials/make_first_git_commit/img/clone_project_v14_9.png
index 98666c95ba3..98666c95ba3 100644
--- a/doc/tutorials/img/clone_project_v14_9.png
+++ b/doc/tutorials/make_first_git_commit/img/clone_project_v14_9.png
Binary files differ
diff --git a/doc/tutorials/img/commit_message_v14_10.png b/doc/tutorials/make_first_git_commit/img/commit_message_v14_10.png
index 5636a135b4e..5636a135b4e 100644
--- a/doc/tutorials/img/commit_message_v14_10.png
+++ b/doc/tutorials/make_first_git_commit/img/commit_message_v14_10.png
Binary files differ
diff --git a/doc/tutorials/make_first_git_commit/index.md b/doc/tutorials/make_first_git_commit/index.md
new file mode 100644
index 00000000000..794b9d1f4b5
--- /dev/null
+++ b/doc/tutorials/make_first_git_commit/index.md
@@ -0,0 +1,271 @@
+---
+stage: none
+group: Tutorials
+info: For assistance with this tutorial, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments-to-other-projects-and-subjects.
+---
+
+# Tutorial: Make your first Git commit
+
+This tutorial is going to teach you a little bit about how Git works. It walks
+you through the steps of creating your own project, editing a file, and
+committing changes to a Git repository from the command line.
+
+When you're done, you'll have a project where you can practice using Git.
+
+## What you need
+
+Before you begin:
+
+- [Install Git on your local machine](../../topics/git/how_to_install_git/index.md).
+- Ensure you can sign in to an instance of GitLab. If your organization doesn't
+ have GitLab, create an account on GitLab.com.
+- [Create SSH keys and add them to GitLab](../../user/ssh.md). SSH keys are how you
+ securely communicate between your computer and GitLab.
+
+## What is Git?
+
+Before we jump into steps, let's go over some basic Git concepts.
+
+Git is a version control system. It's used to track changes to files.
+
+You store files, like code or documents, in a Git *repository*. When you want to edit the files, you
+*clone* the repository to your computer, make the changes, and *push* your changes
+back to the repository. In GitLab, a Git repository is located in
+a *project*.
+
+Each time you push a change, Git records it as a unique *commit*. These commits make up
+the history of when and how a file changed, and who changed it.
+
+```mermaid
+graph LR
+ subgraph Repository commit history
+ direction LR
+ A(Author: Alex<br>Date: 3 Jan at 1PM<br>Commit message: Added sales figures<br> Commit ID: 123abc12) ---> B
+ B(Author: Sam<br>Date: 4 Jan at 10AM<br>Commit message: Removed old info<br> Commit ID: aabb1122) ---> C
+ C(Author: Zhang<br>Date: 5 Jan at 3PM<br>Commit message: Added invoices<br> Commit ID: ddee4455)
+ end
+```
+
+When you work in a Git repository, you work in *branches*. By default, the contents
+of a repository are in a default branch. To make changes, you:
+
+1. Create your own branch, which is a snapshot of the default branch at the time
+ you create it.
+1. Make changes and push them to your branch. Each push creates a commit.
+1. When you're ready, *merge* your branch into the default branch.
+
+```mermaid
+flowchart LR
+ subgraph Default branch
+ A[Commit] --> B[Commit] --> C[Commit] --> D[Commit]
+ end
+ subgraph My branch
+ B --1. Create my branch--> E(Commit)
+ E --2. Add my commit--> F(Commit)
+ F --3. Merge my branch to default--> D
+ end
+```
+
+If this all feels a bit overwhelming, hang in there. You're about to see these concepts in action.
+
+## Steps
+
+Here's an overview of what we're going to do:
+
+1. [Create a sample project](#create-a-sample-project).
+1. [Clone the repository](#clone-the-repository).
+1. [Create a branch and make your changes](#create-a-branch-and-make-changes).
+1. [Commit and push your changes](#commit-and-push-your-changes).
+1. [Merge your changes](#merge-your-changes).
+1. [View your changes in GitLab](#view-your-changes-in-gitlab).
+
+### Create a sample project
+
+To start, create a sample project in GitLab.
+
+1. In GitLab, on the top bar, select **Main menu > Projects > View all projects**.
+1. On the right of the page, select **New project**.
+1. For **Project name**, enter `My sample project`. The project slug is generated for you.
+ This slug is the URL you can use to access the project after it's created.
+1. Ensure **Initialize repository with a README** is selected.
+ How you complete the other fields is up to you.
+1. Select **Create project**.
+
+### Clone the repository
+
+Now you can clone the repository in your project. *Cloning* a repository means you're creating
+a copy on your computer, or wherever you want to store and work with the files.
+
+1. On your project page, select **Clone**. Copy the URL for **Clone with SSH**.
+
+ ![Clone a project with SSH](img/clone_project_v14_9.png)
+
+1. Open a terminal on your computer and go to the directory
+ where you want to clone the files.
+
+1. Enter `git clone` and paste the URL:
+
+ ```shell
+ git clone git@gitlab.com:gitlab-example/my-sample-project.git
+ ```
+
+1. Go to the directory:
+
+ ```shell
+ cd my-sample-project
+ ```
+
+1. By default, you've cloned the default branch for the repository. Usually this
+ branch is `main`. To be sure, get the name of the default branch:
+
+ ```shell
+ git branch
+ ```
+
+ The branch you're on is marked with an asterisk.
+ Press `Q` on your keyboard to return to the main terminal
+ window.
+
+### Create a branch and make changes
+
+Now that you have a copy of the repository, create your own branch so you can
+work on your changes independently.
+
+1. Create a new branch called `example-tutorial-branch`.
+
+ ```shell
+ git checkout -b example-tutorial-branch
+ ```
+
+1. In a text editor like Visual Studio Code, Sublime, `vi`, or any other editor,
+ open the README.md file and add this text:
+
+ ```plaintext
+ Hello world! I'm using Git!
+ ```
+
+1. Save the file.
+
+1. Git keeps track of changed files. To confirm which files have changed, get
+ the status.
+
+ ```shell
+ git status
+ ```
+
+ You should get output similar to the following:
+
+ ```shell
+ On branch example-tutorial-branch
+ Changes not staged for commit:
+ (use "git add <file>..." to update what will be committed)
+ (use "git restore <file>..." to discard changes in working directory)
+ modified: README.md
+
+ no changes added to commit (use "git add" and/or "git commit -a")
+ ```
+
+### Commit and push your changes
+
+You've made changes to a file in your repository. Now it's time to record
+those changes by making your first commit.
+
+1. Add the `README.md` file to the *staging* area. The staging area is where you
+ put files before you commit them.
+
+ ```shell
+ git add README.md
+ ```
+
+1. Confirm the file is staged:
+
+ ```shell
+ git status
+ ```
+
+ You should get output similar to the following, and the filename should be in
+ green text.
+
+ ```shell
+ On branch example-tutorial-branch
+ Changes to be committed:
+ (use "git restore --staged <file>..." to unstage)
+ modified: README.md
+ ```
+
+1. Now commit the staged file, and include a message
+ that describes the change you made. Make sure you surround the message in double
+ quotes (").
+
+ ```shell
+ git commit -m "I added text to the README file"
+ ```
+
+1. The change has been committed to your branch, but your branch and its commits
+ are still only available on your computer. No one else has access to them yet.
+ Push your branch to GitLab:
+
+ ```shell
+ git push origin example-tutorial-branch
+ ```
+
+Your branch is now available on GitLab and visible to other users in your project.
+
+![Branches dropdown list](img/branches_dropdown_v14_10.png)
+
+### Merge your changes
+
+Now you're ready to merge the changes from your `example-tutorial-branch` branch
+to the default branch (`main`).
+
+1. Check out the default branch for your repository.
+
+ ```shell
+ git checkout main
+ ```
+
+1. Merge your branch into the default branch.
+
+ ```shell
+ git merge example-tutorial-branch
+ ```
+
+1. Push the changes.
+
+ ```shell
+ git push
+ ```
+
+NOTE:
+For this tutorial, you merge your branch directly to the default branch for your
+repository. In GitLab, you typically use a [merge request](../../user/project/merge_requests/index.md)
+to merge your branch.
+
+### View your changes in GitLab
+
+You did it! You updated the `README.md` file in your branch, and you merged those changes
+into the `main` branch.
+
+Let's look in the UI and confirm your changes. Go to your project.
+
+- Scroll down and view the contents of the `README.md` file.
+ Your changes should be visible.
+- Above the `README.md` file, view the text in the **Last commit** column.
+ Your commit message is displayed in this column:
+
+ ![Commit message](img/commit_message_v14_10.png)
+
+Now you can return to the command line and change back to your personal branch
+(`git checkout example-tutorial-branch`). You can continue updating files or
+creating new ones. Type `git status` to view the status
+of your changes and commit with abandon.
+
+Don't worry if you mess things up. Everything in Git can be reverted, and if you
+find you can't recover, you can always create a new branch and start again.
+
+Nice work.
+
+## Find more Git learning resources
+
+- Get a complete introduction to Git in the <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Git for GitLab](https://www.youtube.com/watch?v=4lxvVj7wlZw) beginner's course (1h 33m).
+- Find other tutorials about Git and GitLab on the [tutorials page](../index.md).
diff --git a/doc/tutorials/make_your_first_git_commit.md b/doc/tutorials/make_your_first_git_commit.md
index 3df65389c76..04c66a953af 100644
--- a/doc/tutorials/make_your_first_git_commit.md
+++ b/doc/tutorials/make_your_first_git_commit.md
@@ -1,271 +1,11 @@
---
-stage: none
-group: Tutorials
-info: For assistance with this tutorial, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments-to-other-projects-and-subjects.
+redirect_to: 'make_first_git_commit/index.md'
+remove_date: '2023-07-21'
---
-# Tutorial: Make your first Git commit
+This document was moved to [another location](make_first_git_commit/index.md).
-This tutorial is going to teach you a little bit about how Git works. It walks
-you through the steps of creating your own project, editing a file, and
-committing changes to a Git repository from the command line.
-
-When you're done, you'll have a project where you can practice using Git.
-
-## What you need
-
-Before you begin:
-
-- [Install Git on your local machine](../topics/git/how_to_install_git/index.md).
-- Ensure you can sign in to an instance of GitLab. If your organization doesn't
- have GitLab, create an account on GitLab.com.
-- [Create SSH keys and add them to GitLab](../user/ssh.md). SSH keys are how you
- securely communicate between your computer and GitLab.
-
-## What is Git?
-
-Before we jump into steps, let's go over some basic Git concepts.
-
-Git is a version control system. It's used to track changes to files.
-
-You store files, like code or documents, in a Git *repository*. When you want to edit the files, you
-*clone* the repository to your computer, make the changes, and *push* your changes
-back to the repository. In GitLab, a Git repository is located in
-a *project*.
-
-Each time you push a change, Git records it as a unique *commit*. These commits make up
-the history of when and how a file changed, and who changed it.
-
-```mermaid
-graph LR
- subgraph Repository commit history
- direction LR
- A(Author: Alex<br>Date: 3 Jan at 1PM<br>Commit message: Added sales figures<br> Commit ID: 123abc12) ---> B
- B(Author: Sam<br>Date: 4 Jan at 10AM<br>Commit message: Removed old info<br> Commit ID: aabb1122) ---> C
- C(Author: Zhang<br>Date: 5 Jan at 3PM<br>Commit message: Added invoices<br> Commit ID: ddee4455)
- end
-```
-
-When you work in a Git repository, you work in *branches*. By default, the contents
-of a repository are in a default branch. To make changes, you:
-
-1. Create your own branch, which is a snapshot of the default branch at the time
- you create it.
-1. Make changes and push them to your branch. Each push creates a commit.
-1. When you're ready, *merge* your branch into the default branch.
-
-```mermaid
-flowchart LR
- subgraph Default branch
- A[Commit] --> B[Commit] --> C[Commit] --> D[Commit]
- end
- subgraph My branch
- B --1. Create my branch--> E(Commit)
- E --2. Add my commit--> F(Commit)
- F --3. Merge my branch to default--> D
- end
-```
-
-If this all feels a bit overwhelming, hang in there. You're about to see these concepts in action.
-
-## Steps
-
-Here's an overview of what we're going to do:
-
-1. [Create a sample project](#create-a-sample-project).
-1. [Clone the repository](#clone-the-repository).
-1. [Create a branch and make your changes](#create-a-branch-and-make-changes).
-1. [Commit and push your changes](#commit-and-push-your-changes).
-1. [Merge your changes](#merge-your-changes).
-1. [View your changes in GitLab](#view-your-changes-in-gitlab).
-
-### Create a sample project
-
-To start, create a sample project in GitLab.
-
-1. In GitLab, on the top bar, select **Main menu > Projects > View all projects**.
-1. On the right of the page, select **New project**.
-1. For **Project name**, enter `My sample project`. The project slug is generated for you.
- This slug is the URL you can use to access the project after it's created.
-1. Ensure **Initialize repository with a README** is selected.
- How you complete the other fields is up to you.
-1. Select **Create project**.
-
-### Clone the repository
-
-Now you can clone the repository in your project. *Cloning* a repository means you're creating
-a copy on your computer, or wherever you want to store and work with the files.
-
-1. On your project page, select **Clone**. Copy the URL for **Clone with SSH**.
-
- ![Clone a project with SSH](img/clone_project_v14_9.png)
-
-1. Open a terminal on your computer and go to the directory
- where you want to clone the files.
-
-1. Enter `git clone` and paste the URL:
-
- ```shell
- git clone git@gitlab.com:gitlab-example/my-sample-project.git
- ```
-
-1. Go to the directory:
-
- ```shell
- cd my-sample-project
- ```
-
-1. By default, you've cloned the default branch for the repository. Usually this
- branch is `main`. To be sure, get the name of the default branch:
-
- ```shell
- git branch
- ```
-
- The branch you're on is marked with an asterisk.
- Press `Q` on your keyboard to return to the main terminal
- window.
-
-### Create a branch and make changes
-
-Now that you have a copy of the repository, create your own branch so you can
-work on your changes independently.
-
-1. Create a new branch called `example-tutorial-branch`.
-
- ```shell
- git checkout -b example-tutorial-branch
- ```
-
-1. In a text editor like Visual Studio Code, Sublime, `vi`, or any other editor,
- open the README.md file and add this text:
-
- ```plaintext
- Hello world! I'm using Git!
- ```
-
-1. Save the file.
-
-1. Git keeps track of changed files. To confirm which files have changed, get
- the status.
-
- ```shell
- git status
- ```
-
- You should get output similar to the following:
-
- ```shell
- On branch example-tutorial-branch
- Changes not staged for commit:
- (use "git add <file>..." to update what will be committed)
- (use "git restore <file>..." to discard changes in working directory)
- modified: README.md
-
- no changes added to commit (use "git add" and/or "git commit -a")
- ```
-
-### Commit and push your changes
-
-You've made changes to a file in your repository. Now it's time to record
-those changes by making your first commit.
-
-1. Add the `README.md` file to the *staging* area. The staging area is where you
- put files before you commit them.
-
- ```shell
- git add README.md
- ```
-
-1. Confirm the file is staged:
-
- ```shell
- git status
- ```
-
- You should get output similar to the following, and the filename should be in
- green text.
-
- ```shell
- On branch example-tutorial-branch
- Changes to be committed:
- (use "git restore --staged <file>..." to unstage)
- modified: README.md
- ```
-
-1. Now commit the staged file, and include a message
- that describes the change you made. Make sure you surround the message in double
- quotes (").
-
- ```shell
- git commit -m "I added text to the README file"
- ```
-
-1. The change has been committed to your branch, but your branch and its commits
- are still only available on your computer. No one else has access to them yet.
- Push your branch to GitLab:
-
- ```shell
- git push origin example-tutorial-branch
- ```
-
-Your branch is now available on GitLab and visible to other users in your project.
-
-![Branches dropdown list](img/branches_dropdown_v14_10.png)
-
-### Merge your changes
-
-Now you're ready to merge the changes from your `example-tutorial-branch` branch
-to the default branch (`main`).
-
-1. Check out the default branch for your repository.
-
- ```shell
- git checkout main
- ```
-
-1. Merge your branch into the default branch.
-
- ```shell
- git merge example-tutorial-branch
- ```
-
-1. Push the changes.
-
- ```shell
- git push
- ```
-
-NOTE:
-For this tutorial, you merge your branch directly to the default branch for your
-repository. In GitLab, you typically use a [merge request](../user/project/merge_requests/index.md)
-to merge your branch.
-
-### View your changes in GitLab
-
-You did it! You updated the `README.md` file in your branch, and you merged those changes
-into the `main` branch.
-
-Let's look in the UI and confirm your changes. Go to your project.
-
-- Scroll down and view the contents of the `README.md` file.
- Your changes should be visible.
-- Above the `README.md` file, view the text in the **Last commit** column.
- Your commit message is displayed in this column:
-
- ![Commit message](img/commit_message_v14_10.png)
-
-Now you can return to the command line and change back to your personal branch
-(`git checkout example-tutorial-branch`). You can continue updating files or
-creating new ones. Type `git status` to view the status
-of your changes and commit with abandon.
-
-Don't worry if you mess things up. Everything in Git can be reverted, and if you
-find you can't recover, you can always create a new branch and start again.
-
-Nice work.
-
-## Find more Git learning resources
-
-- Get a complete introduction to Git in the <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Git for GitLab](https://www.youtube.com/watch?v=4lxvVj7wlZw) beginner's course (1h 33m).
-- Find other tutorials about Git and GitLab on the [tutorials page](index.md).
+<!-- This redirect file can be deleted after 2023-07-21. -->
+<!-- Redirects that point to other docs in the same project expire in three months. -->
+<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> \ No newline at end of file
diff --git a/doc/tutorials/move_personal_project_to_a_group.md b/doc/tutorials/move_personal_project_to_a_group.md
index 22d7005847b..361181fdde6 100644
--- a/doc/tutorials/move_personal_project_to_a_group.md
+++ b/doc/tutorials/move_personal_project_to_a_group.md
@@ -1,89 +1,11 @@
---
-stage: Data Stores
-group: Tenant Scale
-info: For assistance with this tutorial, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments-to-other-projects-and-subjects.
+redirect_to: 'move_personal_project_to_group/index.md'
+remove_date: '2023-07-21'
---
-# Tutorial: Move your personal project to a group **(FREE SAAS)**
+This document was moved to [another location](move_personal_project_to_group/index.md).
-If you created a project under a [personal namespace](../user/namespace/index.md),
-you can perform common tasks, like managing issue and merge requests,
-and using source control and CI/CD.
-
-However, at some point you might outgrow your personal project and
-want to move your project to a group namespace instead. With a group namespace, you can:
-
-- Give a group of users access to your project, rather than adding users one-by-one.
-- View all issues and merge requests for all projects in the group.
-- View all unique users in the group namespace, across all projects.
-- Manage usage quotas.
-- Start a trial or upgrade to a paid subscription tier. This option is important if you're
- impacted by the [changes to user limits](https://about.gitlab.com/blog/2022/03/24/efficient-free-tier/),
- and need more users.
-
-This tutorial shows you how to move your project from a personal namespace
-to a group namespace.
-
-## Steps
-
-Here's an overview of the steps:
-
-1. [Create a group](#create-a-group).
-1. [Move your project to a group](#move-your-project-to-a-group).
-1. [Work with your group](#work-with-your-group).
-
-### Create a group
-
-To begin, make sure you have a suitable group to move your project to.
-The group must allow the creation of projects, and you must have at least the
-Maintainer role for the group.
-
-If you don't have a group, create one:
-
-1. On the top bar, select **Main menu > Groups > View all groups**.
-1. On the right of the page, select **New group**.
-1. In **Group name**, enter a name for the group.
-1. In **Group URL**, enter a path for the group, which is used as the namespace.
-1. Choose the [visibility level](../user/public_access.md).
-1. Optional. Fill in information to personalize your experience.
-1. Select **Create group**.
-
-### Move your project to a group
-
-Before you move your project to a group:
-
-- You must have the Owner role for the project.
-- Remove any [container images](../user/packages/container_registry/index.md#move-or-rename-container-registry-repositories)
-- Remove any npm packages. If you transfer a project to a different root namespace, the project must not contain any npm packages. When you update the path of a user or group, or transfer a subgroup or project, you must remove any npm packages first. You cannot update the root namespace of a project with npm packages. Make sure you update your .npmrc files to follow the naming convention and run npm publish if necessary.
-
-Now you're ready to move your project:
-
-1. On the top bar, select **Main menu > Projects** and find your project.
-1. On the left sidebar, select **Settings > General**.
-1. Expand **Advanced**.
-1. Under **Transfer project**, choose the group to transfer the project to.
-1. Select **Transfer project**.
-1. Enter the project's name and select **Confirm**.
-
-You are redirected to the project's new page.
-If you have more than one personal project, you can repeat these steps for each
-project.
-
-NOTE:
-For more information about these migration steps,
-see [Transferring your project into another namespace](../user/project/settings/index.md#transfer-a-project-to-another-namespace).
-A migration might result in follow-up work to update the project path in
-your related resources and tools, such as websites and package managers.
-
-### Work with your group
-
-You can now view your project in your group:
-
-1. On the top bar, select **Main menu > Groups** and find your group.
-1. Look for your project under **Subgroups and projects**.
-
-Start enjoying the benefits of a group! For example, as the group Owner, you can
-quickly view all unique users in your namespace:
-
-1. In your group, select **Settings > Usage Quotas**.
-1. The **Seats** tab displays all users across all projects in your group.
+<!-- This redirect file can be deleted after 2023-07-21. -->
+<!-- Redirects that point to other docs in the same project expire in three months. -->
+<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> \ No newline at end of file
diff --git a/doc/tutorials/move_personal_project_to_group/index.md b/doc/tutorials/move_personal_project_to_group/index.md
new file mode 100644
index 00000000000..d3e695b78df
--- /dev/null
+++ b/doc/tutorials/move_personal_project_to_group/index.md
@@ -0,0 +1,89 @@
+---
+stage: Data Stores
+group: Tenant Scale
+info: For assistance with this tutorial, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments-to-other-projects-and-subjects.
+---
+
+# Tutorial: Move your personal project to a group **(FREE SAAS)**
+
+If you created a project under a [personal namespace](../../user/namespace/index.md),
+you can perform common tasks, like managing issue and merge requests,
+and using source control and CI/CD.
+
+However, at some point you might outgrow your personal project and
+want to move your project to a group namespace instead. With a group namespace, you can:
+
+- Give a group of users access to your project, rather than adding users one-by-one.
+- View all issues and merge requests for all projects in the group.
+- View all unique users in the group namespace, across all projects.
+- Manage usage quotas.
+- Start a trial or upgrade to a paid subscription tier. This option is important if you're
+ impacted by the [changes to user limits](https://about.gitlab.com/blog/2022/03/24/efficient-free-tier/),
+ and need more users.
+
+This tutorial shows you how to move your project from a personal namespace
+to a group namespace.
+
+## Steps
+
+Here's an overview of the steps:
+
+1. [Create a group](#create-a-group).
+1. [Move your project to a group](#move-your-project-to-a-group).
+1. [Work with your group](#work-with-your-group).
+
+### Create a group
+
+To begin, make sure you have a suitable group to move your project to.
+The group must allow the creation of projects, and you must have at least the
+Maintainer role for the group.
+
+If you don't have a group, create one:
+
+1. On the top bar, select **Main menu > Groups > View all groups**.
+1. On the right of the page, select **New group**.
+1. In **Group name**, enter a name for the group.
+1. In **Group URL**, enter a path for the group, which is used as the namespace.
+1. Choose the [visibility level](../../user/public_access.md).
+1. Optional. Fill in information to personalize your experience.
+1. Select **Create group**.
+
+### Move your project to a group
+
+Before you move your project to a group:
+
+- You must have the Owner role for the project.
+- Remove any [container images](../../user/packages/container_registry/index.md#move-or-rename-container-registry-repositories)
+- Remove any npm packages. If you transfer a project to a different root namespace, the project must not contain any npm packages. When you update the path of a user or group, or transfer a subgroup or project, you must remove any npm packages first. You cannot update the root namespace of a project with npm packages. Make sure you update your .npmrc files to follow the naming convention and run npm publish if necessary.
+
+Now you're ready to move your project:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Settings > General**.
+1. Expand **Advanced**.
+1. Under **Transfer project**, choose the group to transfer the project to.
+1. Select **Transfer project**.
+1. Enter the project's name and select **Confirm**.
+
+You are redirected to the project's new page.
+If you have more than one personal project, you can repeat these steps for each
+project.
+
+NOTE:
+For more information about these migration steps,
+see [Transferring your project into another namespace](../../user/project/settings/index.md#transfer-a-project-to-another-namespace).
+A migration might result in follow-up work to update the project path in
+your related resources and tools, such as websites and package managers.
+
+### Work with your group
+
+You can now view your project in your group:
+
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. Look for your project under **Subgroups and projects**.
+
+Start enjoying the benefits of a group! For example, as the group Owner, you can
+quickly view all unique users in your namespace:
+
+1. In your group, select **Settings > Usage Quotas**.
+1. The **Seats** tab displays all users across all projects in your group.
diff --git a/doc/tutorials/plan_and_track.md b/doc/tutorials/plan_and_track.md
index de510fff2de..35b552cdaa5 100644
--- a/doc/tutorials/plan_and_track.md
+++ b/doc/tutorials/plan_and_track.md
@@ -14,5 +14,5 @@ issues, epics, and more.
| [GitLab Agile Project Management](https://levelup.gitlab.com/courses/gitlab-agile-project-management) | Learn how to use planning features to manage your projects in this self-paced course. | **{star}** |
| [Create a project from a template](https://gitlab.com/projects/new#create_from_template) | Choose a project template and create a project with files to get you started. | |
| [Migrate to GitLab](../user/project/import/index.md) | If you are coming to GitLab from another platform, you can import or convert your projects. | |
-| [Run an agile iteration](agile_sprint.md) | Use group, projects, and iterations to run an agile development iteration. |
+| [Run an agile iteration](agile_sprint/index.md) | Use group, projects, and iterations to run an agile development iteration. |
| <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Epics and Issue Boards](https://www.youtube.com/watch?v=I1bFIAQBHB8) | Find out how to use epics and issue boards for project management. | |
diff --git a/doc/tutorials/scan_result_policy.md b/doc/tutorials/scan_result_policy.md
index e617f06d321..9aacd8eff7b 100644
--- a/doc/tutorials/scan_result_policy.md
+++ b/doc/tutorials/scan_result_policy.md
@@ -1,125 +1,11 @@
---
-stage: Govern
-group: Security Policies
-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
+redirect_to: 'scan_result_policy/index.md'
+remove_date: '2023-07-21'
---
-# Tutorial: Set up a scan result policy **(ULTIMATE)**
+This document was moved to [another location](scan_result_policy/index.md).
-This tutorial shows you how to create and configure a [scan result policy](../user/application_security/policies/scan-result-policies.md). These policies can be set to take action based on scan results.
-For example, in this tutorial, you'll set up a policy that requires approval from two specified users if a vulnerability is detected in a merge request.
-
-Prerequisites:
-
-The namespace used for this tutorial must:
-
-- Contain a minimum of three users, including your own. If you don't have two other users, you must first
- create them. For details, see [Creating users](../user/profile/account/create_accounts.md).
-
-To set up a scan result policy:
-
-1. [Create a test project](#create-a-test-project).
-1. [Add a scan result policy](#add-a-scan-result-policy).
-1. [Test the scan result policy](#test-the-scan-result-policy).
-
-## Create a test project
-
-1. On the top bar, select **Main menu > Projects**.
-1. Select **New project**.
-1. Select **Create blank project**.
-1. Complete the fields.
- - **Project name**: `sast-scan-result-policy`.
- - Select the **Enable Static Application Security Testing (SAST)** checkbox.
-1. Select **Create project**.
-
-## Add a scan result policy
-
-Next, you'll add a scan result policy to your test project:
-
-1. On the top bar, select **Main menu > Projects** and find the `sast-scan-result-policy` project.
-1. On the left sidebar, go to **Security and Compliance > Policies**.
-1. Select **New policy**.
-1. In **Scan result policy**, select **Select policy**.
-1. Complete the fields.
- - **Name**: `sast-scan-result-policy`
- - **Policy status**: **Enabled**
-1. Add the following rule:
-
- ```plaintext
- IF |Security Scan| from |SAST| find(s) more than |0| |All severity levels| |All vulnerability states| vulnerabilities in an open merge request targeting |All protected branches|
- ```
-
-1. Set **Actions** to the following:
-
- ```plaintext
- THEN Require approval from | 2 | of the following approvers:
- ```
-
-1. Select two users.
-1. Select **Configure with a merge request**.
-
- The application creates a new project to store the policies linked to it, and creates a merge request to define the policy.
-
-1. Select **Merge**.
-1. On the top bar, select **Main menu > Projects** and select the `sast-scan-result-policy` project.
-1. On the left sidebar, select **Security and Compliance > Policies**.
-
- You can see the list of policies added in the previous steps.
-
-## Test the scan result policy
-
-Nice work, you've created a scan result policy. To test it, create some vulnerabilities and check the result:
-
-1. On the top bar, select **Main menu > Projects** and select the `sast-scan-result-policy` project.
-1. On the left sidebar, select **Repository > Files**.
-1. From the **Add** (**{plus}**) dropdown list, select **New file**.
-1. In the **Filename** field enter `main.ts`.
-1. In the file's content, copy the following:
-
- ```typescript
- // Non-literal require - tsr-detect-non-literal-require
- var lib: String = 'fs'
- require(lib)
-
- // Eval with variable - tsr-detect-eval-with-expression
- var myeval: String = 'console.log("Hello.");';
- eval(myeval);
-
- // Unsafe Regexp - tsr-detect-unsafe-regexp
- const regex: RegExp = /(x+x+)+y/;
-
- // Non-literal Regexp - tsr-detect-non-literal-regexp
- var myregexpText: String = "/(x+x+)+y/";
- var myregexp: RegExp = new RegExp(myregexpText);
- myregexp.test("(x+x+)+y");
-
- // Markup escaping disabled - tsr-detect-disable-mustache-escape
- var template: Object = new Object;
- template.escapeMarkup = false;
-
- // Detects HTML injections - tsr-detect-html-injection
- var element: Element = document.getElementById("mydiv");
- var content: String = "mycontent"
- Element.innerHTML = content;
-
- // Timing attack - tsr-detect-possible-timing-attacks
- var userInput: String = "Jane";
- var auth: String = "Jane";
- if (userInput == auth) {
- console.log(userInput);
- }
- ```
-
-1. In the **Commit message** field, enter `Add vulnerable file`.
-1. In the **Target Branch** field, enter `test-branch`.
-1. Select **Commit changes**. The **New merge request** form opens.
-1. Select **Create merge request**.
-1. In the new merge request, select `Create merge request`.
-
- Wait for the pipeline to complete. This could be a few minutes.
-
-The merge request security widget confirms that security scanning detected one potential
-vulnerability. As defined in the scan result policy, the merge request is blocked and waiting for
-approval.
-
-You now know how to set up and use scan result policies to catch vulnerabilities!
+<!-- This redirect file can be deleted after 2023-07-21. -->
+<!-- Redirects that point to other docs in the same project expire in three months. -->
+<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> \ No newline at end of file
diff --git a/doc/tutorials/scan_result_policy/index.md b/doc/tutorials/scan_result_policy/index.md
new file mode 100644
index 00000000000..6f4feb9ec4f
--- /dev/null
+++ b/doc/tutorials/scan_result_policy/index.md
@@ -0,0 +1,125 @@
+---
+stage: Govern
+group: Security Policies
+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
+---
+
+# Tutorial: Set up a scan result policy **(ULTIMATE)**
+
+This tutorial shows you how to create and configure a [scan result policy](../../user/application_security/policies/scan-result-policies.md). These policies can be set to take action based on scan results.
+For example, in this tutorial, you'll set up a policy that requires approval from two specified users if a vulnerability is detected in a merge request.
+
+Prerequisites:
+
+The namespace used for this tutorial must:
+
+- Contain a minimum of three users, including your own. If you don't have two other users, you must first
+ create them. For details, see [Creating users](../../user/profile/account/create_accounts.md).
+
+To set up a scan result policy:
+
+1. [Create a test project](#create-a-test-project).
+1. [Add a scan result policy](#add-a-scan-result-policy).
+1. [Test the scan result policy](#test-the-scan-result-policy).
+
+## Create a test project
+
+1. On the top bar, select **Main menu > Projects**.
+1. Select **New project**.
+1. Select **Create blank project**.
+1. Complete the fields.
+ - **Project name**: `sast-scan-result-policy`.
+ - Select the **Enable Static Application Security Testing (SAST)** checkbox.
+1. Select **Create project**.
+
+## Add a scan result policy
+
+Next, you'll add a scan result policy to your test project:
+
+1. On the top bar, select **Main menu > Projects** and find the `sast-scan-result-policy` project.
+1. On the left sidebar, go to **Security and Compliance > Policies**.
+1. Select **New policy**.
+1. In **Scan result policy**, select **Select policy**.
+1. Complete the fields.
+ - **Name**: `sast-scan-result-policy`
+ - **Policy status**: **Enabled**
+1. Add the following rule:
+
+ ```plaintext
+ IF |Security Scan| from |SAST| find(s) more than |0| |All severity levels| |All vulnerability states| vulnerabilities in an open merge request targeting |All protected branches|
+ ```
+
+1. Set **Actions** to the following:
+
+ ```plaintext
+ THEN Require approval from | 2 | of the following approvers:
+ ```
+
+1. Select two users.
+1. Select **Configure with a merge request**.
+
+ The application creates a new project to store the policies linked to it, and creates a merge request to define the policy.
+
+1. Select **Merge**.
+1. On the top bar, select **Main menu > Projects** and select the `sast-scan-result-policy` project.
+1. On the left sidebar, select **Security and Compliance > Policies**.
+
+ You can see the list of policies added in the previous steps.
+
+## Test the scan result policy
+
+Nice work, you've created a scan result policy. To test it, create some vulnerabilities and check the result:
+
+1. On the top bar, select **Main menu > Projects** and select the `sast-scan-result-policy` project.
+1. On the left sidebar, select **Repository > Files**.
+1. From the **Add** (**{plus}**) dropdown list, select **New file**.
+1. In the **Filename** field enter `main.ts`.
+1. In the file's content, copy the following:
+
+ ```typescript
+ // Non-literal require - tsr-detect-non-literal-require
+ var lib: String = 'fs'
+ require(lib)
+
+ // Eval with variable - tsr-detect-eval-with-expression
+ var myeval: String = 'console.log("Hello.");';
+ eval(myeval);
+
+ // Unsafe Regexp - tsr-detect-unsafe-regexp
+ const regex: RegExp = /(x+x+)+y/;
+
+ // Non-literal Regexp - tsr-detect-non-literal-regexp
+ var myregexpText: String = "/(x+x+)+y/";
+ var myregexp: RegExp = new RegExp(myregexpText);
+ myregexp.test("(x+x+)+y");
+
+ // Markup escaping disabled - tsr-detect-disable-mustache-escape
+ var template: Object = new Object;
+ template.escapeMarkup = false;
+
+ // Detects HTML injections - tsr-detect-html-injection
+ var element: Element = document.getElementById("mydiv");
+ var content: String = "mycontent"
+ Element.innerHTML = content;
+
+ // Timing attack - tsr-detect-possible-timing-attacks
+ var userInput: String = "Jane";
+ var auth: String = "Jane";
+ if (userInput == auth) {
+ console.log(userInput);
+ }
+ ```
+
+1. In the **Commit message** field, enter `Add vulnerable file`.
+1. In the **Target Branch** field, enter `test-branch`.
+1. Select **Commit changes**. The **New merge request** form opens.
+1. Select **Create merge request**.
+1. In the new merge request, select `Create merge request`.
+
+ Wait for the pipeline to complete. This could be a few minutes.
+
+The merge request security widget confirms that security scanning detected one potential
+vulnerability. As defined in the scan result policy, the merge request is blocked and waiting for
+approval.
+
+You now know how to set up and use scan result policies to catch vulnerabilities!
diff --git a/doc/tutorials/secure_application.md b/doc/tutorials/secure_application.md
index 19fcb64085a..5b7d8733d63 100644
--- a/doc/tutorials/secure_application.md
+++ b/doc/tutorials/secure_application.md
@@ -11,7 +11,7 @@ GitLab can check your application for security vulnerabilities and that it meets
| Topic | Description | Good for beginners |
|-------|-------------|--------------------|
| [Set up dependency scanning](https://about.gitlab.com/blog/2021/01/14/try-dependency-scanning/) | Try out dependency scanning, which checks for known vulnerabilities in dependencies. | **{star}** |
-| [Create a compliance pipeline](create_compliance_pipeline.md) | Learn how to create compliance pipelines for your groups. | **{star}** |
-| [Set up a scan result policy](scan_result_policy.md) | Learn how to configure a scan result policy that takes action based on scan results. | **{star}** |
+| [Create a compliance pipeline](compliance_pipeline/index.md) | Learn how to create compliance pipelines for your groups. | **{star}** |
+| [Set up a scan result policy](scan_result_policy/index.md) | Learn how to configure a scan result policy that takes action based on scan results. | **{star}** |
| [Get started with GitLab application security](../user/application_security/get-started-security.md) | Follow recommended steps to set up security tools. | |
| [GitLab Security Essentials](https://levelup.gitlab.com/courses/security-essentials) | Learn about the essential security capabilities of GitLab in this self-paced course. | |
diff --git a/doc/user/group/compliance_frameworks.md b/doc/user/group/compliance_frameworks.md
index 69642833d8a..2fca8b7b678 100644
--- a/doc/user/group/compliance_frameworks.md
+++ b/doc/user/group/compliance_frameworks.md
@@ -108,7 +108,7 @@ For more information, see:
- [Example configuration](#example-configuration) for help configuring a compliance pipeline that runs jobs from
labeled project pipeline configuration.
-- The [Create a compliance pipeline](../../tutorials/create_compliance_pipeline.md) tutorial.
+- The [Create a compliance pipeline](../../tutorials/compliance_pipeline/index.md) tutorial.
### Effect on labeled projects
diff --git a/doc/user/project/merge_requests/reviews/index.md b/doc/user/project/merge_requests/reviews/index.md
index 22e95c7e639..6718422865a 100644
--- a/doc/user/project/merge_requests/reviews/index.md
+++ b/doc/user/project/merge_requests/reviews/index.md
@@ -424,3 +424,4 @@ than 1000. The cached value is rounded to thousands (or millions) and updated ev
## Related topics
- [Merge methods](../methods/index.md)
+- [Draft Notes API](../../../../api/draft_notes.md)
diff --git a/doc/user/project/repository/branches/index.md b/doc/user/project/repository/branches/index.md
index bb02e5876c9..a128155b65c 100644
--- a/doc/user/project/repository/branches/index.md
+++ b/doc/user/project/repository/branches/index.md
@@ -200,6 +200,15 @@ issue number. GitLab uses the issue number to import data into the merge request
> - Repository filter search box [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52967) in GitLab 13.10.
> - Revision swapping [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60491) in GitLab 13.12.
+The default compare mode uses the `git diff from...to` method, instead of the
+`git diff from to` method, to compare branches. The `git diff from...to` method
+provides a more human-readable diff, because it does not include unrelated changes
+made to the target branch after the source branch was created.
+
+NOTE:
+The `git diff from...to` method shows all changes from a cherry-picked commit as
+new changes. It uses the merge base, not the actual commit content, to compare branches.
+
To compare branches in a repository:
1. On the top bar, select **Main menu > Projects** and find your project.
diff --git a/lib/gitlab/ci/reports/codequality_mr_diff.rb b/lib/gitlab/ci/reports/codequality_mr_diff.rb
index 0595b6f966a..bfa7b05e4a9 100644
--- a/lib/gitlab/ci/reports/codequality_mr_diff.rb
+++ b/lib/gitlab/ci/reports/codequality_mr_diff.rb
@@ -30,9 +30,20 @@ module Gitlab
codequality_files[degradation.dig(:location, :path)] << {
line: degradation.dig(:location, :lines, :begin) || degradation.dig(:location, :positions, :begin, :line),
description: degradation[:description],
- severity: degradation[:severity]
+ severity: degradation[:severity],
+ engine_name: degradation[:engine_name],
+ categories: degradation[:categories],
+ content: convert_body(degradation[:content]),
+ location: degradation[:location],
+ other_locations: degradation[:other_locations],
+ type: degradation[:type]
}
end
+
+ def convert_body(content)
+ content["body"] = ::MarkupHelper.markdown(content["body"])
+ content
+ end
end
end
end
diff --git a/lib/gitlab/metrics/subscribers/action_cable.rb b/lib/gitlab/metrics/subscribers/action_cable.rb
index 50d843cc72f..b2b976e0746 100644
--- a/lib/gitlab/metrics/subscribers/action_cable.rb
+++ b/lib/gitlab/metrics/subscribers/action_cable.rb
@@ -6,9 +6,10 @@ module Gitlab
class ActionCable < ActiveSupport::Subscriber
include Gitlab::Utils::StrongMemoize
- BROADCASTING_GRAPHQL_EVENT = 'graphql-event'
- BROADCASTING_GRAPHQL_SUBSCRIPTION = 'graphql-subscription'
- BROADCASTING_OTHER = 'other'
+ SOURCE_DIRECT = 'channel'
+ SOURCE_GRAPHQL_EVENT = 'graphql-event'
+ SOURCE_GRAPHQL_SUBSCRIPTION = 'graphql-subscription'
+ SOURCE_OTHER = 'unknown'
attach_to :action_cable
@@ -27,42 +28,57 @@ module Gitlab
end
def transmit(event)
- transmit_counter.increment
+ payload = event.payload
- if event.payload.present?
- channel = event.payload[:channel_class]
- operation = operation_name_from(event.payload)
- data_size = Gitlab::Json.generate(event.payload[:data]).bytesize
+ labels = {
+ channel: payload[:channel_class],
+ caller: normalize_source(payload[:via])
+ }
+ labels[:broadcasting] = graphql_event_broadcasting_from(payload[:data])
- transmitted_bytes_histogram.observe({ channel: channel, operation: operation }, data_size)
- end
+ transmit_counter.increment(labels)
+ data_size = Gitlab::Json.generate(payload[:data]).bytesize
+ transmitted_bytes_histogram.observe(labels, data_size)
end
def broadcast(event)
- broadcast_counter.increment({ broadcasting: broadcasting_from(event.payload) })
+ broadcast_counter.increment({ broadcasting: normalize_source(event.payload[:broadcasting]) })
end
private
- # Since broadcastings can have high dimensionality when they carry IDs, we need to
+ # Since transmission sources can have high dimensionality when they carry IDs, we need to
# collapse them. If it's not a well-know broadcast, we report it as "other".
- def broadcasting_from(payload)
- broadcasting = payload[:broadcasting]
- if broadcasting.start_with?(BROADCASTING_GRAPHQL_EVENT)
+ def normalize_source(source)
+ return SOURCE_DIRECT if source.blank?
+
+ normalized_source = source.gsub('streamed from ', '')
+
+ if normalized_source.start_with?(SOURCE_GRAPHQL_EVENT)
# Take at most two levels of topic namespacing.
- broadcasting.split(':').reject(&:empty?).take(2).join(':') # rubocop: disable CodeReuse/ActiveRecord
- elsif broadcasting.start_with?(BROADCASTING_GRAPHQL_SUBSCRIPTION)
- BROADCASTING_GRAPHQL_SUBSCRIPTION
+ normalized_source.split(':').reject(&:empty?).take(2).join(':') # rubocop: disable CodeReuse/ActiveRecord
+ elsif normalized_source.start_with?(SOURCE_GRAPHQL_SUBSCRIPTION)
+ SOURCE_GRAPHQL_SUBSCRIPTION
else
- BROADCASTING_OTHER
+ SOURCE_OTHER
end
end
- # When possible tries to query operation name
- def operation_name_from(payload)
- data = payload.dig(:data, 'result', 'data') || {}
-
- data.each_key.first
+ # When possible tries to query operation name. This will only return data
+ # for GraphQL subscription broadcasts.
+ def graphql_event_broadcasting_from(payload_data)
+ # Depending on whether the query result was passed in-process from a direct
+ # execution (e.g. in response to a subcription request) or cross-process by
+ # going through PubSub, we might encounter either string or symbol keys.
+ # We do not use deep_transform_keys here because the payload can be large
+ # and performance would be affected.
+ query_result = payload_data[:result] || payload_data['result'] || {}
+ query_result_data = query_result['data'] || {}
+ gql_operation = query_result_data.each_key.first
+
+ return unless gql_operation
+
+ "#{SOURCE_GRAPHQL_EVENT}:#{gql_operation}"
end
def transmit_counter
diff --git a/lib/sidebars/projects/menus/monitor_menu.rb b/lib/sidebars/projects/menus/monitor_menu.rb
index caaa4e21de2..f1fc9f70ef8 100644
--- a/lib/sidebars/projects/menus/monitor_menu.rb
+++ b/lib/sidebars/projects/menus/monitor_menu.rb
@@ -50,6 +50,8 @@ module Sidebars
end
def metrics_dashboard_menu_item
+ return ::Sidebars::NilMenuItem.new(item_id: :metrics) if Feature.enabled?(:remove_monitor_metrics)
+
unless can?(context.current_user, :metrics_dashboard, context.project)
return ::Sidebars::NilMenuItem.new(item_id: :metrics)
end
diff --git a/qa/qa/page/project/sub_menus/super_sidebar/monitor.rb b/qa/qa/page/project/sub_menus/super_sidebar/monitor.rb
index 745dda06bb5..65ed9dbb4f8 100644
--- a/qa/qa/page/project/sub_menus/super_sidebar/monitor.rb
+++ b/qa/qa/page/project/sub_menus/super_sidebar/monitor.rb
@@ -8,10 +8,6 @@ module QA
module Monitor
extend QA::Page::PageConcern
- def go_to_monitor_metrics
- open_monitor_submenu('Metrics')
- end
-
def go_to_monitor_error_tracking
open_monitor_submenu('Error tracking')
end
diff --git a/scripts/rspec_helpers.sh b/scripts/rspec_helpers.sh
index c6ec773cf78..729892ec84e 100644
--- a/scripts/rspec_helpers.sh
+++ b/scripts/rspec_helpers.sh
@@ -137,6 +137,9 @@ function debug_rspec_variables() {
echoinfo "SKIPPED_FLAKY_TESTS_REPORT_PATH: ${SKIPPED_FLAKY_TESTS_REPORT_PATH}"
echoinfo "CRYSTALBALL: ${CRYSTALBALL:-}"
+
+ echoinfo "RSPEC_TESTS_MAPPING_ENABLED: ${RSPEC_TESTS_MAPPING_ENABLED:-}"
+ echoinfo "RSPEC_TESTS_FILTER_FILE: ${RSPEC_TESTS_FILTER_FILE:-}"
}
function handle_retry_rspec_in_new_process() {
@@ -194,12 +197,7 @@ function rspec_paralellized_job() {
cp "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" "${KNAPSACK_REPORT_PATH}"
- export KNAPSACK_TEST_FILE_PATTERN="spec/{,**/}*_spec.rb"
-
- if [[ "${test_level}" != "foss-impact" ]]; then
- export KNAPSACK_TEST_FILE_PATTERN=$(ruby -r./tooling/quality/test_level.rb -e "puts Quality::TestLevel.new(${spec_folder_prefixes}).pattern(:${test_level})")
- fi
-
+ export KNAPSACK_TEST_FILE_PATTERN=$(ruby -r./tooling/quality/test_level.rb -e "puts Quality::TestLevel.new(${spec_folder_prefixes}).pattern(:${test_level})")
export FLAKY_RSPEC_REPORT_PATH="${rspec_flaky_folder_path}all_${report_name}_report.json"
export NEW_FLAKY_RSPEC_REPORT_PATH="${rspec_flaky_folder_path}new_${report_name}_report.json"
export SKIPPED_FLAKY_TESTS_REPORT_PATH="${rspec_flaky_folder_path}skipped_flaky_tests_${report_name}_report.txt"
diff --git a/scripts/utils.sh b/scripts/utils.sh
index 80057842c28..b41bc18deff 100644
--- a/scripts/utils.sh
+++ b/scripts/utils.sh
@@ -63,6 +63,19 @@ function test_url() {
fi
}
+function section_start () {
+ local section_title="${1}"
+ local section_description="${2:-$section_title}"
+
+ echo -e "section_start:`date +%s`:${section_title}[collapsed=true]\r\e[0K${section_description}"
+}
+
+function section_end () {
+ local section_title="${1}"
+
+ echo -e "section_end:`date +%s`:${section_title}\r\e[0K"
+}
+
function bundle_install_script() {
local extra_install_args="${1}"
@@ -72,7 +85,7 @@ function bundle_install_script() {
exit 1;
fi;
- echo -e "section_start:`date +%s`:bundle-install[collapsed=true]\r\e[0KInstalling gems"
+ section_start "bundle-install" "Installing gems"
gem --version
bundle --version
@@ -93,23 +106,23 @@ function bundle_install_script() {
run_timed_command "bundle pristine pg"
fi
- echo -e "section_end:`date +%s`:bundle-install\r\e[0K"
+ section_end "bundle-install"
}
function yarn_install_script() {
- echo -e "section_start:`date +%s`:yarn-install[collapsed=true]\r\e[0KInstalling Yarn packages"
+ section_start "yarn-install" "Installing Yarn packages"
retry yarn install --frozen-lockfile
- echo -e "section_end:`date +%s`:yarn-install\r\e[0K"
+ section_end "yarn-install"
}
function assets_compile_script() {
- echo -e "section_start:`date +%s`:assets-compile[collapsed=true]\r\e[0KCompiling frontend assets"
+ section_start "assets-compile" "Compiling frontend assets"
bin/rake gitlab:assets:compile
- echo -e "section_end:`date +%s`:assets-compile\r\e[0K"
+ section_end "assets-compile"
}
function setup_db_user_only() {
@@ -121,9 +134,13 @@ function setup_db_praefect() {
}
function setup_db() {
- run_timed_command "setup_db_user_only"
+ section_start "setup-db" "Setting up DBs"
+
+ setup_db_user_only
run_timed_command_with_metric "bundle exec rake db:drop db:create db:schema:load db:migrate gitlab:db:lock_writes" "setup_db"
- run_timed_command "setup_db_praefect"
+ setup_db_praefect
+
+ section_end "setup-db"
}
function install_gitlab_gem() {
@@ -136,7 +153,7 @@ function install_tff_gem() {
}
function install_activesupport_gem() {
- run_timed_command "gem install activesupport --no-document --version 6.1.7.1"
+ run_timed_command "gem install activesupport --no-document --version 6.1.7.2"
}
function install_junit_merge_gem() {
diff --git a/spec/controllers/concerns/send_file_upload_spec.rb b/spec/controllers/concerns/send_file_upload_spec.rb
index 3fe6d62329e..bf6b68df54e 100644
--- a/spec/controllers/concerns/send_file_upload_spec.rb
+++ b/spec/controllers/concerns/send_file_upload_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe SendFileUpload do
+RSpec.describe SendFileUpload, feature_category: :user_profile do
let(:uploader_class) do
Class.new(GitlabUploader) do
include ObjectStorage::Concern
@@ -77,26 +77,6 @@ RSpec.describe SendFileUpload do
allow(uploader).to receive(:model).and_return(image_owner)
end
- it_behaves_like 'handles image resize requests allowed by FF'
-
- context 'when FF is disabled' do
- before do
- stub_feature_flags(dynamic_image_resizing: false)
- end
-
- it_behaves_like 'bypasses image resize requests not allowed by FF'
- end
- end
-
- shared_examples 'bypasses image resize requests not allowed by FF' do
- it 'does not write workhorse command header' do
- expect(headers).not_to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, /^send-scaled-img:/)
-
- subject
- end
- end
-
- shared_examples 'handles image resize requests allowed by FF' do
context 'with valid width parameter' do
it 'renders OK with workhorse command header' do
expect(controller).not_to receive(:send_file)
diff --git a/spec/factories/packages/debian/distribution.rb b/spec/factories/packages/debian/distribution.rb
index 48892d16efb..7a9d8561a9c 100644
--- a/spec/factories/packages/debian/distribution.rb
+++ b/spec/factories/packages/debian/distribution.rb
@@ -4,14 +4,14 @@ FactoryBot.define do
factory :debian_project_distribution, class: 'Packages::Debian::ProjectDistribution' do
container { association(:project) }
- sequence(:codename) { |n| "#{FFaker::Lorem.word}#{n}" }
+ sequence(:codename) { |n| "codename-#{n}" }
factory :debian_group_distribution, class: 'Packages::Debian::GroupDistribution' do
container { association(:group) }
end
trait(:with_suite) do
- sequence(:suite) { |n| "#{FFaker::Lorem.word}#{n}" }
+ sequence(:suite) { |n| "suite-#{n}" }
end
trait(:with_file) do
@@ -24,7 +24,7 @@ FactoryBot.define do
FILESIGNATURE
end
- after(:build) do |distribution, evaluator|
+ after(:build) do |distribution, _evaluator|
distribution.file = fixture_file_upload('spec/fixtures/packages/debian/distribution/Release')
distribution.signed_file = fixture_file_upload('spec/fixtures/packages/debian/distribution/InRelease')
end
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index c078514514b..856f0f6cd05 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -8,8 +8,8 @@ FactoryBot.define do
# Project does not have bare repository.
# Use this factory if you don't need repository in tests
factory :project, class: 'Project' do
- sequence(:name) { |n| "project#{n}" }
- path { name.downcase.gsub(/\s/, '_') }
+ sequence(:path) { |n| "project-#{n}" }
+ name { "#{path.humanize} Name" }
# Behaves differently to nil due to cache_has_external_* methods.
has_external_issue_tracker { false }
@@ -539,7 +539,7 @@ FactoryBot.define do
trait :readme do
custom_repo
- name { 'gitlab-profile' }
+ path { 'gitlab-profile' }
files { { 'README.md' => 'Hello World' } }
end
end
diff --git a/spec/features/alerts_settings/user_views_alerts_settings_spec.rb b/spec/features/alerts_settings/user_views_alerts_settings_spec.rb
index 70223b2c0d4..94f5cf8f5b2 100644
--- a/spec/features/alerts_settings/user_views_alerts_settings_spec.rb
+++ b/spec/features/alerts_settings/user_views_alerts_settings_spec.rb
@@ -19,6 +19,7 @@ RSpec.describe 'Alert integrations settings form', :js, feature_category: :incid
describe 'when viewing alert integrations as a maintainer' do
context 'with the default page permissions' do
before do
+ stub_feature_flags(remove_monitor_metrics: false)
visit project_settings_operations_path(project, anchor: 'js-alert-management-settings')
wait_for_requests
end
diff --git a/spec/features/markdown/sandboxed_mermaid_spec.rb b/spec/features/markdown/sandboxed_mermaid_spec.rb
index 0282d02d809..0ecba8df88b 100644
--- a/spec/features/markdown/sandboxed_mermaid_spec.rb
+++ b/spec/features/markdown/sandboxed_mermaid_spec.rb
@@ -53,4 +53,28 @@ RSpec.describe 'Sandboxed Mermaid rendering', :js, feature_category: :team_plann
end
end
end
+
+ context 'in a project milestone' do
+ let(:milestone) { create(:project_milestone, project: project, description: description) }
+
+ it 'includes mermaid frame correctly' do
+ visit(project_milestone_path(project, milestone))
+
+ wait_for_requests
+
+ expect(page.html).to include(expected)
+ end
+ end
+
+ context 'in a group milestone' do
+ let(:group_milestone) { create(:group_milestone, description: description) }
+
+ it 'includes mermaid frame correctly' do
+ visit(group_milestone_path(group_milestone.group, group_milestone))
+
+ wait_for_requests
+
+ expect(page.html).to include(expected)
+ end
+ end
end
diff --git a/spec/features/monitor_sidebar_link_spec.rb b/spec/features/monitor_sidebar_link_spec.rb
index c4114b28b47..6a1413c04f6 100644
--- a/spec/features/monitor_sidebar_link_spec.rb
+++ b/spec/features/monitor_sidebar_link_spec.rb
@@ -11,6 +11,7 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures, feature_category
before do
project.add_role(user, role) if role
sign_in(user)
+ stub_feature_flags(remove_monitor_metrics: false)
end
shared_examples 'shows Monitor menu based on the access level' do
diff --git a/spec/features/projects/navbar_spec.rb b/spec/features/projects/navbar_spec.rb
index 31f4e9dcf95..532dd7d0a84 100644
--- a/spec/features/projects/navbar_spec.rb
+++ b/spec/features/projects/navbar_spec.rb
@@ -20,6 +20,7 @@ RSpec.describe 'Project navbar', :with_license, feature_category: :projects do
stub_config(registry: { enabled: false })
stub_feature_flags(harbor_registry_integration: false)
stub_feature_flags(ml_experiment_tracking: false)
+ stub_feature_flags(remove_monitor_metrics: false)
insert_package_nav(_('Deployments'))
insert_infrastructure_registry_nav
insert_infrastructure_google_cloud_nav
diff --git a/spec/features/projects/settings/monitor_settings_spec.rb b/spec/features/projects/settings/monitor_settings_spec.rb
index 900f18bf49e..1367ffb0009 100644
--- a/spec/features/projects/settings/monitor_settings_spec.rb
+++ b/spec/features/projects/settings/monitor_settings_spec.rb
@@ -11,6 +11,7 @@ RSpec.describe 'Projects > Settings > For a forked project', :js, feature_catego
before do
sign_in(user)
+ stub_feature_flags(remove_monitor_metrics: false)
end
describe 'Sidebar > Monitor' do
diff --git a/spec/features/projects/user_uses_shortcuts_spec.rb b/spec/features/projects/user_uses_shortcuts_spec.rb
index 05d79ea3b1b..1bad04382f4 100644
--- a/spec/features/projects/user_uses_shortcuts_spec.rb
+++ b/spec/features/projects/user_uses_shortcuts_spec.rb
@@ -9,6 +9,7 @@ RSpec.describe 'User uses shortcuts', :js, feature_category: :projects do
before do
sign_in(user)
+ stub_feature_flags(remove_monitor_metrics: false)
visit(project_path(project))
diff --git a/spec/fixtures/pipeline_artifacts/code_quality_mr_diff.json b/spec/fixtures/pipeline_artifacts/code_quality_mr_diff.json
index 5489330fc1d..97026cf1180 100644
--- a/spec/fixtures/pipeline_artifacts/code_quality_mr_diff.json
+++ b/spec/fixtures/pipeline_artifacts/code_quality_mr_diff.json
@@ -3,21 +3,78 @@
"files": {
"file_a.rb": [
{
+ "categories": [
+ "Complexity"
+ ],
"line": 10,
"description": "Avoid parameter lists longer than 5 parameters. [12/5]",
- "severity": "major"
+ "severity": "major",
+ "location": {
+ "path": "file_a.rb",
+ "lines": {
+ "begin": 10,
+ "end": 10
+ }
+ },
+ "other_locations": [
+
+ ],
+ "content": {
+ "body": ""
+ },
+ "type": "issue",
+ "engine_name": "structure"
},
{
+ "categories": [
+ "Complexity"
+ ],
"line": 10,
"description": "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.",
- "severity": "minor"
+ "severity": "minor",
+ "location": {
+ "path": "file_a.rb",
+ "lines": {
+ "begin": 10,
+ "end": 10
+ }
+ },
+ "other_locations": [
+
+ ],
+ "content": {
+ "body": ""
+ },
+ "type": "issue",
+ "engine_name": "structure"
}
],
"file_b.rb": [
{
+ "categories": [
+ "Complexity"
+ ],
"line": 10,
"description": "This cop checks for methods with too many parameters.\nThe maximum number of parameters is configurable.\nKeyword arguments can optionally be excluded from the total count.",
- "severity": "minor"
+ "severity": "minor",
+ "location": {
+ "path": "file_b.rb",
+ "positions": {
+ "begin": {
+ "column": 14,
+ "line": 10
+ },
+ "end": {
+ "column": 39,
+ "line": 10
+ }
+ }
+ },
+ "content": {
+ "body": ""
+ },
+ "type": "Issue",
+ "engine_name": "rubocop"
}
]
}
diff --git a/spec/frontend/analytics/cycle_analytics/__snapshots__/total_time_spec.js.snap b/spec/frontend/analytics/cycle_analytics/components/__snapshots__/total_time_spec.js.snap
index 92927ef16ec..92927ef16ec 100644
--- a/spec/frontend/analytics/cycle_analytics/__snapshots__/total_time_spec.js.snap
+++ b/spec/frontend/analytics/cycle_analytics/components/__snapshots__/total_time_spec.js.snap
diff --git a/spec/frontend/analytics/cycle_analytics/base_spec.js b/spec/frontend/analytics/cycle_analytics/components/base_spec.js
index 1a1d22626ea..5efb823535e 100644
--- a/spec/frontend/analytics/cycle_analytics/base_spec.js
+++ b/spec/frontend/analytics/cycle_analytics/components/base_spec.js
@@ -19,7 +19,7 @@ import {
currentGroup,
stageCounts,
initialPaginationState as pagination,
-} from './mock_data';
+} from '../mock_data';
const selectedStageEvents = issueEvents.events;
const noDataSvgPath = 'path/to/no/data';
diff --git a/spec/frontend/analytics/cycle_analytics/filter_bar_spec.js b/spec/frontend/analytics/cycle_analytics/components/filter_bar_spec.js
index f1b3af39199..f1b3af39199 100644
--- a/spec/frontend/analytics/cycle_analytics/filter_bar_spec.js
+++ b/spec/frontend/analytics/cycle_analytics/components/filter_bar_spec.js
diff --git a/spec/frontend/analytics/cycle_analytics/formatted_stage_count_spec.js b/spec/frontend/analytics/cycle_analytics/components/formatted_stage_count_spec.js
index 6dd7e2e6223..6dd7e2e6223 100644
--- a/spec/frontend/analytics/cycle_analytics/formatted_stage_count_spec.js
+++ b/spec/frontend/analytics/cycle_analytics/components/formatted_stage_count_spec.js
diff --git a/spec/frontend/analytics/cycle_analytics/path_navigation_spec.js b/spec/frontend/analytics/cycle_analytics/components/path_navigation_spec.js
index 9a598ee0ad1..9084cec1c53 100644
--- a/spec/frontend/analytics/cycle_analytics/path_navigation_spec.js
+++ b/spec/frontend/analytics/cycle_analytics/components/path_navigation_spec.js
@@ -3,7 +3,7 @@ import { mount } from '@vue/test-utils';
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import Component from '~/analytics/cycle_analytics/components/path_navigation.vue';
-import { transformedProjectStagePathData, selectedStage } from './mock_data';
+import { transformedProjectStagePathData, selectedStage } from '../mock_data';
describe('Project PathNavigation', () => {
let wrapper = null;
diff --git a/spec/frontend/analytics/cycle_analytics/stage_table_spec.js b/spec/frontend/analytics/cycle_analytics/components/stage_table_spec.js
index fbc63a80de8..e92cb6f73d6 100644
--- a/spec/frontend/analytics/cycle_analytics/stage_table_spec.js
+++ b/spec/frontend/analytics/cycle_analytics/components/stage_table_spec.js
@@ -5,7 +5,7 @@ import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import StageTable from '~/analytics/cycle_analytics/components/stage_table.vue';
import { PAGINATION_SORT_FIELD_DURATION } from '~/analytics/cycle_analytics/constants';
-import { issueEvents, issueStage, reviewStage, reviewEvents } from './mock_data';
+import { issueEvents, issueStage, reviewStage, reviewEvents } from '../mock_data';
let wrapper = null;
let trackingSpy = null;
diff --git a/spec/frontend/analytics/cycle_analytics/total_time_spec.js b/spec/frontend/analytics/cycle_analytics/components/total_time_spec.js
index 6597b6fa3d5..6597b6fa3d5 100644
--- a/spec/frontend/analytics/cycle_analytics/total_time_spec.js
+++ b/spec/frontend/analytics/cycle_analytics/components/total_time_spec.js
diff --git a/spec/frontend/analytics/cycle_analytics/value_stream_filters_spec.js b/spec/frontend/analytics/cycle_analytics/components/value_stream_filters_spec.js
index c6915c9054c..e3bcb0ab557 100644
--- a/spec/frontend/analytics/cycle_analytics/value_stream_filters_spec.js
+++ b/spec/frontend/analytics/cycle_analytics/components/value_stream_filters_spec.js
@@ -8,7 +8,7 @@ import {
createdBefore as endDate,
currentGroup,
selectedProjects,
-} from './mock_data';
+} from '../mock_data';
const { path } = currentGroup;
const groupPath = `groups/${path}`;
diff --git a/spec/frontend/analytics/cycle_analytics/value_stream_metrics_spec.js b/spec/frontend/analytics/cycle_analytics/components/value_stream_metrics_spec.js
index 6a64737bc80..c918d020c81 100644
--- a/spec/frontend/analytics/cycle_analytics/value_stream_metrics_spec.js
+++ b/spec/frontend/analytics/cycle_analytics/components/value_stream_metrics_spec.js
@@ -10,7 +10,7 @@ import { prepareTimeMetricsData } from '~/analytics/shared/utils';
import MetricTile from '~/analytics/shared/components/metric_tile.vue';
import ValueStreamsDashboardLink from '~/analytics/shared/components/value_streams_dashboard_link.vue';
import { createAlert } from '~/alert';
-import { group } from './mock_data';
+import { group } from '../mock_data';
jest.mock('~/alert');
diff --git a/spec/frontend/analytics/components/activity_chart_spec.js b/spec/frontend/analytics/product_analytics/components/activity_chart_spec.js
index 4f8126aaacf..4f8126aaacf 100644
--- a/spec/frontend/analytics/components/activity_chart_spec.js
+++ b/spec/frontend/analytics/product_analytics/components/activity_chart_spec.js
diff --git a/spec/frontend/ci/runner/components/runner_projects_spec.js b/spec/frontend/ci/runner/components/runner_projects_spec.js
index afdc54d8ebc..7d49874bfbc 100644
--- a/spec/frontend/ci/runner/components/runner_projects_spec.js
+++ b/spec/frontend/ci/runner/components/runner_projects_spec.js
@@ -67,6 +67,7 @@ describe('RunnerProjects', () => {
expect(mockRunnerProjectsQuery).toHaveBeenCalledWith({
id: mockRunner.id,
search: '',
+ sort: 'ID_ASC',
first: RUNNER_DETAILS_PROJECTS_PAGE_SIZE,
});
});
@@ -108,7 +109,6 @@ describe('RunnerProjects', () => {
name,
fullName: nameWithNamespace,
avatarUrl,
- isOwner: true, // first project is always owner
});
});
@@ -124,6 +124,7 @@ describe('RunnerProjects', () => {
expect(mockRunnerProjectsQuery).toHaveBeenLastCalledWith({
id: mockRunner.id,
search: '',
+ sort: 'ID_ASC',
first: RUNNER_DETAILS_PROJECTS_PAGE_SIZE,
after: 'AFTER_CURSOR',
});
@@ -138,6 +139,7 @@ describe('RunnerProjects', () => {
expect(mockRunnerProjectsQuery).toHaveBeenLastCalledWith({
id: mockRunner.id,
search: '',
+ sort: 'ID_ASC',
last: RUNNER_DETAILS_PROJECTS_PAGE_SIZE,
before: 'BEFORE_CURSOR',
});
@@ -151,6 +153,7 @@ describe('RunnerProjects', () => {
expect(mockRunnerProjectsQuery).toHaveBeenLastCalledWith({
id: mockRunner.id,
search: 'my search',
+ sort: 'ID_ASC',
first: RUNNER_DETAILS_PROJECTS_PAGE_SIZE,
});
});
@@ -167,6 +170,7 @@ describe('RunnerProjects', () => {
expect(mockRunnerProjectsQuery).toHaveBeenLastCalledWith({
id: mockRunner.id,
search: 'my search',
+ sort: 'ID_ASC',
first: RUNNER_DETAILS_PROJECTS_PAGE_SIZE,
});
});
diff --git a/spec/frontend/fixtures/jobs.rb b/spec/frontend/fixtures/jobs.rb
index 376c04cd629..043d6a9db29 100644
--- a/spec/frontend/fixtures/jobs.rb
+++ b/spec/frontend/fixtures/jobs.rb
@@ -92,6 +92,11 @@ RSpec.describe 'Jobs (JavaScript fixtures)' do
it_behaves_like 'graphql queries', 'pages/admin/jobs/components/table/graphql/queries', 'get_all_jobs.query.graphql' do
let(:user) { create(:admin) }
end
+
+ it_behaves_like 'graphql queries', 'pages/admin/jobs/components/table/graphql/queries', 'get_cancelable_jobs_count.query.graphql' do
+ let(:variables) { { statuses: %w[PENDING RUNNING] } }
+ let(:user) { create(:admin) }
+ end
end
describe 'get_jobs_count.query.graphql', type: :request do
diff --git a/spec/frontend/fixtures/prometheus_integration.rb b/spec/frontend/fixtures/prometheus_integration.rb
index 13130c00118..fcba8b596a8 100644
--- a/spec/frontend/fixtures/prometheus_integration.rb
+++ b/spec/frontend/fixtures/prometheus_integration.rb
@@ -14,6 +14,7 @@ RSpec.describe Projects::Settings::IntegrationsController, '(JavaScript fixtures
before do
sign_in(user)
+ stub_feature_flags(remove_monitor_metrics: false)
end
after do
diff --git a/spec/frontend/jobs/components/table/jobs_table_tabs_spec.js b/spec/frontend/jobs/components/table/jobs_table_tabs_spec.js
index 70bcac82a3f..d20a732508a 100644
--- a/spec/frontend/jobs/components/table/jobs_table_tabs_spec.js
+++ b/spec/frontend/jobs/components/table/jobs_table_tabs_spec.js
@@ -3,6 +3,7 @@ import { mount } from '@vue/test-utils';
import { trimText } from 'helpers/text_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import JobsTableTabs from '~/jobs/components/table/jobs_table_tabs.vue';
+import CancelJobs from '~/pages/admin/jobs/components/cancel_jobs.vue';
describe('Jobs Table Tabs', () => {
let wrapper;
@@ -12,6 +13,11 @@ describe('Jobs Table Tabs', () => {
loading: false,
};
+ const adminProps = {
+ ...defaultProps,
+ showCancelAllJobsButton: true,
+ };
+
const statuses = {
success: 'SUCCESS',
failed: 'FAILED',
@@ -20,6 +26,7 @@ describe('Jobs Table Tabs', () => {
const findAllTab = () => wrapper.findByTestId('jobs-all-tab');
const findFinishedTab = () => wrapper.findByTestId('jobs-finished-tab');
+ const findCancelJobsButton = () => wrapper.findAllComponents(CancelJobs);
const triggerTabChange = (index) => wrapper.findAllComponents(GlTab).at(index).vm.$emit('click');
@@ -59,4 +66,16 @@ describe('Jobs Table Tabs', () => {
expect(wrapper.emitted()).toEqual({ fetchJobsByStatus: [[expectedScope]] });
});
+
+ it('does not displays cancel all jobs button', () => {
+ expect(findCancelJobsButton().exists()).toBe(false);
+ });
+
+ describe('admin mode', () => {
+ it('displays cancel all jobs button', () => {
+ createComponent(adminProps);
+
+ expect(findCancelJobsButton().exists()).toBe(true);
+ });
+ });
});
diff --git a/spec/frontend/jobs/mock_data.js b/spec/frontend/jobs/mock_data.js
index fb1ded7b4ef..ed89221d062 100644
--- a/spec/frontend/jobs/mock_data.js
+++ b/spec/frontend/jobs/mock_data.js
@@ -5,6 +5,7 @@ import mockAllJobsPaginated from 'test_fixtures/graphql/jobs/get_all_jobs.query.
import mockJobs from 'test_fixtures/graphql/jobs/get_jobs.query.graphql.json';
import mockAllJobs from 'test_fixtures/graphql/jobs/get_all_jobs.query.graphql.json';
import mockJobsAsGuest from 'test_fixtures/graphql/jobs/get_jobs.query.graphql.as_guest.json';
+import mockCancelableJobsCount from 'test_fixtures/graphql/jobs/get_cancelable_jobs_count.query.graphql.json';
import { TEST_HOST } from 'spec/test_constants';
import { TOKEN_TYPE_STATUS } from '~/vue_shared/components/filtered_search_bar/constants';
@@ -19,6 +20,7 @@ export const mockJobsNodes = mockJobs.data.project.jobs.nodes;
export const mockAllJobsNodes = mockAllJobs.data.jobs.nodes;
export const mockJobsNodesAsGuest = mockJobsAsGuest.data.project.jobs.nodes;
export const mockJobsCountResponse = mockJobsCount;
+export const mockCancelableJobsCountResponse = mockCancelableJobsCount;
export const stages = [
{
diff --git a/spec/frontend/milestones/index_spec.js b/spec/frontend/milestones/index_spec.js
new file mode 100644
index 00000000000..477217fc10f
--- /dev/null
+++ b/spec/frontend/milestones/index_spec.js
@@ -0,0 +1,38 @@
+import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
+import { initShow, MILESTONE_DESCRIPTION_ELEMENT } from '~/milestones/index';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
+
+jest.mock('~/behaviors/markdown/render_gfm');
+jest.mock('~/milestones/milestone');
+jest.mock('~/right_sidebar');
+jest.mock('~/sidebar/mount_milestone_sidebar');
+
+describe('#initShow', () => {
+ beforeEach(() => {
+ setHTMLFixture(`
+ <div class="detail-page-description milestone-detail">
+ <div class="description">
+ <div class="markdown-code-block">
+ <pre class="js-render-mermaid">
+ graph TD;
+ A-- > B;
+ A-- > C;
+ B-- > D;
+ C-- > D;
+ </pre>
+ </div>
+ </div>
+ </div>
+ `);
+ });
+
+ afterEach(() => {
+ resetHTMLFixture();
+ });
+
+ it('calls `renderGFM` to ensure that all gitlab-flavoured markdown is rendered on the milestone details page', () => {
+ initShow();
+
+ expect(renderGFM).toHaveBeenCalledWith(document.querySelector(MILESTONE_DESCRIPTION_ELEMENT));
+ });
+});
diff --git a/spec/frontend/pages/admin/jobs/components/table/admin_job_table_app_spec.js b/spec/frontend/pages/admin/jobs/components/table/admin_job_table_app_spec.js
index 686c818f45a..1341949439b 100644
--- a/spec/frontend/pages/admin/jobs/components/table/admin_job_table_app_spec.js
+++ b/spec/frontend/pages/admin/jobs/components/table/admin_job_table_app_spec.js
@@ -4,15 +4,18 @@ import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import JobsTable from '~/jobs/components/table/jobs_table.vue';
import JobsTableTabs from '~/jobs/components/table/jobs_table_tabs.vue';
-import getJobsQuery from '~/pages/admin/jobs/components/table/graphql/queries/get_all_jobs.query.graphql';
-import AdminJobsTableApp from '~/pages/admin/jobs/components/table/admin_jobs_table_app.vue';
import JobsSkeletonLoader from '~/pages/admin/jobs/components/jobs_skeleton_loader.vue';
+import getAllJobsQuery from '~/pages/admin/jobs/components/table/graphql/queries/get_all_jobs.query.graphql';
+import getCancelableJobsQuery from '~/pages/admin/jobs/components/table/graphql/queries/get_cancelable_jobs_count.query.graphql';
+import AdminJobsTableApp from '~/pages/admin/jobs/components/table/admin_jobs_table_app.vue';
+import CancelJobs from '~/pages/admin/jobs/components/cancel_jobs.vue';
+import JobsTable from '~/jobs/components/table/jobs_table.vue';
import {
mockAllJobsResponsePaginated,
mockJobsResponseEmpty,
+ mockCancelableJobsCountResponse,
statuses,
} from '../../../../../jobs/mock_data';
@@ -24,6 +27,7 @@ describe('Job table app', () => {
const successHandler = jest.fn().mockResolvedValue(mockAllJobsResponsePaginated);
const emptyHandler = jest.fn().mockResolvedValue(mockJobsResponseEmpty);
const failedHandler = jest.fn().mockRejectedValue(new Error('GraphQL error'));
+ const cancelHandler = jest.fn().mockResolvedValue(mockCancelableJobsCountResponse);
const findSkeletonLoader = () => wrapper.findComponent(JobsSkeletonLoader);
const findLoadingSpinner = () => wrapper.findComponent(GlLoadingIcon);
@@ -31,15 +35,20 @@ describe('Job table app', () => {
const findEmptyState = () => wrapper.findComponent(GlEmptyState);
const findAlert = () => wrapper.findComponent(GlAlert);
const findTabs = () => wrapper.findComponent(JobsTableTabs);
+ const findCancelJobsButton = () => wrapper.findComponent(CancelJobs);
- const createMockApolloProvider = (handler) => {
- const requestHandlers = [[getJobsQuery, handler]];
+ const createMockApolloProvider = (handler, cancelableHandler) => {
+ const requestHandlers = [
+ [getAllJobsQuery, handler],
+ [getCancelableJobsQuery, cancelableHandler],
+ ];
return createMockApollo(requestHandlers);
};
const createComponent = ({
handler = successHandler,
+ cancelableHandler = cancelHandler,
mountFn = shallowMount,
data = {},
} = {}) => {
@@ -52,7 +61,7 @@ describe('Job table app', () => {
provide: {
jobStatuses: statuses,
},
- apolloProvider: createMockApolloProvider(handler),
+ apolloProvider: createMockApolloProvider(handler, cancelableHandler),
});
};
@@ -129,4 +138,22 @@ describe('Job table app', () => {
expect(findTable().exists()).toBe(false);
});
});
+
+ describe('cancel jobs button', () => {
+ it('should display cancel all jobs button', async () => {
+ createComponent({ cancelableHandler: cancelHandler, mountFn: mount });
+
+ await waitForPromises();
+
+ expect(findCancelJobsButton().exists()).toBe(true);
+ });
+
+ it('should not display cancel all jobs button', async () => {
+ createComponent();
+
+ await waitForPromises();
+
+ expect(findCancelJobsButton().exists()).toBe(false);
+ });
+ });
});
diff --git a/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap b/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap
index 6fe60f3c2e6..a0b545add27 100644
--- a/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap
+++ b/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap
@@ -8,6 +8,7 @@ exports[`Repository last commit component renders commit widget 1`] = `
class="gl-my-2 gl-mr-4"
imgalt=""
imgcssclasses=""
+ imgcsswrapperclasses=""
imgsize="32"
imgsrc="https://test.com"
linkhref="/test"
diff --git a/spec/graphql/resolvers/ci/runner_projects_resolver_spec.rb b/spec/graphql/resolvers/ci/runner_projects_resolver_spec.rb
index 44203fb2912..2fb5149c7f4 100644
--- a/spec/graphql/resolvers/ci/runner_projects_resolver_spec.rb
+++ b/spec/graphql/resolvers/ci/runner_projects_resolver_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe Resolvers::Ci::RunnerProjectsResolver, feature_category: :runner_
describe '#resolve' do
context 'with authorized user', :enable_admin_mode do
- let(:current_user) { create(:user, :admin) }
+ let_it_be(:current_user) { create(:user, :admin) }
context 'with search argument' do
let(:args) { { search: 'Project1.' } }
@@ -69,15 +69,15 @@ RSpec.describe Resolvers::Ci::RunnerProjectsResolver, feature_category: :runner_
end
context 'without arguments' do
- it 'returns a lazy value with all projects sorted by :id_asc' do
+ it 'returns a lazy value with all projects sorted by :id_desc' do
expect(subject).to be_a(GraphQL::Execution::Lazy)
- expect(subject.value.items).to eq([project1, project2, project3])
+ expect(subject.value.items).to eq([project3, project2, project1])
end
end
end
context 'with unauthorized user' do
- let(:current_user) { create(:user) }
+ let_it_be(:current_user) { create(:user) }
it { is_expected.to be_nil }
end
diff --git a/spec/lib/gitlab/ci/reports/codequality_mr_diff_spec.rb b/spec/lib/gitlab/ci/reports/codequality_mr_diff_spec.rb
index 73b916da2e9..8b094c64b54 100644
--- a/spec/lib/gitlab/ci/reports/codequality_mr_diff_spec.rb
+++ b/spec/lib/gitlab/ci/reports/codequality_mr_diff_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Reports::CodequalityMrDiff do
+RSpec.describe Gitlab::Ci::Reports::CodequalityMrDiff, feature_category: :code_quality do
let(:codequality_report) { Gitlab::Ci::Reports::CodequalityReports.new }
let(:degradation_1) { build(:codequality_degradation_1) }
let(:degradation_2) { build(:codequality_degradation_2) }
@@ -18,8 +18,24 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityMrDiff do
it 'generates quality report for mr diff' do
expect(report.files).to match(
"file_a.rb" => [
- { line: 10, description: "Avoid parameter lists longer than 5 parameters. [12/5]", severity: "major" },
- { line: 10, description: "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.", severity: "major" }
+ { line: 10,
+ description: "Avoid parameter lists longer than 5 parameters. [12/5]",
+ severity: "major",
+ engine_name: "structure",
+ categories: ["Complexity"],
+ content: { "body" => "" },
+ location: { "lines" => { "begin" => 10, "end" => 10 }, "path" => "file_a.rb" },
+ other_locations: [],
+ type: "issue" },
+ { line: 10,
+ description: "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.",
+ severity: "major",
+ engine_name: "structure",
+ categories: ["Complexity"],
+ content: { "body" => "" },
+ location: { "lines" => { "begin" => 10, "end" => 10 }, "path" => "file_a.rb" },
+ other_locations: [],
+ type: "issue" }
]
)
end
@@ -28,16 +44,14 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityMrDiff do
context 'with several degradations on several files' do
let(:new_degradations) { [degradation_1, degradation_2, degradation_3] }
- it 'returns quality report for mr diff' do
- expect(report.files).to match(
- "file_a.rb" => [
- { line: 10, description: "Avoid parameter lists longer than 5 parameters. [12/5]", severity: "major" },
- { line: 10, description: "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.", severity: "major" }
- ],
- "file_b.rb" => [
- { line: 10, description: "Avoid parameter lists longer than 5 parameters. [12/5]", severity: "minor" }
- ]
- )
+ it 'returns quality report including the files' do
+ expect(report.files.keys).to match_array(["file_a.rb", "file_b.rb"])
+ end
+
+ it 'converts the content body to html' do
+ body = report.files["file_b.rb"].first[:content]["body"]
+
+ expect(body).to eq('<p data-sourcepos="1:1-3:66" dir="auto">This cop checks for methods with too many parameters.&#x000A;The maximum number of parameters is configurable.&#x000A;Keyword arguments can optionally be excluded from the total count.</p>')
end
end
end
diff --git a/spec/lib/gitlab/metrics/subscribers/action_cable_spec.rb b/spec/lib/gitlab/metrics/subscribers/action_cable_spec.rb
index 77c42f57f3c..6a4ef894fae 100644
--- a/spec/lib/gitlab/metrics/subscribers/action_cable_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/action_cable_spec.rb
@@ -5,19 +5,22 @@ require 'spec_helper'
RSpec.describe Gitlab::Metrics::Subscribers::ActionCable, :request_store, feature_category: :application_performance do
let(:subscriber) { described_class.new }
let(:counter) { double(:counter) }
- let(:data) { { 'result' => { 'data' => { 'event' => 'updated' } } } }
+ let(:histogram) { double(:histogram) }
let(:channel_class) { 'IssuesChannel' }
- let(:event) do
- double(
- :event,
- name: name,
- payload: payload
- )
+ let(:event) { double(:event, name: name, payload: payload) }
+
+ before do
+ allow(::Gitlab::Metrics).to receive(:counter).with(
+ :action_cable_single_client_transmissions_total, /transmit/
+ ).and_return(counter)
+ allow(::Gitlab::Metrics).to receive(:histogram).with(
+ :action_cable_transmitted_bytes, /transmit/
+ ).and_return(histogram)
end
describe '#transmit' do
let(:name) { 'transmit.action_cable' }
- let(:via) { 'streamed from issues:Z2lkOi8vZs2l0bGFiL0lzc3VlLzQ0Ng' }
+ let(:via) { nil }
let(:payload) do
{
channel_class: channel_class,
@@ -26,25 +29,71 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActionCable, :request_store, featur
}
end
- it 'tracks the transmit event' do
- allow(::Gitlab::Metrics).to receive(:counter).with(
- :action_cable_single_client_transmissions_total, /transmit/
- ).and_return(counter)
+ let(:message_size) { ::Gitlab::Json.generate(data).bytesize }
- expect(counter).to receive(:increment)
+ context 'for transmissions initiated by Channel instance' do
+ let(:data) { {} }
+ let(:expected_labels) do
+ {
+ channel: channel_class,
+ broadcasting: nil,
+ caller: 'channel'
+ }
+ end
+
+ it 'tracks the event with "caller" set to "channel"' do
+ expect(counter).to receive(:increment).with(expected_labels)
+ expect(histogram).to receive(:observe).with(expected_labels, message_size)
- subscriber.transmit(event)
+ subscriber.transmit(event)
+ end
end
- it 'tracks size of payload as JSON' do
- allow(::Gitlab::Metrics).to receive(:histogram).with(
- :action_cable_transmitted_bytes, /transmit/
- ).and_return(counter)
- message_size = ::Gitlab::Json.generate(data).bytesize
+ context 'for transmissions initiated by GraphQL event subscriber' do
+ let(:via) { 'streamed from graphql-subscription:09ae595a-45c4-4ae0-b765-4e503203211d' }
+ let(:data) { { result: { 'data' => { 'issuableEpicUpdated' => '<GQL query result>' } } } }
+ let(:expected_labels) do
+ {
+ channel: channel_class,
+ broadcasting: 'graphql-event:issuableEpicUpdated',
+ caller: 'graphql-subscription'
+ }
+ end
- expect(counter).to receive(:observe).with({ channel: channel_class, operation: 'event' }, message_size)
+ it 'tracks the event with correct "caller" and "broadcasting"' do
+ expect(counter).to receive(:increment).with(expected_labels)
+ expect(histogram).to receive(:observe).with(expected_labels, message_size)
+
+ subscriber.transmit(event)
+ end
- subscriber.transmit(event)
+ it 'is indifferent to keys being symbols or strings in result payload' do
+ expect(counter).to receive(:increment).with(expected_labels)
+ expect(histogram).to receive(:observe).with(expected_labels, message_size)
+
+ event.payload[:data].deep_stringify_keys!
+
+ subscriber.transmit(event)
+ end
+ end
+
+ context 'when transmission is coming from unknown source' do
+ let(:via) { 'streamed from something else' }
+ let(:data) { {} }
+ let(:expected_labels) do
+ {
+ channel: channel_class,
+ broadcasting: nil,
+ caller: 'unknown'
+ }
+ end
+
+ it 'tracks the event with "caller" set to "unknown"' do
+ expect(counter).to receive(:increment).with(expected_labels)
+ expect(histogram).to receive(:observe).with(expected_labels, message_size)
+
+ subscriber.transmit(event)
+ end
end
end
@@ -92,8 +141,8 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActionCable, :request_store, featur
context 'when broadcast is something else' do
let(:broadcasting) { 'unknown-topic' }
- it 'tracks the event as "other"' do
- expect(counter).to receive(:increment).with({ broadcasting: 'other' })
+ it 'tracks the event as "unknown"' do
+ expect(counter).to receive(:increment).with({ broadcasting: 'unknown' })
subscriber.broadcast(event)
end
diff --git a/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb b/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb
index a1e6ae13e68..aa1e67085cd 100644
--- a/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb
@@ -57,6 +57,10 @@ RSpec.describe Sidebars::Projects::Menus::MonitorMenu do
end
context 'Menu items' do
+ before do
+ stub_feature_flags(remove_monitor_metrics: false)
+ end
+
subject { described_class.new(context).renderable_items.index { |e| e.item_id == item_id } }
shared_examples 'access rights checks' do
diff --git a/spec/models/packages/debian/group_distribution_spec.rb b/spec/models/packages/debian/group_distribution_spec.rb
index 90fb0d0e7d8..1af23ad3ac0 100644
--- a/spec/models/packages/debian/group_distribution_spec.rb
+++ b/spec/models/packages/debian/group_distribution_spec.rb
@@ -2,6 +2,9 @@
require 'spec_helper'
-RSpec.describe Packages::Debian::GroupDistribution do
- it_behaves_like 'Debian Distribution', :debian_group_distribution, :group, false
+RSpec.describe Packages::Debian::GroupDistribution, feature_category: :package_registry do
+ include_context 'for Debian Distribution', :debian_group_distribution, false
+
+ it_behaves_like 'Debian Distribution for common behavior'
+ it_behaves_like 'Debian Distribution with group container'
end
diff --git a/spec/models/packages/debian/project_distribution_spec.rb b/spec/models/packages/debian/project_distribution_spec.rb
index 5f4041ad9fe..2b79cdb539f 100644
--- a/spec/models/packages/debian/project_distribution_spec.rb
+++ b/spec/models/packages/debian/project_distribution_spec.rb
@@ -2,6 +2,9 @@
require 'spec_helper'
-RSpec.describe Packages::Debian::ProjectDistribution do
- it_behaves_like 'Debian Distribution', :debian_project_distribution, :project, true
+RSpec.describe Packages::Debian::ProjectDistribution, feature_category: :package_registry do
+ include_context 'for Debian Distribution', :debian_project_distribution, true
+
+ it_behaves_like 'Debian Distribution for common behavior'
+ it_behaves_like 'Debian Distribution with project container'
end
diff --git a/spec/presenters/ci/pipeline_artifacts/code_quality_mr_diff_presenter_spec.rb b/spec/presenters/ci/pipeline_artifacts/code_quality_mr_diff_presenter_spec.rb
index 94a743d4d89..3f40b5469ad 100644
--- a/spec/presenters/ci/pipeline_artifacts/code_quality_mr_diff_presenter_spec.rb
+++ b/spec/presenters/ci/pipeline_artifacts/code_quality_mr_diff_presenter_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::PipelineArtifacts::CodeQualityMrDiffPresenter do
+RSpec.describe Ci::PipelineArtifacts::CodeQualityMrDiffPresenter, feature_category: :code_quality do
let(:pipeline_artifact) { create(:ci_pipeline_artifact, :with_codequality_mr_diff_report) }
let(:merge_request) { double(id: 123456789, new_paths: filenames) }
@@ -36,8 +36,24 @@ RSpec.describe Ci::PipelineArtifacts::CodeQualityMrDiffPresenter do
expect(quality_data).to match(
files: {
"file_a.rb" => [
- { line: 10, description: "Avoid parameter lists longer than 5 parameters. [12/5]", severity: "major" },
- { line: 10, description: "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.", severity: "minor" }
+ { line: 10,
+ description: "Avoid parameter lists longer than 5 parameters. [12/5]",
+ severity: "major",
+ engine_name: "structure",
+ categories: ["Complexity"],
+ content: { "body" => "" },
+ location: { "lines" => { "begin" => 10, "end" => 10 }, "path" => "file_a.rb" },
+ other_locations: [],
+ type: "issue" },
+ { line: 10,
+ description: "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.",
+ severity: "minor",
+ engine_name: "structure",
+ categories: ["Complexity"],
+ content: { "body" => "" },
+ location: { "lines" => { "begin" => 10, "end" => 10 }, "path" => "file_a.rb" },
+ other_locations: [],
+ type: "issue" }
]
}
)
@@ -51,11 +67,34 @@ RSpec.describe Ci::PipelineArtifacts::CodeQualityMrDiffPresenter do
expect(quality_data).to match(
files: {
"file_a.rb" => [
- { line: 10, description: "Avoid parameter lists longer than 5 parameters. [12/5]", severity: "major" },
- { line: 10, description: "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.", severity: "minor" }
+ { line: 10,
+ description: "Avoid parameter lists longer than 5 parameters. [12/5]",
+ severity: "major",
+ engine_name: "structure",
+ categories: ["Complexity"],
+ content: { "body" => "" },
+ location: { "lines" => { "begin" => 10, "end" => 10 }, "path" => "file_a.rb" },
+ other_locations: [],
+ type: "issue" },
+ { line: 10,
+ description: "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.",
+ severity: "minor",
+ engine_name: "structure",
+ categories: ["Complexity"],
+ content: { "body" => "" },
+ location: { "lines" => { "begin" => 10, "end" => 10 }, "path" => "file_a.rb" },
+ other_locations: [],
+ type: "issue" }
],
"file_b.rb" => [
- { line: 10, description: "This cop checks for methods with too many parameters.\nThe maximum number of parameters is configurable.\nKeyword arguments can optionally be excluded from the total count.", severity: "minor" }
+ { line: 10,
+ description: "This cop checks for methods with too many parameters.\nThe maximum number of parameters is configurable.\nKeyword arguments can optionally be excluded from the total count.",
+ severity: "minor",
+ engine_name: "rubocop",
+ categories: ["Complexity"],
+ content: { "body" => "" },
+ location: { "positions" => { "begin" => { "column" => 14, "line" => 10 }, "end" => { "column" => 39, "line" => 10 } }, "path" => "file_b.rb" },
+ type: "Issue" }
]
}
)
diff --git a/spec/requests/api/badges_spec.rb b/spec/requests/api/badges_spec.rb
index 6c6a7cc7cc6..1c09c1129a2 100644
--- a/spec/requests/api/badges_spec.rb
+++ b/spec/requests/api/badges_spec.rb
@@ -72,9 +72,9 @@ RSpec.describe API::Badges, feature_category: :projects do
context 'when authenticated as a non-member' do
%i[maintainer developer access_requester stranger].each do |type|
- let(:badge) { source.badges.first }
-
context "as a #{type}" do
+ let(:badge) { source.badges.first }
+
it 'returns 200', :quarantine do
user = public_send(type)
diff --git a/spec/requests/api/graphql/ci/runner_spec.rb b/spec/requests/api/graphql/ci/runner_spec.rb
index ed180522c98..68c722c5e9d 100644
--- a/spec/requests/api/graphql/ci/runner_spec.rb
+++ b/spec/requests/api/graphql/ci/runner_spec.rb
@@ -614,8 +614,8 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do
'projectCount' => 2,
'projects' => {
'nodes' => [
- a_graphql_entity_for(project1),
- a_graphql_entity_for(project2)
+ a_graphql_entity_for(project2),
+ a_graphql_entity_for(project1)
]
})
expect(runner2_data).to match a_hash_including(
diff --git a/spec/requests/api/graphql/project/work_items_spec.rb b/spec/requests/api/graphql/project/work_items_spec.rb
index b792505374e..628a2117e9d 100644
--- a/spec/requests/api/graphql/project/work_items_spec.rb
+++ b/spec/requests/api/graphql/project/work_items_spec.rb
@@ -120,24 +120,55 @@ RSpec.describe 'getting a work item list for a project', feature_category: :team
end
context 'when querying WorkItemWidgetHierarchy' do
- let_it_be(:children) { create_list(:work_item, 3, :task, project: project) }
+ let_it_be(:children) { create_list(:work_item, 4, :task, project: project) }
let_it_be(:child_link1) { create(:parent_link, work_item_parent: item1, work_item: children[0]) }
+ let_it_be(:child_link2) { create(:parent_link, work_item_parent: item1, work_item: children[1]) }
let(:fields) do
<<~GRAPHQL
- nodes {
- widgets {
- type
- ... on WorkItemWidgetHierarchy {
- hasChildren
- parent { id }
- children { nodes { id } }
- }
+ nodes {
+ id
+ widgets {
+ type
+ ... on WorkItemWidgetHierarchy {
+ hasChildren
+ parent { id }
+ children { nodes { id } }
}
}
+ }
GRAPHQL
end
+ context 'with ordered children' do
+ let(:items_data) { graphql_data['project']['workItems']['nodes'] }
+ let(:work_item_data) { items_data.find { |item| item['id'] == item1.to_gid.to_s } }
+ let(:work_item_widget) { work_item_data["widgets"].find { |widget| widget.key?("children") } }
+ let(:children_ids) { work_item_widget.dig("children", "nodes").pluck("id") }
+
+ let(:first_child) { children[0].to_gid.to_s }
+ let(:second_child) { children[1].to_gid.to_s }
+
+ it 'returns children ordered by created_at by default' do
+ post_graphql(query, current_user: current_user)
+
+ expect(children_ids).to eq([first_child, second_child])
+ end
+
+ context 'when ordered by relative position' do
+ before do
+ child_link1.update!(relative_position: 20)
+ child_link2.update!(relative_position: 10)
+ end
+
+ it 'returns children in correct order' do
+ post_graphql(query, current_user: current_user)
+
+ expect(children_ids).to eq([second_child, first_child])
+ end
+ end
+ end
+
it 'executes limited number of N+1 queries' do
post_graphql(query, current_user: current_user) # warm-up
@@ -146,13 +177,11 @@ RSpec.describe 'getting a work item list for a project', feature_category: :team
end
parent_work_items = create_list(:work_item, 2, project: project)
- create(:parent_link, work_item_parent: parent_work_items[0], work_item: children[1])
- create(:parent_link, work_item_parent: parent_work_items[1], work_item: children[2])
+ create(:parent_link, work_item_parent: parent_work_items[0], work_item: children[2])
+ create(:parent_link, work_item_parent: parent_work_items[1], work_item: children[3])
- # There are 2 extra queries for fetching the children field
- # See: https://gitlab.com/gitlab-org/gitlab/-/issues/363569
expect { post_graphql(query, current_user: current_user) }
- .not_to exceed_query_limit(control).with_threshold(2)
+ .not_to exceed_query_limit(control)
end
it 'avoids N+1 queries when children are added to a work item' do
@@ -162,8 +191,8 @@ RSpec.describe 'getting a work item list for a project', feature_category: :team
post_graphql(query, current_user: current_user)
end
- create(:parent_link, work_item_parent: item1, work_item: children[1])
create(:parent_link, work_item_parent: item1, work_item: children[2])
+ create(:parent_link, work_item_parent: item1, work_item: children[3])
expect { post_graphql(query, current_user: current_user) }
.not_to exceed_query_limit(control)
diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb
index 78b83356675..8ad5aaa8bc3 100644
--- a/spec/requests/api/project_import_spec.rb
+++ b/spec/requests/api/project_import_spec.rb
@@ -247,7 +247,7 @@ RSpec.describe API::ProjectImport, :aggregate_failures, feature_category: :impor
subject
expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['message']).to eq('Project namespace name has already been taken')
+ expect(json_response['message']).to eq('Path has already been taken')
end
context 'when param overwrite is true' do
diff --git a/spec/serializers/ci/codequality_mr_diff_entity_spec.rb b/spec/serializers/ci/codequality_mr_diff_entity_spec.rb
index 4f161c36b06..a6e29a3914d 100644
--- a/spec/serializers/ci/codequality_mr_diff_entity_spec.rb
+++ b/spec/serializers/ci/codequality_mr_diff_entity_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::CodequalityMrDiffEntity do
+RSpec.describe Ci::CodequalityMrDiffEntity, feature_category: :code_quality do
let(:entity) { described_class.new(mr_diff_report) }
let(:mr_diff_report) { Gitlab::Ci::Reports::CodequalityMrDiff.new(codequality_report.all_degradations) }
let(:codequality_report) { Gitlab::Ci::Reports::CodequalityReports.new }
@@ -19,8 +19,17 @@ RSpec.describe Ci::CodequalityMrDiffEntity do
end
it 'contains correct codequality mr diff report', :aggregate_failures do
- expect(report[:files].keys).to eq(["file_a.rb"])
- expect(report[:files]["file_a.rb"].first).to include(:line, :description, :severity)
+ expect(report[:files].keys).to match_array(["file_a.rb"])
+ expect(report[:files]["file_a.rb"].first).to include(
+ :line,
+ :description,
+ :severity,
+ :engine_name,
+ :categories,
+ :content,
+ :location,
+ :other_locations,
+ :type)
end
end
end
diff --git a/spec/services/spam/spam_action_service_spec.rb b/spec/services/spam/spam_action_service_spec.rb
index 882b325b7b7..e2cc2ea7ce3 100644
--- a/spec/services/spam/spam_action_service_spec.rb
+++ b/spec/services/spam/spam_action_service_spec.rb
@@ -53,6 +53,16 @@ RSpec.describe Spam::SpamActionService, feature_category: :instance_resiliency d
end
end
+ shared_examples 'allows user' do
+ it 'does not perform spam check' do
+ expect(Spam::SpamVerdictService).not_to receive(:new)
+
+ response = subject
+
+ expect(response.message).to match(/user was allowlisted/)
+ end
+ end
+
shared_examples 'creates a spam log' do |target_type|
it do
expect { subject }
@@ -73,7 +83,6 @@ RSpec.describe Spam::SpamActionService, feature_category: :instance_resiliency d
shared_examples 'execute spam action service' do |target_type|
let(:fake_captcha_verification_service) { double(:captcha_verification_service) }
let(:fake_verdict_service) { double(:spam_verdict_service) }
- let(:allowlisted) { false }
let(:verdict_service_opts) do
{
@@ -101,7 +110,6 @@ RSpec.describe Spam::SpamActionService, feature_category: :instance_resiliency d
subject do
described_service = described_class.new(spammable: target, spam_params: spam_params, extra_features:
extra_features, user: user, action: :create)
- allow(described_service).to receive(:allowlisted?).and_return(allowlisted)
described_service.execute
end
@@ -158,16 +166,20 @@ RSpec.describe Spam::SpamActionService, feature_category: :instance_resiliency d
target.description = 'Lovely Spam! Wonderful Spam!'
end
- context 'when allowlisted' do
- let(:allowlisted) { true }
-
- it 'does not perform spam check' do
- expect(Spam::SpamVerdictService).not_to receive(:new)
+ context 'when user is a gitlab bot' do
+ before do
+ allow(user).to receive(:gitlab_bot?).and_return(true)
+ end
- response = subject
+ it_behaves_like 'allows user'
+ end
- expect(response.message).to match(/user was allowlisted/)
+ context 'when user is a gitlab service user' do
+ before do
+ allow(user).to receive(:gitlab_service_user?).and_return(true)
end
+
+ it_behaves_like 'allows user'
end
context 'when disallowed by the spam verdict service' do
diff --git a/spec/support/shared_contexts/models/distribution_shared_context.rb b/spec/support/shared_contexts/models/distribution_shared_context.rb
new file mode 100644
index 00000000000..30f6b750e22
--- /dev/null
+++ b/spec/support/shared_contexts/models/distribution_shared_context.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+# This shared context requires:
+# - factory: either :debian_project_distribution or :debian_group_distribution
+# - can_freeze: whether to freeze the created object or not
+RSpec.shared_context 'for Debian Distribution' do |factory, can_freeze|
+ let_it_be(:distribution_with_suite, freeze: can_freeze) { create(factory, :with_suite) }
+ let_it_be(:distribution_with_same_container, freeze: can_freeze) do
+ create(factory, container: distribution_with_suite.container)
+ end
+
+ let_it_be(:distribution_with_same_codename, freeze: can_freeze) do
+ create(factory, codename: distribution_with_suite.codename)
+ end
+
+ let_it_be(:distribution_with_same_suite, freeze: can_freeze) { create(factory, suite: distribution_with_suite.suite) }
+ let_it_be(:distribution_with_codename_and_suite_flipped, freeze: can_freeze) do
+ create(factory, codename: distribution_with_suite.suite, suite: distribution_with_suite.codename)
+ end
+
+ let_it_be_with_refind(:distribution) { create(factory, container: distribution_with_suite.container) }
+end
diff --git a/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb b/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb
index ac4ad4525aa..3ea2ff4d8f0 100644
--- a/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb
+++ b/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb
@@ -1,32 +1,13 @@
# frozen_string_literal: true
-require 'spec_helper'
-
-RSpec.shared_examples 'Debian Distribution' do |factory, container, can_freeze|
- let_it_be(:distribution_with_suite, freeze: can_freeze) { create(factory, :with_suite) }
- let_it_be(:distribution_with_same_container, freeze: can_freeze) { create(factory, container: distribution_with_suite.container ) }
- let_it_be(:distribution_with_same_codename, freeze: can_freeze) { create(factory, codename: distribution_with_suite.codename ) }
- let_it_be(:distribution_with_same_suite, freeze: can_freeze) { create(factory, suite: distribution_with_suite.suite ) }
- let_it_be(:distribution_with_codename_and_suite_flipped, freeze: can_freeze) { create(factory, codename: distribution_with_suite.suite, suite: distribution_with_suite.codename) }
-
- let_it_be_with_refind(:distribution) { create(factory, container: distribution_with_suite.container ) }
-
+RSpec.shared_examples 'Debian Distribution for common behavior' do
subject { distribution }
describe 'relationships' do
- it { is_expected.to belong_to(container) }
it { is_expected.to belong_to(:creator).class_name('User') }
-
- it { is_expected.to have_one(:key).class_name("Packages::Debian::#{container.capitalize}DistributionKey").with_foreign_key(:distribution_id).inverse_of(:distribution) }
- it { is_expected.to have_many(:components).class_name("Packages::Debian::#{container.capitalize}Component").inverse_of(:distribution) }
- it { is_expected.to have_many(:architectures).class_name("Packages::Debian::#{container.capitalize}Architecture").inverse_of(:distribution) }
end
describe 'validations' do
- describe "##{container}" do
- it { is_expected.to validate_presence_of(container) }
- end
-
describe "#creator" do
it { is_expected.not_to validate_presence_of(:creator) }
end
@@ -47,57 +28,6 @@ RSpec.shared_examples 'Debian Distribution' do |factory, container, can_freeze|
it { is_expected.not_to allow_value('hé').for(:suite) }
end
- describe '#unique_debian_suite_and_codename' do
- using RSpec::Parameterized::TableSyntax
-
- where(:with_existing_suite, :suite, :codename, :errors) do
- false | nil | :keep | nil
- false | 'testing' | :keep | nil
- false | nil | :codename | ["Codename has already been taken"]
- false | :codename | :keep | ["Suite has already been taken as Codename"]
- false | :codename | :codename | ["Codename has already been taken", "Suite has already been taken as Codename"]
- true | nil | :keep | nil
- true | 'testing' | :keep | nil
- true | nil | :codename | ["Codename has already been taken"]
- true | :codename | :keep | ["Suite has already been taken as Codename"]
- true | :codename | :codename | ["Codename has already been taken", "Suite has already been taken as Codename"]
- true | nil | :suite | ["Codename has already been taken as Suite"]
- true | :suite | :keep | ["Suite has already been taken"]
- true | :suite | :suite | ["Suite has already been taken", "Codename has already been taken as Suite"]
- end
-
- with_them do
- context factory do
- let(:new_distribution) { build(factory, container: distribution.container) }
-
- before do
- distribution.update_column(:suite, 'suite-' + distribution.codename) if with_existing_suite
-
- if suite.is_a?(Symbol)
- new_distribution.suite = distribution.send suite unless suite == :keep
- else
- new_distribution.suite = suite
- end
-
- if codename.is_a?(Symbol)
- new_distribution.codename = distribution.send codename unless codename == :keep
- else
- new_distribution.codename = codename
- end
- end
-
- it do
- if errors
- expect(new_distribution).not_to be_valid
- expect(new_distribution.errors.to_a).to eq(errors)
- else
- expect(new_distribution).to be_valid
- end
- end
- end
- end
- end
-
describe '#origin' do
it { is_expected.to allow_value(nil).for(:origin) }
it { is_expected.to allow_value('Debian').for(:origin) }
@@ -179,7 +109,11 @@ RSpec.shared_examples 'Debian Distribution' do |factory, container, can_freeze|
subject { described_class.with_codename_or_suite(distribution_with_suite.codename) }
it 'does not return other distributions' do
- expect(subject.to_a).to contain_exactly(distribution_with_suite, distribution_with_same_codename, distribution_with_codename_and_suite_flipped)
+ expect(subject.to_a)
+ .to contain_exactly(
+ distribution_with_suite,
+ distribution_with_same_codename,
+ distribution_with_codename_and_suite_flipped)
end
end
@@ -187,54 +121,169 @@ RSpec.shared_examples 'Debian Distribution' do |factory, container, can_freeze|
subject { described_class.with_codename_or_suite(distribution_with_suite.suite) }
it 'does not return other distributions' do
- expect(subject.to_a).to contain_exactly(distribution_with_suite, distribution_with_same_suite, distribution_with_codename_and_suite_flipped)
+ expect(subject.to_a)
+ .to contain_exactly(
+ distribution_with_suite,
+ distribution_with_same_suite,
+ distribution_with_codename_and_suite_flipped)
end
end
end
end
+end
- if container == :project
- describe 'project distribution specifics' do
- describe 'relationships' do
- it { is_expected.to have_many(:publications).class_name('Packages::Debian::Publication').inverse_of(:distribution).with_foreign_key(:distribution_id) }
- it { is_expected.to have_many(:packages).class_name('Packages::Package').through(:publications) }
- end
+RSpec.shared_examples 'Debian Distribution for specific behavior' do |factory|
+ describe '#unique_debian_suite_and_codename' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:with_existing_suite, :suite, :codename, :errors) do
+ false | nil | :keep | nil
+ false | 'testing' | :keep | nil
+ false | nil | :codename | ["Codename has already been taken"]
+ false | :codename | :keep | ["Suite has already been taken as Codename"]
+ false | :codename | :codename | ["Codename has already been taken", "Suite has already been taken as Codename"]
+ true | nil | :keep | nil
+ true | 'testing' | :keep | nil
+ true | nil | :codename | ["Codename has already been taken"]
+ true | :codename | :keep | ["Suite has already been taken as Codename"]
+ true | :codename | :codename | ["Codename has already been taken", "Suite has already been taken as Codename"]
+ true | nil | :suite | ["Codename has already been taken as Suite"]
+ true | :suite | :keep | ["Suite has already been taken"]
+ true | :suite | :suite | ["Suite has already been taken", "Codename has already been taken as Suite"]
end
- else
- describe 'group distribution specifics' do
- let_it_be(:public_project) { create(:project, :public, group: distribution_with_suite.container) }
- let_it_be(:public_distribution_with_same_codename) { create(:debian_project_distribution, container: public_project, codename: distribution_with_suite.codename) }
- let_it_be(:public_package_with_same_codename) { create(:debian_package, project: public_project, published_in: public_distribution_with_same_codename) }
- let_it_be(:public_distribution_with_same_suite) { create(:debian_project_distribution, container: public_project, suite: distribution_with_suite.suite) }
- let_it_be(:public_package_with_same_suite) { create(:debian_package, project: public_project, published_in: public_distribution_with_same_suite) }
-
- let_it_be(:private_project) { create(:project, :private, group: distribution_with_suite.container) }
- let_it_be(:private_distribution_with_same_codename) { create(:debian_project_distribution, container: private_project, codename: distribution_with_suite.codename) }
- let_it_be(:private_package_with_same_codename) { create(:debian_package, project: private_project, published_in: private_distribution_with_same_codename) }
- let_it_be(:private_distribution_with_same_suite) { create(:debian_project_distribution, container: private_project, suite: distribution_with_suite.suite) }
- let_it_be(:private_package_with_same_suite) { create(:debian_package, project: private_project, published_in: private_distribution_with_same_codename) }
-
- describe '#packages' do
- subject { distribution_with_suite.packages }
-
- it 'returns only public packages with same codename' do
- expect(subject.to_a).to contain_exactly(public_package_with_same_codename)
+
+ with_them do
+ context factory do
+ let(:new_distribution) { build(factory, container: distribution.container) }
+
+ before do
+ distribution.update_column(:suite, "suite-#{distribution.codename}") if with_existing_suite
+
+ if suite.is_a?(Symbol)
+ new_distribution.suite = distribution.send suite unless suite == :keep
+ else
+ new_distribution.suite = suite
+ end
+
+ if codename.is_a?(Symbol)
+ new_distribution.codename = distribution.send codename unless codename == :keep
+ else
+ new_distribution.codename = codename
+ end
+ end
+
+ it do
+ if errors
+ expect(new_distribution).not_to be_valid
+ expect(new_distribution.errors.to_a).to eq(errors)
+ else
+ expect(new_distribution).to be_valid
+ end
end
end
+ end
+ end
+end
- describe '#package_files' do
- subject { distribution_with_suite.package_files }
+RSpec.shared_examples 'Debian Distribution with project container' do
+ it_behaves_like 'Debian Distribution for specific behavior', :debian_project_distribution
- it 'returns only files from public packages with same codename' do
- expect(subject.to_a).to contain_exactly(*public_package_with_same_codename.package_files)
- end
+ describe 'relationships' do
+ it { is_expected.to belong_to(:project) }
- context 'with pending destruction package files' do
- let_it_be(:package_file_pending_destruction) { create(:package_file, :pending_destruction, package: public_package_with_same_codename) }
+ it { is_expected.to have_one(:key).class_name("Packages::Debian::ProjectDistributionKey").with_foreign_key(:distribution_id).inverse_of(:distribution) }
+ it { is_expected.to have_many(:components).class_name("Packages::Debian::ProjectComponent").inverse_of(:distribution) }
+ it { is_expected.to have_many(:architectures).class_name("Packages::Debian::ProjectArchitecture").inverse_of(:distribution) }
+ end
- it 'does not return them' do
- expect(subject.to_a).not_to include(package_file_pending_destruction)
- end
+ describe "#project" do
+ it { is_expected.to validate_presence_of(:project) }
+ end
+
+ describe 'project distribution specifics' do
+ describe 'relationships' do
+ it do
+ is_expected.to have_many(:publications).class_name('Packages::Debian::Publication').inverse_of(:distribution)
+ .with_foreign_key(:distribution_id)
+ end
+
+ it { is_expected.to have_many(:packages).class_name('Packages::Package').through(:publications) }
+ end
+ end
+end
+
+RSpec.shared_examples 'Debian Distribution with group container' do
+ it_behaves_like 'Debian Distribution for specific behavior', :debian_group_distribution
+
+ describe 'relationships' do
+ it { is_expected.to belong_to(:group) }
+
+ it { is_expected.to have_one(:key).class_name("Packages::Debian::GroupDistributionKey").with_foreign_key(:distribution_id).inverse_of(:distribution) }
+ it { is_expected.to have_many(:components).class_name("Packages::Debian::GroupComponent").inverse_of(:distribution) }
+ it { is_expected.to have_many(:architectures).class_name("Packages::Debian::GroupArchitecture").inverse_of(:distribution) }
+ end
+
+ describe "#group" do
+ it { is_expected.to validate_presence_of(:group) }
+ end
+
+ describe 'group distribution specifics' do
+ let_it_be(:public_project) { create(:project, :public, group: distribution_with_suite.container) }
+ let_it_be(:public_distribution_with_same_codename) do
+ create(:debian_project_distribution, container: public_project, codename: distribution_with_suite.codename)
+ end
+
+ let_it_be(:public_package_with_same_codename) do
+ create(:debian_package, project: public_project, published_in: public_distribution_with_same_codename)
+ end
+
+ let_it_be(:public_distribution_with_same_suite) do
+ create(:debian_project_distribution, container: public_project, suite: distribution_with_suite.suite)
+ end
+
+ let_it_be(:public_package_with_same_suite) do
+ create(:debian_package, project: public_project, published_in: public_distribution_with_same_suite)
+ end
+
+ let_it_be(:private_project) { create(:project, :private, group: distribution_with_suite.container) }
+ let_it_be(:private_distribution_with_same_codename) do
+ create(:debian_project_distribution, container: private_project, codename: distribution_with_suite.codename)
+ end
+
+ let_it_be(:private_package_with_same_codename) do
+ create(:debian_package, project: private_project, published_in: private_distribution_with_same_codename)
+ end
+
+ let_it_be(:private_distribution_with_same_suite) do
+ create(:debian_project_distribution, container: private_project, suite: distribution_with_suite.suite)
+ end
+
+ let_it_be(:private_package_with_same_suite) do
+ create(:debian_package, project: private_project, published_in: private_distribution_with_same_codename)
+ end
+
+ describe '#packages' do
+ subject { distribution_with_suite.packages }
+
+ it 'returns only public packages with same codename' do
+ expect(subject.to_a).to contain_exactly(public_package_with_same_codename)
+ end
+ end
+
+ describe '#package_files' do
+ subject { distribution_with_suite.package_files }
+
+ it 'returns only files from public packages with same codename' do
+ expect(subject.to_a).to contain_exactly(*public_package_with_same_codename.package_files)
+ end
+
+ context 'with pending destruction package files' do
+ let_it_be(:package_file_pending_destruction) do
+ create(:package_file, :pending_destruction, package: public_package_with_same_codename)
+ end
+
+ it 'does not return them' do
+ expect(subject.to_a).not_to include(package_file_pending_destruction)
end
end
end
diff --git a/spec/tooling/lib/tooling/parallel_rspec_runner_spec.rb b/spec/tooling/lib/tooling/parallel_rspec_runner_spec.rb
index 4b44d991d89..b7b39c37819 100644
--- a/spec/tooling/lib/tooling/parallel_rspec_runner_spec.rb
+++ b/spec/tooling/lib/tooling/parallel_rspec_runner_spec.rb
@@ -1,90 +1,84 @@
# frozen_string_literal: true
+require 'tempfile'
+
require_relative '../../../../tooling/lib/tooling/parallel_rspec_runner'
RSpec.describe Tooling::ParallelRSpecRunner do # rubocop:disable RSpec/FilePath
describe '#run' do
- let(:allocator) { instance_double(Knapsack::Allocator) }
- let(:rspec_args) { '--seed 123' }
- let(:filter_tests_file) { 'tests.txt' }
- let(:node_tests) { %w[01_spec.rb 03_spec.rb 05_spec.rb] }
- let(:filter_tests) { '01_spec.rb 02_spec.rb 03_spec.rb' }
let(:test_dir) { 'spec' }
+ let(:node_tests) { %w[01_spec.rb 03_spec.rb] }
+ let(:allocator) { instance_double(Knapsack::Allocator, test_dir: test_dir, node_tests: node_tests) }
+ let(:allocator_builder) { double(Knapsack::AllocatorBuilder, allocator: allocator) } # rubocop:disable RSpec/VerifiedDoubles
+
+ let(:filter_tests) { [] }
+ let(:filter_tests_file) { nil }
+ let(:filter_tests_file_path) { nil }
before do
+ allow(Knapsack::AllocatorBuilder).to receive(:new).and_return(allocator_builder)
allow(Knapsack.logger).to receive(:info)
- allow(allocator).to receive(:node_tests).and_return(node_tests)
- allow(allocator).to receive(:test_dir).and_return(test_dir)
- allow(File).to receive(:exist?).with(filter_tests_file).and_return(true)
- allow(File).to receive(:read).and_call_original
- allow(File).to receive(:read).with(filter_tests_file).and_return(filter_tests)
- allow(subject).to receive(:exec)
end
- subject { described_class.new(allocator: allocator, filter_tests_file: filter_tests_file, rspec_args: rspec_args) }
-
- shared_examples 'runs node tests' do
- it 'runs rspec with tests allocated for this node' do
- expect_command(%w[bundle exec rspec --seed 123 --default-path spec -- 01_spec.rb 03_spec.rb 05_spec.rb])
-
- subject.run
+ after do
+ if filter_tests_file.respond_to?(:close)
+ filter_tests_file.close
+ File.unlink(filter_tests_file)
end
end
- context 'given filter tests' do
- it 'reads filter tests file for list of tests' do
- expect(File).to receive(:read).with(filter_tests_file)
+ subject { described_class.new(filter_tests_file: filter_tests_file_path, rspec_args: rspec_args) }
- subject.run
- end
+ shared_examples 'runs node tests' do
+ let(:rspec_args) { nil }
- it 'runs rspec filter tests that are allocated for this node' do
- expect_command(%w[bundle exec rspec --seed 123 --default-path spec -- 01_spec.rb 03_spec.rb])
+ it 'runs rspec with tests allocated for this node' do
+ expect(allocator_builder).to receive(:filter_tests=).with(filter_tests)
+ expect_command(%W[bundle exec rspec#{rspec_args} --] + node_tests)
subject.run
end
-
- context 'when there is no intersect between allocated tests and filtered tests' do
- let(:filter_tests) { '99_spec.rb' }
-
- it 'does not run rspec' do
- expect(subject).not_to receive(:exec)
-
- subject.run
- end
- end
end
- context 'with empty filter tests file' do
- let(:filter_tests) { '' }
+ context 'without filter_tests_file option' do
+ subject { described_class.new(rspec_args: rspec_args) }
it_behaves_like 'runs node tests'
end
- context 'without filter_tests_file option' do
- let(:filter_tests_file) { nil }
+ context 'given filter tests file' do
+ let(:filter_tests_file) do
+ Tempfile.create.tap do |f| # rubocop:disable Rails/SaveBang
+ f.write(filter_tests.join(' '))
+ f.rewind
+ end
+ end
- it_behaves_like 'runs node tests'
- end
+ let(:filter_tests_file_path) { filter_tests_file.path }
- context 'if filter_tests_file does not exist' do
- before do
- allow(File).to receive(:exist?).with(filter_tests_file).and_return(false)
+ context 'when filter_tests_file is empty' do
+ it_behaves_like 'runs node tests'
end
- it_behaves_like 'runs node tests'
- end
+ context 'when filter_tests_file does not exist' do
+ let(:filter_tests_file_path) { 'doesnt_exist' }
- context 'without rspec args' do
- let(:rspec_args) { nil }
+ it_behaves_like 'runs node tests'
+ end
- it 'runs rspec with without extra arguments' do
- expect_command(%w[bundle exec rspec --default-path spec -- 01_spec.rb 03_spec.rb])
+ context 'when filter_tests_file is not empty' do
+ let(:filter_tests) { %w[01_spec.rb 02_spec.rb 03_spec.rb] }
- subject.run
+ it_behaves_like 'runs node tests'
end
end
+ context 'with rspec args' do
+ let(:rspec_args) { ' --seed 123' }
+
+ it_behaves_like 'runs node tests'
+ end
+
def expect_command(cmd)
expect(subject).to receive(:exec).with(*cmd)
end
diff --git a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb
index 15f5229cc43..94ea9043857 100644
--- a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb
+++ b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb
@@ -382,6 +382,10 @@ RSpec.describe 'layouts/nav/sidebar/_project', feature_category: :navigation do
end
describe 'Monitor' do
+ before do
+ stub_feature_flags(remove_monitor_metrics: false)
+ end
+
it 'top level navigation link is visible for user with permissions' do
render
diff --git a/tooling/lib/tooling/parallel_rspec_runner.rb b/tooling/lib/tooling/parallel_rspec_runner.rb
index b1ddc91e831..834d9ec23a7 100644
--- a/tooling/lib/tooling/parallel_rspec_runner.rb
+++ b/tooling/lib/tooling/parallel_rspec_runner.rb
@@ -1,6 +1,60 @@
# frozen_string_literal: true
require 'knapsack'
+require 'fileutils'
+
+module Knapsack
+ module Distributors
+ class BaseDistributor
+ # Refine https://github.com/KnapsackPro/knapsack/blob/v1.21.1/lib/knapsack/distributors/base_distributor.rb
+ # to take in account the additional filtering we do for predictive jobs.
+ module BaseDistributorWithTestFiltering
+ attr_reader :filter_tests
+
+ def initialize(args = {})
+ super
+
+ @filter_tests = args[:filter_tests]
+ end
+
+ def all_tests
+ @all_tests ||= begin
+ pattern_tests = Dir.glob(test_file_pattern).uniq
+
+ if filter_tests.empty?
+ pattern_tests
+ else
+ pattern_tests & filter_tests
+ end
+ end.sort
+ end
+ end
+
+ prepend BaseDistributorWithTestFiltering
+ end
+ end
+
+ class AllocatorBuilder
+ # Refine https://github.com/KnapsackPro/knapsack/blob/v1.21.1/lib/knapsack/allocator_builder.rb
+ # to take in account the additional filtering we do for predictive jobs.
+ module AllocatorBuilderWithTestFiltering
+ attr_accessor :filter_tests
+
+ def allocator
+ Knapsack::Allocator.new({
+ report: Knapsack.report.open,
+ test_file_pattern: test_file_pattern,
+ ci_node_total: Knapsack::Config::Env.ci_node_total,
+ ci_node_index: Knapsack::Config::Env.ci_node_index,
+ # Additional argument
+ filter_tests: filter_tests
+ })
+ end
+ end
+
+ prepend AllocatorBuilderWithTestFiltering
+ end
+end
# A custom parallel rspec runner based on Knapsack runner
# which takes in additional option for a file containing
@@ -13,32 +67,26 @@ require 'knapsack'
# would be executed in the CI node.
#
# Reference:
-# https://github.com/ArturT/knapsack/blob/v1.20.0/lib/knapsack/runners/rspec_runner.rb
+# https://github.com/ArturT/knapsack/blob/v1.21.1/lib/knapsack/runners/rspec_runner.rb
module Tooling
class ParallelRSpecRunner
def self.run(rspec_args: nil, filter_tests_file: nil)
new(rspec_args: rspec_args, filter_tests_file: filter_tests_file).run
end
- def initialize(allocator: knapsack_allocator, filter_tests_file: nil, rspec_args: nil)
- @allocator = allocator
+ def initialize(filter_tests_file: nil, rspec_args: nil)
@filter_tests_file = filter_tests_file
@rspec_args = rspec_args&.split(' ') || []
end
def run
- Knapsack.logger.info
- Knapsack.logger.info 'Knapsack node specs:'
- Knapsack.logger.info node_tests
- Knapsack.logger.info
- Knapsack.logger.info 'Filter specs:'
- Knapsack.logger.info filter_tests
- Knapsack.logger.info
- Knapsack.logger.info 'Running specs:'
- Knapsack.logger.info tests_to_run
- Knapsack.logger.info
-
- if tests_to_run.empty?
+ if ENV['KNAPSACK_RSPEC_SUITE_REPORT_PATH']
+ knapsack_dir = File.dirname(ENV['KNAPSACK_RSPEC_SUITE_REPORT_PATH'])
+ FileUtils.mkdir_p(knapsack_dir)
+ File.write(File.join(knapsack_dir, 'node_specs.txt'), node_tests.join("\n"))
+ end
+
+ if node_tests.empty?
Knapsack.logger.info 'No tests to run on this node, exiting.'
return
end
@@ -50,26 +98,16 @@ module Tooling
private
- attr_reader :allocator, :filter_tests_file, :rspec_args
+ attr_reader :filter_tests_file, :rspec_args
def rspec_command
%w[bundle exec rspec].tap do |cmd|
cmd.push(*rspec_args)
- cmd.push('--default-path', allocator.test_dir)
cmd.push('--')
- cmd.push(*tests_to_run)
+ cmd.push(*node_tests)
end
end
- def tests_to_run
- if filter_tests.empty?
- Knapsack.logger.info 'Running all node tests without filter'
- return node_tests
- end
-
- @tests_to_run ||= node_tests & filter_tests
- end
-
def node_tests
allocator.node_tests
end
@@ -85,8 +123,11 @@ module Tooling
File.read(filter_tests_file).split(' ')
end
- def knapsack_allocator
- Knapsack::AllocatorBuilder.new(Knapsack::Adapters::RSpecAdapter).allocator
+ def allocator
+ @allocator ||=
+ Knapsack::AllocatorBuilder.new(Knapsack::Adapters::RSpecAdapter).tap do |builder|
+ builder.filter_tests = filter_tests
+ end.allocator
end
end
end