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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-06-06 18:09:27 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-06-06 18:09:27 +0300
commit638e2f1c5f55988135da63c7aa57bcecb9355a2b (patch)
treec25a1deeec9e02411f52a5eb831c42fa41778f9a
parent4958d96e262f6b31b2850123e4949536555b2d29 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/ci-templates.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/dev-fixtures.gitlab-ci.yml4
-rw-r--r--.gitlab/ci/frontend.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/glfm.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/global.gitlab-ci.yml87
-rw-r--r--.gitlab/ci/memory.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/rails.gitlab-ci.yml462
-rw-r--r--.gitlab/ci/rails/rspec-foss-impact.gitlab-ci.yml.erb2
-rw-r--r--.gitlab/ci/rails/shared.gitlab-ci.yml38
-rw-r--r--.gitlab/ci/setup.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/static-analysis.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/test-metadata.gitlab-ci.yml20
-rw-r--r--.gitlab/issue_templates/Pipeline Security issue implementation98
-rw-r--r--.rubocop_todo/rspec/verified_doubles.yml1
-rw-r--r--app/assets/javascripts/deprecated_notes.js18
-rw-r--r--app/assets/javascripts/notes/components/comment_form.vue8
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue23
-rw-r--r--app/assets/javascripts/notes/i18n.js1
-rw-r--r--app/assets/javascripts/notes/stores/actions.js27
-rw-r--r--app/assets/javascripts/notes/stores/mutations.js2
-rw-r--r--app/assets/javascripts/notes/utils.js17
-rw-r--r--app/controllers/concerns/notes_actions.rb8
-rw-r--r--app/controllers/concerns/spammable_actions/captcha_check/html_format_actions_support.rb5
-rw-r--r--app/controllers/projects/issues_controller.rb10
-rw-r--r--app/graphql/mutations/issues/create.rb4
-rw-r--r--app/graphql/mutations/issues/set_confidential.rb7
-rw-r--r--app/graphql/mutations/issues/update.rb3
-rw-r--r--app/graphql/mutations/snippets/create.rb3
-rw-r--r--app/graphql/mutations/snippets/update.rb3
-rw-r--r--app/graphql/mutations/work_items/convert.rb4
-rw-r--r--app/graphql/mutations/work_items/create.rb2
-rw-r--r--app/graphql/mutations/work_items/create_from_task.rb5
-rw-r--r--app/graphql/mutations/work_items/update.rb3
-rw-r--r--app/graphql/mutations/work_items/update_task.rb4
-rw-r--r--app/helpers/appearances_helper.rb8
-rw-r--r--app/models/merge_request/diff_llm_summary.rb1
-rw-r--r--app/services/boards/issues/create_service.rb2
-rw-r--r--app/services/ci/pipelines/add_job_service.rb6
-rw-r--r--app/services/import_csv/base_service.rb6
-rw-r--r--app/services/incident_management/incidents/create_service.rb2
-rw-r--r--app/services/integrations/slack_interactions/incident_management/incident_modal_submit_service.rb2
-rw-r--r--app/services/issues/clone_service.rb6
-rw-r--r--app/services/issues/create_service.rb19
-rw-r--r--app/services/issues/move_service.rb6
-rw-r--r--app/services/issues/update_service.rb13
-rw-r--r--app/services/snippets/create_service.rb25
-rw-r--r--app/services/snippets/update_service.rb24
-rw-r--r--app/services/spam/spam_action_service.rb13
-rw-r--r--app/services/tasks_to_be_done/base_service.rb2
-rw-r--r--app/services/user_agent_detail_service.rb13
-rw-r--r--app/services/work_items/create_and_link_service.rb6
-rw-r--r--app/services/work_items/create_from_task_service.rb6
-rw-r--r--app/services/work_items/create_service.rb4
-rw-r--r--app/services/work_items/update_service.rb4
-rw-r--r--app/views/devise/sessions/_new_base.html.haml3
-rw-r--r--app/views/layouts/devise.html.haml16
-rw-r--r--db/migrate/20230601084041_add_merge_request_diff_llm_summaries_unique_index.rb18
-rw-r--r--db/post_migrate/20230605093005_add_index_for_sbom_occurrences_on_project_id_source_id.rb15
-rw-r--r--db/schema_migrations/202306010840411
-rw-r--r--db/schema_migrations/202306050930051
-rw-r--r--db/structure.sql6
-rw-r--r--doc/administration/lfs/index.md2
-rw-r--r--doc/ci/cloud_services/azure/index.md2
-rw-r--r--doc/ci/cloud_services/index.md3
-rw-r--r--doc/ci/pipelines/cicd_minutes.md70
-rw-r--r--doc/ci/testing/test_coverage_visualization.md2
-rw-r--r--doc/ci/testing/unit_test_report_examples.md2
-rw-r--r--doc/development/contributing/design.md4
-rw-r--r--doc/development/integrations/index.md2
-rw-r--r--doc/development/pipelines/index.md28
-rw-r--r--doc/development/pipelines/internals.md2
-rw-r--r--doc/development/snowplow/implementation.md2
-rw-r--r--doc/development/spam_protection_and_captcha/graphql_api.md10
-rw-r--r--doc/development/spam_protection_and_captcha/rest_api.md10
-rw-r--r--doc/development/spam_protection_and_captcha/web_ui.md16
-rw-r--r--doc/security/hardening.md67
-rw-r--r--doc/security/hardening_application_recommendations.md240
-rw-r--r--doc/security/hardening_cicd_recommendations.md69
-rw-r--r--doc/security/hardening_configuration_recommendations.md161
-rw-r--r--doc/security/hardening_general_concepts.md88
-rw-r--r--doc/security/hardening_operating_system_recommendations.md167
-rw-r--r--doc/security/index.md2
-rw-r--r--doc/subscriptions/gitlab_com/index.md14
-rw-r--r--doc/user/application_security/policies/scan-result-policies.md3
-rw-r--r--doc/user/group/saml_sso/group_sync.md2
-rw-r--r--doc/user/project/settings/project_access_tokens.md5
-rw-r--r--lib/api/issues.rb7
-rw-r--r--lib/api/project_snippets.rb8
-rw-r--r--lib/api/snippets.rb8
-rw-r--r--lib/gitlab/database/schema_validation/track_inconsistency.rb7
-rw-r--r--lib/gitlab/email/handler/create_issue_handler.rb2
-rw-r--r--lib/gitlab/email/handler/service_desk_handler.rb2
-rw-r--r--lib/gitlab/slash_commands/issue_new.rb2
-rw-r--r--lib/gitlab/usage_data_counters/hll_redis_counter.rb5
-rw-r--r--lib/quality/seeders/issues.rb2
-rw-r--r--locale/gitlab.pot65
-rw-r--r--qa/qa/fixtures/mocks/import/github.yml2
-rwxr-xr-xscripts/failed_tests.rb4
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb20
-rw-r--r--spec/controllers/projects/notes_controller_spec.rb25
-rw-r--r--spec/features/calendar_spec.rb18
-rw-r--r--spec/features/unsubscribe_links_spec.rb2
-rw-r--r--spec/features/users/user_browses_projects_on_user_page_spec.rb2
-rw-r--r--spec/fixtures/scripts/test_report.json2
-rw-r--r--spec/frontend/jobs/components/job/manual_variables_form_spec.js146
-rw-r--r--spec/frontend/notes/components/comment_form_spec.js30
-rw-r--r--spec/frontend/notes/components/noteable_discussion_spec.js79
-rw-r--r--spec/frontend/notes/stores/actions_spec.js20
-rw-r--r--spec/frontend/notes/stores/mutation_spec.js22
-rw-r--r--spec/frontend/notes/utils_spec.js46
-rw-r--r--spec/graphql/mutations/issues/create_spec.rb1
-rw-r--r--spec/graphql/mutations/issues/set_confidential_spec.rb4
-rw-r--r--spec/graphql/mutations/issues/update_spec.rb4
-rw-r--r--spec/graphql/mutations/work_items/update_task_spec.rb4
-rw-r--r--spec/helpers/appearances_helper_spec.rb24
-rw-r--r--spec/lib/gitlab/database/schema_validation/track_inconsistency_spec.rb4
-rw-r--r--spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb22
-rw-r--r--spec/models/integrations/microsoft_teams_spec.rb2
-rw-r--r--spec/models/merge_request/diff_llm_summary_spec.rb1
-rw-r--r--spec/requests/api/graphql/mutations/snippets/create_spec.rb6
-rw-r--r--spec/requests/api/graphql/mutations/snippets/update_spec.rb4
-rw-r--r--spec/scripts/failed_tests_spec.rb6
-rw-r--r--spec/services/ci/pipelines/add_job_service_spec.rb10
-rw-r--r--spec/services/issues/create_service_spec.rb70
-rw-r--r--spec/services/snippets/create_service_spec.rb7
-rw-r--r--spec/services/snippets/update_service_spec.rb7
-rw-r--r--spec/services/spam/spam_action_service_spec.rb8
-rw-r--r--spec/services/tasks_to_be_done/base_service_spec.rb2
-rw-r--r--spec/services/user_agent_detail_service_spec.rb41
-rw-r--r--spec/services/work_items/create_and_link_service_spec.rb7
-rw-r--r--spec/services/work_items/create_from_task_service_spec.rb7
-rw-r--r--spec/services/work_items/create_service_spec.rb22
-rw-r--r--spec/services/work_items/update_service_spec.rb8
-rw-r--r--spec/spec_helper.rb1
-rw-r--r--spec/support/helpers/stub_spam_services.rb23
-rw-r--r--spec/support/shared_examples/graphql/mutations/can_mutate_spammable_examples.rb19
-rw-r--r--spec/support/shared_examples/models/chat_integration_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/rate_limited_service_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/services/snippets_shared_examples.rb1
140 files changed, 2082 insertions, 851 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 443807bfb90..7816ae1391c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -121,7 +121,7 @@ workflow:
PIPELINE_NAME: 'Ruby $RUBY_VERSION $CI_COMMIT_BRANCH branch pipeline'
variables:
- PG_VERSION: "13"
+ PG_VERSION: "14"
DEFAULT_CI_IMAGE: "${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-${DEBIAN_VERSION}-ruby-${RUBY_VERSION}.patched-golang-${GO_VERSION}-rust-${RUST_VERSION}-node-18.16-postgresql-${PG_VERSION}:rubygems-${RUBYGEMS_VERSION}-git-2.36-lfs-2.9-chrome-${CHROME_VERSION}-yarn-1.22-graphicsmagick-1.3.36"
# We set $GITLAB_DEPENDENCY_PROXY to another variable (since it's set at the group level and has higher precedence than .gitlab-ci.yml)
# so that we can override $GITLAB_DEPENDENCY_PROXY_ADDRESS in workflow rules.
diff --git a/.gitlab/ci/ci-templates.gitlab-ci.yml b/.gitlab/ci/ci-templates.gitlab-ci.yml
index c6681db9685..a00de8cb533 100644
--- a/.gitlab/ci/ci-templates.gitlab-ci.yml
+++ b/.gitlab/ci/ci-templates.gitlab-ci.yml
@@ -4,7 +4,7 @@ templates-shellcheck:
- .default-before_script
- .default-retry
- .ruby-cache
- - .use-pg14
+ - .use-pg15
stage: test
needs:
- setup-test-env
diff --git a/.gitlab/ci/dev-fixtures.gitlab-ci.yml b/.gitlab/ci/dev-fixtures.gitlab-ci.yml
index 5464e154b3f..642bad53772 100644
--- a/.gitlab/ci/dev-fixtures.gitlab-ci.yml
+++ b/.gitlab/ci/dev-fixtures.gitlab-ci.yml
@@ -3,7 +3,7 @@
- .default-retry
- .ruby-cache
- .default-before_script
- - .use-pg13
+ - .use-pg14
stage: test
needs: ["setup-test-env"]
variables:
@@ -29,7 +29,7 @@ run-dev-fixtures-ee:
extends:
- .run-dev-fixtures
- .dev-fixtures:rules:ee-only
- - .use-pg13-es7-ee
+ - .use-pg14-es7-ee
script:
- cp ee/db/fixtures/development/* $FIXTURE_PATH
- *run-dev-fixtures-script
diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml
index 790a9140445..0e630fcd811 100644
--- a/.gitlab/ci/frontend.gitlab-ci.yml
+++ b/.gitlab/ci/frontend.gitlab-ci.yml
@@ -129,7 +129,7 @@ retrieve-frontend-fixtures:
- .default-retry
- .default-before_script
- .ruby-cache
- - .use-pg13
+ - .use-pg14
stage: fixtures
needs: ["setup-test-env", "retrieve-tests-metadata", "retrieve-frontend-fixtures"]
variables:
diff --git a/.gitlab/ci/glfm.gitlab-ci.yml b/.gitlab/ci/glfm.gitlab-ci.yml
index 008bb03de25..3f9ddaa791c 100644
--- a/.gitlab/ci/glfm.gitlab-ci.yml
+++ b/.gitlab/ci/glfm.gitlab-ci.yml
@@ -2,7 +2,7 @@ glfm-verify:
extends:
- .rails-job-base
- .glfm:rules:glfm-verify
- - .use-pg13
+ - .use-pg14
stage: test
needs: ["setup-test-env"]
script:
diff --git a/.gitlab/ci/global.gitlab-ci.yml b/.gitlab/ci/global.gitlab-ci.yml
index 120e5117fb2..39eb005ac2d 100644
--- a/.gitlab/ci/global.gitlab-ci.yml
+++ b/.gitlab/ci/global.gitlab-ci.yml
@@ -249,6 +249,18 @@
POSTGRES_HOST_AUTH_METHOD: trust
PG_VERSION: "14"
+.use-pg15:
+ services:
+ - name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-15-pgvector-0.4.1
+ command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
+ alias: postgres
+ - name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:redis-cluster-6.2.12
+ alias: rediscluster # configure connections in config/redis.yml
+ - name: redis:6.2-alpine
+ variables:
+ POSTGRES_HOST_AUTH_METHOD: trust
+ PG_VERSION: "15"
+
.use-pg12-es7-ee:
services:
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-12-pgvector-0.4.1
@@ -303,6 +315,24 @@
ZOEKT_INDEX_BASE_URL: http://zoekt-ci-image:6060
ZOEKT_SEARCH_BASE_URL: http://zoekt-ci-image:6070
+.use-pg15-es7-ee:
+ services:
+ - name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-15-pgvector-0.4.1
+ command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
+ alias: postgres
+ - name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:redis-cluster-6.2.12
+ alias: rediscluster # configure connections in config/redis.yml
+ - name: redis:6.2-alpine
+ - name: elasticsearch:7.17.6
+ command: ["elasticsearch", "-E", "discovery.type=single-node", "-E", "xpack.security.enabled=false"]
+ - name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:zoekt-ci-image-1.0
+ alias: zoekt-ci-image
+ variables:
+ POSTGRES_HOST_AUTH_METHOD: trust
+ PG_VERSION: "15"
+ ZOEKT_INDEX_BASE_URL: http://zoekt-ci-image:6060
+ ZOEKT_SEARCH_BASE_URL: http://zoekt-ci-image:6070
+
.use-pg13-es8-ee:
services:
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-13-pgvector-0.4.1
@@ -341,6 +371,25 @@
ZOEKT_INDEX_BASE_URL: http://zoekt-ci-image:6060
ZOEKT_SEARCH_BASE_URL: http://zoekt-ci-image:6070
+.use-pg15-es8-ee:
+ services:
+ - name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-15-pgvector-0.4.1
+ command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
+ alias: postgres
+ - name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:redis-cluster-6.2.12
+ alias: rediscluster # configure connections in config/redis.yml
+ - name: redis:6.0-alpine
+ - name: elasticsearch:8.6.2
+ - name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:zoekt-ci-image-1.0
+ alias: zoekt-ci-image
+ variables:
+ POSTGRES_HOST_AUTH_METHOD: trust
+ PG_VERSION: "15"
+ ES_SETTING_DISCOVERY_TYPE: "single-node"
+ ES_SETTING_XPACK_SECURITY_ENABLED: "false"
+ ZOEKT_INDEX_BASE_URL: http://zoekt-ci-image:6060
+ ZOEKT_SEARCH_BASE_URL: http://zoekt-ci-image:6070
+
.use-pg13-opensearch1-ee:
services:
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-13-pgvector-0.4.1
@@ -417,6 +466,44 @@
ZOEKT_INDEX_BASE_URL: http://zoekt-ci-image:6060
ZOEKT_SEARCH_BASE_URL: http://zoekt-ci-image:6070
+.use-pg15-opensearch1-ee:
+ services:
+ - name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-15-pgvector-0.4.1
+ command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
+ alias: postgres
+ - name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:redis-cluster-6.2.12
+ alias: rediscluster # configure connections in config/redis.yml
+ - name: redis:6.0-alpine
+ - name: opensearchproject/opensearch:1.3.5
+ alias: elasticsearch
+ command: ["bin/opensearch", "-E", "discovery.type=single-node", "-E", "plugins.security.disabled=true"]
+ - name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:zoekt-ci-image-1.0
+ alias: zoekt-ci-image
+ variables:
+ POSTGRES_HOST_AUTH_METHOD: trust
+ PG_VERSION: "15"
+ ZOEKT_INDEX_BASE_URL: http://zoekt-ci-image:6060
+ ZOEKT_SEARCH_BASE_URL: http://zoekt-ci-image:6070
+
+.use-pg15-opensearch2-ee:
+ services:
+ - name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-15-pgvector-0.4.1
+ command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
+ alias: postgres
+ - name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:redis-cluster-6.2.12
+ alias: rediscluster # configure connections in config/redis.yml
+ - name: redis:6.0-alpine
+ - name: opensearchproject/opensearch:2.2.1
+ alias: elasticsearch
+ command: ["bin/opensearch", "-E", "discovery.type=single-node", "-E", "plugins.security.disabled=true"]
+ - name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:zoekt-ci-image-1.0
+ alias: zoekt-ci-image
+ variables:
+ POSTGRES_HOST_AUTH_METHOD: trust
+ PG_VERSION: "15"
+ ZOEKT_INDEX_BASE_URL: http://zoekt-ci-image:6060
+ ZOEKT_SEARCH_BASE_URL: http://zoekt-ci-image:6070
+
.use-kaniko:
image:
name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:kaniko
diff --git a/.gitlab/ci/memory.gitlab-ci.yml b/.gitlab/ci/memory.gitlab-ci.yml
index 004188b502c..33fbed15a23 100644
--- a/.gitlab/ci/memory.gitlab-ci.yml
+++ b/.gitlab/ci/memory.gitlab-ci.yml
@@ -20,7 +20,7 @@ memory-on-boot:
extends:
- .only-code-memory-job-base
- .production
- - .use-pg13
+ - .use-pg14
stage: test
needs: ["setup-test-env", "compile-test-assets"]
variables:
diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml
index 9c305503357..f465be5cff3 100644
--- a/.gitlab/ci/rails.gitlab-ci.yml
+++ b/.gitlab/ci/rails.gitlab-ci.yml
@@ -64,145 +64,145 @@ update-gitaly-binaries-cache:
- export BUNDLE_WITHOUT="${BUNDLE_WITHOUT}:default:test:puma:kerberos:metrics:omnibus:ed25519"
- bundle_install_script
-rspec migration pg13:
+rspec migration pg14:
extends:
- - .rspec-base-pg13
+ - .rspec-base-pg14
- .rspec-base-migration
- .rails:rules:ee-and-foss-migration
- .rspec-migration-parallel
-rspec background_migration pg13:
+rspec background_migration pg14:
extends:
- - .rspec-base-pg13
+ - .rspec-base-pg14
- .rspec-base-migration
- .rails:rules:ee-and-foss-background-migration
- .rspec-background-migration-parallel
-rspec migration pg13 single-db:
+rspec migration pg14 single-db:
extends:
- - rspec migration pg13
+ - rspec migration pg14
- .single-db-rspec
- .rails:rules:single-db
-rspec background_migration pg13 single-db:
+rspec background_migration pg14 single-db:
extends:
- - rspec background_migration pg13
+ - rspec background_migration pg14
- .single-db-rspec
- .rails:rules:single-db
-rspec migration pg13 single-db-ci-connection:
+rspec migration pg14 single-db-ci-connection:
extends:
- - rspec migration pg13
+ - rspec migration pg14
- .single-db-ci-connection-rspec
- .rails:rules:single-db-ci-connection
-rspec background_migration pg13 single-db-ci-connection:
+rspec background_migration pg14 single-db-ci-connection:
extends:
- - rspec background_migration pg13
+ - rspec background_migration pg14
- .single-db-ci-connection-rspec
- .rails:rules:single-db-ci-connection
-rspec migration pg13 praefect:
+rspec migration pg14 praefect:
extends:
- - rspec migration pg13
+ - rspec migration pg14
- .praefect-with-db
- .rails:rules:praefect-with-db
-rspec background_migration pg13 praefect:
+rspec background_migration pg14 praefect:
extends:
- - rspec background_migration pg13
+ - rspec background_migration pg14
- .praefect-with-db
- .rails:rules:praefect-with-db
-rspec unit pg13:
+rspec unit pg14:
extends:
- - .rspec-base-pg13
+ - .rspec-base-pg14
- .rails:rules:ee-and-foss-unit
- .rspec-unit-parallel
-rspec unit pg13 single-redis:
+rspec unit pg14 single-redis:
extends:
- - rspec unit pg13
+ - rspec unit pg14
- .no-redis-cluster
- .rails:rules:single-redis
-rspec unit pg13 single-db:
+rspec unit pg14 single-db:
extends:
- - rspec unit pg13
+ - rspec unit pg14
- .single-db-rspec
- .rails:rules:single-db
-rspec unit pg13 single-db-ci-connection:
+rspec unit pg14 single-db-ci-connection:
extends:
- - rspec unit pg13
+ - rspec unit pg14
- .single-db-ci-connection-rspec
- .rails:rules:single-db-ci-connection
-rspec unit pg13 praefect:
+rspec unit pg14 praefect:
extends:
- - rspec unit pg13
+ - rspec unit pg14
- .praefect-with-db
- .rails:rules:praefect-with-db
-rspec integration pg13:
+rspec integration pg14:
extends:
- - .rspec-base-pg13
+ - .rspec-base-pg14
- .rails:rules:ee-and-foss-integration
- .rspec-integration-parallel
-rspec integration pg13 single-redis:
+rspec integration pg14 single-redis:
extends:
- - rspec integration pg13
+ - rspec integration pg14
- .no-redis-cluster
- .rails:rules:single-redis
-rspec integration pg13 single-db:
+rspec integration pg14 single-db:
extends:
- - rspec integration pg13
+ - rspec integration pg14
- .single-db-rspec
- .rails:rules:single-db
-rspec integration pg13 single-db-ci-connection:
+rspec integration pg14 single-db-ci-connection:
extends:
- - rspec integration pg13
+ - rspec integration pg14
- .single-db-ci-connection-rspec
- .rails:rules:single-db-ci-connection
-rspec integration pg13 praefect:
+rspec integration pg14 praefect:
extends:
- - rspec integration pg13
+ - rspec integration pg14
- .praefect-with-db
- .rails:rules:praefect-with-db
-rspec system pg13:
+rspec system pg14:
extends:
- - .rspec-base-pg13
+ - .rspec-base-pg14
- .rails:rules:ee-and-foss-system
- .rspec-system-parallel
variables:
DEBUG_GITLAB_TRANSACTION_STACK: "true"
-rspec system pg13 single-redis:
+rspec system pg14 single-redis:
extends:
- - rspec system pg13
+ - rspec system pg14
- .no-redis-cluster
- .rails:rules:single-redis
-rspec system pg13 single-db:
+rspec system pg14 single-db:
extends:
- - rspec system pg13
+ - rspec system pg14
- .single-db-rspec
- .rails:rules:single-db
-rspec system pg13 single-db-ci-connection:
+rspec system pg14 single-db-ci-connection:
extends:
- - rspec system pg13
+ - rspec system pg14
- .single-db-ci-connection-rspec
- .rails:rules:single-db-ci-connection
-rspec system pg13 praefect:
+rspec system pg14 praefect:
extends:
- - rspec system pg13
+ - rspec system pg14
- .praefect-with-db
- .rails:rules:praefect-with-db
@@ -216,9 +216,19 @@ rspec db-library-code pg12:
- !reference [.base-script, script]
- rspec_db_library_code
-rspec fast_spec_helper:
+# Dedicated job to test DB library code against PG13.
+# Note that these are already tested against PG13 in the `rspec unit pg13` / `rspec-ee unit pg13` jobs.
+rspec db-library-code pg13:
extends:
- .rspec-base-pg13
+ - .rails:rules:ee-and-foss-db-library-code
+ script:
+ - !reference [.base-script, script]
+ - rspec_db_library_code
+
+rspec fast_spec_helper:
+ extends:
+ - .rspec-base-pg14
- .rails:rules:ee-and-foss-fast_spec_helper
script:
- fast_spec_helper_specs=$(git grep -l -E '^require.*fast_spec_helper')
@@ -253,16 +263,16 @@ rspec:deprecations:
# We cannot use needs since it would mean needing 84 jobs (since most are parallelized)
# so we use `dependencies` here.
dependencies:
- - rspec migration pg13
- - rspec background_migration pg13
- - rspec unit pg13
- - rspec integration pg13
- - rspec system pg13
- - rspec-ee migration pg13
- - rspec-ee background_migration pg13
- - rspec-ee unit pg13
- - rspec-ee integration pg13
- - rspec-ee system pg13
+ - rspec migration pg14
+ - rspec background_migration pg14
+ - rspec unit pg14
+ - rspec integration pg14
+ - rspec system pg14
+ - rspec-ee migration pg14
+ - rspec-ee background_migration pg14
+ - rspec-ee unit pg14
+ - rspec-ee integration pg14
+ - rspec-ee system pg14
variables:
SETUP_DB: "false"
script:
@@ -285,29 +295,29 @@ rspec:coverage:
dependencies:
- setup-test-env
# FOSS/EE jobs
- - rspec migration pg13
- - rspec background_migration pg13
- - rspec unit pg13
- - rspec unit pg13 single-redis
- - rspec integration pg13
- - rspec integration pg13 single-redis
- - rspec system pg13
- - rspec system pg13 single-redis
+ - rspec migration pg14
+ - rspec background_migration pg14
+ - rspec unit pg14
+ - rspec unit pg14 single-redis
+ - rspec integration pg14
+ - rspec integration pg14 single-redis
+ - rspec system pg14
+ - rspec system pg14 single-redis
# as-if-foss jobs
- - rspec migration pg13-as-if-foss
- - rspec background_migration pg13-as-if-foss
- - rspec unit pg13-as-if-foss
- - rspec integration pg13-as-if-foss
- - rspec system pg13-as-if-foss
+ - rspec migration pg14-as-if-foss
+ - rspec background_migration pg14-as-if-foss
+ - rspec unit pg14-as-if-foss
+ - rspec integration pg14-as-if-foss
+ - rspec system pg14-as-if-foss
# EE jobs
- - rspec-ee migration pg13
- - rspec-ee background_migration pg13
- - rspec-ee unit pg13
- - rspec-ee unit pg13 single-redis
- - rspec-ee integration pg13
- - rspec-ee integration pg13 single-redis
- - rspec-ee system pg13
- - rspec-ee system pg13 single-redis
+ - rspec-ee migration pg14
+ - rspec-ee background_migration pg14
+ - rspec-ee unit pg14
+ - rspec-ee unit pg14 single-redis
+ - rspec-ee integration pg14
+ - rspec-ee integration pg14 single-redis
+ - rspec-ee system pg14
+ - rspec-ee system pg14 single-redis
# Memory jobs
- memory-on-boot
script:
@@ -437,232 +447,232 @@ rspec-ee:predictive:trigger:
- artifact: "${RSPEC_PREDICTIVE_PIPELINE_TEMPLATE_YML}-ee.yml"
job: rspec-predictive:pipeline-generate
-rspec migration pg13-as-if-foss:
+rspec migration pg14-as-if-foss:
extends:
- - .rspec-base-pg13-as-if-foss
+ - .rspec-base-pg14-as-if-foss
- .rspec-base-migration
- .rails:rules:as-if-foss-migration
- .rspec-migration-parallel
-rspec background_migration pg13-as-if-foss:
+rspec background_migration pg14-as-if-foss:
extends:
- - .rspec-base-pg13-as-if-foss
+ - .rspec-base-pg14-as-if-foss
- .rspec-base-migration
- .rails:rules:as-if-foss-background-migration
- .rspec-background-migration-parallel
-rspec migration pg13-as-if-foss single-db:
+rspec migration pg14-as-if-foss single-db:
extends:
- - rspec migration pg13-as-if-foss
+ - rspec migration pg14-as-if-foss
- .single-db-rspec
- .rails:rules:single-db
-rspec background_migration pg13-as-if-foss single-db:
+rspec background_migration pg14-as-if-foss single-db:
extends:
- - rspec background_migration pg13-as-if-foss
+ - rspec background_migration pg14-as-if-foss
- .single-db-rspec
- .rails:rules:single-db
-rspec migration pg13-as-if-foss single-db-ci-connection:
+rspec migration pg14-as-if-foss single-db-ci-connection:
extends:
- - rspec migration pg13-as-if-foss
+ - rspec migration pg14-as-if-foss
- .single-db-ci-connection-rspec
- .rails:rules:single-db-ci-connection
-rspec background_migration pg13-as-if-foss single-db-ci-connection:
+rspec background_migration pg14-as-if-foss single-db-ci-connection:
extends:
- - rspec background_migration pg13-as-if-foss
+ - rspec background_migration pg14-as-if-foss
- .single-db-ci-connection-rspec
- .rails:rules:single-db-ci-connection
-rspec unit pg13-as-if-foss:
+rspec unit pg14-as-if-foss:
extends:
- - .rspec-base-pg13-as-if-foss
+ - .rspec-base-pg14-as-if-foss
- .rails:rules:as-if-foss-unit
- .rspec-unit-parallel
-rspec unit pg13-as-if-foss single-db:
+rspec unit pg14-as-if-foss single-db:
extends:
- - rspec unit pg13-as-if-foss
+ - rspec unit pg14-as-if-foss
- .single-db-rspec
- .rails:rules:single-db
-rspec unit pg13-as-if-foss single-db-ci-connection:
+rspec unit pg14-as-if-foss single-db-ci-connection:
extends:
- - rspec unit pg13-as-if-foss
+ - rspec unit pg14-as-if-foss
- .single-db-ci-connection-rspec
- .rails:rules:single-db-ci-connection
-rspec integration pg13-as-if-foss:
+rspec integration pg14-as-if-foss:
extends:
- - .rspec-base-pg13-as-if-foss
+ - .rspec-base-pg14-as-if-foss
- .rails:rules:as-if-foss-integration
- .rspec-integration-parallel
-rspec integration pg13-as-if-foss single-db:
+rspec integration pg14-as-if-foss single-db:
extends:
- - rspec integration pg13-as-if-foss
+ - rspec integration pg14-as-if-foss
- .single-db-rspec
- .rails:rules:single-db
-rspec integration pg13-as-if-foss single-db-ci-connection:
+rspec integration pg14-as-if-foss single-db-ci-connection:
extends:
- - rspec integration pg13-as-if-foss
+ - rspec integration pg14-as-if-foss
- .single-db-ci-connection-rspec
- .rails:rules:single-db-ci-connection
-rspec system pg13-as-if-foss:
+rspec system pg14-as-if-foss:
extends:
- - .rspec-base-pg13-as-if-foss
+ - .rspec-base-pg14-as-if-foss
- .rails:rules:as-if-foss-system
- .rspec-system-parallel
-rspec system pg13-as-if-foss single-db:
+rspec system pg14-as-if-foss single-db:
extends:
- - rspec system pg13-as-if-foss
+ - rspec system pg14-as-if-foss
- .single-db-rspec
- .rails:rules:single-db
-rspec system pg13-as-if-foss single-db-ci-connection:
+rspec system pg14-as-if-foss single-db-ci-connection:
extends:
- - rspec system pg13-as-if-foss
+ - rspec system pg14-as-if-foss
- .single-db-ci-connection-rspec
- .rails:rules:single-db-ci-connection
-rspec-ee migration pg13:
+rspec-ee migration pg14:
extends:
- - .rspec-ee-base-pg13
+ - .rspec-ee-base-pg14
- .rspec-base-migration
- .rails:rules:ee-only-migration
- .rspec-ee-migration-parallel
-rspec-ee background_migration pg13:
+rspec-ee background_migration pg14:
extends:
- - .rspec-ee-base-pg13
+ - .rspec-ee-base-pg14
- .rspec-base-migration
- .rails:rules:ee-only-background-migration
- .rspec-ee-background-migration-parallel
-rspec-ee migration pg13 single-db:
+rspec-ee migration pg14 single-db:
extends:
- - rspec-ee migration pg13
+ - rspec-ee migration pg14
- .single-db-rspec
- .rails:rules:single-db
-rspec-ee background_migration pg13 single-db:
+rspec-ee background_migration pg14 single-db:
extends:
- - rspec-ee background_migration pg13
+ - rspec-ee background_migration pg14
- .single-db-rspec
- .rails:rules:single-db
-rspec-ee migration pg13 single-db-ci-connection:
+rspec-ee migration pg14 single-db-ci-connection:
extends:
- - rspec-ee migration pg13
+ - rspec-ee migration pg14
- .single-db-ci-connection-rspec
- .rails:rules:single-db-ci-connection
-rspec-ee background_migration pg13 single-db-ci-connection:
+rspec-ee background_migration pg14 single-db-ci-connection:
extends:
- - rspec-ee background_migration pg13
+ - rspec-ee background_migration pg14
- .single-db-ci-connection-rspec
- .rails:rules:single-db-ci-connection
-rspec-ee migration pg13 praefect:
+rspec-ee migration pg14 praefect:
extends:
- - rspec migration pg13
+ - rspec migration pg14
- .praefect-with-db
- .rails:rules:praefect-with-db
-rspec-ee background_migration pg13 praefect:
+rspec-ee background_migration pg14 praefect:
extends:
- - rspec background_migration pg13
+ - rspec background_migration pg14
- .praefect-with-db
- .rails:rules:praefect-with-db
-rspec-ee unit pg13:
+rspec-ee unit pg14:
extends:
- - .rspec-ee-base-pg13
+ - .rspec-ee-base-pg14
- .rails:rules:ee-only-unit
- .rspec-ee-unit-parallel
-rspec-ee unit pg13 es8:
+rspec-ee unit pg14 es8:
extends:
- - .rspec-ee-base-pg13-es8
+ - .rspec-ee-base-pg14-es8
- .rspec-ee-unit-parallel
-rspec-ee unit pg13 single-db:
+rspec-ee unit pg14 single-db:
extends:
- - rspec-ee unit pg13
+ - rspec-ee unit pg14
- .single-db-rspec
- .rails:rules:single-db
-rspec-ee unit pg13 single-redis:
+rspec-ee unit pg14 single-redis:
extends:
- - rspec-ee unit pg13
+ - rspec-ee unit pg14
- .no-redis-cluster
- .rails:rules:single-redis
-rspec-ee unit pg13 single-db-ci-connection:
+rspec-ee unit pg14 single-db-ci-connection:
extends:
- - rspec-ee unit pg13
+ - rspec-ee unit pg14
- .single-db-ci-connection-rspec
- .rails:rules:single-db-ci-connection
-rspec-ee integration pg13:
+rspec-ee integration pg14:
extends:
- - .rspec-ee-base-pg13
+ - .rspec-ee-base-pg14
- .rails:rules:ee-only-integration
- .rspec-ee-integration-parallel
-rspec-ee integration pg13 es8:
+rspec-ee integration pg14 es8:
extends:
- - .rspec-ee-base-pg13-es8
+ - .rspec-ee-base-pg14-es8
- .rspec-ee-integration-parallel
-rspec-ee integration pg13 single-db:
+rspec-ee integration pg14 single-db:
extends:
- - rspec-ee integration pg13
+ - rspec-ee integration pg14
- .single-db-rspec
- .rails:rules:single-db
-rspec-ee integration pg13 single-redis:
+rspec-ee integration pg14 single-redis:
extends:
- - rspec-ee integration pg13
+ - rspec-ee integration pg14
- .no-redis-cluster
- .rails:rules:single-redis
-rspec-ee integration pg13 single-db-ci-connection:
+rspec-ee integration pg14 single-db-ci-connection:
extends:
- - rspec-ee integration pg13
+ - rspec-ee integration pg14
- .single-db-ci-connection-rspec
- .rails:rules:single-db-ci-connection
-rspec-ee system pg13:
+rspec-ee system pg14:
extends:
- - .rspec-ee-base-pg13
+ - .rspec-ee-base-pg14
- .rails:rules:ee-only-system
- .rspec-ee-system-parallel
-rspec-ee system pg13 es8:
+rspec-ee system pg14 es8:
extends:
- - .rspec-ee-base-pg13-es8
+ - .rspec-ee-base-pg14-es8
- .rspec-ee-system-parallel
-rspec-ee system pg13 single-db:
+rspec-ee system pg14 single-db:
extends:
- - rspec-ee system pg13
+ - rspec-ee system pg14
- .single-db-rspec
- .rails:rules:single-db
-rspec-ee system pg13 single-redis:
+rspec-ee system pg14 single-redis:
extends:
- - rspec-ee system pg13
+ - rspec-ee system pg14
- .no-redis-cluster
- .rails:rules:single-redis
-rspec-ee system pg13 single-db-ci-connection:
+rspec-ee system pg14 single-db-ci-connection:
extends:
- - rspec-ee system pg13
+ - rspec-ee system pg14
- .single-db-ci-connection-rspec
- .rails:rules:single-db-ci-connection
# EE: default refs (MRs, default branch, schedules) jobs #
@@ -704,36 +714,69 @@ rspec system pg12:
- .rails:rules:rspec-on-pg12
- .rspec-system-parallel
-# PG14
-rspec migration pg14:
+# PG13
+rspec migration pg13:
extends:
- - .rspec-base-pg14
+ - .rspec-base-pg13
- .rspec-base-migration
- .rails:rules:default-branch-schedule-nightly--code-backstage
- .rspec-migration-parallel
-rspec background_migration pg14:
+rspec background_migration pg13:
extends:
- - .rspec-base-pg14
+ - .rspec-base-pg13
- .rspec-base-migration
- .rails:rules:default-branch-schedule-nightly--code-backstage
- .rspec-background-migration-parallel
-rspec unit pg14:
+rspec unit pg13:
extends:
- - .rspec-base-pg14
+ - .rspec-base-pg13
- .rails:rules:default-branch-schedule-nightly--code-backstage
- .rspec-unit-parallel
-rspec integration pg14:
+rspec integration pg13:
extends:
- - .rspec-base-pg14
+ - .rspec-base-pg13
- .rails:rules:default-branch-schedule-nightly--code-backstage
- .rspec-integration-parallel
-rspec system pg14:
+rspec system pg13:
extends:
- - .rspec-base-pg14
+ - .rspec-base-pg13
+ - .rails:rules:default-branch-schedule-nightly--code-backstage
+ - .rspec-system-parallel
+
+# PG15
+rspec migration pg15:
+ extends:
+ - .rspec-base-pg15
+ - .rspec-base-migration
+ - .rails:rules:default-branch-schedule-nightly--code-backstage
+ - .rspec-migration-parallel
+
+rspec background_migration pg15:
+ extends:
+ - .rspec-base-pg15
+ - .rspec-base-migration
+ - .rails:rules:default-branch-schedule-nightly--code-backstage
+ - .rspec-background-migration-parallel
+
+rspec unit pg15:
+ extends:
+ - .rspec-base-pg15
+ - .rails:rules:default-branch-schedule-nightly--code-backstage
+ - .rspec-unit-parallel
+
+rspec integration pg15:
+ extends:
+ - .rspec-base-pg15
+ - .rails:rules:default-branch-schedule-nightly--code-backstage
+ - .rspec-integration-parallel
+
+rspec system pg15:
+ extends:
+ - .rspec-base-pg15
- .rails:rules:default-branch-schedule-nightly--code-backstage
- .rspec-system-parallel
# EE/FOSS: default branch nightly scheduled jobs #
@@ -843,51 +886,88 @@ rspec-ee system pg14 opensearch2:
- .rspec-ee-system-parallel
- .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only
-rspec-ee migration pg14:
+# PG15
+rspec-ee unit pg15 opensearch1:
extends:
- - .rspec-ee-base-pg14
+ - .rspec-ee-base-pg15-opensearch1
+ - .rspec-ee-unit-parallel
+ - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only
+
+rspec-ee unit pg15 opensearch2:
+ extends:
+ - .rspec-ee-base-pg15-opensearch2
+ - .rspec-ee-unit-parallel
+ - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only
+
+rspec-ee integration pg15 opensearch1:
+ extends:
+ - .rspec-ee-base-pg15-opensearch1
+ - .rspec-ee-integration-parallel
+ - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only
+
+rspec-ee integration pg15 opensearch2:
+ extends:
+ - .rspec-ee-base-pg15-opensearch2
+ - .rspec-ee-integration-parallel
+ - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only
+
+rspec-ee system pg15 opensearch1:
+ extends:
+ - .rspec-ee-base-pg15-opensearch1
+ - .rspec-ee-system-parallel
+ - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only
+
+rspec-ee system pg15 opensearch2:
+ extends:
+ - .rspec-ee-base-pg15-opensearch2
+ - .rspec-ee-system-parallel
+ - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only
+
+rspec-ee migration pg15:
+ extends:
+ - .rspec-ee-base-pg15
- .rspec-base-migration
- .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only
- .rspec-ee-migration-parallel
-rspec-ee background_migration pg14:
+rspec-ee background_migration pg15:
extends:
- - .rspec-ee-base-pg14
+ - .rspec-ee-base-pg15
- .rspec-base-migration
- .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only
- .rspec-ee-background-migration-parallel
-rspec-ee unit pg14:
+rspec-ee unit pg15:
extends:
- - .rspec-ee-base-pg14
+ - .rspec-ee-base-pg15
- .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only
- .rspec-ee-unit-parallel
-rspec-ee unit pg14 es8:
+rspec-ee unit pg15 es8:
extends:
- - .rspec-ee-base-pg14-es8
+ - .rspec-ee-base-pg15-es8
- .rspec-ee-unit-parallel
-rspec-ee integration pg14:
+rspec-ee integration pg15:
extends:
- - .rspec-ee-base-pg14
+ - .rspec-ee-base-pg15
- .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only
- .rspec-ee-integration-parallel
-rspec-ee integration pg14 es8:
+rspec-ee integration pg15 es8:
extends:
- - .rspec-ee-base-pg14-es8
+ - .rspec-ee-base-pg15-es8
- .rspec-ee-integration-parallel
-rspec-ee system pg14:
+rspec-ee system pg15:
extends:
- - .rspec-ee-base-pg14
+ - .rspec-ee-base-pg15
- .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only
- .rspec-ee-system-parallel
-rspec-ee system pg14 es8:
+rspec-ee system pg15 es8:
extends:
- - .rspec-ee-base-pg14-es8
+ - .rspec-ee-base-pg15-es8
- .rspec-ee-system-parallel
# EE: default branch nightly scheduled jobs #
#####################################
@@ -905,14 +985,14 @@ rspec-ee system pg14 es8:
rspec fail-fast:
extends:
- - .rspec-base-pg13
- - .rspec-fail-fast # extends from .rspec-fail-fast last to override script from .rspec-base-pg13
+ - .rspec-base-pg14
+ - .rspec-fail-fast # extends from .rspec-fail-fast last to override script from .rspec-base-pg14
variables:
MATCHING_TESTS_PATH: "${RSPEC_MATCHING_TESTS_FOSS_PATH}"
rspec-ee fail-fast:
extends:
- - .rspec-ee-base-pg13
+ - .rspec-ee-base-pg14
- .rspec-fail-fast # extends from .rspec-fail-fast last to override script from .rspec-ee-base-pg13
variables:
MATCHING_TESTS_PATH: "${RSPEC_MATCHING_TESTS_EE_PATH}"
@@ -969,7 +1049,7 @@ fail-pipeline-early:
script:
- fail_pipeline_early
-.base-rspec-pg13-rerun-previous-failed-tests:
+.base-rspec-pg14-rerun-previous-failed-tests:
extends:
- .rails:rules:rerun-previous-failed-tests
stage: test
@@ -978,17 +1058,17 @@ fail-pipeline-early:
- !reference [.base-script, script]
- rspec_rerun_previous_failed_tests "${PREVIOUS_FAILED_TESTS_FILE}"
-rspec rspec-pg13-rerun-previous-failed-tests:
+rspec rspec-pg14-rerun-previous-failed-tests:
extends:
- - .rspec-base-pg13
- - .base-rspec-pg13-rerun-previous-failed-tests
+ - .rspec-base-pg14
+ - .base-rspec-pg14-rerun-previous-failed-tests
variables:
PREVIOUS_FAILED_TESTS_FILE: tmp/previous_failed_tests/rspec_failed_tests.txt
-rspec rspec-ee-pg13-rerun-previous-failed-tests:
+rspec rspec-ee-pg14-rerun-previous-failed-tests:
extends:
- - .rspec-ee-base-pg13
- - .base-rspec-pg13-rerun-previous-failed-tests
+ - .rspec-ee-base-pg14
+ - .base-rspec-pg14-rerun-previous-failed-tests
variables:
PREVIOUS_FAILED_TESTS_FILE: tmp/previous_failed_tests/rspec_ee_failed_files.txt
# EE: Canonical MR pipelines
diff --git a/.gitlab/ci/rails/rspec-foss-impact.gitlab-ci.yml.erb b/.gitlab/ci/rails/rspec-foss-impact.gitlab-ci.yml.erb
index 84aa67de4c9..dc469c30207 100644
--- a/.gitlab/ci/rails/rspec-foss-impact.gitlab-ci.yml.erb
+++ b/.gitlab/ci/rails/rspec-foss-impact.gitlab-ci.yml.erb
@@ -22,7 +22,7 @@ dont-interrupt-me:
- echo "This jobs makes sure this pipeline won't be interrupted! See https://docs.gitlab.com/ee/ci/yaml/#interruptible."
.base-rspec-foss-impact:
- extends: .rspec-base-pg13-as-if-foss
+ extends: .rspec-base-pg14-as-if-foss
needs:
- pipeline: $PARENT_PIPELINE_ID
job: detect-tests
diff --git a/.gitlab/ci/rails/shared.gitlab-ci.yml b/.gitlab/ci/rails/shared.gitlab-ci.yml
index 618c4e3b654..c0ea7d7078a 100644
--- a/.gitlab/ci/rails/shared.gitlab-ci.yml
+++ b/.gitlab/ci/rails/shared.gitlab-ci.yml
@@ -109,11 +109,16 @@ include:
- .rspec-base
- .use-pg13
-.rspec-base-pg13-as-if-foss:
+.rspec-base-pg14:
+ extends:
+ - .rspec-base
+ - .use-pg14
+
+.rspec-base-pg14-as-if-foss:
extends:
- .rspec-base
- .as-if-foss
- - .use-pg13
+ - .use-pg14
needs:
- job: "setup-test-env"
- job: "retrieve-tests-metadata"
@@ -121,10 +126,10 @@ include:
- job: "detect-tests"
optional: true
-.rspec-base-pg14:
+.rspec-base-pg15:
extends:
- .rspec-base
- - .use-pg14
+ - .use-pg15
.rspec-ee-base-pg12:
extends:
@@ -177,11 +182,34 @@ include:
- .use-pg14-opensearch2-ee
- .rails:rules:run-search-tests
+.rspec-ee-base-pg15:
+ extends:
+ - .rspec-base
+ - .use-pg15-es7-ee
+
+.rspec-ee-base-pg15-es8:
+ extends:
+ - .rspec-base
+ - .use-pg15-es8-ee
+ - .rails:rules:run-search-tests
+
+.rspec-ee-base-pg15-opensearch1:
+ extends:
+ - .rspec-base
+ - .use-pg15-opensearch1-ee
+ - .rails:rules:run-search-tests
+
+.rspec-ee-base-pg15-opensearch2:
+ extends:
+ - .rspec-base
+ - .use-pg15-opensearch2-ee
+ - .rails:rules:run-search-tests
+
.db-job-base:
extends:
- .rails-job-base
- .rails:rules:ee-and-foss-migration
- - .use-pg13
+ - .use-pg14
stage: test
needs: ["setup-test-env"]
# rspec job base specs
diff --git a/.gitlab/ci/setup.gitlab-ci.yml b/.gitlab/ci/setup.gitlab-ci.yml
index a9047522413..85409d37fa4 100644
--- a/.gitlab/ci/setup.gitlab-ci.yml
+++ b/.gitlab/ci/setup.gitlab-ci.yml
@@ -83,7 +83,7 @@ verify-approvals:
generate-frontend-fixtures-mapping:
extends:
- .setup:rules:generate-frontend-fixtures-mapping
- - .use-pg13
+ - .use-pg14
- .ruby-cache
needs: ["setup-test-env"]
stage: prepare
diff --git a/.gitlab/ci/static-analysis.gitlab-ci.yml b/.gitlab/ci/static-analysis.gitlab-ci.yml
index b9c9200d78b..3d030026e90 100644
--- a/.gitlab/ci/static-analysis.gitlab-ci.yml
+++ b/.gitlab/ci/static-analysis.gitlab-ci.yml
@@ -45,7 +45,7 @@ static-verification-with-database:
- .static-analysis-base
- .rubocop-job-cache
- .static-analysis:rules:static-verification-with-database
- - .use-pg13
+ - .use-pg14
script:
- bundle exec rake lint:static_verification_with_database
variables:
diff --git a/.gitlab/ci/test-metadata.gitlab-ci.yml b/.gitlab/ci/test-metadata.gitlab-ci.yml
index 41fd0b49173..3f06fcbce40 100644
--- a/.gitlab/ci/test-metadata.gitlab-ci.yml
+++ b/.gitlab/ci/test-metadata.gitlab-ci.yml
@@ -32,17 +32,17 @@ update-tests-metadata:
- retrieve-tests-metadata
- generate-frontend-fixtures-mapping
- setup-test-env
- - rspec migration pg13
+ - rspec migration pg14
- rspec-all frontend_fixture
- - rspec unit pg13
- - rspec integration pg13
- - rspec system pg13
- - rspec background_migration pg13
- - rspec-ee migration pg13
- - rspec-ee unit pg13
- - rspec-ee integration pg13
- - rspec-ee system pg13
- - rspec-ee background_migration pg13
+ - rspec unit pg14
+ - rspec integration pg14
+ - rspec system pg14
+ - rspec background_migration pg14
+ - rspec-ee migration pg14
+ - rspec-ee unit pg14
+ - rspec-ee integration pg14
+ - rspec-ee system pg14
+ - rspec-ee background_migration pg14
script:
- run_timed_command "retry gem install fog-aws mime-types activesupport rspec_profiling postgres-copy --no-document"
- source ./scripts/rspec_helpers.sh
diff --git a/.gitlab/issue_templates/Pipeline Security issue implementation b/.gitlab/issue_templates/Pipeline Security issue implementation
new file mode 100644
index 00000000000..72807e98012
--- /dev/null
+++ b/.gitlab/issue_templates/Pipeline Security issue implementation
@@ -0,0 +1,98 @@
+<!--
+## Implementation Issue To-Do list
+(_NOTE: This section can be removed when the issue is ready for creation_)
+- [ ] Ensure that the issue title is concise yet descriptive.
+- [ ] Add `Frontend :` or `Backend :` to the title per group [naming conventions](https://about.gitlab.com/handbook/engineering/development/ops/verify/pipeline-security/#splitting-issues)
+- [ ] Ensure the issue containing the feature or change proposal and related discussions is linked as related to this implementation issue.
+- [ ] Aside from default labeling, please make sure to include relevant labels for `~type::`, `~workflow::`, and `~frontend` or `~backend`.
+- [ ] Issues with user-facing changes should include the `~UX` label, and `~documentation` if docs changes will be required.
+
+*This template is meant to be a reference tool. Not all sections are applicable to each feature, bug, or maintenance item. Use your best judgment when completion the sections below.*
+-->
+
+## Summary
+<!-- Briefly describe the issue. -->
+
+
+### Why this matters and how we measure
+<!-- What is the value to the customer or our business? Does this align with our OKRs? If we need to create or update existing instrumentation, please note here. -->
+
+### User Stories
+<!--
+A user story is a requirement for any functionality or feature and follows this format:
+
+- _As a `<user role/customer>`, I want to `<JTBD>` so that I can `<achieve a benefit or result>`._
+
+Please try to include one user story for the main [persona](https://about.gitlab.com/handbook/product/personas/#list-of-user-personas) who needs this feature.
+-->
+
+
+## Proposal
+<!-- Try to keep the proposal limited in scope. Plan for iterations, create follow up issues as required and add them as related. -->
+
+## Performance Considerations
+<!-- Performance concerns to be aware of and monitor when implementing the issue.-->
+
+## Out of Scope
+<!-- Include this section for specific use cases that are out of scope / out of bounds for this specific issue. -->
+
+## Acceptance Criteria
+<!-- This needs to be true or demonstrable to consider this specific issue complete. Keep this dependent on other issues when possible -->
+
+## Additional details
+<!--
+_NOTE: If the issue has addressed all of these questions, this separate section can be removed._
+-->
+
+Some relevant technical details, if applicable, such as:
+
+- Does this need a ~"feature flag"?
+- Does there need to be an associated ~"instrumentation" issue created related to this work?
+- Is there an example response showing the data structure that should be returned (new endpoints only)?
+- What permissions should be used?
+- Which tier(s) is this for?
+ - [ ] ~"GitLab Ultimate"
+ - [ ] ~"GitLab Premium"
+ - [ ] ~"GitLab Free"
+- Additional comments:
+
+## Implementation Table
+
+<!--
+_NOTE: Use this to indicate all dependent issues related to this one which are required for launch._
+-->
+
+
+| Group | Issue Link |
+| ------ | ------ |
+| ~backend | :point_left: You are here |
+| ~frontend | [Issue Title](url) |
+| ~documentation | [Issue Title](url) |
+| Instrumentation | [Issue Title](url) |
+
+<!--
+## Documentation
+
+_NOTE: This section is optional, but can be used for easy access to any relevant documentation URLs._
+-->
+
+## Links/References
+
+
+
+
+/label ~"group::pipeline security"
+/milestone %Backlog
+
+<!-- select the correct category (and feature label if applicable) below:
+/label ~"category:Build Artifacts"
+/label ~"category:Secrets Management"
+/label ~"ci variables"
+/label ~"ci job token"
+-->
+
+<!-- select the appropriate licence below (Use the highest tier applicable):
+/label ~"GitLab Ultimate"
+/label ~"GitLab Premium"
+/label ~"GitLab Free"
+-->
diff --git a/.rubocop_todo/rspec/verified_doubles.yml b/.rubocop_todo/rspec/verified_doubles.yml
index 704a426595c..6bccfcbeb9f 100644
--- a/.rubocop_todo/rspec/verified_doubles.yml
+++ b/.rubocop_todo/rspec/verified_doubles.yml
@@ -941,7 +941,6 @@ RSpec/VerifiedDoubles:
- 'spec/support/helpers/ldap_helpers.rb'
- 'spec/support/helpers/project_forks_helper.rb'
- 'spec/support/helpers/stub_metrics.rb'
- - 'spec/support/helpers/stub_spam_services.rb'
- 'spec/support/import_export/common_util.rb'
- 'spec/support/prometheus/additional_metrics_shared_examples.rb'
- 'spec/support/shared_contexts/lib/gitlab/sidekiq_middleware/server_metrics_shared_context.rb'
diff --git a/app/assets/javascripts/deprecated_notes.js b/app/assets/javascripts/deprecated_notes.js
index a46a8d4affa..e5b3b8a397c 100644
--- a/app/assets/javascripts/deprecated_notes.js
+++ b/app/assets/javascripts/deprecated_notes.js
@@ -25,6 +25,7 @@ import syntaxHighlight from '~/syntax_highlight';
import CommentTypeDropdown from '~/notes/components/comment_type_dropdown.vue';
import * as constants from '~/notes/constants';
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
+import { COMMENT_FORM } from '~/notes/i18n';
import Autosave from './autosave';
import loadAwardsHandler from './awards_handler';
import { defaultAutocompleteConfig } from './gfm_auto_complete';
@@ -687,17 +688,22 @@ export default class Notes {
return this.renderNote(note);
}
- addNoteError($form) {
+ addNoteError(error, $form) {
let formParentTimeline;
if ($form.hasClass('js-main-target-form')) {
formParentTimeline = $form.parents('.timeline');
} else if ($form.hasClass('js-discussion-note-form')) {
formParentTimeline = $form.closest('.discussion-notes').find('.notes');
}
+
+ const serverErrorMessage = error?.response?.data?.errors;
+
+ const alertMessage = serverErrorMessage
+ ? sprintf(COMMENT_FORM.error, { reason: serverErrorMessage.toLowerCase() }, false)
+ : COMMENT_FORM.GENERIC_UNSUBMITTABLE_NETWORK;
+
return this.addAlert({
- message: __(
- 'Your comment could not be submitted! Please check your network connection and try again.',
- ),
+ message: alertMessage,
parent: formParentTimeline.get(0),
});
}
@@ -1777,7 +1783,7 @@ export default class Notes {
$form.trigger('ajax:success', [note]);
})
- .catch(() => {
+ .catch((error) => {
// Submission failed, remove placeholder note and show Flash error message
$notesContainer.find(`#${noteUniqueId}`).remove();
$submitBtn.prop('disabled', false);
@@ -1806,7 +1812,7 @@ export default class Notes {
$form.find('.js-note-text').val(formContentOriginal);
this.reenableTargetFormSubmitButton(e);
- this.addNoteError($form);
+ this.addNoteError(error, $form);
});
}
diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue
index 6794f838c84..cba0f960c00 100644
--- a/app/assets/javascripts/notes/components/comment_form.vue
+++ b/app/assets/javascripts/notes/components/comment_form.vue
@@ -6,7 +6,6 @@ import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests
import { createAlert } from '~/alert';
import { badgeState } from '~/issuable/components/status_box.vue';
import { STATUS_CLOSED, STATUS_MERGED, STATUS_OPEN, STATUS_REOPENED } from '~/issues/constants';
-import { HTTP_STATUS_UNPROCESSABLE_ENTITY } from '~/lib/utils/http_status';
import { containsSensitiveToken, confirmSensitiveAction } from '~/lib/utils/secret_detection';
import {
capitalizeFirstCharacter,
@@ -21,6 +20,7 @@ import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import * as constants from '../constants';
import eventHub from '../event_hub';
import { COMMENT_FORM } from '../i18n';
+import { getErrorMessages } from '../utils';
import issuableStateMixin from '../mixins/issuable_state';
import CommentFieldLayout from './comment_field_layout.vue';
@@ -219,11 +219,7 @@ export default {
'toggleIssueLocalState',
]),
handleSaveError({ data, status }) {
- if (status === HTTP_STATUS_UNPROCESSABLE_ENTITY && data.errors?.commands_only?.length) {
- this.errors = data.errors.commands_only;
- } else {
- this.errors = [this.$options.i18n.GENERIC_UNSUBMITTABLE_NETWORK];
- }
+ this.errors = getErrorMessages(data, status);
},
handleSaveDraft() {
this.handleSave({ isDraft: true });
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index 375b16f6ce2..f5fe2cc1284 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -15,6 +15,7 @@ import { containsSensitiveToken, confirmSensitiveAction } from '~/lib/utils/secr
import eventHub from '../event_hub';
import noteable from '../mixins/noteable';
import resolvable from '../mixins/resolvable';
+import { getErrorMessages } from '../utils';
import DiffDiscussionHeader from './diff_discussion_header.vue';
import DiffWithNote from './diff_with_note.vue';
import DiscussionActions from './discussion_actions.vue';
@@ -244,26 +245,24 @@ export default {
};
this.saveNote(replyData)
- .then((res) => {
- if (res.hasAlert !== true) {
- this.isReplying = false;
- clearDraft(this.autosaveKey);
- }
+ .then(() => {
+ this.isReplying = false;
+ clearDraft(this.autosaveKey);
+
callback();
})
.catch((err) => {
- this.removePlaceholderNotes();
this.handleSaveError(err); // The 'err' parameter is being used in JH, don't remove it
- this.$refs.noteForm.note = noteText;
+ this.removePlaceholderNotes();
+
callback(err);
});
},
- handleSaveError() {
- const msg = __(
- 'Your comment could not be submitted! Please check your network connection and try again.',
- );
+ handleSaveError({ response }) {
+ const errorMessage = getErrorMessages(response.data, response.status)[0];
+
createAlert({
- message: msg,
+ message: errorMessage,
parent: this.$el,
});
},
diff --git a/app/assets/javascripts/notes/i18n.js b/app/assets/javascripts/notes/i18n.js
index 4bf2a8d70a7..633466a5fd3 100644
--- a/app/assets/javascripts/notes/i18n.js
+++ b/app/assets/javascripts/notes/i18n.js
@@ -4,6 +4,7 @@ export const COMMENT_FORM = {
GENERIC_UNSUBMITTABLE_NETWORK: __(
'Your comment could not be submitted! Please check your network connection and try again.',
),
+ error: __('Your comment could not be submitted because %{reason}.'),
note: __('Note'),
comment: __('Comment'),
internalComment: __('Add internal note'),
diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js
index f7f211c65c2..1bb44988c4d 100644
--- a/app/assets/javascripts/notes/stores/actions.js
+++ b/app/assets/javascripts/notes/stores/actions.js
@@ -555,36 +555,11 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
return res;
};
- const processErrors = (error) => {
- if (error.response) {
- const {
- response: { data = {} },
- } = error;
- const { errors = {} } = data;
- const { base = [] } = errors;
-
- // we handle only errors.base for now
- if (base.length > 0) {
- const errorMsg = sprintf(__('Your comment could not be submitted because %{error}'), {
- error: base[0].toLowerCase(),
- });
- createAlert({
- message: errorMsg,
- parent: noteData.flashContainer,
- });
- return { ...data, hasAlert: true };
- }
- }
-
- throw error;
- };
-
return dispatch(methodToDispatch, postData, { root: true })
.then(processQuickActions)
.then(processEmojiAward)
.then(processTimeTracking)
- .then(removePlaceholder)
- .catch(processErrors);
+ .then(removePlaceholder);
};
export const setFetchingState = ({ commit }, fetchingState) =>
diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js
index c3407936847..e2a10a1c1f3 100644
--- a/app/assets/javascripts/notes/stores/mutations.js
+++ b/app/assets/javascripts/notes/stores/mutations.js
@@ -82,7 +82,7 @@ export default {
const note = discussions[i];
const children = note.notes;
- if (children.length && !note.individual_note) {
+ if (children.length > 1) {
// remove placeholder from discussions
for (let j = children.length - 1; j >= 0; j -= 1) {
if (children[j].isPlaceholderNote) {
diff --git a/app/assets/javascripts/notes/utils.js b/app/assets/javascripts/notes/utils.js
index ed1c80e7a6e..c5859a89182 100644
--- a/app/assets/javascripts/notes/utils.js
+++ b/app/assets/javascripts/notes/utils.js
@@ -2,6 +2,9 @@ import { marked } from 'marked';
import markedBidi from 'marked-bidi';
import { sanitize } from '~/lib/dompurify';
import { markdownConfig } from '~/lib/utils/text_utility';
+import { HTTP_STATUS_UNPROCESSABLE_ENTITY } from '~/lib/utils/http_status';
+import { sprintf } from '~/locale';
+import { COMMENT_FORM } from './i18n';
/**
* Tracks snowplow event when User toggles timeline view
@@ -19,3 +22,17 @@ marked.use(markedBidi());
export const renderMarkdown = (rawMarkdown) => {
return sanitize(marked(rawMarkdown), markdownConfig);
};
+
+export const getErrorMessages = (data, status) => {
+ const errors = data?.errors;
+
+ if (errors && status === HTTP_STATUS_UNPROCESSABLE_ENTITY) {
+ if (errors.commands_only?.length) {
+ return errors.commands_only;
+ }
+
+ return [sprintf(COMMENT_FORM.error, { reason: errors.toLowerCase() }, false)];
+ }
+
+ return [COMMENT_FORM.GENERIC_UNSUBMITTABLE_NETWORK];
+};
diff --git a/app/controllers/concerns/notes_actions.rb b/app/controllers/concerns/notes_actions.rb
index eda9fbc9e3d..030b438afc7 100644
--- a/app/controllers/concerns/notes_actions.rb
+++ b/app/controllers/concerns/notes_actions.rb
@@ -62,7 +62,7 @@ module NotesActions
end
if @note.errors.present? && @note.errors.attribute_names != [:commands_only, :command_names]
- render json: json, status: :unprocessable_entity
+ render json: { errors: errors_on_create(@note.errors) }, status: :unprocessable_entity
else
render json: json
end
@@ -309,6 +309,12 @@ module NotesActions
noteable.discussions_rendered_on_frontend?
end
+
+ def errors_on_create(errors)
+ return { commands_only: errors.messages[:commands_only] } if errors.key?(:commands_only)
+
+ errors.full_messages.to_sentence
+ end
end
NotesActions.prepend_mod_with('NotesActions')
diff --git a/app/controllers/concerns/spammable_actions/captcha_check/html_format_actions_support.rb b/app/controllers/concerns/spammable_actions/captcha_check/html_format_actions_support.rb
index 23db6a4b368..9cad61ed362 100644
--- a/app/controllers/concerns/spammable_actions/captcha_check/html_format_actions_support.rb
+++ b/app/controllers/concerns/spammable_actions/captcha_check/html_format_actions_support.rb
@@ -28,8 +28,13 @@ module SpammableActions::CaptchaCheck::HtmlFormatActionsSupport
# recaptcha gem. This is a field which is automatically included by calling the
# `#recaptcha_tags` method within a HAML template's form.
def convert_html_spam_params_to_headers
+ return unless params['g-recaptcha-response'] || params[:spam_log_id]
+
request.headers['X-GitLab-Captcha-Response'] = params['g-recaptcha-response'] if params['g-recaptcha-response']
request.headers['X-GitLab-Spam-Log-Id'] = params[:spam_log_id] if params[:spam_log_id]
+
+ # Reset the spam_params on the request context, since they have changed mid-request
+ Gitlab::RequestContext.instance.spam_params = ::Spam::SpamParams.new_from_request(request: request)
end
end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index ac12dcb6f49..6311907a859 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -158,8 +158,7 @@ class Projects::IssuesController < Projects::ApplicationController
discussion_to_resolve: params[:discussion_to_resolve]
)
- spam_params = ::Spam::SpamParams.new_from_request(request: request)
- service = ::Issues::CreateService.new(container: project, current_user: current_user, params: create_params, spam_params: spam_params)
+ service = ::Issues::CreateService.new(container: project, current_user: current_user, params: create_params)
result = service.execute
# Only irrecoverable errors such as unauthorized user won't contain an issue in the response
@@ -373,8 +372,11 @@ class Projects::IssuesController < Projects::ApplicationController
end
def update_service
- spam_params = ::Spam::SpamParams.new_from_request(request: request)
- ::Issues::UpdateService.new(container: project, current_user: current_user, params: issue_params, spam_params: spam_params)
+ ::Issues::UpdateService.new(
+ container: project,
+ current_user: current_user,
+ params: issue_params,
+ perform_spam_check: true)
end
def finder_type
diff --git a/app/graphql/mutations/issues/create.rb b/app/graphql/mutations/issues/create.rb
index 0c1acdf316e..c8a4d0aaa86 100644
--- a/app/graphql/mutations/issues/create.rb
+++ b/app/graphql/mutations/issues/create.rb
@@ -81,9 +81,7 @@ module Mutations
def resolve(project_path:, **attributes)
project = authorized_find!(project_path)
params = build_create_issue_params(attributes.merge(author_id: current_user.id), project)
-
- spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
- result = ::Issues::CreateService.new(container: project, current_user: current_user, params: params, spam_params: spam_params).execute
+ result = ::Issues::CreateService.new(container: project, current_user: current_user, params: params).execute
check_spam_action_response!(result[:issue]) if result[:issue]
diff --git a/app/graphql/mutations/issues/set_confidential.rb b/app/graphql/mutations/issues/set_confidential.rb
index 08578881a13..79216e0f821 100644
--- a/app/graphql/mutations/issues/set_confidential.rb
+++ b/app/graphql/mutations/issues/set_confidential.rb
@@ -15,11 +15,8 @@ module Mutations
def resolve(project_path:, iid:, confidential:)
issue = authorized_find!(project_path: project_path, iid: iid)
project = issue.project
- # Changing confidentiality affects spam checking rules, therefore we need to provide
- # spam_params so a check can be performed.
- spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
-
- ::Issues::UpdateService.new(container: project, current_user: current_user, params: { confidential: confidential }, spam_params: spam_params)
+ # Changing confidentiality affects spam checking rules, therefore we need to perform a spam check
+ ::Issues::UpdateService.new(container: project, current_user: current_user, params: { confidential: confidential }, perform_spam_check: true)
.execute(issue)
check_spam_action_response!(issue)
diff --git a/app/graphql/mutations/issues/update.rb b/app/graphql/mutations/issues/update.rb
index b5af048dc07..2a863893cf1 100644
--- a/app/graphql/mutations/issues/update.rb
+++ b/app/graphql/mutations/issues/update.rb
@@ -41,8 +41,7 @@ module Mutations
args = parse_arguments(args)
- spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
- ::Issues::UpdateService.new(container: project, current_user: current_user, params: args, spam_params: spam_params).execute(issue)
+ ::Issues::UpdateService.new(container: project, current_user: current_user, params: args, perform_spam_check: true).execute(issue)
{
issue: issue,
diff --git a/app/graphql/mutations/snippets/create.rb b/app/graphql/mutations/snippets/create.rb
index 96ac3f8a113..1c7dbfa751d 100644
--- a/app/graphql/mutations/snippets/create.rb
+++ b/app/graphql/mutations/snippets/create.rb
@@ -48,8 +48,7 @@ module Mutations
process_args_for_params!(args)
- spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
- service = ::Snippets::CreateService.new(project: project, current_user: current_user, params: args, spam_params: spam_params)
+ service = ::Snippets::CreateService.new(project: project, current_user: current_user, params: args)
service_response = service.execute
# Only when the user is not an api user and the operation was successful
diff --git a/app/graphql/mutations/snippets/update.rb b/app/graphql/mutations/snippets/update.rb
index 39843a3714a..7faf9cf9019 100644
--- a/app/graphql/mutations/snippets/update.rb
+++ b/app/graphql/mutations/snippets/update.rb
@@ -33,8 +33,7 @@ module Mutations
process_args_for_params!(args)
- spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
- service = ::Snippets::UpdateService.new(project: snippet.project, current_user: current_user, params: args, spam_params: spam_params)
+ service = ::Snippets::UpdateService.new(project: snippet.project, current_user: current_user, params: args, perform_spam_check: true)
service_response = service.execute(snippet)
# TODO: DRY this up - From here down, this is all duplicated with Mutations::Snippets::Create#resolve, except for
diff --git a/app/graphql/mutations/work_items/convert.rb b/app/graphql/mutations/work_items/convert.rb
index 83bca56d900..b1936027fdc 100644
--- a/app/graphql/mutations/work_items/convert.rb
+++ b/app/graphql/mutations/work_items/convert.rb
@@ -27,13 +27,11 @@ module Mutations
work_item_type = find_work_item_type!(attributes[:work_item_type_id])
authorize_work_item_type!(work_item, work_item_type)
- spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
-
update_result = ::WorkItems::UpdateService.new(
container: work_item.project,
current_user: current_user,
params: { work_item_type: work_item_type, issue_type: work_item_type.base_type },
- spam_params: spam_params
+ perform_spam_check: true
).execute(work_item)
check_spam_action_response!(work_item)
diff --git a/app/graphql/mutations/work_items/create.rb b/app/graphql/mutations/work_items/create.rb
index dfd2d5d1f88..9f7b7b5db97 100644
--- a/app/graphql/mutations/work_items/create.rb
+++ b/app/graphql/mutations/work_items/create.rb
@@ -60,7 +60,6 @@ module Mutations
container_path = project_path || namespace_path
container = authorized_find!(container_path)
- spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
params = global_id_compatibility_params(attributes).merge(author_id: current_user.id)
type = ::WorkItems::Type.find(attributes[:work_item_type_id])
widget_params = extract_widget_params!(type, params)
@@ -69,7 +68,6 @@ module Mutations
container: container,
current_user: current_user,
params: params,
- spam_params: spam_params,
widget_params: widget_params
).execute
diff --git a/app/graphql/mutations/work_items/create_from_task.rb b/app/graphql/mutations/work_items/create_from_task.rb
index 23ae09b23fd..bf5c999bf75 100644
--- a/app/graphql/mutations/work_items/create_from_task.rb
+++ b/app/graphql/mutations/work_items/create_from_task.rb
@@ -30,13 +30,10 @@ module Mutations
def resolve(id:, work_item_data:)
work_item = authorized_find!(id: id)
- spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
-
result = ::WorkItems::CreateFromTaskService.new(
work_item: work_item,
current_user: current_user,
- work_item_params: work_item_data,
- spam_params: spam_params
+ work_item_params: work_item_data
).execute
check_spam_action_response!(result[:work_item]) if result[:work_item]
diff --git a/app/graphql/mutations/work_items/update.rb b/app/graphql/mutations/work_items/update.rb
index 3fd0f5aab62..f22e9bcf393 100644
--- a/app/graphql/mutations/work_items/update.rb
+++ b/app/graphql/mutations/work_items/update.rb
@@ -21,7 +21,6 @@ module Mutations
work_item = authorized_find!(id: id)
- spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
widget_params = extract_widget_params!(work_item.work_item_type, attributes)
interpret_quick_actions!(work_item, current_user, widget_params, attributes)
@@ -31,7 +30,7 @@ module Mutations
current_user: current_user,
params: attributes,
widget_params: widget_params,
- spam_params: spam_params
+ perform_spam_check: true
).execute(work_item)
check_spam_action_response!(work_item)
diff --git a/app/graphql/mutations/work_items/update_task.rb b/app/graphql/mutations/work_items/update_task.rb
index 8dcc4c325ea..d3df235f894 100644
--- a/app/graphql/mutations/work_items/update_task.rb
+++ b/app/graphql/mutations/work_items/update_task.rb
@@ -29,13 +29,11 @@ module Mutations
work_item = authorized_find!(id: id)
task = authorized_find_task!(task_data_hash[:id])
- spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
-
::WorkItems::UpdateService.new(
container: task.project,
current_user: current_user,
params: task_data_hash.except(:id),
- spam_params: spam_params
+ perform_spam_check: true
).execute(task)
check_spam_action_response!(task)
diff --git a/app/helpers/appearances_helper.rb b/app/helpers/appearances_helper.rb
index e9465e0db22..5beefbb943c 100644
--- a/app/helpers/appearances_helper.rb
+++ b/app/helpers/appearances_helper.rb
@@ -53,8 +53,12 @@ module AppearancesHelper
image_path('logo.svg')
end
- def brand_text
- markdown_field(current_appearance, :description)
+ def custom_sign_in_description
+ [
+ markdown_field(current_appearance, :description),
+ markdown_field(Gitlab::CurrentSettings.current_application_settings, :sign_in_text),
+ markdown(Gitlab::CurrentSettings.help_text)
+ ].compact_blank.join("<br>").html_safe
end
def brand_new_project_guidelines
diff --git a/app/models/merge_request/diff_llm_summary.rb b/app/models/merge_request/diff_llm_summary.rb
index 5e7d80712e2..e13fe5e1f50 100644
--- a/app/models/merge_request/diff_llm_summary.rb
+++ b/app/models/merge_request/diff_llm_summary.rb
@@ -5,6 +5,7 @@ class MergeRequest::DiffLlmSummary < ApplicationRecord
belongs_to :merge_request_diff
belongs_to :user, optional: true
+ validates :merge_request_diff_id, uniqueness: true
validates :provider, presence: true
validates :content, presence: true, length: { maximum: 2056 }
diff --git a/app/services/boards/issues/create_service.rb b/app/services/boards/issues/create_service.rb
index 77e297b6b11..04b5d826416 100644
--- a/app/services/boards/issues/create_service.rb
+++ b/app/services/boards/issues/create_service.rb
@@ -32,7 +32,7 @@ module Boards
def create_issue(params)
# NOTE: We are intentionally not doing a spam/CAPTCHA check for issues created via boards.
# See https://gitlab.com/gitlab-org/gitlab/-/issues/29400#note_598479184 for more context.
- ::Issues::CreateService.new(container: project, current_user: current_user, params: params, spam_params: nil).execute
+ ::Issues::CreateService.new(container: project, current_user: current_user, params: params, perform_spam_check: false).execute
end
end
end
diff --git a/app/services/ci/pipelines/add_job_service.rb b/app/services/ci/pipelines/add_job_service.rb
index 1a5c8d0dccf..dfbb37cf0dc 100644
--- a/app/services/ci/pipelines/add_job_service.rb
+++ b/app/services/ci/pipelines/add_job_service.rb
@@ -18,12 +18,6 @@ module Ci
in_lock("ci:pipelines:#{pipeline.id}:add-job", ttl: LOCK_TIMEOUT, sleep_sec: LOCK_SLEEP, retries: LOCK_RETRIES) do
Ci::Pipeline.transaction do
- # This is used to reduce the deadlocks when partitioning `ci_builds`
- # since inserting into this table requires locks on all foreign keys
- # and we need to lock all the tables in a specific order for the
- # migration to succeed.
- Ci::Pipeline.connection.execute('LOCK "ci_pipelines", "ci_stages" IN ROW SHARE MODE;')
-
yield(job)
job.update_older_statuses_retried!
diff --git a/app/services/import_csv/base_service.rb b/app/services/import_csv/base_service.rb
index 70834b8a85a..cfaf3e831eb 100644
--- a/app/services/import_csv/base_service.rb
+++ b/app/services/import_csv/base_service.rb
@@ -103,16 +103,12 @@ module ImportCsv
strong_memoize_attr :detect_col_sep
def create_object(attributes)
- # NOTE: CSV imports are performed by workers, so we do not have a request context in order
- # to create a SpamParams object to pass to the issuable create service.
- spam_params = nil
-
# default_params can be extracted into a method if we need
# to support creation of objects that belongs to groups.
default_params = { container: project,
current_user: user,
params: attributes,
- spam_params: spam_params }
+ perform_spam_check: false }
create_service = create_object_class.new(**default_params.merge(extra_create_service_params))
diff --git a/app/services/incident_management/incidents/create_service.rb b/app/services/incident_management/incidents/create_service.rb
index a75c5d2e75c..fe0b41a3a31 100644
--- a/app/services/incident_management/incidents/create_service.rb
+++ b/app/services/incident_management/incidents/create_service.rb
@@ -25,7 +25,7 @@ module IncidentManagement
severity: severity,
alert_management_alerts: [alert].compact
},
- spam_params: nil
+ perform_spam_check: false
).execute
if alert
diff --git a/app/services/integrations/slack_interactions/incident_management/incident_modal_submit_service.rb b/app/services/integrations/slack_interactions/incident_management/incident_modal_submit_service.rb
index 34af03640d3..e9d86e9228d 100644
--- a/app/services/integrations/slack_interactions/incident_management/incident_modal_submit_service.rb
+++ b/app/services/integrations/slack_interactions/incident_management/incident_modal_submit_service.rb
@@ -22,7 +22,7 @@ module Integrations
container: project,
current_user: find_user.user,
params: incident_params,
- spam_params: nil
+ perform_spam_check: false
).execute
raise IssueCreateError, create_response.errors.to_sentence if create_response.error?
diff --git a/app/services/issues/clone_service.rb b/app/services/issues/clone_service.rb
index c2a724254a7..8af44fb1e3c 100644
--- a/app/services/issues/clone_service.rb
+++ b/app/services/issues/clone_service.rb
@@ -68,9 +68,7 @@ module Issues
new_params.delete(:created_at)
new_params.delete(:updated_at)
- # spam checking is not necessary, as no new content is being created. Passing nil for
- # spam_params will cause SpamActionService to skip checking and return a success response.
- spam_params = nil
+ # spam checking is not necessary, as no new content is being created.
# Skip creation of system notes for existing attributes of the issue when cloning with notes.
# The system notes of the old issue are copied over so we don't want to end up with duplicate notes.
@@ -79,7 +77,7 @@ module Issues
container: target_project,
current_user: current_user,
params: new_params,
- spam_params: spam_params
+ perform_spam_check: false
).execute(skip_system_notes: with_notes)
raise CloneError, create_result.errors.join(', ') if create_result.error? && create_result[:issue].blank?
diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb
index ba8f00d03d4..4731a5dd39c 100644
--- a/app/services/issues/create_service.rb
+++ b/app/services/issues/create_service.rb
@@ -9,14 +9,10 @@ module Issues
rate_limit key: :issues_create,
opts: { scope: [:project, :current_user, :external_author] }
- # NOTE: For Issues::CreateService, we require the spam_params and do not default it to nil, because
- # spam_checking is likely to be necessary. However, if there is not a request available in scope
- # in the caller (for example, an issue created via email) and the required arguments to the
- # SpamParams constructor are not otherwise available, spam_params: must be explicitly passed as nil.
- def initialize(container:, spam_params:, current_user: nil, params: {}, build_service: nil)
+ def initialize(container:, current_user: nil, params: {}, build_service: nil, perform_spam_check: true)
@extra_params = params.delete(:extra_params) || {}
super(container: container, current_user: current_user, params: params)
- @spam_params = spam_params
+ @perform_spam_check = perform_spam_check
@build_service = build_service ||
BuildService.new(container: project, current_user: current_user, params: params)
end
@@ -51,12 +47,7 @@ module Issues
end
def before_create(issue)
- Spam::SpamActionService.new(
- spammable: issue,
- spam_params: spam_params,
- user: current_user,
- action: :create
- ).execute
+ Spam::SpamActionService.new(spammable: issue, user: current_user, action: :create).execute if perform_spam_check
# current_user (defined in BaseService) is not available within run_after_commit block
user = current_user
@@ -109,7 +100,7 @@ module Issues
:create_issue
end
- attr_reader :spam_params, :extra_params
+ attr_reader :perform_spam_check, :extra_params
def create_timeline_event(issue)
return unless issue.work_item_type&.incident?
@@ -118,7 +109,7 @@ module Issues
end
def user_agent_detail_service
- UserAgentDetailService.new(spammable: @issue, spam_params: spam_params)
+ UserAgentDetailService.new(spammable: @issue, perform_spam_check: perform_spam_check)
end
def handle_add_related_issue(issue)
diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb
index a2180dabdea..c1599ceef6e 100644
--- a/app/services/issues/move_service.rb
+++ b/app/services/issues/move_service.rb
@@ -90,9 +90,7 @@ module Issues
new_params = original_entity.serializable_hash.symbolize_keys.merge(new_params)
new_params = new_params.merge(rewritten_old_entity_attributes)
- # spam checking is not necessary, as no new content is being created. Passing nil for
- # spam_params will cause SpamActionService to skip checking and return a success response.
- spam_params = nil
+ # spam checking is not necessary, as no new content is being created.
# Skip creation of system notes for existing attributes of the issue. The system notes of the old
# issue are copied over so we don't want to end up with duplicate notes.
@@ -100,7 +98,7 @@ module Issues
container: @target_project,
current_user: @current_user,
params: new_params,
- spam_params: spam_params
+ perform_spam_check: false
).execute(skip_system_notes: true)
raise MoveError, create_result.errors.join(', ') if create_result.error? && create_result[:issue].blank?
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index 201bf19b535..88eb7e69267 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -2,12 +2,12 @@
module Issues
class UpdateService < Issues::BaseService
- # NOTE: For Issues::UpdateService, we default the spam_params to nil, because spam_checking is not
- # necessary in many cases, and we don't want to require every caller to explicitly pass it as nil
+ # NOTE: For Issues::UpdateService, we default perform_spam_check to false, because spam_checking is not
+ # necessary in many cases, and we don't want to require every caller to explicitly pass it
# to disable spam checking.
- def initialize(container:, current_user: nil, params: {}, spam_params: nil)
+ def initialize(container:, current_user: nil, params: {}, perform_spam_check: false)
super(container: container, current_user: current_user, params: params)
- @spam_params = spam_params
+ @perform_spam_check = perform_spam_check
end
def execute(issue)
@@ -26,11 +26,10 @@ module Issues
def before_update(issue, skip_spam_check: false)
change_work_item_type(issue)
- return if skip_spam_check
+ return if skip_spam_check || !perform_spam_check
Spam::SpamActionService.new(
spammable: issue,
- spam_params: spam_params,
user: current_user,
action: :update
).execute
@@ -115,7 +114,7 @@ module Issues
private
- attr_reader :spam_params
+ attr_reader :perform_spam_check
def handle_date_changes(issue)
return unless issue.previous_changes.slice('due_date', 'start_date').any?
diff --git a/app/services/snippets/create_service.rb b/app/services/snippets/create_service.rb
index a62d5290271..111c6211dab 100644
--- a/app/services/snippets/create_service.rb
+++ b/app/services/snippets/create_service.rb
@@ -2,11 +2,9 @@
module Snippets
class CreateService < Snippets::BaseService
- # NOTE: For Issues::CreateService, we require the spam_params and do not default it to nil, because
- # spam_checking is likely to be necessary.
- def initialize(project:, spam_params:, current_user: nil, params: {})
+ def initialize(project:, current_user: nil, params: {}, perform_spam_check: true)
super(project: project, current_user: current_user, params: params)
- @spam_params = spam_params
+ @perform_spam_check = perform_spam_check
end
def execute
@@ -20,16 +18,17 @@ module Snippets
@snippet.author = current_user
- Spam::SpamActionService.new(
- spammable: @snippet,
- spam_params: spam_params,
- user: current_user,
- action: :create,
- extra_features: { files: file_paths_to_commit }
- ).execute
+ if perform_spam_check
+ Spam::SpamActionService.new(
+ spammable: @snippet,
+ user: current_user,
+ action: :create,
+ extra_features: { files: file_paths_to_commit }
+ ).execute
+ end
if save_and_commit
- UserAgentDetailService.new(spammable: @snippet, spam_params: spam_params).create
+ UserAgentDetailService.new(spammable: @snippet, perform_spam_check: perform_spam_check).create
Gitlab::UsageDataCounters::SnippetCounter.count(:create)
move_temporary_files
@@ -42,7 +41,7 @@ module Snippets
private
- attr_reader :snippet, :spam_params
+ attr_reader :snippet, :perform_spam_check
def build_from_params
if project
diff --git a/app/services/snippets/update_service.rb b/app/services/snippets/update_service.rb
index 067680f2abc..684ede1f2eb 100644
--- a/app/services/snippets/update_service.rb
+++ b/app/services/snippets/update_service.rb
@@ -6,12 +6,9 @@ module Snippets
UpdateError = Class.new(StandardError)
- # NOTE: For Snippets::UpdateService, we default the spam_params to nil, because spam_checking is not
- # necessary in many cases, and we don't want every caller to have to explicitly pass it as nil
- # to disable spam checking.
- def initialize(project:, current_user: nil, params: {}, spam_params: nil)
+ def initialize(project:, current_user: nil, params: {}, perform_spam_check: false)
super(project: project, current_user: current_user, params: params)
- @spam_params = spam_params
+ @perform_spam_check = perform_spam_check
end
def execute(snippet)
@@ -25,13 +22,14 @@ module Snippets
files = snippet.all_files.map { |f| { path: f } } + file_paths_to_commit
- Spam::SpamActionService.new(
- spammable: snippet,
- spam_params: spam_params,
- user: current_user,
- action: :update,
- extra_features: { files: files }
- ).execute
+ if perform_spam_check
+ Spam::SpamActionService.new(
+ spammable: snippet,
+ user: current_user,
+ action: :update,
+ extra_features: { files: files }
+ ).execute
+ end
if save_and_commit(snippet)
Gitlab::UsageDataCounters::SnippetCounter.count(:update)
@@ -44,7 +42,7 @@ module Snippets
private
- attr_reader :spam_params
+ attr_reader :perform_spam_check
def visibility_changed?(snippet)
visibility_level && visibility_level.to_i != snippet.visibility_level
diff --git a/app/services/spam/spam_action_service.rb b/app/services/spam/spam_action_service.rb
index 7c96f003e46..9a55576384b 100644
--- a/app/services/spam/spam_action_service.rb
+++ b/app/services/spam/spam_action_service.rb
@@ -4,9 +4,8 @@ module Spam
class SpamActionService
include SpamConstants
- def initialize(spammable:, spam_params:, user:, action:, extra_features: {})
+ def initialize(spammable:, user:, action:, extra_features: {})
@target = spammable
- @spam_params = spam_params
@user = user
@action = action
@extra_features = extra_features
@@ -14,10 +13,6 @@ module Spam
# rubocop:disable Metrics/AbcSize
def execute
- # If spam_params is passed as `nil`, no check will be performed. This is the easiest way to allow
- # composed services which may not need to do spam checking to "opt out". For example, when
- # MoveService is calling CreateService, spam checking is not necessary, as no new content is
- # being created.
return ServiceResponse.success(message: 'Skipped spam check because spam_params was not present') unless spam_params
recaptcha_verified = Captcha::CaptchaVerificationService.new(spam_params: spam_params).execute
@@ -41,7 +36,11 @@ module Spam
private
- attr_reader :user, :action, :target, :spam_params, :spam_log, :extra_features
+ attr_reader :user, :action, :target, :spam_log, :extra_features
+
+ def spam_params
+ Gitlab::RequestContext.instance.spam_params
+ end
##
# In order to be proceed to the spam check process, the target must be
diff --git a/app/services/tasks_to_be_done/base_service.rb b/app/services/tasks_to_be_done/base_service.rb
index 1c74e803e0b..1d50e5081ff 100644
--- a/app/services/tasks_to_be_done/base_service.rb
+++ b/app/services/tasks_to_be_done/base_service.rb
@@ -19,7 +19,7 @@ module TasksToBeDone
update_service = Issues::UpdateService.new(container: project, current_user: current_user, params: { add_assignee_ids: params[:assignee_ids] })
update_service.execute(issue)
else
- create_service = Issues::CreateService.new(container: project, current_user: current_user, params: params, spam_params: nil)
+ create_service = Issues::CreateService.new(container: project, current_user: current_user, params: params, perform_spam_check: false)
create_service.execute
end
end
diff --git a/app/services/user_agent_detail_service.rb b/app/services/user_agent_detail_service.rb
index 01a98a15869..ccb5cec2df8 100644
--- a/app/services/user_agent_detail_service.rb
+++ b/app/services/user_agent_detail_service.rb
@@ -1,15 +1,16 @@
# frozen_string_literal: true
class UserAgentDetailService
- def initialize(spammable:, spam_params:)
+ def initialize(spammable:, perform_spam_check:)
@spammable = spammable
- @spam_params = spam_params
+ @perform_spam_check = perform_spam_check
end
def create
- unless spam_params&.user_agent && spam_params&.ip_address
- messasge = 'Skipped UserAgentDetail creation because necessary spam_params were not provided'
- return ServiceResponse.success(message: messasge)
+ spam_params = Gitlab::RequestContext.instance.spam_params
+ if !perform_spam_check || spam_params&.user_agent.blank? || spam_params&.ip_address.blank?
+ message = 'Skipped UserAgentDetail creation because necessary spam_params were not provided'
+ return ServiceResponse.success(message: message)
end
spammable.create_user_agent_detail(user_agent: spam_params.user_agent, ip_address: spam_params.ip_address)
@@ -17,5 +18,5 @@ class UserAgentDetailService
private
- attr_reader :spammable, :spam_params
+ attr_reader :spammable, :perform_spam_check
end
diff --git a/app/services/work_items/create_and_link_service.rb b/app/services/work_items/create_and_link_service.rb
index ae09e44b952..ba22b170a28 100644
--- a/app/services/work_items/create_and_link_service.rb
+++ b/app/services/work_items/create_and_link_service.rb
@@ -6,12 +6,12 @@ module WorkItems
# This class should always be run inside a transaction as we could end up with
# new work items that were never associated with other work items as expected.
class CreateAndLinkService
- def initialize(project:, spam_params:, current_user: nil, params: {}, link_params: {})
+ def initialize(project:, perform_spam_check: true, current_user: nil, params: {}, link_params: {})
@project = project
@current_user = current_user
@params = params
@link_params = link_params
- @spam_params = spam_params
+ @perform_spam_check = perform_spam_check
end
def execute
@@ -19,7 +19,7 @@ module WorkItems
container: @project,
current_user: @current_user,
params: @params.merge(title: @params[:title].strip).reverse_merge(confidential: confidential_parent),
- spam_params: @spam_params
+ perform_spam_check: @perform_spam_check
).execute
return create_result if create_result.error?
diff --git a/app/services/work_items/create_from_task_service.rb b/app/services/work_items/create_from_task_service.rb
index ced5b17a21c..25ec3169fe7 100644
--- a/app/services/work_items/create_from_task_service.rb
+++ b/app/services/work_items/create_from_task_service.rb
@@ -2,11 +2,11 @@
module WorkItems
class CreateFromTaskService
- def initialize(work_item:, spam_params:, current_user: nil, work_item_params: {})
+ def initialize(work_item:, perform_spam_check: true, current_user: nil, work_item_params: {})
@work_item = work_item
@current_user = current_user
@work_item_params = work_item_params
- @spam_params = spam_params
+ @perform_spam_check = perform_spam_check
@errors = []
end
@@ -16,7 +16,7 @@ module WorkItems
project: @work_item.project,
current_user: @current_user,
params: @work_item_params.slice(:title, :work_item_type_id),
- spam_params: @spam_params,
+ perform_spam_check: @perform_spam_check,
link_params: { parent_work_item: @work_item }
).execute
diff --git a/app/services/work_items/create_service.rb b/app/services/work_items/create_service.rb
index ae355dc6d96..903736cf662 100644
--- a/app/services/work_items/create_service.rb
+++ b/app/services/work_items/create_service.rb
@@ -5,12 +5,12 @@ module WorkItems
extend ::Gitlab::Utils::Override
include WidgetableService
- def initialize(container:, spam_params:, current_user: nil, params: {}, widget_params: {})
+ def initialize(container:, perform_spam_check: true, current_user: nil, params: {}, widget_params: {})
super(
container: container,
current_user: current_user,
params: params,
- spam_params: spam_params,
+ perform_spam_check: perform_spam_check,
build_service: ::WorkItems::BuildService.new(container: container, current_user: current_user, params: params)
)
@widget_params = widget_params
diff --git a/app/services/work_items/update_service.rb b/app/services/work_items/update_service.rb
index defdeebfed8..c96a6334d24 100644
--- a/app/services/work_items/update_service.rb
+++ b/app/services/work_items/update_service.rb
@@ -5,10 +5,10 @@ module WorkItems
extend Gitlab::Utils::Override
include WidgetableService
- def initialize(container:, current_user: nil, params: {}, spam_params: nil, widget_params: {})
+ def initialize(container:, current_user: nil, params: {}, perform_spam_check: false, widget_params: {})
params[:widget_params] = true if widget_params.present?
- super(container: container, current_user: current_user, params: params, spam_params: spam_params)
+ super(container: container, current_user: current_user, params: params, perform_spam_check: perform_spam_check)
@widget_params = widget_params
end
diff --git a/app/views/devise/sessions/_new_base.html.haml b/app/views/devise/sessions/_new_base.html.haml
index e21510d450a..be93073f3d2 100644
--- a/app/views/devise/sessions/_new_base.html.haml
+++ b/app/views/devise/sessions/_new_base.html.haml
@@ -26,6 +26,3 @@
.submit-container.gl-px-5.gl-pb-5
= f.button _('Sign in'), type: :submit, class: "gl-button btn btn-block btn-confirm js-sign-in-button#{' js-no-auto-disable' if Feature.enabled?(:arkose_labs_login_challenge)}", data: { qa_selector: 'sign_in_button', testid: 'sign-in-button' }
- - if Gitlab::CurrentSettings.sign_in_text.present? && Feature.enabled?(:restyle_login_page, @project)
- .gl-px-5
- = markdown_field(Gitlab::CurrentSettings.current_application_settings, :sign_in_text)
diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml
index 71771dd7cb6..6e1d3ba678c 100644
--- a/app/views/layouts/devise.html.haml
+++ b/app/views/layouts/devise.html.haml
@@ -1,4 +1,5 @@
- add_page_specific_style 'page_bundles/login'
+- custom_text = custom_sign_in_description
!!! 5
%html.devise-layout-html{ class: system_message_class }
= render "layouts/head", { startup_filename: 'signin' }
@@ -10,14 +11,13 @@
.container.navless-container
.content
= render "layouts/flash"
- - if current_appearance&.description?
+ - if custom_text.present?
.row
.col-md.order-12.sm-bg-gray-10
.col-sm-12
%h1.mb-3.gl-font-size-h2
= brand_title
- = brand_text
- = render_if_exists 'layouts/devise_help_text'
+ = custom_text
.col-md.order-md-12
.col-sm-12.bar
.gl-text-center
@@ -29,7 +29,6 @@
= brand_image
%h1.mb-3.gl-font-size-h2
= brand_title
- = render_if_exists 'layouts/devise_help_text'
.mb-3
.gl-w-half.gl-xs-w-full.gl-ml-auto.gl-mr-auto.bar
= yield
@@ -49,8 +48,8 @@
.col-md-6.order-12.order-sm-1.brand-holder
- unless recently_confirmed_com?
= brand_image
- - if current_appearance&.description?
- = brand_text
+ - if custom_text.present?
+ = custom_text
- else
%h3.gl-sm-mt-0
= _('A complete DevOps platform')
@@ -61,11 +60,6 @@
%p
= _('This is a self-managed instance of GitLab.')
- - if Gitlab::CurrentSettings.sign_in_text.present?
- = markdown_field(Gitlab::CurrentSettings.current_application_settings, :sign_in_text)
-
- = render_if_exists 'layouts/devise_help_text'
-
.col-md-6.order-1.new-session-forms-container{ class: recently_confirmed_com? ? 'order-sm-first' : 'order-sm-12' }
= yield
diff --git a/db/migrate/20230601084041_add_merge_request_diff_llm_summaries_unique_index.rb b/db/migrate/20230601084041_add_merge_request_diff_llm_summaries_unique_index.rb
new file mode 100644
index 00000000000..6f8afb561e9
--- /dev/null
+++ b/db/migrate/20230601084041_add_merge_request_diff_llm_summaries_unique_index.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddMergeRequestDiffLlmSummariesUniqueIndex < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'unique_merge_request_diff_llm_summaries_on_mr_diff_id'
+ OLD_INDEX_NAME = 'index_merge_request_diff_llm_summaries_on_mr_diff_id'
+
+ def up
+ add_concurrent_index :merge_request_diff_llm_summaries, :merge_request_diff_id, name: INDEX_NAME, unique: true
+ remove_concurrent_index_by_name :merge_request_diff_llm_summaries, OLD_INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :merge_request_diff_llm_summaries, :merge_request_diff_id, name: OLD_INDEX_NAME
+ remove_concurrent_index_by_name :merge_request_diff_llm_summaries, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20230605093005_add_index_for_sbom_occurrences_on_project_id_source_id.rb b/db/post_migrate/20230605093005_add_index_for_sbom_occurrences_on_project_id_source_id.rb
new file mode 100644
index 00000000000..868cf276354
--- /dev/null
+++ b/db/post_migrate/20230605093005_add_index_for_sbom_occurrences_on_project_id_source_id.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddIndexForSbomOccurrencesOnProjectIdSourceId < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'idx_sbom_occurrences_on_project_id_and_source_id'
+
+ def up
+ add_concurrent_index :sbom_occurrences, [:project_id, :source_id], name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :sbom_occurrences, INDEX_NAME
+ end
+end
diff --git a/db/schema_migrations/20230601084041 b/db/schema_migrations/20230601084041
new file mode 100644
index 00000000000..7eac90f4c33
--- /dev/null
+++ b/db/schema_migrations/20230601084041
@@ -0,0 +1 @@
+c64ff0998a0cd143f98c3b2461f71ddadafac5e3834c6980d658e390d1533dd9 \ No newline at end of file
diff --git a/db/schema_migrations/20230605093005 b/db/schema_migrations/20230605093005
new file mode 100644
index 00000000000..273961a1f40
--- /dev/null
+++ b/db/schema_migrations/20230605093005
@@ -0,0 +1 @@
+df354a2edec37d3b09bc9deebe895637f8a86c90ffb2569cefe6d791458a65ba \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 2c12ac39a8a..b0a528aa8c1 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -29690,6 +29690,8 @@ CREATE INDEX idx_repository_states_on_wiki_failure_partial ON project_repository
CREATE INDEX idx_repository_states_outdated_checksums ON project_repository_states USING btree (project_id) WHERE (((repository_verification_checksum IS NULL) AND (last_repository_verification_failure IS NULL)) OR ((wiki_verification_checksum IS NULL) AND (last_wiki_verification_failure IS NULL)));
+CREATE INDEX idx_sbom_occurrences_on_project_id_and_source_id ON sbom_occurrences USING btree (project_id, source_id);
+
CREATE UNIQUE INDEX idx_security_scans_on_build_and_scan_type ON security_scans USING btree (build_id, scan_type);
CREATE INDEX idx_security_scans_on_scan_type ON security_scans USING btree (scan_type);
@@ -31410,8 +31412,6 @@ CREATE INDEX index_merge_request_diff_details_on_verification_state ON merge_req
CREATE INDEX index_merge_request_diff_details_pending_verification ON merge_request_diff_details USING btree (verified_at NULLS FIRST) WHERE (verification_state = 0);
-CREATE INDEX index_merge_request_diff_llm_summaries_on_mr_diff_id ON merge_request_diff_llm_summaries USING btree (merge_request_diff_id);
-
CREATE INDEX index_merge_request_diff_llm_summaries_on_user_id ON merge_request_diff_llm_summaries USING btree (user_id);
CREATE INDEX index_merge_request_diffs_by_id_partial ON merge_request_diffs USING btree (id) WHERE ((files_count > 0) AND ((NOT stored_externally) OR (stored_externally IS NULL)));
@@ -33304,6 +33304,8 @@ CREATE UNIQUE INDEX unique_index_for_project_pages_unique_domain ON project_sett
CREATE UNIQUE INDEX unique_index_on_system_note_metadata_id ON resource_link_events USING btree (system_note_metadata_id);
+CREATE UNIQUE INDEX unique_merge_request_diff_llm_summaries_on_mr_diff_id ON merge_request_diff_llm_summaries USING btree (merge_request_diff_id);
+
CREATE UNIQUE INDEX unique_merge_request_metrics_by_merge_request_id ON merge_request_metrics USING btree (merge_request_id);
CREATE UNIQUE INDEX unique_organizations_on_path ON organizations USING btree (path);
diff --git a/doc/administration/lfs/index.md b/doc/administration/lfs/index.md
index ee5cd04f3d6..e4475e877d8 100644
--- a/doc/administration/lfs/index.md
+++ b/doc/administration/lfs/index.md
@@ -401,7 +401,7 @@ To delete these references:
If you configure GitLab to [disable TLS v1.2](https://docs.gitlab.com/omnibus/settings/nginx.html)
and only enable TLS v1.3 connections, LFS operations require a
-[Git LFS client](https://git-lfs.github.com) version 2.11.0 or later. If you use
+[Git LFS client](https://git-lfs.com/) version 2.11.0 or later. If you use
a Git LFS client earlier than version 2.11.0, GitLab displays an error:
```plaintext
diff --git a/doc/ci/cloud_services/azure/index.md b/doc/ci/cloud_services/azure/index.md
index 912e6597985..29d27e440ec 100644
--- a/doc/ci/cloud_services/azure/index.md
+++ b/doc/ci/cloud_services/azure/index.md
@@ -34,7 +34,7 @@ To complete this tutorial:
1. [Grant permissions for the service principal](#grant-permissions-for-the-service-principal).
1. [Retrieve a temporary credential](#retrieve-a-temporary-credential).
-For more information, review Azure's documentation on [Workload identity federation](https://learn.microsoft.com/en-us/azure/active-directory/develop/workload-identity-federation).
+For more information, review Azure's documentation on [Workload identity federation](https://learn.microsoft.com/en-us/azure/active-directory/workload-identities/workload-identity-federation).
## Create Azure AD application and service principal
diff --git a/doc/ci/cloud_services/index.md b/doc/ci/cloud_services/index.md
index 54cadc9e1b6..eadf0656d48 100644
--- a/doc/ci/cloud_services/index.md
+++ b/doc/ci/cloud_services/index.md
@@ -38,8 +38,7 @@ ID tokens support cloud providers with OIDC, including:
NOTE:
Configuring OIDC enables JWT token access to the target environments for all pipelines.
When you configure OIDC for a pipeline, you should complete a software supply chain security
-review for the pipeline, focusing on the additional access. You can use the [software supply chain security awareness assessment](https://about.gitlab.com/quiz/software-supply-chain-security/)
-as a starting point, and for more information about supply chain attacks, see
+review for the pipeline, focusing on the additional access. For more information about supply chain attacks, see
[How a DevOps Platform helps protect against supply chain attacks](https://about.gitlab.com/blog/2021/04/28/devops-platform-supply-chain-attacks/).
## Use cases
diff --git a/doc/ci/pipelines/cicd_minutes.md b/doc/ci/pipelines/cicd_minutes.md
index 571a47c5426..190c732e48b 100644
--- a/doc/ci/pipelines/cicd_minutes.md
+++ b/doc/ci/pipelines/cicd_minutes.md
@@ -5,38 +5,38 @@ info: To determine the technical writer assigned to the Stage/Group associated w
type: reference
---
-# CI/CD minutes quota **(PREMIUM)**
+# Compute quota **(PREMIUM)**
NOTE:
The term `CI/CD minutes` is being renamed to `units of compute`. During this transition, you might see references in the UI and documentation to `CI/CD minutes`, `CI minutes`, `pipeline minutes`, `CI pipeline minutes`, `pipeline minutes quota`, and `units of compute`. For more information, see [epic 2150](https://gitlab.com/groups/gitlab-com/-/epics/2150).
Administrators can limit the amount of time that projects can use to run jobs on
[shared runners](../runners/runners_scope.md#shared-runners) each month. This limit
-is tracked with a quota of CI/CD minutes.
+is tracked with a compute quota.
By default, one minute of execution time by a single job uses
-one CI/CD minute. The total amount of CI/CD minutes used by a pipeline is
+one unit of compute. The total execution time for a pipeline is
[the sum of all its jobs' durations](#how-compute-usage-is-calculated).
-Jobs can run concurrently, so the total CI/CD minute usage can be higher than the
+Jobs can run concurrently, so the total usage can be higher than the
end-to-end duration of a pipeline.
On GitLab.com:
-- CI/CD minutes quotas are enabled for all projects, but certain
- projects [consume CI/CD minutes at a slower rate](#cost-factor).
-- The base monthly CI/CD minutes quota for a GitLab.com [namespace](../../user/namespace/index.md)
+- Compute quotas are enabled for all projects, but certain
+ projects [consume units of compute at a slower rate](#cost-factor).
+- The base monthly compute quota for a GitLab.com [namespace](../../user/namespace/index.md)
is determined by its [license tier](https://about.gitlab.com/pricing/).
-- You can [purchase additional CI/CD minutes](#purchase-additional-cicd-minutes)
- if you need more than the number of CI/CD minutes in your monthly quota.
+- You can [purchase additional units of compute](#purchase-additional-units-of-compute)
+ if you need more than the amount of compute in your monthly quota.
On self-managed GitLab instances:
-- CI/CD minutes quotas are disabled by default.
-- When enabled, CI/CD minutes quotas apply to private projects only.
-- Administrators can [assign more CI/CD minutes](#set-the-compute-quota-for-a-specific-namespace)
- if a namespace uses all the CI/CD minutes in its monthly quota.
+- Compute quotas are disabled by default.
+- When enabled, compute quotas apply to private projects only.
+- Administrators can [assign more units of compute](#set-the-compute-quota-for-a-specific-namespace)
+ if a namespace uses all its monthly quota.
-[Project runners](../runners/runners_scope.md#project-runners) are not subject to a quota of CI/CD minutes.
+[Project runners](../runners/runners_scope.md#project-runners) are not subject to a compute quota.
## Set the compute quota for all namespaces
@@ -129,64 +129,64 @@ The projects list shows [personal projects](../../user/project/working_with_proj
with compute usage or shared runners usage in the current month only. The list
is sorted in descending order of compute usage.
-## Purchase additional CI/CD minutes **(FREE SAAS)**
+## Purchase additional units of compute **(FREE SAAS)**
-If you're using GitLab SaaS, you can purchase additional packs of CI/CD minutes.
-These additional CI/CD minutes:
+If you're using GitLab SaaS, you can purchase additional packs of units of compute.
+These additional units of compute:
- Are used only after the monthly quota included in your subscription runs out.
- Are carried over to the next month, if any remain at the end of the month.
-- Are valid for 12 months from date of purchase or until all minutes are consumed, whichever comes first. Expiry of minutes is not enforced.
+- Are valid for 12 months from date of purchase or until all units of compute are consumed, whichever comes first. Expiry of units of compute is not enforced.
For example, with a GitLab SaaS Premium license:
-- You have `10,000` monthly minutes.
-- You purchase an additional `5,000` minutes.
-- Your total limit is `15,000` minutes.
+- You have `10,000` monthly units of compute.
+- You purchase an additional `5,000` units of compute.
+- Your total limit is `15,000` units of compute.
-If you use `13,000` minutes during the month, the next month your additional minutes become
-`2,000`. If you use `9,000` minutes during the month, your additional minutes remain the same.
+If you use `13,000` units of compute during the month, the next month your additional units of compute become
+`2,000`. If you use `9,000` units of compute during the month, your additional units of compute remain the same.
-If you bought additional CI/CD minutes while on a trial subscription, those minutes are available after the trial ends or you upgrade to a paid plan.
+If you bought additional units of compute while on a trial subscription, those units of compute are available after the trial ends or you upgrade to a paid plan.
-You can find pricing for additional CI/CD minutes on the
+You can find pricing for additional units of compute on the
[GitLab Pricing page](https://about.gitlab.com/pricing/).
-### Purchase CI/CD minutes for a group **(FREE SAAS)**
+### Purchase units of compute for a group **(FREE SAAS)**
Prerequisite:
- You must have the Owner role for the group.
-You can purchase additional CI/CD minutes for your group.
-You cannot transfer purchased CI/CD minutes from one group to another,
+You can purchase additional units of compute for your group.
+You cannot transfer purchased units of compute from one group to another,
so be sure to select the correct group.
1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > Usage Quotas**.
1. Select **Pipelines**.
-1. Select **Buy additional minutes**.
+1. Select **Buy additional units of compute**.
1. Complete the details of the transaction.
-After your payment is processed, the additional CI/CD minutes are added to your group
+After your payment is processed, the additional units of compute are added to your group
namespace.
-### Purchase CI/CD minutes for a personal namespace **(FREE SAAS)**
+### Purchase units of compute for a personal namespace **(FREE SAAS)**
Prerequisite:
- The namespace must be your personal namespace.
-To purchase additional minutes for your personal namespace:
+To purchase additional units of compute for your personal namespace:
1. On the top bar, in the upper-right corner, select your avatar.
1. Select **Edit profile**.
1. On the left sidebar, select **Usage Quotas**.
-1. Select **Buy additional minutes**. GitLab redirects you to the Customers Portal.
-1. Locate the subscription card that's linked to your personal namespace on GitLab SaaS, select **Buy more CI minutes**,
+1. Select **Buy additional units of compute**. GitLab redirects you to the Customers Portal.
+1. Locate the subscription card that's linked to your personal namespace on GitLab SaaS, select **Buy more units of compute**,
and complete the details of the transaction.
-After your payment is processed, the additional CI/CD minutes are added to your personal
+After your payment is processed, the additional units of compute are added to your personal
namespace.
## How compute usage is calculated
diff --git a/doc/ci/testing/test_coverage_visualization.md b/doc/ci/testing/test_coverage_visualization.md
index 2fcd1ecfad2..8ebe636e1ad 100644
--- a/doc/ci/testing/test_coverage_visualization.md
+++ b/doc/ci/testing/test_coverage_visualization.md
@@ -297,7 +297,7 @@ run tests:
The following [`.gitlab-ci.yml`](../yaml/index.md) example for PHP uses [PHPUnit](https://phpunit.readthedocs.io/)
to collect test coverage data and generate the report.
-With a minimal [`phpunit.xml`](https://phpunit.readthedocs.io/en/9.5/configuration.html) file (you may reference
+With a minimal [`phpunit.xml`](https://docs.phpunit.de/en/10.2/configuration.html) file (you may reference
[this example repository](https://gitlab.com/yookoala/code-coverage-visualization-with-php/)), you can run the test and
generate the `coverage.xml`:
diff --git a/doc/ci/testing/unit_test_report_examples.md b/doc/ci/testing/unit_test_report_examples.md
index c63e225a2a7..1b1600b3d99 100644
--- a/doc/ci/testing/unit_test_report_examples.md
+++ b/doc/ci/testing/unit_test_report_examples.md
@@ -260,7 +260,7 @@ test:
This example uses [PHPUnit](https://phpunit.de/) with the `--log-junit` flag.
You can also add this option using
-[XML](https://phpunit.readthedocs.io/en/9.5/configuration.html#the-junit-element)
+[XML](https://docs.phpunit.de/en/10.2/configuration.html#the-junit-element)
in the `phpunit.xml` configuration file.
```yaml
diff --git a/doc/development/contributing/design.md b/doc/development/contributing/design.md
index d68bc194266..90f11234b46 100644
--- a/doc/development/contributing/design.md
+++ b/doc/development/contributing/design.md
@@ -110,8 +110,8 @@ Check accessibility using your browser's _accessibility inspector_ ([Chrome](htt
When the design is ready, _before_ starting its implementation:
-- Share design specifications in the related issue, preferably through a [Figma link](https://help.figma.com/hc/en-us/articles/360040531773-Share-Files-with-anyone-using-Link-Sharing#copy-link)
- link or [GitLab Designs feature](../../user/project/issues/design_management.md).
+- Share design specifications in the related issue, preferably through a [Figma link](https://help.figma.com/hc/en-us/articles/360040531773-Share-Files-with-anyone-using-Link-Sharing#Copy_link)
+ or [GitLab Designs feature](../../user/project/issues/design_management.md).
See [when you should use each tool](https://about.gitlab.com/handbook/product/ux/product-designer/#deliver).
- Document user flow and states (for example, using [Mermaid flowcharts in Markdown](../../user/markdown.md#mermaid)).
- Document animations and transitions.
diff --git a/doc/development/integrations/index.md b/doc/development/integrations/index.md
index 996ba75d463..b51a2799088 100644
--- a/doc/development/integrations/index.md
+++ b/doc/development/integrations/index.md
@@ -10,7 +10,7 @@ description: "GitLab's development guidelines for Integrations"
This page provides development guidelines for implementing [GitLab integrations](../../user/project/integrations/index.md),
which are part of our [main Rails project](https://gitlab.com/gitlab-org/gitlab).
-Also see our [direction page](https://about.gitlab.com/direction/manage/integrations/) for an overview of our strategy around integrations.
+Also see our [direction page](https://about.gitlab.com/direction/manage/import_and_integrate/integrations/) for an overview of our strategy around integrations.
This guide is a work in progress. You're welcome to ping `@gitlab-org/manage/integrations`
if you need clarification or spot any outdated information.
diff --git a/doc/development/pipelines/index.md b/doc/development/pipelines/index.md
index 383bc338dc5..7c9340b5571 100644
--- a/doc/development/pipelines/index.md
+++ b/doc/development/pipelines/index.md
@@ -186,8 +186,8 @@ This number can be overridden by setting a CI/CD variable named `RSPEC_FAIL_FAST
## Re-run previously failed tests in merge request pipelines
-In order to reduce the feedback time after resolving failed tests for a merge request, the `rspec rspec-pg13-rerun-previous-failed-tests`
-and `rspec rspec-ee-pg13-rerun-previous-failed-tests` jobs run the failed tests from the previous MR pipeline.
+In order to reduce the feedback time after resolving failed tests for a merge request, the `rspec rspec-pg14-rerun-previous-failed-tests`
+and `rspec rspec-ee-pg14-rerun-previous-failed-tests` jobs run the failed tests from the previous MR pipeline.
This was introduced on August 25th 2021, with <https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69053>.
@@ -195,7 +195,7 @@ This was introduced on August 25th 2021, with <https://gitlab.com/gitlab-org/git
1. The `detect-previous-failed-tests` job (`prepare` stage) detects the test files associated with failed RSpec
jobs from the previous MR pipeline.
-1. The `rspec rspec-pg13-rerun-previous-failed-tests` and `rspec rspec-ee-pg13-rerun-previous-failed-tests` jobs
+1. The `rspec rspec-pg14-rerun-previous-failed-tests` and `rspec rspec-ee-pg14-rerun-previous-failed-tests` jobs
will run the test files gathered by the `detect-previous-failed-tests` job.
```mermaid
@@ -205,8 +205,8 @@ graph LR
end
subgraph "test stage";
- B["rspec rspec-pg13-rerun-previous-failed-tests"];
- C["rspec rspec-ee-pg13-rerun-previous-failed-tests"];
+ B["rspec rspec-pg14-rerun-previous-failed-tests"];
+ C["rspec rspec-ee-pg14-rerun-previous-failed-tests"];
end
A --"artifact: list of test files"--> B & C
@@ -625,22 +625,22 @@ This should let us:
### PostgreSQL versions testing
-Our test suite runs against PG13 as GitLab.com runs on PG13 and
-[Omnibus defaults to PG13 for new installs and upgrades](../../administration/package_information/postgresql_versions.md).
+Our test suite runs against PostgreSQL 14 as GitLab.com runs on PostgreSQL 14 and
+[Omnibus defaults to PG14 for new installs and upgrades](../../administration/package_information/postgresql_versions.md).
-We do run our test suite against PG13 on nightly scheduled pipelines.
+We do run our test suite against PostgreSQL 14 on nightly scheduled pipelines.
-We also run our test suite against PG13 upon specific database library changes in MRs and `main` pipelines (with the `rspec db-library-code pg13` job).
+We also run our test suite against PostgreSQL 12 and PostgreSQL 13 upon specific database library changes in merge requests and `main` pipelines (with the `rspec db-library-code pg12` and `rspec db-library-code pg13` jobs).
#### Current versions testing
| Where? | PostgreSQL version | Ruby version |
|------------------------------------------------------------------------------------------------|-------------------------------------------------|-----------------------|
-| Merge requests | 13 (default version), 12 for DB library changes | 3.0 (default version) |
-| `master` branch commits | 13 (default version), 12 for DB library changes | 3.0 (default version) |
-| `maintenance` scheduled pipelines for the `master` branch (every even-numbered hour) | 13 (default version), 12 for DB library changes | 3.0 (default version) |
-| `maintenance` scheduled pipelines for the `ruby2` branch (every odd-numbered hour), see below. | 13 (default version), 12 for DB library changes | 2.7 |
-| `nightly` scheduled pipelines for the `master` branch | 13 (default version), 12, 14 | 3.0 (default version) |
+| Merge requests | 14 (default version), 13 for DB library changes | 3.0 (default version) |
+| `master` branch commits | 14 (default version), 13 for DB library changes | 3.0 (default version) |
+| `maintenance` scheduled pipelines for the `master` branch (every even-numbered hour) | 14 (default version), 13 for DB library changes | 3.0 (default version) |
+| `maintenance` scheduled pipelines for the `ruby2` branch (every odd-numbered hour), see below. | 14 (default version), 13 for DB library changes | 2.7 |
+| `nightly` scheduled pipelines for the `master` branch | 14 (default version), 12, 13, 15 | 3.0 (default version) |
There are 2 pipeline schedules used for testing Ruby 2.7. One is triggering a
pipeline in `ruby2-sync` branch, which updates the `ruby2` branch with latest
diff --git a/doc/development/pipelines/internals.md b/doc/development/pipelines/internals.md
index 4cdaf50641e..35881db8c6d 100644
--- a/doc/development/pipelines/internals.md
+++ b/doc/development/pipelines/internals.md
@@ -140,6 +140,8 @@ that are scoped to a single [configuration keyword](../../ci/yaml/index.md#job-k
| `.use-pg13-ee` | Same as `.use-pg13` but also use an `elasticsearch` service (see [`.gitlab/ci/global.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/global.gitlab-ci.yml) for the specific version of the service). |
| `.use-pg14` | Allows a job to use the `postgres` 14, `redis`, and `rediscluster` services (see [`.gitlab/ci/global.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/global.gitlab-ci.yml) for the specific versions of the services). |
| `.use-pg14-ee` | Same as `.use-pg14` but also use an `elasticsearch` service (see [`.gitlab/ci/global.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/global.gitlab-ci.yml) for the specific version of the service). |
+| `.use-pg15` | Allows a job to use the `postgres` 15, `redis`, and `rediscluster` services (see [`.gitlab/ci/global.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/global.gitlab-ci.yml) for the specific versions of the services). |
+| `.use-pg15-ee` | Same as `.use-pg15` but also use an `elasticsearch` service (see [`.gitlab/ci/global.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/global.gitlab-ci.yml) for the specific version of the service). |
| `.use-kaniko` | Allows a job to use the `kaniko` tool to build Docker images. |
| `.as-if-foss` | Simulate the FOSS project by setting the `FOSS_ONLY='1'` CI/CD variable. |
| `.use-docker-in-docker` | Allows a job to use Docker in Docker. |
diff --git a/doc/development/snowplow/implementation.md b/doc/development/snowplow/implementation.md
index 37948f2a3e0..09f617c4ecb 100644
--- a/doc/development/snowplow/implementation.md
+++ b/doc/development/snowplow/implementation.md
@@ -14,7 +14,7 @@ This page describes how to:
## Event definitions
Every Snowplow event, regardless of frontend or backend, requires a corresponding event definition. These definitions document the event and its properties to make it easier to maintain and analyze.
-These defintions can be browsed in the [event dictionary](https://metrics.gitlab.com/snowplow). The [event dictionary guide](event_dictionary_guide.md) provides instructions for setting up an event definition.
+These definitions can be browsed in the [event dictionary](https://metrics.gitlab.com/snowplow/). The [event dictionary guide](event_dictionary_guide.md) provides instructions for setting up an event definition.
## Snowplow JavaScript frontend tracking
diff --git a/doc/development/spam_protection_and_captcha/graphql_api.md b/doc/development/spam_protection_and_captcha/graphql_api.md
index 383b52df1fc..5723433203b 100644
--- a/doc/development/spam_protection_and_captcha/graphql_api.md
+++ b/doc/development/spam_protection_and_captcha/graphql_api.md
@@ -16,9 +16,8 @@ related to changing a model's confidential/public flag.
The main steps are:
1. Use `include Mutations::SpamProtection` in your mutation.
-1. Create a `spam_params` instance based on the request. Obtain the request from the context
- via `context[:request]` when creating the `SpamParams` instance.
-1. Pass `spam_params` to the relevant Service class constructor.
+1. Pass `perform_spam_check: true` to the Update Service class constructor.
+ It is set to `true` by default in the Create Service.
1. After you create or update the `Spammable` model instance, call `#check_spam_action_response!`
and pass it the model instance. This call:
1. Performs the necessary spam checks on the model.
@@ -44,13 +43,10 @@ module Mutations
include Mutations::SpamProtection
def resolve(args)
- spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
-
service_response = ::Widgets::CreateService.new(
project: project,
current_user: current_user,
- params: args,
- spam_params: spam_params
+ params: args
).execute
widget = service_response.payload[:widget]
diff --git a/doc/development/spam_protection_and_captcha/rest_api.md b/doc/development/spam_protection_and_captcha/rest_api.md
index d7012ffb418..76ffbc2f157 100644
--- a/doc/development/spam_protection_and_captcha/rest_api.md
+++ b/doc/development/spam_protection_and_captcha/rest_api.md
@@ -16,8 +16,8 @@ related to changing a model's confidential/public flag.
The main steps are:
1. Add `helpers SpammableActions::CaptchaCheck::RestApiActionsSupport` in your `resource`.
-1. Create a `spam_params` instance based on the request.
-1. Pass `spam_params` to the relevant Service class constructor.
+1. Pass `perform_spam_check: true` to the Update Service class constructor.
+ It is set to `true` by default in the Create Service.
1. After you create or update the `Spammable` model instance, call `#check_spam_action_response!`,
save the created or updated instance in a variable.
1. Identify the error handling logic for the `failure` case of the request,
@@ -53,8 +53,7 @@ module API
post do
#...
- spam_params = ::Spam::SpamParams.new_from_request(request: request)
- service_response = ::Snippets::CreateService.new(project: nil, current_user: current_user, params: attrs, spam_params: spam_params).execute
+ service_response = ::Snippets::CreateService.new(project: nil, current_user: current_user, params: attrs).execute
snippet = service_response.payload[:snippet]
if service_response.success?
@@ -71,8 +70,7 @@ module API
put ':id' do
#...
- spam_params = ::Spam::SpamParams.new_from_request(request: request)
- service_response = ::Snippets::UpdateService.new(project: nil, current_user: current_user, params: attrs, spam_params: spam_params).execute(snippet)
+ service_response = ::Snippets::UpdateService.new(project: nil, current_user: current_user, params: attrs, perform_spam_check: true).execute(snippet)
snippet = service_response.payload[:snippet]
diff --git a/doc/development/spam_protection_and_captcha/web_ui.md b/doc/development/spam_protection_and_captcha/web_ui.md
index 0ae5e98f399..0cc17854010 100644
--- a/doc/development/spam_protection_and_captcha/web_ui.md
+++ b/doc/development/spam_protection_and_captcha/web_ui.md
@@ -75,11 +75,9 @@ sequenceDiagram
The backend is also cleanly abstracted via mixin modules and helper methods. The three main
changes required to the relevant backend controller actions (normally just `create`/`update`) are:
-1. Create a `SpamParams` parameter object instance based on the request, using the static
- `#new_from_request` factory method. This method takes a request, and returns a `SpamParams` instance.
-1. Pass the created `SpamParams` instance as the `spam_params` named argument to the
- Service class constructor, which you should have already added. If the spam check indicates
- the changes to the model are possibly spam, then:
+1. Pass `perform_spam_check: true` to the Update Service class constructor.
+ It is set to `true` by default in the Create Service.
+1. If the spam check indicates the changes to the model are possibly spam, then:
- An error is added to the model.
- The `needs_recaptcha` property on the model is set to true.
1. Wrap the existing controller action return value (rendering or redirecting) in a block passed to
@@ -116,12 +114,10 @@ module WidgetsActions
include SpammableActions::CaptchaCheck::JsonFormatActionsSupport
def create
- spam_params = ::Spam::SpamParams.new_from_request(request: request)
widget = ::Widgets::CreateService.new(
project: project,
current_user: current_user,
- params: params,
- spam_params: spam_params
+ params: params
).execute
respond_to do |format|
@@ -166,13 +162,11 @@ class WidgetsController < ApplicationController
def update
# Existing logic to find the `widget` model instance...
-
- spam_params = ::Spam::SpamParams.new_from_request(request: request)
::Widgets::UpdateService.new(
project: project,
current_user: current_user,
params: params,
- spam_params: spam_params
+ perform_spam_check: true
).execute(widget)
respond_to do |format|
diff --git a/doc/security/hardening.md b/doc/security/hardening.md
new file mode 100644
index 00000000000..21b8594fc6e
--- /dev/null
+++ b/doc/security/hardening.md
@@ -0,0 +1,67 @@
+---
+type: reference, howto
+stage: Manage
+group: Authentication and Authorization
+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
+---
+
+# GitLab Hardening Recommendations **(FREE SELF)**
+
+This documentation is for GitLab instances where the overall system can be "hardened"
+against common and even not-so-common attacks. It is not designed to completely
+eradicate attacks, but to provide strong mitigation thereby reducing overall risk. Some
+of the techniques apply to any GitLab deployment, such as SaaS or self-managed, while other
+techniques apply to the underlying OS.
+
+These techniques are a work in progress, and have not been tested at scale
+(such as a large environments with many users). They have been tested on a self-managed
+single instance running a Linux package installation, and while many of the techniques can
+translated to other deployment types, they may not all work or apply.
+
+Most of the listed recommendations provide specific recommendations or
+reference choices one can make based upon the general documentation.
+Through hardening, there may be impact to certain features your users may specifically
+want or depend on, so you should communicate with users and do a phased rollout of hardening
+changes.
+
+The hardening instructions are in five categories for easier
+understanding. They are listed in the following section.
+
+## GitLab hardening general concepts
+
+This details information on hardening as an approach to security and some of the larger
+philosophies. For more information, see [hardening general concepts](hardening_general_concepts.md).
+
+## GitLab application settings
+
+Application settings made using the GitLab GUI to the application itself. For more information, see
+[application recommendations](hardening_application_recommendations.md).
+
+## GitLab CI/CD settings
+
+CI/CD is a core component of GitLab, and while application of security principles
+are based upon needs, there are several things you can do to make your CI/CD more secure.
+For more information, see [CI/CD Recommendations](hardening_cicd_recommendations.md).
+
+## GitLab configuration settings
+
+Configuration file settings used to control and configure the
+application (such as `gitlab.rb`) are documented separately. For more information, see the
+[configuration recommendations](hardening_configuration_recommendations.md).
+
+## Operating System settings
+
+You can adjust the underlying operating system to increase overall security. For more information, see the
+[operating system recommendations](hardening_operating_system_recommendations.md).
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, for example `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/security/hardening_application_recommendations.md b/doc/security/hardening_application_recommendations.md
new file mode 100644
index 00000000000..6ad72569b88
--- /dev/null
+++ b/doc/security/hardening_application_recommendations.md
@@ -0,0 +1,240 @@
+---
+type: reference, howto
+stage: Manage
+group: Authentication and Authorization
+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
+---
+
+# Hardening - Application Recommendations
+
+For general hardening guidelines, see the [main hardening documentation](hardening.md).
+
+You control the hardening recommendations for GitLab instances through the
+web interface.
+
+## System hooks
+
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **System hooks**.
+
+In a typical hardened environment, internal information is not transmitted or stored
+outside of the system. For an offline environment system, this is
+implied. System hooks provide a way for local events in the environment to communicate
+information outside of the environment based upon triggers.
+
+Use cases for this capability are supported, particularly monitoring the
+system through a remote system.
+However, you must apply extreme caution when deploying system hooks. For hardened
+systems, if they are intended to be an offline environment, a perimeter of trusted
+systems allowed to communicate with each other must be enforced, so any hooks
+(system, web, or file) must only communicate with those trusted systems. TLS is strongly
+encouraged for communications through system hooks.
+
+## Push rules
+
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Push rules**.
+
+Ensure that the following items are selected:
+
+- **Reject unverified users**
+- **Do not allow users to remove Git tags with `git push`**
+- **Check whether the commit author is a GitLab user**
+- **Prevent pushing secret files**
+
+The adjustments help limit pushes to established and authorized users.
+
+## Deploy keys
+
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Deploy keys**.
+
+Public deploy keys at are used to give read or read/write access to
+**all** projects on the instance, and are intended for remote automation to access
+projects. Public deploy keys should not be used in a hardened environment. If you
+must use deploy keys, use project deploy keys instead. For more information, refer to
+the documentation on [deploy keys](../user/project/deploy_keys/index.md) and
+[project deploy keys](../user/project/deploy_keys/index.md#create-a-project-deploy-key).
+
+## General
+
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Settings**.
+1. Select **General**.
+
+Hardening adjustments can be made in 4 sections.
+
+### Visibility and access control
+
+The default for the following settings is **Private**:
+
+- **Default project visibility**
+- **Default snippet visibility**
+- **Default group visibility**
+
+Only users that are granted specific access to a project, snippet, or group can
+access these resources. This can be adjusted later as needed or at the time of
+their creation. This helps prevent accidental or malicious disclosure of information.
+
+Depending on your security policy and posture, you might wish to set your
+**Restricted visibility level** to **Public**, as this prevents user profiles
+from being viewed by non-authenticated users.
+
+In **Import sources**, select only the sources you really need.
+
+A typical deployment has **Enabled Git access protocols** set to **Both SSH and HTTP(S)**,
+however if one of the Git protocols is not in use by your users, set it to either
+**Only SSH** or **Only HTTP(S)** accordingly. This helps shrink the attack surface.
+
+For SSH key types, the following are preferred: `ED25519` (and `ED25519-SK`), `RSA`, and
+`ECDSA` (and `ECDSA-SK`) in that order. `ED25519` is considered as secure as `RSA` when
+`RSA` is set to 2048 bits or higher, however the `ED25519` keys are smaller and the
+algorithm is much faster.
+
+`ED25519-SK` and `ECDSA-SK` both end with `-SK` which stands for
+"Security Key". The `-SK` types are compatible with FIDO/U2F standards and pertain to
+usage with hardware tokens, for example YubiKeys.
+
+`DSA` should be set to "Are forbidden". `DSA` has known flaws, and many cryptographers
+are suspicious of and do not support using `ECDSA`.
+
+If GitLab is in FIPS mode, use the following:
+
+- If running in FIPS mode:
+ - Use `RSA`, set to **Must be at least 2048 bits**.
+ - Use `ECDSA` (and `ECDSA-SK`), set to **Must be at least 256 bits**.
+ - Set all other key types to **Are forbidden**.
+ `RSA` and `ECDSA` are both approved for FIPS use.
+- If not running in FIPS mode, you must use `ED25519` and can also use `RSA`:
+ - Set `ED25519` (and `ED25519-SK`) to **Must be at least 256 bits**.
+ - If using `RSA`, set it to **Must be at least 2048 bits**.
+ - Set all other key types to **Are forbidden**.
+- If you are setting up an instance for a new group of users, define your user SSH
+key policy with the maximum bits settings for added security.
+
+In a hardened environment RSS feeds are typically not required, and in **Feed token**,
+select the **Disabled feed token** checkbox.
+
+If all of your users are coming from specific IP addresses, use **Global-allowed IP ranges**
+to specifically allow only those addresses.
+
+For more details on **Visibility and access control**, see [visibility and access controls](../user/admin_area/settings/visibility_and_access_controls.md).
+For information on SSH settings, see
+[SSH keys restrictions](../security/ssh_keys_restrictions.md).
+
+### Account and limit
+
+For hardening purposes, ensure the checkbox next to **Gravatar enabled** is not selected.
+All extraneous communications should be curtailed, and in some environments might be
+restricted. Account avatars can be manually uploaded by users.
+
+The settings in this section are intended to help enforce a custom implementation
+of your own specific standards on your users. As the various scenarios are too many
+and too varied, you should review the
+[account and limit settings documentation](../user/admin_area/settings/account_and_limit_settings.md)
+and apply changes to enforce your own policies.
+
+### Sign-up restrictions
+
+Ensure open sign-up is disabled on your hardened instance. Ensure the **Sign-up enabled** checkbox is not selected.
+
+In **Email confirmation settings**, ensure that **Hard** is selected. User verification
+of their email address is now enforced before access is granted.
+
+The **Minimum password length (number of characters)** default setting is 12 which
+should be fine as long as additional authentication techniques are used. The password
+should be complex, so ensure that all four of these checkboxes are selected:
+
+- **Require numbers**
+- **Require uppercase letters**
+- **Require lowercase letters**
+- **Require symbols**
+
+If all of your users belong to the same organization that uses a specific domain for
+email addresses, then list that domain in **Allowed domains for sign-ups**. This
+prevents those with email addresses in other domains from signing up.
+
+For more detailed information, see
+[sign-up restrictions](../user/admin_area/settings/sign_up_restrictions.md).
+
+### Sign-in restrictions
+
+Two-factor authentication (2FA) should be enabled for all users. Ensure that the
+checkbox next to **Two-factor authentication** (2FA) is selected.
+
+The default setting for **Two-factor grace period** is 48 hours. This should be adjusted
+to a much lower value, such as 8 hours.
+
+Ensure the checkbox next to **Enable admin mode** is selected so that **Admin Mode** is
+active. This requires users with Admin access to have to use additional
+authentication in order to perform administrative tasks, enforcing additional 2FA by the user.
+
+In **Email notification for unknown sign-ins**, ensure that **Enable email notification**
+is selected. This sends an email to users when a sign-in occurs from an unrecognized location.
+
+For more detailed information, see
+[sign-in restrictions](../user/admin_area/settings/sign_in_restrictions.md).
+
+## Integrations
+
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Settings**.
+1. Select **Integrations**.
+
+In general, as long as administrators control and monitor usage, integrations
+are fine in a hardened environment. Be cautious about integrations that allow
+for actions from an outside system that trigger actions and processes that typically
+require a level of access you would restrict or audit if performed by a local
+process or authenticated user.
+
+## Metrics and profiling
+
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Settings**.
+1. Under **Integrations**.
+
+The main focus for hardening is **Usage statistics**:
+
+- You should make sure **Enable version check** is selected. This checks to see if you
+are running the latest version of GitLab, and as new versions with new features and
+security patches come out frequently, this helps you stay up to date.
+
+- If your environment is isolated or one where your organizational requirements
+restrict data gathering and statistics reporting to a software vendor, you may have
+to disable the **Enable service ping** feature. For more information on what data is collected to
+help you make an informed decision, see
+[service ping](../development/service_ping/index.md).
+
+## Network
+
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Settings**.
+1. Under **Network**.
+
+For any setting that enables rate limiting, make sure it is selected. Default values
+should be fine. Additionally there are numerous settings that enable access, and all
+of these should be cleared.
+
+After you've made these adjustments you can fine tune the system to meet performance
+and user needs, which may require disabling and adjusting rate limits or enabling
+accesses. Here are a few notables to keep in mind:
+
+- In **Outbound requests**, if you need to open up access to a limited
+number of systems, you can limit access to just those systems by specifying
+IP address or hostname. Also in this section, make sure you've selected
+**Enforce DNS rebinding attack protection** if you're allowing any access at all.
+
+- Under **Notes rate limit** and **Users API rate limit** you can exclude specific users
+from those limits if needed.
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, for example `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/security/hardening_cicd_recommendations.md b/doc/security/hardening_cicd_recommendations.md
new file mode 100644
index 00000000000..16b649cbdd7
--- /dev/null
+++ b/doc/security/hardening_cicd_recommendations.md
@@ -0,0 +1,69 @@
+---
+type: reference, howto
+stage: Manage
+group: Authentication and Authorization
+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
+---
+
+# Hardening - CI/CD Recommendations
+
+General hardening guidelines and philosophies are outlined in the [main hardening documentation](hardening.md).
+
+The hardening recommendations and concepts for CI/CD are listed below.
+
+## Basic Recommendations
+
+How you configure the different CI/CD settings depends on your use of CI/CD. For example if you are using it to build
+packages, you often need real-time access to external resources such as Docker
+images or external code repositories. If you are using it for Infrastructure
+as Code (IaC), you often need to store credentials for external systems to
+automate deployment. For these and many other scenarios, you need to store
+potentially sensitive information to be used during CI/CD operations. As the
+individual scenarios themselves are numerous, we have summarized some basic
+information to help harden the CI/CD process.
+
+- **Secrets Management**. Passwords, tokens, keys, and other secrets that require any
+level of protection should never be stored in plaintext. Some type of encrypted
+container technology should be used, such as GCP Secret Manager, AWS KMS, or
+HashiCorp Vault. For self-managed and standalone instances, HashiCorp Vault is
+recommended, and many GitLab features can take advantage of Vault and are well
+documented in the main [Documentation](../index.md). For detailed CI/CD examples, see [using external secrets in CI](../ci/secrets/index.md).
+- **External Communications**. If your CI/CD process requires connectivity to other
+hosts, ensure that these communication channels are encrypted. You should use TLS 1.2 or 1.3, and where possible implement mutual TLS.
+- **Logging**. Logging can be very important for auditing and troubleshooting, so it
+is important that you enable any logging features to ensure you are getting
+the information in logs you need. Make sure through periodic testing that
+plaintext secrets or other sensitive information is not inadvertently added to log
+files.
+
+## Specific Recommendations
+
+### Pipelines
+
+Pipelines are a part of jobs that execute steps in stages to automate tasks on behalf
+of the users of a project. They are a core component of CD/CD.
+
+By default, only the default branch gets a protected pipeline. An owner of a project
+can ensure that other branches are protected by
+[configuring a protected branch](../user/project/protected_branches.md).
+This allows for more restricted security on pipelines. For more information, see
+[pipeline security on a protected branch](../ci/pipelines/index.md#pipeline-security-on-protected-branches).
+
+Deployment is the part of the CI/CD that deploys the results of the pipeline in
+relationship to a given environment. Default settings do not impose many
+restrictions, and as different users with different roles and responsibilities can
+trigger pipelines that can interact with those environments, you should
+restrict these environments. For more information, see
+[protected environments](../ci/environments/protected_environments.md).
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, for example `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/security/hardening_configuration_recommendations.md b/doc/security/hardening_configuration_recommendations.md
new file mode 100644
index 00000000000..06a13015633
--- /dev/null
+++ b/doc/security/hardening_configuration_recommendations.md
@@ -0,0 +1,161 @@
+---
+type: reference, howto
+stage: Manage
+group: Authentication and Authorization
+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
+---
+
+# Hardening - Configuration Recommendations
+
+General hardening guidelines are outlined in the [main hardening documentation](hardening.md).
+
+Some hardening recommendations for GitLab instances involve additional
+services or control through configuration files. As a reminder, any time you are
+making changes to configuration files, make backup copies of
+them before editing. Additionally, if you are making a lot of changes it is
+recommended you do not do all of the changes at once, and test them after each
+change to ensure everything is working.
+
+## NGINX
+
+NGINX is used to serve up the web interface used to access the GitLab instance. As
+NGINX is controlled and integrated into GitLab, modification of the
+`/etc/gitlab/gitlab.rb` file used for adjustments. Here are a few recommendations for helping to improve
+the security of NGINX itself:
+
+```shell
+#
+# Only strong ciphers are used
+#
+nginx['ssl_ciphers'] = "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256"
+#
+# Follow preferred ciphers and the order listed as preference
+#
+nginx['ssl_prefer_server_ciphers'] = "on"
+#
+# Only allow TLSv1.2 and TLSv1.3
+#
+nginx['ssl_protocols'] = "TLSv1.2 TLSv1.3"
+
+##! **Recommended in: https://nginx.org/en/docs/http/ngx_http_ssl_module.html**
+nginx['ssl_session_cache'] = "builtin:1000 shared:SSL:10m"
+
+##! **Default according to https://nginx.org/en/docs/http/ngx_http_ssl_module.html**
+nginx['ssl_session_timeout'] = "5m"
+
+# Should prevent logjam attack etc
+# For the example below, run the following first:
+# openssl dhparam -out /etc/gitlab/ssl/dhparam.pem 4096
+nginx['ssl_dhparam'] = "/etc/gitlab/ssl/dhparams.pem" # changed from nil
+
+# Turn off session ticket reuse
+nginx['ssl_session_tickets'] = "off"
+# Pick our own curve instead of what openssl hands us
+nginx['ssl_ecdh_curve'] = "secp384r1"
+```
+
+## Consul
+
+Consul can be integrated into a GitLab environment, and is intended for larger
+deployments. In general for self-managed and standalone deployments with less than
+1000 users, Consul may not be needed. If it is needed, first review the
+[documentation on Consul](../administration/consul.md), but
+more importantly ensure that encryption is used during communications. For more
+detailed information on Consul visit the
+[HashiCorp website](https://developer.hashicorp.com/consul/docs) to understand how it
+works, and review the information on
+[encryption security](https://developer.hashicorp.com/consul/docs/security/encryption).
+
+## Environment Variables
+
+You can customize multiple
+[environment variables](https://docs.gitlab.com/omnibus/settings/environment-variables.md)
+on self-managed systems. The main environment variable to
+take advantage of from a security perspective is `GITLAB_ROOT_PASSWORD` during the
+installation process. If you are installing the self-managed system with a
+public-facing IP address exposed to the Internet, make sure the password is set to
+something strong. Historically, setting up any type of public-facing service - whether
+it is GitLab or some other application - has shown that opportunistic attacks occur
+as soon as those systems are discovered, so the hardening process should start during
+the installation process.
+
+As mentioned in the [operating system recommendations](hardening_operating_system_recommendations.md)
+ideally there should be firewall rules already in place before the GitLab
+installation begins, but you should still set a secure password before the
+installation through `GITLAB_ROOT_PASSWORD`.
+
+## Git Protocols
+
+To ensure that only authorized users are using SSH for Git access, add the following
+to your `/etc/ssh/sshd_config` file:
+
+```shell
+# Ensure only authorized users are using Git
+AcceptEnv GIT_PROTOCOL
+```
+
+This ensures that users cannot pull down projects using SSH unless they have a valid
+GitLab account that can perform `git` operations over SSH. More details can be found
+under [Configuring Git Protocol](../administration/git_protocol.md).
+
+## Incoming Email
+
+You can configure a GitLab self-managed instance to allow for incoming email to be
+used for commenting or creating issues and merge requests by registered users on
+the GitLab instance. In a hardened environment you should not configure
+this feature as it involves outside communications sending in information.
+
+If the feature is required, follow the instructions in the
+[incoming email documentation](../administration/incoming_email.md), with
+the following recommendations to ensure maximum security:
+
+- Dedicate an email address specifically for inbound emails to the instance.
+- Use [email sub-addressing](../administration/incoming_email.md).
+- Email accounts used by users to send emails should require and have multi-factor authentication (MFA) enabled on those accounts.
+- For Postfix specifically, follow the [set up Postfix for incoming email documentation](../administration/reply_by_email_postfix_setup.md).
+
+## Redis Replication and Failover
+
+Redis is used on a Linux package installation for replication and failover, and can be
+set up when scaling requires that capability. Bear in mind that this opens TCP ports
+`6379` for Redis and `26379` for Sentinel. Follow the
+[replication and failover documentation](../administration/redis/replication_and_failover.md)
+but note the IP addresses of all of the nodes, and set up firewall rules between
+nodes that only allow the other node to access those particular ports.
+
+## Sidekiq Configuration
+
+In the [instructions for configuring an external Sidekiq](../administration/sidekiq/index.md)
+there are numerous references to configuring IP ranges. You must
+[configure HTTPS](../administration/sidekiq/index.md#enable-https),
+and consider restricting those IP addresses to specific systems that Sidekiq talks to.
+You might have to adjust firewall rules at the operating system level as well.
+
+## S/MIME Signing of Email
+
+If the GitLab instance is configured for sending out email notifications to users,
+configure S/MIME signing to help the recipients ensure that the emails are
+legitimate. Follow the instructions on [signing outgoing email](../administration/smime_signing_email.md).
+
+## Container Registry
+
+If Lets Encrypt is configured, the Container Registry is enabled by default. This
+allows projects to store their own Docker images. Follow the instructions for
+configuring the [Container Registry](../administration/packages/container_registry.md),
+so you can do things like restrict automatic enablement on new projects and
+disabling the Container Registry entirely. You may have to adjust firewall rules to
+allow access - if a completely standalone system, you should restrict access to the
+Container Registry to localhost only. Specific examples of ports used and their
+configuration are also included in the documentation.
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, for example `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/security/hardening_general_concepts.md b/doc/security/hardening_general_concepts.md
new file mode 100644
index 00000000000..a227f0134d0
--- /dev/null
+++ b/doc/security/hardening_general_concepts.md
@@ -0,0 +1,88 @@
+---
+type: reference, howto
+stage: Manage
+group: Authentication and Authorization
+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
+---
+
+# Hardening - General Concepts
+
+General hardening guidelines are outlined in the [main hardening documentation](hardening.md).
+
+The following documentation summarises some of the underlying philosophies for GitLab instance hardening.
+While we reference GitLab, in many cases they can actually apply to all computer systems.
+
+## Layered security
+
+If there are two ways to implement security, both ways should be implemented instead of
+just one. A quick example is account security:
+
+- Use a long, complex, and unique password for the account.
+- Implement a second factor to the authentication process for added security.
+- Use a hardware token as a second factor.
+- Lock out an account (for at least a fixed amount of time) for failed authentication
+attempts.
+- An account that is unused for a specific time frame should be disabled, enforce this
+with either automation or regular audits.
+
+Instead of using only one or two items on the list, use as many as possible. This
+philosophy can apply to other areas besides account security - it should be applied to
+every area possible.
+
+## Eliminate security through obscurity
+
+Security through obscurity means that one does not discuss certain
+elements of a system, service, or process because of a fear that a potential attacker
+might use those details to formulate an attack. Instead, the system should be secured to
+the point that details about its configuration could be public and the system would still
+be as secure as it could be. In essence, if an attacker learned about the details of the
+configuration of a computer system it would not give them an advantage. One of the
+downsides of security through obscurity is that it can lead to a potential false sense of
+security by the administrator of the system who thinks the system is more secure than it
+actually is.
+
+An example of this is running a service on a non-standard TCP port. For example the
+default SSH daemon port on servers is TCP port 22, but it is possible to configure the
+SSH daemon to run on another port such as TCP port 2222. The administrator who configured
+this might think it increases the security of the system, however it is quite common for
+an attacker to port scan a system to discover all open ports, allowing for quick discovery
+of the SSH service, and eliminating any perceived security advantage.
+
+As GitLab is an open-core system and all of the configuration options are well documented
+and public information, the idea of security through obscurity goes against a
+GitLab core value - transparency. These hardening recommendations are intended to be
+public, to help eliminate any security through obscurity.
+
+## Attack Surface Reduction
+
+GitLab is a large system with many components. As a general rule for security, it helps
+if unused systems are disabled. This eliminates the
+available "attack surface" a potential attacker can use to strike. This can also have
+the added advantage of increasing available system resources as well.
+
+As an example, there is a process on a system that fires up and checks queues for input every
+five minutes, querying multiple sub-processes while performing its checks. If you are not
+using that process, there is no reason to have it configured and it should be disabled.
+If an attacker has figured out an attack vector that uses this process, the attacker might exploit it despite your organization not using it. As a general
+rule, you should disable any service not being used.
+
+## External systems
+
+In larger but still hardened deployments, multiple nodes are often used to
+handle the load your GitLab deployment
+requires. In those cases, use a combination of external, operating system, and
+configuration options for firewall rules. Any option that uses restrictions should only
+be opened up enough to allow the subsystem to function. Whenever possible use TLS
+encryption for network traffic.
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, for example `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/security/hardening_operating_system_recommendations.md b/doc/security/hardening_operating_system_recommendations.md
new file mode 100644
index 00000000000..8b4b706815c
--- /dev/null
+++ b/doc/security/hardening_operating_system_recommendations.md
@@ -0,0 +1,167 @@
+---
+type: reference, howto
+stage: Manage
+group: Authentication and Authorization
+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
+---
+
+# Hardening - Operating System Recommendations
+
+General hardening guidelines are outlined in the [main hardening documentation](hardening.md).
+
+You can configure the underlying operating system to increase overall security. In a
+a controlled environment such as a self-managed GitLab instance it requires additional
+steps, and in fact is often required for certain deployments. FedRAMP is an example of
+such a deployment.
+
+## SSH Configuration
+
+### SSH Client Configuration
+
+For client access (either to the GitLab instance or to the underlying operating
+system), here are a couple of recommendations for SSH key generation. The first one
+is a typical SSH key:
+
+```shell
+ssh-keygen -a 64 -t ed25519 -f ~/.ssh/id_ed25519 -C "ED25519 Key"
+```
+
+For a FIPS-compliant SSH key, use the following:
+
+```shell
+ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa -C "RSA FIPS-compliant Key"
+```
+
+### SSH Server Configuration
+
+At the operating system level, if you are allowing SSH access (typically through
+OpenSSH), here is an example of configuration options for the `sshd_config` file
+(the exact location may vary depending on the operating system but it is usually
+`/etc/ssh/sshd_config`):
+
+```shell
+#
+# Example sshd config file. This supports public key authentication and
+# turns off several potential security risk areas
+#
+PubkeyAuthentication yes
+PasswordAuthentication yes
+UsePAM yes
+UseDNS no
+AllowTcpForwarding no
+X11Forwarding no
+PrintMotd no
+PermitTunnel no
+# Allow client to pass locale environment variables
+AcceptEnv LANG LC_*
+# override default of no subsystems
+Subsystem sftp /usr/lib/openssh/sftp-server
+# Protocol adjustments, these would be needed/recommended in a FIPS or
+# FedRAMP deployment, and use only strong and proven algorithm choices
+Protocol 2
+Ciphers aes128-ctr,aes192-ctr,aes256-ctr
+HostKeyAlgorithms ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521
+KexAlgorithms ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521
+Macs hmac-sha2-256,hmac-sha2-512
+
+```
+
+## Firewall Rules
+
+For firewall rules, only TCP ports `80` and `443` need to be open for basic usage. By
+default, `5050` is open for remote access to the container registry, however in a
+hardened environment this would most likely exist on a different host, and in some
+environments not open at all. Hence, the recommendation is for ports `80` and `443`
+only, and port `80` should only be used to redirect to `443`.
+
+For a truly hardened or isolated environment such as FedRAMP, you should adjust the firewall rules to restrict all ports except to those networks
+accessing it. For example, if the IP address is `192.168.1.2` and all of the authorized
+clients are also on `192.168.1.0/24`, restrict access to ports `80` and `443` to just
+`192.168.1.0/24` only (as a safety restriction), even if access is restricted
+elsewhere with another firewall.
+
+Ideally, if you're installing a self-managed instance, you should implement the firewall rules before the installation begins with access restricted to the admins and installers, and only add additional ranges of IP addresses for
+users after the instance is installed and properly hardened.
+
+Usage of `iptables` or `ufw` is acceptable to implement and enforce port `80` and `443`
+access on a per-host basis, otherwise usage of cloud-based firewall rules through GCP
+Google Compute or AWS Security Groups should enforce this. All other ports should
+be blocked, or at least restricted to specific ranges. For more information on ports, see
+[Package Defaults](../administration/package_information/defaults.md).
+
+### Firewall Additions
+
+It is possible that various services may be enabled that require external access
+(for example Sidekiq) and need network access to be opened up. Restrict these types
+of services to specific IP addresses, or a specific Class C. As a layered and added
+precaution, where possible restrict these extra services to specific nodes or
+sub-networks in GitLab.
+
+## Kernel Adjustments
+
+Kernel adjustments can be made by editing `/etc/sysctl.conf`, or one of the files in
+`/etc/sysctl.d/`. Kernel adjustments do not completely eliminate the threat of an
+attack, but add an extra layer of security. The following notes explain
+some of the advantages for these adjustments.
+
+```shell
+## Kernel tweaks for sysctl.conf ##
+##
+## The following help mitigate out of bounds, null pointer dereference, heap and
+## buffer overflow bugs, use-after-free etc from being exploited. It does not 100%
+## fix the issues, but seriously hampers exploitation.
+##
+# Default is 65536, 4096 helps mitigate memory issues used in exploitation
+vm.mmap_min_addr=4096
+# Default is 0, randomize virtual address space in memory, makes vuln exploitation
+# harder
+kernel.randomize_va_space=2
+# Restrict kernel pointer access (e.g. cat /proc/kallsyms) for exploit assistance
+kernel.kptr_restrict=2
+# Restrict verbose kernel errors in dmesg
+kernel.dmesg_restrict=1
+# Restrict eBPF
+kernel.unprivileged_bpf_disabled=1
+net.core.bpf_jit_harden=2
+# Prevent common use-after-free exploits
+vm.unprivileged_userfaultfd=0
+
+## Networking tweaks ##
+##
+## Prevent common attacks at the IP stack layer
+##
+# Prevent SYNFLOOD denial of service attacks
+net.ipv4.tcp_syncookies=1
+# Prevent time wait assassination attacks
+net.ipv4.tcp_rfc1337=1
+# IP spoofing/source routing protection
+net.ipv4.conf.all.rp_filter=1
+net.ipv4.conf.default.rp_filter=1
+net.ipv6.conf.all.accept_ra=0
+net.ipv6.conf.default.accept_ra=0
+net.ipv4.conf.all.accept_source_route=0
+net.ipv4.conf.default.accept_source_route=0
+net.ipv6.conf.all.accept_source_route=0
+net.ipv6.conf.default.accept_source_route=0
+# IP redirection protection
+net.ipv4.conf.all.accept_redirects=0
+net.ipv4.conf.default.accept_redirects=0
+net.ipv4.conf.all.secure_redirects=0
+net.ipv4.conf.default.secure_redirects=0
+net.ipv6.conf.all.accept_redirects=0
+net.ipv6.conf.default.accept_redirects=0
+net.ipv4.conf.all.send_redirects=0
+net.ipv4.conf.default.send_redirects=0
+```
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, for example `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/security/index.md b/doc/security/index.md
index 7a78461d717..a62d7171112 100644
--- a/doc/security/index.md
+++ b/doc/security/index.md
@@ -27,6 +27,6 @@ type: index
- [Project Import decompressed archive size limits](project_import_decompressed_archive_size_limits.md)
- [Responding to security incidents](responding_to_security_incidents.md)
-To harden your GitLab instance and minimize the risk of unwanted user account creation, consider access control features like [Sign up restrictions](../user/admin_area/settings/sign_up_restrictions.md) and [Authentication options](../topics/authentication/index.md) .
+To harden your GitLab instance and minimize the risk of unwanted user account creation, consider access control features like [Sign up restrictions](../user/admin_area/settings/sign_up_restrictions.md) and [Authentication options](../topics/authentication/index.md). For more detailed information, refer to [Hardening](hardening.md).
Self-managed GitLab customers and administrators are responsible for the security of their underlying hosts, and for keeping GitLab itself up to date. It is important to [regularly patch GitLab](../policy/maintenance.md), patch your operating system and its software, and harden your hosts in accordance with vendor guidance.
diff --git a/doc/subscriptions/gitlab_com/index.md b/doc/subscriptions/gitlab_com/index.md
index bc96768bbd1..c3593d0b986 100644
--- a/doc/subscriptions/gitlab_com/index.md
+++ b/doc/subscriptions/gitlab_com/index.md
@@ -16,7 +16,7 @@ You don't need to install anything to use GitLab SaaS, you only need to
The subscription determines which features are available for your private projects. Organizations with public open source projects can actively apply to our [GitLab for Open Source Program](https://about.gitlab.com/solutions/open-source/join/).
-Qualifying open source projects also get 50,000 CI/CD minutes and free access to the **Ultimate** tier
+Qualifying open source projects also get 50,000 units of compute and free access to the **Ultimate** tier
through the [GitLab for Open Source program](https://about.gitlab.com/solutions/open-source/).
## Obtain a GitLab SaaS subscription
@@ -355,18 +355,18 @@ To change the contacts:
[these requirements](https://about.gitlab.com/handbook/support/license-and-renewals/workflows/customersdot/associating_purchases.html).
1. [Create a ticket with the Support team](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293). Include any relevant material in your request.
-## CI/CD minutes
+## Compute
-CI/CD minutes are the execution time for your [pipelines](../../ci/pipelines/index.md)
+Compute is the resource consumed when running [pipelines](../../ci/pipelines/index.md)
on GitLab shared runners.
-Refer to [CI/CD minutes](../../ci/pipelines/cicd_minutes.md)
+Refer to [Compute usage](../../ci/pipelines/cicd_minutes.md)
for more information.
-### Purchase additional CI/CD minutes
+### Purchase additional units of compute
-You can [purchase additional minutes](../../ci/pipelines/cicd_minutes.md#purchase-additional-cicd-minutes)
-for your personal or group namespace. CI/CD minutes are a **one-time purchase**, so they do not renew.
+You can [purchase additional units of compute](../../ci/pipelines/cicd_minutes.md#purchase-additional-units-of-compute)
+for your personal or group namespace. Units of compute are a **one-time purchase**, so they do not renew.
## Add-on subscription for additional Storage and Transfer
diff --git a/doc/user/application_security/policies/scan-result-policies.md b/doc/user/application_security/policies/scan-result-policies.md
index ecbbf4703b0..d8cd984ad40 100644
--- a/doc/user/application_security/policies/scan-result-policies.md
+++ b/doc/user/application_security/policies/scan-result-policies.md
@@ -15,6 +15,9 @@ findings of one or more security scan jobs. Scan result policies are evaluated a
NOTE:
Scan result policies are applicable only to [protected](../../project/protected_branches.md) target branches.
+NOTE:
+When a protected branch is created or deleted, the policy approval rules synchronize, with a delay of 1 minute.
+
The following video gives you an overview of GitLab scan result policies:
<div class="video-fallback">
diff --git a/doc/user/group/saml_sso/group_sync.md b/doc/user/group/saml_sso/group_sync.md
index ee59eeb98db..430f2c4a69f 100644
--- a/doc/user/group/saml_sso/group_sync.md
+++ b/doc/user/group/saml_sso/group_sync.md
@@ -225,7 +225,7 @@ in the user's SAML assertion.
With an Azure AD premium subscription, you can allow up to 500 group IDs to be sent in a SAML token using the
[Azure AD documentation configuration steps](https://support.esri.com/en/technical-article/000022190).
-Otherwise, you can work around this issue by changing the [group claims](https://learn.microsoft.com/en-us/azure/active-directory/hybrid/how-to-connect-fed-group-claims#configure-the-azure-ad-application-registration-for-group-attributes) to use the `Groups assigned to the application` option instead.
+Otherwise, you can work around this issue by changing the [group claims](https://learn.microsoft.com/en-us/azure/active-directory/hybrid/connect/how-to-connect-fed-group-claims#configure-the-azure-ad-application-registration-for-group-attributes) to use the `Groups assigned to the application` option instead.
![Manage Group Claims](img/Azure-manage-group-claims.png).
diff --git a/doc/user/project/settings/project_access_tokens.md b/doc/user/project/settings/project_access_tokens.md
index ff3a581638f..b0554b80b4c 100644
--- a/doc/user/project/settings/project_access_tokens.md
+++ b/doc/user/project/settings/project_access_tokens.md
@@ -11,6 +11,7 @@ type: reference, howto
> - [Became available on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/235765) in GitLab 13.5 for paid groups only.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/235765) in GitLab 13.5.
> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/342327) in GitLab 14.5. Default prefix added.
+> - [Became available in trial subscriptions](https://gitlab.com/gitlab-org/gitlab/-/issues/386041) in GitLab 16.1. Default prefix added.
Project access tokens are similar to passwords, except you can [limit access to resources](#scopes-for-a-project-access-token),
select a limited role, and provide an expiry date.
@@ -32,7 +33,7 @@ The ability to create project access tokens without expiry was [deprecated](http
You can use project access tokens:
-- On GitLab SaaS: If you have the Premium or Ultimate license tier. Only one project access token is available with a [trial license](https://about.gitlab.com/free-trial/).
+- On GitLab SaaS: If you have the Premium or Ultimate license tier, only one project access token is available with a [trial license](https://about.gitlab.com/free-trial/).
- On self-managed instances of GitLab: With any license tier. If you have the Free tier:
- Review your security and compliance policies around
[user self-enrollment](../../admin_area/settings/sign_up_restrictions.md#disable-new-sign-ups).
@@ -139,4 +140,4 @@ See also [Bot users for groups](../../group/settings/group_access_tokens.md#bot-
## Token availability
-More than one project access token is only available in paid subscriptions, and only one is available in trial subscriptions. For more information, see the ["What is included" section of the GitLab Trial FAQ](https://about.gitlab.com/free-trial/#what-is-included-in-my-free-trial-what-is-excluded).
+More than one project access token is only available in paid subscriptions. In Premium and Ultimate trial subscriptions, only one project access token is included. For more information, see the ["What is included" section of the GitLab Trial FAQ](https://about.gitlab.com/free-trial/#what-is-included-in-my-free-trial-what-is-excluded).
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index d033913aa71..a0f7c5c9b21 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -271,11 +271,9 @@ module API
issue_params = convert_parameters_from_legacy_format(issue_params)
begin
- spam_params = ::Spam::SpamParams.new_from_request(request: request)
result = ::Issues::CreateService.new(container: user_project,
current_user: current_user,
- params: issue_params,
- spam_params: spam_params).execute
+ params: issue_params).execute
if result.success?
present result[:issue], with: Entities::Issue, current_user: current_user, project: user_project
@@ -318,11 +316,10 @@ module API
update_params = convert_parameters_from_legacy_format(update_params)
- spam_params = ::Spam::SpamParams.new_from_request(request: request)
issue = ::Issues::UpdateService.new(container: user_project,
current_user: current_user,
params: update_params,
- spam_params: spam_params).execute(issue)
+ perform_spam_check: true).execute(issue)
if issue.valid?
present issue, with: Entities::Issue, current_user: current_user, project: user_project
diff --git a/lib/api/project_snippets.rb b/lib/api/project_snippets.rb
index 7ef722301ca..a503b941593 100644
--- a/lib/api/project_snippets.rb
+++ b/lib/api/project_snippets.rb
@@ -90,9 +90,7 @@ module API
authorize! :create_snippet, user_project
snippet_params = process_create_params(declared_params(include_missing: false))
-
- spam_params = ::Spam::SpamParams.new_from_request(request: request)
- service_response = ::Snippets::CreateService.new(project: user_project, current_user: current_user, params: snippet_params, spam_params: spam_params).execute
+ service_response = ::Snippets::CreateService.new(project: user_project, current_user: current_user, params: snippet_params).execute
snippet = service_response.payload[:snippet]
if service_response.success?
@@ -138,9 +136,7 @@ module API
validate_params_for_multiple_files(snippet)
snippet_params = process_update_params(declared_params(include_missing: false))
-
- spam_params = ::Spam::SpamParams.new_from_request(request: request)
- service_response = ::Snippets::UpdateService.new(project: user_project, current_user: current_user, params: snippet_params, spam_params: spam_params).execute(snippet)
+ service_response = ::Snippets::UpdateService.new(project: user_project, current_user: current_user, params: snippet_params, perform_spam_check: true).execute(snippet)
snippet = service_response.payload[:snippet]
if service_response.success?
diff --git a/lib/api/snippets.rb b/lib/api/snippets.rb
index 104848206a3..77872e7d13c 100644
--- a/lib/api/snippets.rb
+++ b/lib/api/snippets.rb
@@ -113,9 +113,7 @@ module API
authorize! :create_snippet
attrs = process_create_params(declared_params(include_missing: false))
-
- spam_params = ::Spam::SpamParams.new_from_request(request: request)
- service_response = ::Snippets::CreateService.new(project: nil, current_user: current_user, params: attrs, spam_params: spam_params).execute
+ service_response = ::Snippets::CreateService.new(project: nil, current_user: current_user, params: attrs).execute
snippet = service_response.payload[:snippet]
if service_response.success?
@@ -162,9 +160,7 @@ module API
validate_params_for_multiple_files(snippet)
attrs = process_update_params(declared_params(include_missing: false))
-
- spam_params = ::Spam::SpamParams.new_from_request(request: request)
- service_response = ::Snippets::UpdateService.new(project: nil, current_user: current_user, params: attrs, spam_params: spam_params).execute(snippet)
+ service_response = ::Snippets::UpdateService.new(project: nil, current_user: current_user, params: attrs, perform_spam_check: true).execute(snippet)
snippet = service_response.payload[:snippet]
diff --git a/lib/gitlab/database/schema_validation/track_inconsistency.rb b/lib/gitlab/database/schema_validation/track_inconsistency.rb
index 524c114810f..6e167653d32 100644
--- a/lib/gitlab/database/schema_validation/track_inconsistency.rb
+++ b/lib/gitlab/database/schema_validation/track_inconsistency.rb
@@ -16,8 +16,11 @@ module Gitlab
return unless Gitlab.com?
return refresh_issue if inconsistency_record.present?
- result = ::Issues::CreateService.new(container: project, current_user: user, params: params,
- spam_params: nil).execute
+ result = ::Issues::CreateService.new(
+ container: project,
+ current_user: user,
+ params: params,
+ perform_spam_check: false).execute
track_inconsistency(result[:issue]) if result.success?
end
diff --git a/lib/gitlab/email/handler/create_issue_handler.rb b/lib/gitlab/email/handler/create_issue_handler.rb
index c325112b673..869bcc6e2be 100644
--- a/lib/gitlab/email/handler/create_issue_handler.rb
+++ b/lib/gitlab/email/handler/create_issue_handler.rb
@@ -68,7 +68,7 @@ module Gitlab
title: mail.subject,
description: message_including_reply_or_only_quotes
},
- spam_params: nil
+ perform_spam_check: false
).execute
end
diff --git a/lib/gitlab/email/handler/service_desk_handler.rb b/lib/gitlab/email/handler/service_desk_handler.rb
index 076ba42daac..215ba77db13 100644
--- a/lib/gitlab/email/handler/service_desk_handler.rb
+++ b/lib/gitlab/email/handler/service_desk_handler.rb
@@ -103,7 +103,7 @@ module Gitlab
cc: mail.cc
}
},
- spam_params: nil
+ perform_spam_check: false
).execute
raise InvalidIssueError if result.error?
diff --git a/lib/gitlab/slash_commands/issue_new.rb b/lib/gitlab/slash_commands/issue_new.rb
index 508526ac500..dd2bf632e2c 100644
--- a/lib/gitlab/slash_commands/issue_new.rb
+++ b/lib/gitlab/slash_commands/issue_new.rb
@@ -37,7 +37,7 @@ module Gitlab
private
def create_issue(title:, description:)
- ::Issues::CreateService.new(container: project, current_user: current_user, params: { title: title, description: description }, spam_params: nil).execute
+ ::Issues::CreateService.new(container: project, current_user: current_user, params: { title: title, description: description }, perform_spam_check: false).execute
end
def presenter(issue)
diff --git a/lib/gitlab/usage_data_counters/hll_redis_counter.rb b/lib/gitlab/usage_data_counters/hll_redis_counter.rb
index 7ddd496cd85..badcda1def0 100644
--- a/lib/gitlab/usage_data_counters/hll_redis_counter.rb
+++ b/lib/gitlab/usage_data_counters/hll_redis_counter.rb
@@ -120,12 +120,11 @@ module Gitlab
aggregation = events.first[:aggregation]
if Feature.disabled?(:revert_daily_hll_events_to_weekly_aggregation)
- aggregation = :weekly
- events.each { |e| e[:aggregation] = :weekly }
+ aggregation = 'weekly'
+ events = events.map { |e| e.merge(aggregation: 'weekly') }
end
keys = keys_for_aggregation(aggregation, events: events, start_date: start_date, end_date: end_date, context: context)
-
return FALLBACK unless keys.any?
redis_usage_data { Gitlab::Redis::HLL.count(keys: keys) }
diff --git a/lib/quality/seeders/issues.rb b/lib/quality/seeders/issues.rb
index cac034767f6..fb3d78bc8d2 100644
--- a/lib/quality/seeders/issues.rb
+++ b/lib/quality/seeders/issues.rb
@@ -31,7 +31,7 @@ module Quality
}
params[:closed_at] = params[:created_at] + rand(35).days if params[:state] == 'closed'
- create_result = ::Issues::CreateService.new(container: project, current_user: team.sample, params: params, spam_params: nil).execute_without_rate_limiting
+ create_result = ::Issues::CreateService.new(container: project, current_user: team.sample, params: params, perform_spam_check: false).execute_without_rate_limiting
if create_result.success?
created_issues_count += 1
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 9be08531e71..deaf1e2fa2d 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -2498,13 +2498,13 @@ msgstr ""
msgid "AccountValidation|I'll bring my own runners"
msgstr ""
-msgid "AccountValidation|In order to use free CI/CD minutes on shared runners, you'll need to validate your account using one of our verification options. If you prefer not to, you can run pipelines by bringing your own runners and disabling shared runners for your project."
+msgid "AccountValidation|In order to use free units of compute on shared runners, you'll need to validate your account using one of our verification options. If you prefer not to, you can run pipelines by bringing your own runners and disabling shared runners for your project."
msgstr ""
msgid "AccountValidation|Learn more."
msgstr ""
-msgid "AccountValidation|Looks like you’ll need to validate your account to use free CI/CD minutes"
+msgid "AccountValidation|Looks like you'll need to validate your account to use free units of compute"
msgstr ""
msgid "AccountValidation|Validate your account"
@@ -7127,28 +7127,28 @@ msgstr ""
msgid "BillingPlans|%{group_name} is currently using the %{plan_name}"
msgstr ""
-msgid "BillingPlans|10,000 CI/CD minutes per month"
+msgid "BillingPlans|10,000 units of compute per month"
msgstr ""
-msgid "BillingPlans|10000 CI/CD minutes"
+msgid "BillingPlans|10000 units of compute"
msgstr ""
msgid "BillingPlans|10GB transfer per month"
msgstr ""
-msgid "BillingPlans|400 CI/CD minutes"
+msgid "BillingPlans|400 units of compute"
msgstr ""
-msgid "BillingPlans|400 CI/CD minutes per month"
+msgid "BillingPlans|400 units of compute per month"
msgstr ""
msgid "BillingPlans|5 users per namespace"
msgstr ""
-msgid "BillingPlans|50,000 CI/CD minutes per month"
+msgid "BillingPlans|50,000 units of compute per month"
msgstr ""
-msgid "BillingPlans|50000 CI/CD minutes"
+msgid "BillingPlans|50000 units of compute"
msgstr ""
msgid "BillingPlans|5GB storage"
@@ -7409,10 +7409,10 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
+msgid "Billings|To use free units of compute on shared runners, you'll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
-msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd}"
+msgid "Billings|To use free units of compute on shared runners, you'll need to validate your account with a credit card. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd}"
msgstr ""
msgid "Billings|User validation required"
@@ -7424,7 +7424,7 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
+msgid "Billings|You'll now be able to take advantage of free units of compute on shared runners."
msgstr ""
msgid "Billings|Your account has been validated"
@@ -9258,27 +9258,24 @@ msgstr ""
msgid "Checkout|$%{selectedPlanPrice} per 10 GB storage pack per year"
msgstr ""
-msgid "Checkout|$%{selectedPlanPrice} per pack of 1,000 minutes"
+msgid "Checkout|$%{selectedPlanPrice} per pack of 1,000 units of compute"
msgstr ""
-msgid "Checkout|%d CI minute pack"
-msgid_plural "Checkout|%d CI minute packs"
+msgid "Checkout|%d compute pack"
+msgid_plural "Checkout|%d compute packs"
msgstr[0] ""
msgstr[1] ""
msgid "Checkout|%{cardType} ending in %{lastFourDigits}"
msgstr ""
-msgid "Checkout|%{name}'s CI minutes"
-msgstr ""
-
msgid "Checkout|%{name}'s GitLab subscription"
msgstr ""
msgid "Checkout|%{name}'s storage subscription"
msgstr ""
-msgid "Checkout|%{quantity} CI minutes"
+msgid "Checkout|%{name}'s units of compute"
msgstr ""
msgid "Checkout|%{quantity} GB of storage"
@@ -9289,6 +9286,9 @@ msgid_plural "Checkout|%{quantity} storage packs"
msgstr[0] ""
msgstr[1] ""
+msgid "Checkout|%{quantity} units of compute"
+msgstr ""
+
msgid "Checkout|%{selectedPlanText} plan"
msgstr ""
@@ -9313,12 +9313,6 @@ msgstr ""
msgid "Checkout|Billing address"
msgstr ""
-msgid "Checkout|CI minute pack"
-msgstr ""
-
-msgid "Checkout|CI minute packs are only used after you've used your subscription's monthly quota. The additional minutes will roll over month to month and are valid for one year."
-msgstr ""
-
msgid "Checkout|CI minutes"
msgstr ""
@@ -9331,6 +9325,12 @@ msgstr ""
msgid "Checkout|City"
msgstr ""
+msgid "Checkout|Compute pack"
+msgstr ""
+
+msgid "Checkout|Compute packs are only used after you've used your subscription's monthly quota. The additional units of compute will roll over month to month and are valid for one year."
+msgstr ""
+
msgid "Checkout|Confirm purchase"
msgstr ""
@@ -9469,10 +9469,10 @@ msgstr ""
msgid "Checkout|Total"
msgstr ""
-msgid "Checkout|Total minutes: %{quantity}"
+msgid "Checkout|Total storage: %{quantity} GB"
msgstr ""
-msgid "Checkout|Total storage: %{quantity} GB"
+msgid "Checkout|Total units of compute: %{quantity}"
msgstr ""
msgid "Checkout|Users"
@@ -9499,7 +9499,7 @@ msgstr ""
msgid "Checkout|company or team"
msgstr ""
-msgid "Checkout|minutes"
+msgid "Checkout|units of compute"
msgstr ""
msgid "Checkout|x %{quantity} %{units} per pack"
@@ -29934,9 +29934,6 @@ msgstr ""
msgid "Nav|Sign out and sign in with a different account"
msgstr ""
-msgid "Need help?"
-msgstr ""
-
msgid "Needs"
msgstr ""
@@ -37656,7 +37653,7 @@ msgstr ""
msgid "RegistrationVerification|Are you sure you want to skip this step?"
msgstr ""
-msgid "RegistrationVerification|Enable free CI/CD minutes"
+msgid "RegistrationVerification|Enable free units of compute"
msgstr ""
msgid "RegistrationVerification|GitLab will not charge your card, it will only be used for validation."
@@ -37668,7 +37665,7 @@ msgstr ""
msgid "RegistrationVerification|Skip this for now"
msgstr ""
-msgid "RegistrationVerification|To keep GitLab spam and abuse free we ask that you verify your identity with a valid payment method, such as a debit or credit card. Until then, you can't use free CI/CD minutes to build your application."
+msgid "RegistrationVerification|To keep GitLab spam and abuse free we ask that you verify your identity with a valid payment method, such as a debit or credit card. Until then, you can't use free units of compute to build your application."
msgstr ""
msgid "RegistrationVerification|Validate account"
@@ -49864,7 +49861,7 @@ msgstr ""
msgid "VerificationReminder|Your account has been validated"
msgstr ""
-msgid "VerificationReminder|You’ll now be able to take advantage of free CI/CD minutes on shared runners."
+msgid "VerificationReminder|You’ll now be able to take advantage of free units of compute on shared runners."
msgstr ""
msgid "Verified"
@@ -52749,7 +52746,7 @@ msgstr ""
msgid "Your changes have been successfully committed."
msgstr ""
-msgid "Your comment could not be submitted because %{error}"
+msgid "Your comment could not be submitted because %{reason}."
msgstr ""
msgid "Your comment could not be submitted! Please check your network connection and try again."
diff --git a/qa/qa/fixtures/mocks/import/github.yml b/qa/qa/fixtures/mocks/import/github.yml
index 6ee6be471c4..16d038ed091 100644
--- a/qa/qa/fixtures/mocks/import/github.yml
+++ b/qa/qa/fixtures/mocks/import/github.yml
@@ -575,8 +575,6 @@
path: /repos/gitlab-qa-github/import-test/pulls
method: GET
query_params:
- per_page: '100'
- sort: created
state: all
headers:
Host: api.github.com
diff --git a/scripts/failed_tests.rb b/scripts/failed_tests.rb
index 0ba454894b7..f828155f6f0 100755
--- a/scripts/failed_tests.rb
+++ b/scripts/failed_tests.rb
@@ -12,8 +12,8 @@ class FailedTests
previous_tests_report_path: 'test_results/previous/test_reports.json',
output_directory: 'tmp/previous_failed_tests/',
format: :oneline,
- rspec_pg_regex: /rspec .+ pg13( .+)?/,
- rspec_ee_pg_regex: /rspec-ee .+ pg13( .+)?/
+ rspec_pg_regex: /rspec .+ pg14( .+)?/,
+ rspec_ee_pg_regex: /rspec-ee .+ pg14( .+)?/
}.freeze
def initialize(options)
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 5f606b1f4f3..5e9135c00e3 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::IssuesController, feature_category: :team_planning do
+RSpec.describe Projects::IssuesController, :request_store, feature_category: :team_planning do
include ProjectForksHelper
include_context 'includes Spam constants'
@@ -12,6 +12,11 @@ RSpec.describe Projects::IssuesController, feature_category: :team_planning do
let(:issue) { create(:issue, project: project) }
let(:spam_action_response_fields) { { 'stub_spam_action_response_fields' => true } }
+ before do
+ # We need the spam_params object to be present in the request context
+ Gitlab::RequestContext.start_request_context(request: request)
+ end
+
describe "GET #index" do
context 'external issue tracker' do
before do
@@ -937,13 +942,8 @@ RSpec.describe Projects::IssuesController, feature_category: :team_planning do
let(:spammy_title) { 'Whatever' }
let!(:spam_logs) { create_list(:spam_log, 2, user: user, title: spammy_title) }
- before do
- request.headers['X-GitLab-Captcha-Response'] = 'a-valid-captcha-response'
- request.headers['X-GitLab-Spam-Log-Id'] = spam_logs.last.id
- end
-
def update_verified_issue
- update_issue(issue_params: { title: spammy_title })
+ update_issue(issue_params: { title: spammy_title }, additional_params: { spam_log_id: spam_logs.last.id, 'g-recaptcha-response': 'a-valid-captcha-response' })
end
it 'returns 200 status' do
@@ -960,10 +960,9 @@ RSpec.describe Projects::IssuesController, feature_category: :team_planning do
end
it 'does not mark spam log as recaptcha_verified when it does not belong to current_user' do
- spam_log = create(:spam_log)
- request.headers['X-GitLab-Spam-Log-Id'] = spam_log.id
+ create(:spam_log)
- expect { update_issue }
+ expect { update_verified_issue }
.not_to change { SpamLog.last.recaptcha_verified }
end
end
@@ -1314,6 +1313,7 @@ RSpec.describe Projects::IssuesController, feature_category: :team_planning do
context 'user agent details are saved' do
before do
request.env['action_dispatch.remote_ip'] = '127.0.0.1'
+ Gitlab::RequestContext.start_request_context(request: request)
end
it 'creates a user agent detail' do
diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb
index 5e4e47be2c5..2241a7cf590 100644
--- a/spec/controllers/projects/notes_controller_spec.rb
+++ b/spec/controllers/projects/notes_controller_spec.rb
@@ -276,6 +276,7 @@ RSpec.describe Projects::NotesController, type: :controller, feature_category: :
it "returns status 422 for json" do
expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ expect(response.body).to eq('{"errors":"Note can\'t be blank"}')
end
end
end
@@ -469,6 +470,30 @@ RSpec.describe Projects::NotesController, type: :controller, feature_category: :
expect(json_response['command_names']).to include('move', 'title')
end
end
+
+ context 'with commands that return an error' do
+ let(:extra_request_params) { { format: :json } }
+
+ before do
+ errors = ActiveModel::Errors.new(note)
+ errors.add(:commands_only, 'Failed to apply commands.')
+ errors.add(:command_names, ['label'])
+ errors.add(:commands, 'Failed to apply commands.')
+
+ allow(note).to receive(:errors).and_return(errors)
+
+ allow_next_instance_of(Notes::CreateService) do |service|
+ allow(service).to receive(:execute).and_return(note)
+ end
+ end
+
+ it 'returns status 422 with error message' do
+ create!
+
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ expect(response.body).to eq('{"errors":{"commands_only":["Failed to apply commands."]}}')
+ end
+ end
end
end
diff --git a/spec/features/calendar_spec.rb b/spec/features/calendar_spec.rb
index 67baed5dc91..8ad27b65f11 100644
--- a/spec/features/calendar_spec.rb
+++ b/spec/features/calendar_spec.rb
@@ -155,14 +155,12 @@ RSpec.describe 'Contributions Calendar', :js, feature_category: :user_profile do
Issues::CreateService.new(
container: contributed_project,
current_user: user,
- params: issue_params,
- spam_params: nil
+ params: issue_params
).execute
WorkItems::CreateService.new(
container: contributed_project,
current_user: user,
- params: { title: 'new task' },
- spam_params: nil
+ params: { title: 'new task' }
).execute
end
@@ -204,8 +202,7 @@ RSpec.describe 'Contributions Calendar', :js, feature_category: :user_profile do
Issues::CreateService.new(
container: contributed_project,
current_user: user,
- params: issue_params,
- spam_params: nil
+ params: issue_params
).execute
end
end
@@ -301,14 +298,12 @@ RSpec.describe 'Contributions Calendar', :js, feature_category: :user_profile do
Issues::CreateService.new(
container: contributed_project,
current_user: user,
- params: issue_params,
- spam_params: nil
+ params: issue_params
).execute
WorkItems::CreateService.new(
container: contributed_project,
current_user: user,
- params: { title: 'new task' },
- spam_params: nil
+ params: { title: 'new task' }
).execute
end
@@ -339,8 +334,7 @@ RSpec.describe 'Contributions Calendar', :js, feature_category: :user_profile do
Issues::CreateService.new(
container: contributed_project,
current_user: user,
- params: issue_params,
- spam_params: nil
+ params: issue_params
).execute
end
end
diff --git a/spec/features/unsubscribe_links_spec.rb b/spec/features/unsubscribe_links_spec.rb
index 28699bc2c24..77ef3df97f6 100644
--- a/spec/features/unsubscribe_links_spec.rb
+++ b/spec/features/unsubscribe_links_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe 'Unsubscribe links', :sidekiq_inline, feature_category: :shared d
let_it_be(:recipient) { create(:user) }
let(:params) { { title: 'A bug!', description: 'Fix it!', assignee_ids: [recipient.id] } }
- let(:issue) { Issues::CreateService.new(container: project, current_user: author, params: params, spam_params: nil).execute[:issue] }
+ let(:issue) { Issues::CreateService.new(container: project, current_user: author, params: params).execute[:issue] }
let(:mail) { ActionMailer::Base.deliveries.last }
let(:body) { Capybara::Node::Simple.new(mail.default_part_body.to_s) }
diff --git a/spec/features/users/user_browses_projects_on_user_page_spec.rb b/spec/features/users/user_browses_projects_on_user_page_spec.rb
index ccfb66838c5..8bdc09f3f87 100644
--- a/spec/features/users/user_browses_projects_on_user_page_spec.rb
+++ b/spec/features/users/user_browses_projects_on_user_page_spec.rb
@@ -129,7 +129,7 @@ RSpec.describe 'Users > User browses projects on user page', :js, feature_catego
end
before do
- Issues::CreateService.new(container: contributed_project, current_user: user, params: { title: 'Bug in old browser' }, spam_params: nil).execute
+ Issues::CreateService.new(container: contributed_project, current_user: user, params: { title: 'Bug in old browser' }).execute
event = create(:push_event, project: contributed_project, author: user)
create(:push_event_payload, event: event, commit_count: 3)
end
diff --git a/spec/fixtures/scripts/test_report.json b/spec/fixtures/scripts/test_report.json
index 520ab3a8578..820c45c6c5a 100644
--- a/spec/fixtures/scripts/test_report.json
+++ b/spec/fixtures/scripts/test_report.json
@@ -1,7 +1,7 @@
{
"suites": [
{
- "name": "rspec unit pg13",
+ "name": "rspec unit pg14",
"total_time": 975.6635620000018,
"total_count": 3811,
"success_count": 3800,
diff --git a/spec/frontend/jobs/components/job/manual_variables_form_spec.js b/spec/frontend/jobs/components/job/manual_variables_form_spec.js
index a48155d93ac..989fe5c11e9 100644
--- a/spec/frontend/jobs/components/job/manual_variables_form_spec.js
+++ b/spec/frontend/jobs/components/job/manual_variables_form_spec.js
@@ -13,6 +13,8 @@ import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line imp
import ManualVariablesForm from '~/jobs/components/job/manual_variables_form.vue';
import getJobQuery from '~/jobs/components/job/graphql/queries/get_job.query.graphql';
import playJobMutation from '~/jobs/components/job/graphql/mutations/job_play_with_variables.mutation.graphql';
+import retryJobMutation from '~/jobs/components/job/graphql/mutations/job_retry_with_variables.mutation.graphql';
+
import {
mockFullPath,
mockId,
@@ -38,9 +40,32 @@ const defaultProvide = {
describe('Manual Variables Form', () => {
let wrapper;
let mockApollo;
- let getJobQueryResponse;
+ let requestHandlers;
+
+ const getJobQueryResponseHandlerWithVariables = jest.fn().mockResolvedValue(mockJobResponse);
+ const playJobMutationHandler = jest.fn().mockResolvedValue({});
+ const retryJobMutationHandler = jest.fn().mockResolvedValue({});
+
+ const defaultHandlers = {
+ getJobQueryResponseHandlerWithVariables,
+ playJobMutationHandler,
+ retryJobMutationHandler,
+ };
+
+ const createComponent = ({ props = {}, handlers = defaultHandlers } = {}) => {
+ requestHandlers = handlers;
+
+ mockApollo = createMockApollo([
+ [getJobQuery, handlers.getJobQueryResponseHandlerWithVariables],
+ [playJobMutation, handlers.playJobMutationHandler],
+ [retryJobMutation, handlers.retryJobMutationHandler],
+ ]);
+
+ const options = {
+ localVue,
+ apolloProvider: mockApollo,
+ };
- const createComponent = ({ options = {}, props = {} } = {}) => {
wrapper = mountExtended(ManualVariablesForm, {
propsData: {
jobId: mockId,
@@ -52,22 +77,6 @@ describe('Manual Variables Form', () => {
},
...options,
});
- };
-
- const createComponentWithApollo = ({ props = {} } = {}) => {
- const requestHandlers = [[getJobQuery, getJobQueryResponse]];
-
- mockApollo = createMockApollo(requestHandlers);
-
- const options = {
- localVue,
- apolloProvider: mockApollo,
- };
-
- createComponent({
- props,
- options,
- });
return waitForPromises();
};
@@ -96,18 +105,13 @@ describe('Manual Variables Form', () => {
nextTick();
};
- beforeEach(() => {
- getJobQueryResponse = jest.fn();
- });
-
afterEach(() => {
createAlert.mockClear();
});
describe('when page renders', () => {
beforeEach(async () => {
- getJobQueryResponse.mockResolvedValue(mockJobResponse);
- await createComponentWithApollo();
+ await createComponent();
});
it('renders help text with provided link', () => {
@@ -120,8 +124,11 @@ describe('Manual Variables Form', () => {
describe('when query is unsuccessful', () => {
beforeEach(async () => {
- getJobQueryResponse.mockRejectedValue({});
- await createComponentWithApollo();
+ await createComponent({
+ handlers: {
+ getJobQueryResponseHandlerWithVariables: jest.fn().mockRejectedValue({}),
+ },
+ });
});
it('shows an alert with error', () => {
@@ -133,8 +140,13 @@ describe('Manual Variables Form', () => {
describe('when job has not been retried', () => {
beforeEach(async () => {
- getJobQueryResponse.mockResolvedValue(mockJobWithVariablesResponse);
- await createComponentWithApollo();
+ await createComponent({
+ handlers: {
+ getJobQueryResponseHandlerWithVariables: jest
+ .fn()
+ .mockResolvedValue(mockJobWithVariablesResponse),
+ },
+ });
});
it('does not render the cancel button', () => {
@@ -145,8 +157,13 @@ describe('Manual Variables Form', () => {
describe('when job has variables', () => {
beforeEach(async () => {
- getJobQueryResponse.mockResolvedValue(mockJobWithVariablesResponse);
- await createComponentWithApollo();
+ await createComponent({
+ handlers: {
+ getJobQueryResponseHandlerWithVariables: jest
+ .fn()
+ .mockResolvedValue(mockJobWithVariablesResponse),
+ },
+ });
});
it('sets manual job variables', () => {
@@ -161,8 +178,11 @@ describe('Manual Variables Form', () => {
describe('when play mutation fires', () => {
beforeEach(async () => {
- await createComponentWithApollo();
- jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockJobPlayMutationData);
+ await createComponent({
+ handlers: {
+ playJobMutationHandler: jest.fn().mockResolvedValue(mockJobPlayMutationData),
+ },
+ });
});
it('passes variables in correct format', async () => {
@@ -172,18 +192,15 @@ describe('Manual Variables Form', () => {
await findRunBtn().vm.$emit('click');
- expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledTimes(1);
- expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
- mutation: playJobMutation,
- variables: {
- id: convertToGraphQLId(TYPENAME_CI_BUILD, mockId),
- variables: [
- {
- key: 'new key',
- value: 'new value',
- },
- ],
- },
+ expect(requestHandlers.playJobMutationHandler).toHaveBeenCalledTimes(1);
+ expect(requestHandlers.playJobMutationHandler).toHaveBeenCalledWith({
+ id: convertToGraphQLId(TYPENAME_CI_BUILD, mockId),
+ variables: [
+ {
+ key: 'new key',
+ value: 'new value',
+ },
+ ],
});
});
@@ -191,15 +208,18 @@ describe('Manual Variables Form', () => {
findRunBtn().vm.$emit('click');
await waitForPromises();
- expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledTimes(1);
+ expect(requestHandlers.playJobMutationHandler).toHaveBeenCalledTimes(1);
expect(redirectTo).toHaveBeenCalledWith(mockJobPlayMutationData.data.jobPlay.job.webPath); // eslint-disable-line import/no-deprecated
});
});
describe('when play mutation is unsuccessful', () => {
beforeEach(async () => {
- jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValue({});
- await createComponentWithApollo();
+ await createComponent({
+ handlers: {
+ playJobMutationHandler: jest.fn().mockRejectedValue({}),
+ },
+ });
});
it('shows an alert with error', async () => {
@@ -214,8 +234,12 @@ describe('Manual Variables Form', () => {
describe('when job is retryable', () => {
beforeEach(async () => {
- await createComponentWithApollo({ props: { isRetryable: true } });
- jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockJobRetryMutationData);
+ await createComponent({
+ props: { isRetryable: true },
+ handlers: {
+ retryJobMutationHandler: jest.fn().mockResolvedValue(mockJobRetryMutationData),
+ },
+ });
});
it('renders cancel button', () => {
@@ -226,15 +250,19 @@ describe('Manual Variables Form', () => {
findRunBtn().vm.$emit('click');
await waitForPromises();
- expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledTimes(1);
+ expect(requestHandlers.retryJobMutationHandler).toHaveBeenCalledTimes(1);
expect(redirectTo).toHaveBeenCalledWith(mockJobRetryMutationData.data.jobRetry.job.webPath); // eslint-disable-line import/no-deprecated
});
});
describe('when retry mutation is unsuccessful', () => {
beforeEach(async () => {
- jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValue({});
- await createComponentWithApollo({ props: { isRetryable: true } });
+ await createComponent({
+ props: { isRetryable: true },
+ handlers: {
+ retryJobMutationHandler: jest.fn().mockRejectedValue({}),
+ },
+ });
});
it('shows an alert with error', async () => {
@@ -249,8 +277,11 @@ describe('Manual Variables Form', () => {
describe('updating variables in UI', () => {
beforeEach(async () => {
- getJobQueryResponse.mockResolvedValue(mockJobResponse);
- await createComponentWithApollo();
+ await createComponent({
+ handlers: {
+ getJobQueryResponseHandlerWithVariables: jest.fn().mockResolvedValue(mockJobResponse),
+ },
+ });
});
it('creates a new variable when user enters a new key value', async () => {
@@ -305,8 +336,11 @@ describe('Manual Variables Form', () => {
describe('variable delete button placeholder', () => {
beforeEach(async () => {
- getJobQueryResponse.mockResolvedValue(mockJobResponse);
- await createComponentWithApollo();
+ await createComponent({
+ handlers: {
+ getJobQueryResponseHandlerWithVariables: jest.fn().mockResolvedValue(mockJobResponse),
+ },
+ });
});
it('delete variable button placeholder should only exist when a user cannot remove', () => {
diff --git a/spec/frontend/notes/components/comment_form_spec.js b/spec/frontend/notes/components/comment_form_spec.js
index 70f25afc5ba..6c774a1ecd0 100644
--- a/spec/frontend/notes/components/comment_form_spec.js
+++ b/spec/frontend/notes/components/comment_form_spec.js
@@ -19,6 +19,7 @@ import * as constants from '~/notes/constants';
import eventHub from '~/notes/event_hub';
import { COMMENT_FORM } from '~/notes/i18n';
import notesModule from '~/notes/stores/modules';
+import { sprintf } from '~/locale';
import { loggedOutnoteableData, notesDataMock, userDataMock, noteableDataMock } from '../mock_data';
jest.mock('autosize');
@@ -195,6 +196,35 @@ describe('issue_comment_form component', () => {
},
);
+ describe('if response contains validation errors', () => {
+ beforeEach(() => {
+ store = createStore({
+ actions: {
+ saveNote: jest.fn().mockRejectedValue({
+ response: {
+ status: HTTP_STATUS_UNPROCESSABLE_ENTITY,
+ data: { errors: 'error 1 and error 2' },
+ },
+ }),
+ },
+ });
+
+ mountComponent({ mountFunction: mount, initialData: { note: 'invalid note' } });
+
+ clickCommentButton();
+ });
+
+ it('renders an error message', () => {
+ const errorAlerts = findErrorAlerts();
+
+ expect(errorAlerts.length).toBe(1);
+
+ expect(errorAlerts[0].text()).toBe(
+ sprintf(COMMENT_FORM.error, { reason: 'error 1 and error 2' }),
+ );
+ });
+ });
+
it('should remove the correct error from the list when it is dismissed', async () => {
const commandErrors = ['1', '2', '3'];
store = createStore({
diff --git a/spec/frontend/notes/components/noteable_discussion_spec.js b/spec/frontend/notes/components/noteable_discussion_spec.js
index ac0c037fe36..0ed7069a98d 100644
--- a/spec/frontend/notes/components/noteable_discussion_spec.js
+++ b/spec/frontend/notes/components/noteable_discussion_spec.js
@@ -1,14 +1,24 @@
import { mount } from '@vue/test-utils';
-import { nextTick } from 'vue';
+import Vue, { nextTick } from 'vue';
+import Vuex from 'vuex';
+import MockAdapter from 'axios-mock-adapter';
import discussionWithTwoUnresolvedNotes from 'test_fixtures/merge_requests/resolved_diff_discussion.json';
+import waitForPromises from 'helpers/wait_for_promises';
+import axios from '~/lib/utils/axios_utils';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
import { trimText } from 'helpers/text_helper';
+import { HTTP_STATUS_UNPROCESSABLE_ENTITY } from '~/lib/utils/http_status';
import { getDiffFileMock } from 'jest/diffs/mock_data/diff_file';
import DiscussionNotes from '~/notes/components/discussion_notes.vue';
import ReplyPlaceholder from '~/notes/components/discussion_reply_placeholder.vue';
import ResolveWithIssueButton from '~/notes/components/discussion_resolve_with_issue_button.vue';
import NoteForm from '~/notes/components/note_form.vue';
import NoteableDiscussion from '~/notes/components/noteable_discussion.vue';
-import createStore from '~/notes/stores';
+import { COMMENT_FORM } from '~/notes/i18n';
+import notesModule from '~/notes/stores/modules';
+import { sprintf } from '~/locale';
+import { createAlert } from '~/alert';
+
import {
noteableDataMock,
discussionMock,
@@ -17,22 +27,46 @@ import {
userDataMock,
} from '../mock_data';
+Vue.use(Vuex);
+
jest.mock('~/behaviors/markdown/render_gfm');
+jest.mock('~/alert');
describe('noteable_discussion component', () => {
let store;
let wrapper;
+ let axiosMock;
- beforeEach(() => {
- window.mrTabs = {};
- store = createStore();
+ const createStore = ({ saveNoteMock = jest.fn() } = {}) => {
+ const baseModule = notesModule();
+
+ return new Vuex.Store({
+ ...baseModule,
+ actions: {
+ ...baseModule.actions,
+ saveNote: saveNoteMock,
+ },
+ });
+ };
+
+ const createComponent = ({ storeMock = createStore() } = {}) => {
+ store = storeMock;
store.dispatch('setNoteableData', noteableDataMock);
store.dispatch('setNotesData', notesDataMock);
- wrapper = mount(NoteableDiscussion, {
+ wrapper = mountExtended(NoteableDiscussion, {
store,
propsData: { discussion: discussionMock },
});
+ };
+
+ beforeEach(() => {
+ axiosMock = new MockAdapter(axios);
+ createComponent();
+ });
+
+ afterEach(() => {
+ axiosMock.restore();
});
it('should not render thread header for non diff threads', () => {
@@ -161,6 +195,39 @@ describe('noteable_discussion component', () => {
});
});
+ describe('save reply', () => {
+ describe('if response contains validation errors', () => {
+ beforeEach(async () => {
+ const storeMock = createStore({
+ saveNoteMock: jest.fn().mockRejectedValue({
+ response: {
+ status: HTTP_STATUS_UNPROCESSABLE_ENTITY,
+ data: { errors: 'error 1 and error 2' },
+ },
+ }),
+ });
+
+ createComponent({ storeMock });
+
+ wrapper.findComponent(ReplyPlaceholder).vm.$emit('focus');
+ await nextTick();
+
+ wrapper
+ .findComponent(NoteForm)
+ .vm.$emit('handleFormUpdate', 'invalid note', null, () => {});
+
+ await waitForPromises();
+ });
+
+ it('renders an error message', () => {
+ expect(createAlert).toHaveBeenCalledWith({
+ message: sprintf(COMMENT_FORM.error, { reason: 'error 1 and error 2' }),
+ parent: wrapper.vm.$el,
+ });
+ });
+ });
+ });
+
describe('signout widget', () => {
describe('user is logged in', () => {
beforeEach(() => {
diff --git a/spec/frontend/notes/stores/actions_spec.js b/spec/frontend/notes/stores/actions_spec.js
index 4083546a711..50df63d06af 100644
--- a/spec/frontend/notes/stores/actions_spec.js
+++ b/spec/frontend/notes/stores/actions_spec.js
@@ -874,26 +874,6 @@ describe('Actions Notes Store', () => {
});
});
- describe('if response contains errors.base', () => {
- const res = { errors: { base: ['something went wrong'] } };
- const error = { message: 'Unprocessable entity', response: { data: res } };
-
- it('sets an alert using errors.base message', async () => {
- const resp = await actions.saveNote(
- {
- commit() {},
- dispatch: () => Promise.reject(error),
- },
- { ...payload, flashContainer },
- );
- expect(resp.hasAlert).toBe(true);
- expect(createAlert).toHaveBeenCalledWith({
- message: 'Your comment could not be submitted because something went wrong',
- parent: flashContainer,
- });
- });
- });
-
describe('if response contains no errors', () => {
const res = { valid: true };
diff --git a/spec/frontend/notes/stores/mutation_spec.js b/spec/frontend/notes/stores/mutation_spec.js
index 8809a496c52..385aee2c1aa 100644
--- a/spec/frontend/notes/stores/mutation_spec.js
+++ b/spec/frontend/notes/stores/mutation_spec.js
@@ -114,13 +114,33 @@ describe('Notes Store mutations', () => {
});
describe('REMOVE_PLACEHOLDER_NOTES', () => {
- it('should remove all placeholder notes in indivudal notes and discussion', () => {
+ it('should remove all placeholder individual notes', () => {
const placeholderNote = { ...individualNote, isPlaceholderNote: true };
const state = { discussions: [placeholderNote] };
+
mutations.REMOVE_PLACEHOLDER_NOTES(state);
expect(state.discussions).toEqual([]);
});
+
+ it.each`
+ discussionType | discussion
+ ${'initial'} | ${individualNote}
+ ${'continued'} | ${discussionMock}
+ `('should remove all placeholder notes from $discussionType discussions', ({ discussion }) => {
+ const lengthBefore = discussion.notes.length;
+
+ const placeholderNote = { ...individualNote, isPlaceholderNote: true };
+ discussion.notes.push(placeholderNote);
+
+ const state = {
+ discussions: [discussion],
+ };
+
+ mutations.REMOVE_PLACEHOLDER_NOTES(state);
+
+ expect(state.discussions[0].notes.length).toEqual(lengthBefore);
+ });
});
describe('SET_NOTES_DATA', () => {
diff --git a/spec/frontend/notes/utils_spec.js b/spec/frontend/notes/utils_spec.js
new file mode 100644
index 00000000000..0882e0a5759
--- /dev/null
+++ b/spec/frontend/notes/utils_spec.js
@@ -0,0 +1,46 @@
+import { sprintf } from '~/locale';
+import { getErrorMessages } from '~/notes/utils';
+import { HTTP_STATUS_UNPROCESSABLE_ENTITY, HTTP_STATUS_BAD_REQUEST } from '~/lib/utils/http_status';
+import { COMMENT_FORM } from '~/notes/i18n';
+
+describe('getErrorMessages', () => {
+ describe('when http status is not HTTP_STATUS_UNPROCESSABLE_ENTITY', () => {
+ it('returns generic error', () => {
+ const errorMessages = getErrorMessages(
+ { errors: ['unknown error'] },
+ HTTP_STATUS_BAD_REQUEST,
+ );
+
+ expect(errorMessages).toStrictEqual([COMMENT_FORM.GENERIC_UNSUBMITTABLE_NETWORK]);
+ });
+ });
+
+ describe('when http status is HTTP_STATUS_UNPROCESSABLE_ENTITY', () => {
+ it('returns all errors', () => {
+ const errorMessages = getErrorMessages(
+ { errors: 'error 1 and error 2' },
+ HTTP_STATUS_UNPROCESSABLE_ENTITY,
+ );
+
+ expect(errorMessages).toStrictEqual([
+ sprintf(COMMENT_FORM.error, { reason: 'error 1 and error 2' }),
+ ]);
+ });
+
+ describe('when response contains commands_only errors', () => {
+ it('only returns commands_only errors', () => {
+ const errorMessages = getErrorMessages(
+ {
+ errors: {
+ commands_only: ['commands_only error 1', 'commands_only error 2'],
+ base: ['base error 1'],
+ },
+ },
+ HTTP_STATUS_UNPROCESSABLE_ENTITY,
+ );
+
+ expect(errorMessages).toStrictEqual(['commands_only error 1', 'commands_only error 2']);
+ });
+ });
+ });
+});
diff --git a/spec/graphql/mutations/issues/create_spec.rb b/spec/graphql/mutations/issues/create_spec.rb
index e3094e84703..24348097021 100644
--- a/spec/graphql/mutations/issues/create_spec.rb
+++ b/spec/graphql/mutations/issues/create_spec.rb
@@ -50,7 +50,6 @@ RSpec.describe Mutations::Issues::Create do
stub_licensed_features(multiple_issue_assignees: false, issue_weights: false)
project.add_guest(assignee1)
project.add_guest(assignee2)
- stub_spam_services
end
def resolve
diff --git a/spec/graphql/mutations/issues/set_confidential_spec.rb b/spec/graphql/mutations/issues/set_confidential_spec.rb
index 495b8442d95..c3269e5c0c0 100644
--- a/spec/graphql/mutations/issues/set_confidential_spec.rb
+++ b/spec/graphql/mutations/issues/set_confidential_spec.rb
@@ -17,10 +17,6 @@ RSpec.describe Mutations::Issues::SetConfidential do
subject { mutation.resolve(project_path: project.full_path, iid: issue.iid, confidential: confidential) }
- before do
- stub_spam_services
- end
-
it_behaves_like 'permission level for issue mutation is correctly verified'
context 'when the user can update the issue' do
diff --git a/spec/graphql/mutations/issues/update_spec.rb b/spec/graphql/mutations/issues/update_spec.rb
index 324f225f209..ac82037b7e2 100644
--- a/spec/graphql/mutations/issues/update_spec.rb
+++ b/spec/graphql/mutations/issues/update_spec.rb
@@ -35,10 +35,6 @@ RSpec.describe Mutations::Issues::Update do
subject { mutation.resolve(**mutation_params) }
- before do
- stub_spam_services
- end
-
it_behaves_like 'permission level for issue mutation is correctly verified'
context 'when the user can update the issue' do
diff --git a/spec/graphql/mutations/work_items/update_task_spec.rb b/spec/graphql/mutations/work_items/update_task_spec.rb
index cb93e97504a..cb37a72bbdd 100644
--- a/spec/graphql/mutations/work_items/update_task_spec.rb
+++ b/spec/graphql/mutations/work_items/update_task_spec.rb
@@ -20,10 +20,6 @@ RSpec.describe Mutations::WorkItems::UpdateTask do
mutation.resolve(**input)
end
- before do
- stub_spam_services
- end
-
context 'when user has sufficient permissions' do
let(:current_user) { developer }
diff --git a/spec/helpers/appearances_helper_spec.rb b/spec/helpers/appearances_helper_spec.rb
index 2b0192d24b3..4a32c586315 100644
--- a/spec/helpers/appearances_helper_spec.rb
+++ b/spec/helpers/appearances_helper_spec.rb
@@ -205,6 +205,30 @@ RSpec.describe AppearancesHelper do
end
end
+ describe '#custom_sign_in_description' do
+ it 'returns an empty string if no custom description is found' do
+ allow(helper).to receive(:current_appearance).and_return(nil)
+ allow(Gitlab::CurrentSettings).to receive(:current_application_settings).and_return(nil)
+ allow(Gitlab::CurrentSettings).to receive(:help_text).and_return(nil)
+
+ expect(helper.custom_sign_in_description).to eq('')
+ end
+
+ it 'returns a custom description if all the setting options are found' do
+ allow(helper).to receive(:markdown_field).and_return('1', '2')
+ allow(helper).to receive(:markdown).and_return('3')
+
+ expect(helper.custom_sign_in_description).to eq('1<br>2<br>3')
+ end
+
+ it 'returns a custom description if only one setting options is found' do
+ allow(helper).to receive(:markdown_field).and_return('', '2')
+ allow(helper).to receive(:markdown).and_return('')
+
+ expect(helper.custom_sign_in_description).to eq('2')
+ end
+ end
+
describe '#brand_header_logo' do
let(:options) { {} }
diff --git a/spec/lib/gitlab/database/schema_validation/track_inconsistency_spec.rb b/spec/lib/gitlab/database/schema_validation/track_inconsistency_spec.rb
index 315e687b8ae..0b104e40c11 100644
--- a/spec/lib/gitlab/database/schema_validation/track_inconsistency_spec.rb
+++ b/spec/lib/gitlab/database/schema_validation/track_inconsistency_spec.rb
@@ -24,10 +24,6 @@ RSpec.describe Gitlab::Database::SchemaValidation::TrackInconsistency, feature_c
subject(:execute) { described_class.new(inconsistency, project, user).execute }
- before do
- stub_spam_services
- end
-
context 'when is not GitLab.com' do
it 'does not create a schema inconsistency record' do
allow(Gitlab).to receive(:com?).and_return(false)
diff --git a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
index 2bf4c8bfca9..b962757c35b 100644
--- a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
@@ -26,10 +26,12 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
describe '.track_event' do
# ToDo: remove during https://gitlab.com/groups/gitlab-org/-/epics/9542 cleanup
describe 'daily to weekly key migration precautions' do
- let(:event_name) { 'example_event' }
+ let(:event_a_name) { 'example_event_a' }
+ let(:event_b_name) { 'example_event_b' }
let(:known_events) do
[
- { name: event_name, aggregation: 'daily' }
+ { name: event_a_name, aggregation: 'daily' },
+ { name: event_b_name, aggregation: 'weekly' }
].map(&:with_indifferent_access)
end
@@ -44,7 +46,8 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
end
before do
- allow(described_class).to receive(:known_events).and_return(known_events)
+ allow(described_class).to receive(:load_events).with(described_class::KNOWN_EVENTS_PATH).and_return(known_events)
+ allow(described_class).to receive(:load_events).with(/ee/).and_return([])
end
shared_examples 'writes daily events to daily and weekly keys' do
@@ -52,7 +55,7 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
expect(Gitlab::Redis::HLL).to receive(:add).with(expiry: 29.days, key: daily_key, value: 1).and_call_original
expect(Gitlab::Redis::HLL).to receive(:add).with(expiry: 6.weeks, key: weekly_key, value: 1).and_call_original
- described_class.track_event(event_name, values: 1, time: start_date)
+ described_class.track_event(event_a_name, values: 1, time: start_date)
end
end
@@ -67,7 +70,14 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
expect(Gitlab::Redis::HLL).to receive(:count).with(keys: [weekly_key]).and_call_original
expect(Gitlab::Redis::HLL).not_to receive(:count).with(keys: [daily_key]).and_call_original
- described_class.unique_events(event_names: [event_name], start_date: start_date, end_date: end_date)
+ described_class.unique_events(event_names: [event_a_name], start_date: start_date, end_date: end_date)
+ end
+
+ it 'does not persists changes to event aggregation attribute' do
+ described_class.unique_events(event_names: [event_a_name], start_date: start_date, end_date: end_date)
+
+ expect(described_class.known_events.find { |e| e[:name] == event_a_name }[:aggregation])
+ .to eql 'daily'
end
end
@@ -83,7 +93,7 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
expect(Gitlab::Redis::HLL).to receive(:count).with(keys: [daily_key]).and_call_original
expect(Gitlab::Redis::HLL).not_to receive(:count).with(keys: [weekly_key]).and_call_original
- described_class.unique_events(event_names: [event_name], start_date: start_date, end_date: start_date)
+ described_class.unique_events(event_names: [event_a_name], start_date: start_date, end_date: start_date)
end
end
end
diff --git a/spec/models/integrations/microsoft_teams_spec.rb b/spec/models/integrations/microsoft_teams_spec.rb
index 4d5f4065420..a0748ee79cd 100644
--- a/spec/models/integrations/microsoft_teams_spec.rb
+++ b/spec/models/integrations/microsoft_teams_spec.rb
@@ -53,7 +53,7 @@ RSpec.describe Integrations::MicrosoftTeams do
context 'with issue events' do
let(:opts) { { title: 'Awesome issue', description: 'please fix' } }
let(:issues_sample_data) do
- service = Issues::CreateService.new(container: project, current_user: user, params: opts, spam_params: nil)
+ service = Issues::CreateService.new(container: project, current_user: user, params: opts)
issue = service.execute[:issue]
service.hook_data(issue, 'open')
end
diff --git a/spec/models/merge_request/diff_llm_summary_spec.rb b/spec/models/merge_request/diff_llm_summary_spec.rb
index a94adae9fa5..860457add62 100644
--- a/spec/models/merge_request/diff_llm_summary_spec.rb
+++ b/spec/models/merge_request/diff_llm_summary_spec.rb
@@ -10,6 +10,7 @@ RSpec.describe ::MergeRequest::DiffLlmSummary, feature_category: :code_review_wo
describe 'associations' do
it { is_expected.to belong_to(:merge_request_diff) }
it { is_expected.to belong_to(:user).optional }
+ it { is_expected.to validate_uniqueness_of(:merge_request_diff_id) }
it { is_expected.to validate_presence_of(:content) }
it { is_expected.to validate_length_of(:content).is_at_most(2056) }
it { is_expected.to validate_presence_of(:provider) }
diff --git a/spec/requests/api/graphql/mutations/snippets/create_spec.rb b/spec/requests/api/graphql/mutations/snippets/create_spec.rb
index 0b1af2bf628..0f02b1b4f23 100644
--- a/spec/requests/api/graphql/mutations/snippets/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/snippets/create_spec.rb
@@ -103,10 +103,6 @@ RSpec.describe 'Creating a Snippet', feature_category: :source_code_management d
end
it_behaves_like 'snippet edit usage data counters'
-
- it_behaves_like 'a mutation which can mutate a spammable' do
- let(:service) { Snippets::CreateService }
- end
end
context 'with PersonalSnippet' do
@@ -165,7 +161,7 @@ RSpec.describe 'Creating a Snippet', feature_category: :source_code_management d
it do
expect(::Snippets::CreateService).to receive(:new)
- .with(project: nil, current_user: user, params: hash_including(files: expected_value), spam_params: instance_of(::Spam::SpamParams))
+ .with(project: nil, current_user: user, params: hash_including(files: expected_value))
.and_return(double(execute: creation_response))
subject
diff --git a/spec/requests/api/graphql/mutations/snippets/update_spec.rb b/spec/requests/api/graphql/mutations/snippets/update_spec.rb
index 3b98ee3c2e9..7c5ab691b51 100644
--- a/spec/requests/api/graphql/mutations/snippets/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/snippets/update_spec.rb
@@ -110,10 +110,6 @@ RSpec.describe 'Updating a Snippet', feature_category: :source_code_management d
end
end
- it_behaves_like 'a mutation which can mutate a spammable' do
- let(:service) { Snippets::UpdateService }
- end
-
def blob_at(filename)
snippet.repository.blob_at('HEAD', filename)
end
diff --git a/spec/scripts/failed_tests_spec.rb b/spec/scripts/failed_tests_spec.rb
index c9fe6eecd11..c379d3448a6 100644
--- a/spec/scripts/failed_tests_spec.rb
+++ b/spec/scripts/failed_tests_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe FailedTests do
'suites' => [
{
'failed_count' => 1,
- 'name' => 'rspec unit pg13 10/12',
+ 'name' => 'rspec unit pg14 10/12',
'test_cases' => [
{
'status' => 'failed',
@@ -23,7 +23,7 @@ RSpec.describe FailedTests do
},
{
'failed_count' => 1,
- 'name' => 'rspec-ee unit pg13',
+ 'name' => 'rspec-ee unit pg14',
'test_cases' => [
{
'status' => 'failed',
@@ -33,7 +33,7 @@ RSpec.describe FailedTests do
},
{
'failed_count' => 1,
- 'name' => 'rspec unit pg14 10/12',
+ 'name' => 'rspec unit pg15 10/12',
'test_cases' => [
{
'status' => 'failed',
diff --git a/spec/services/ci/pipelines/add_job_service_spec.rb b/spec/services/ci/pipelines/add_job_service_spec.rb
index 6380a6a5ec3..9fb1d6933c6 100644
--- a/spec/services/ci/pipelines/add_job_service_spec.rb
+++ b/spec/services/ci/pipelines/add_job_service_spec.rb
@@ -86,15 +86,5 @@ RSpec.describe Ci::Pipelines::AddJobService, feature_category: :continuous_integ
expect(execute.payload[:job]).to eq(job)
end
end
-
- it 'locks pipelines and stages before persisting builds', :aggregate_failures do
- expect(job).not_to be_persisted
-
- recorder = ActiveRecord::QueryRecorder.new(skip_cached: false) { execute }
- entries = recorder.log.select { |query| query.match(/LOCK|INSERT INTO ".{0,2}ci_builds"/) }
-
- expect(entries.size).to eq(2)
- expect(entries.first).to match(/LOCK "ci_pipelines", "ci_stages" IN ROW SHARE MODE;/)
- end
end
end
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index 548d9455ebf..cbedf3263d8 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -10,8 +10,7 @@ RSpec.describe Issues::CreateService, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let(:opts) { { title: 'title' } }
- let(:spam_params) { double }
- let(:service) { described_class.new(container: project, current_user: user, params: opts, spam_params: spam_params) }
+ let(:service) { described_class.new(container: project, current_user: user, params: opts) }
it_behaves_like 'rate limited service' do
let(:key) { :issues_create }
@@ -27,10 +26,6 @@ RSpec.describe Issues::CreateService, feature_category: :team_planning do
let(:result) { service.execute }
let(:issue) { result[:issue] }
- before do
- stub_spam_services
- end
-
context 'when params are invalid' do
let(:opts) { { title: '' } }
@@ -155,7 +150,7 @@ RSpec.describe Issues::CreateService, feature_category: :team_planning do
end
context 'when a build_service is provided' do
- let(:result) { described_class.new(container: project, current_user: user, params: opts, spam_params: spam_params, build_service: build_service).execute }
+ let(:result) { described_class.new(container: project, current_user: user, params: opts, build_service: build_service).execute }
let(:issue_from_builder) { build(:work_item, project: project, title: 'Issue from builder') }
let(:build_service) { double(:build_service, execute: issue_from_builder) }
@@ -168,7 +163,7 @@ RSpec.describe Issues::CreateService, feature_category: :team_planning do
end
context 'when skip_system_notes is true' do
- let(:issue) { described_class.new(container: project, current_user: user, params: opts, spam_params: spam_params).execute(skip_system_notes: true) }
+ let(:issue) { described_class.new(container: project, current_user: user, params: opts).execute(skip_system_notes: true) }
it 'does not call Issuable::CommonSystemNotesService' do
expect(Issuable::CommonSystemNotesService).not_to receive(:new)
@@ -264,7 +259,7 @@ RSpec.describe Issues::CreateService, feature_category: :team_planning do
let_it_be(:non_member) { create(:user) }
it 'filters out params that cannot be set without the :set_issue_metadata permission' do
- result = described_class.new(container: project, current_user: non_member, params: opts, spam_params: spam_params).execute
+ result = described_class.new(container: project, current_user: non_member, params: opts).execute
issue = result[:issue]
expect(result).to be_success
@@ -278,7 +273,7 @@ RSpec.describe Issues::CreateService, feature_category: :team_planning do
end
it 'can create confidential issues' do
- result = described_class.new(container: project, current_user: non_member, params: opts.merge(confidential: true), spam_params: spam_params).execute
+ result = described_class.new(container: project, current_user: non_member, params: opts.merge(confidential: true)).execute
issue = result[:issue]
expect(result).to be_success
@@ -289,7 +284,7 @@ RSpec.describe Issues::CreateService, feature_category: :team_planning do
it 'moves the issue to the end, in an asynchronous worker' do
expect(Issues::PlacementWorker).to receive(:perform_async).with(be_nil, Integer)
- described_class.new(container: project, current_user: user, params: opts, spam_params: spam_params).execute
+ described_class.new(container: project, current_user: user, params: opts).execute
end
context 'when label belongs to project group' do
@@ -376,13 +371,13 @@ RSpec.describe Issues::CreateService, feature_category: :team_planning do
it 'invalidates open issues counter for assignees when issue is assigned' do
project.add_maintainer(assignee)
- described_class.new(container: project, current_user: user, params: opts, spam_params: spam_params).execute
+ described_class.new(container: project, current_user: user, params: opts).execute
expect(assignee.assigned_open_issues_count).to eq 1
end
it 'records the assignee assignment event' do
- result = described_class.new(container: project, current_user: user, params: opts, spam_params: spam_params).execute
+ result = described_class.new(container: project, current_user: user, params: opts).execute
issue = result.payload[:issue]
expect(issue.assignment_events).to match([have_attributes(user_id: assignee.id, action: 'add')])
@@ -454,7 +449,7 @@ RSpec.describe Issues::CreateService, feature_category: :team_planning do
expect(project.project_namespace).to receive(:execute_hooks).with(expected_payload, :issue_hooks)
expect(project.project_namespace).to receive(:execute_integrations).with(expected_payload, :issue_hooks)
- described_class.new(container: project, current_user: user, params: opts, spam_params: spam_params).execute
+ described_class.new(container: project, current_user: user, params: opts).execute
end
context 'when issue is confidential' do
@@ -477,7 +472,7 @@ RSpec.describe Issues::CreateService, feature_category: :team_planning do
expect(project.project_namespace).to receive(:execute_hooks).with(expected_payload, :confidential_issue_hooks)
expect(project.project_namespace).to receive(:execute_integrations).with(expected_payload, :confidential_issue_hooks)
- described_class.new(container: project, current_user: user, params: opts, spam_params: spam_params).execute
+ described_class.new(container: project, current_user: user, params: opts).execute
end
end
end
@@ -523,7 +518,7 @@ RSpec.describe Issues::CreateService, feature_category: :team_planning do
it 'removes assignee when user id is invalid' do
opts = { title: 'Title', description: 'Description', assignee_ids: [-1] }
- result = described_class.new(container: project, current_user: user, params: opts, spam_params: spam_params).execute
+ result = described_class.new(container: project, current_user: user, params: opts).execute
issue = result[:issue]
expect(result).to be_success
@@ -533,7 +528,7 @@ RSpec.describe Issues::CreateService, feature_category: :team_planning do
it 'removes assignee when user id is 0' do
opts = { title: 'Title', description: 'Description', assignee_ids: [0] }
- result = described_class.new(container: project, current_user: user, params: opts, spam_params: spam_params).execute
+ result = described_class.new(container: project, current_user: user, params: opts).execute
issue = result[:issue]
expect(result).to be_success
@@ -544,7 +539,7 @@ RSpec.describe Issues::CreateService, feature_category: :team_planning do
project.add_maintainer(assignee)
opts = { title: 'Title', description: 'Description', assignee_ids: [assignee.id] }
- result = described_class.new(container: project, current_user: user, params: opts, spam_params: spam_params).execute
+ result = described_class.new(container: project, current_user: user, params: opts).execute
issue = result[:issue]
expect(result).to be_success
@@ -564,7 +559,7 @@ RSpec.describe Issues::CreateService, feature_category: :team_planning do
project.update!(visibility_level: level)
opts = { title: 'Title', description: 'Description', assignee_ids: [assignee.id] }
- result = described_class.new(container: project, current_user: user, params: opts, spam_params: spam_params).execute
+ result = described_class.new(container: project, current_user: user, params: opts).execute
issue = result[:issue]
expect(result).to be_success
@@ -576,7 +571,7 @@ RSpec.describe Issues::CreateService, feature_category: :team_planning do
end
it_behaves_like 'issuable record that supports quick actions' do
- let(:issuable) { described_class.new(container: project, current_user: user, params: params, spam_params: spam_params).execute[:issue] }
+ let(:issuable) { described_class.new(container: project, current_user: user, params: params).execute[:issue] }
end
context 'Quick actions' do
@@ -703,14 +698,14 @@ RSpec.describe Issues::CreateService, feature_category: :team_planning do
let(:opts) { { discussion_to_resolve: discussion.id, merge_request_to_resolve_discussions_of: merge_request.iid } }
it 'resolves the discussion' do
- described_class.new(container: project, current_user: user, params: opts, spam_params: spam_params).execute
+ described_class.new(container: project, current_user: user, params: opts).execute
discussion.first_note.reload
expect(discussion.resolved?).to be(true)
end
it 'added a system note to the discussion' do
- described_class.new(container: project, current_user: user, params: opts, spam_params: spam_params).execute
+ described_class.new(container: project, current_user: user, params: opts).execute
reloaded_discussion = MergeRequest.find(merge_request.id).discussions.first
@@ -720,8 +715,7 @@ RSpec.describe Issues::CreateService, feature_category: :team_planning do
it 'sets default title and description values if not provided' do
result = described_class.new(
container: project, current_user: user,
- params: opts,
- spam_params: spam_params
+ params: opts
).execute
issue = result[:issue]
@@ -738,8 +732,7 @@ RSpec.describe Issues::CreateService, feature_category: :team_planning do
params: opts.merge(
description: 'Custom issue description',
title: 'My new issue'
- ),
- spam_params: spam_params
+ )
).execute
issue = result[:issue]
@@ -754,14 +747,14 @@ RSpec.describe Issues::CreateService, feature_category: :team_planning do
let(:opts) { { merge_request_to_resolve_discussions_of: merge_request.iid } }
it 'resolves the discussion' do
- described_class.new(container: project, current_user: user, params: opts, spam_params: spam_params).execute
+ described_class.new(container: project, current_user: user, params: opts).execute
discussion.first_note.reload
expect(discussion.resolved?).to be(true)
end
it 'added a system note to the discussion' do
- described_class.new(container: project, current_user: user, params: opts, spam_params: spam_params).execute
+ described_class.new(container: project, current_user: user, params: opts).execute
reloaded_discussion = MergeRequest.find(merge_request.id).discussions.first
@@ -771,8 +764,7 @@ RSpec.describe Issues::CreateService, feature_category: :team_planning do
it 'sets default title and description values if not provided' do
result = described_class.new(
container: project, current_user: user,
- params: opts,
- spam_params: spam_params
+ params: opts
).execute
issue = result[:issue]
@@ -789,8 +781,7 @@ RSpec.describe Issues::CreateService, feature_category: :team_planning do
params: opts.merge(
description: 'Custom issue description',
title: 'My new issue'
- ),
- spam_params: spam_params
+ )
).execute
issue = result[:issue]
@@ -836,8 +827,10 @@ RSpec.describe Issues::CreateService, feature_category: :team_planning do
}
end
+ let(:perform_spam_check) { true }
+
subject do
- described_class.new(container: project, current_user: user, params: params, spam_params: spam_params)
+ described_class.new(container: project, current_user: user, params: params, perform_spam_check: perform_spam_check)
end
it 'executes SpamActionService' do
@@ -845,7 +838,6 @@ RSpec.describe Issues::CreateService, feature_category: :team_planning do
Spam::SpamActionService,
{
spammable: kind_of(Issue),
- spam_params: spam_params,
user: an_instance_of(User),
action: :create
}
@@ -855,6 +847,16 @@ RSpec.describe Issues::CreateService, feature_category: :team_planning do
subject.execute
end
+
+ context 'when `perform_spam_check` is set to `false`' do
+ let(:perform_spam_check) { false }
+
+ it 'does not execute the SpamActionService' do
+ expect(Spam::SpamActionService).not_to receive(:new)
+
+ subject.execute
+ end
+ end
end
end
end
diff --git a/spec/services/snippets/create_service_spec.rb b/spec/services/snippets/create_service_spec.rb
index 725f1b165a2..59deffe9ccd 100644
--- a/spec/services/snippets/create_service_spec.rb
+++ b/spec/services/snippets/create_service_spec.rb
@@ -20,9 +20,8 @@ RSpec.describe Snippets::CreateService, feature_category: :source_code_managemen
let(:extra_opts) { {} }
let(:creator) { admin }
- let(:spam_params) { double }
- subject { described_class.new(project: project, current_user: creator, params: opts, spam_params: spam_params).execute }
+ subject { described_class.new(project: project, current_user: creator, params: opts).execute }
let(:snippet) { subject.payload[:snippet] }
@@ -303,10 +302,6 @@ RSpec.describe Snippets::CreateService, feature_category: :source_code_managemen
end
end
- before do
- stub_spam_services
- end
-
context 'when ProjectSnippet' do
let_it_be(:project) { create(:project) }
diff --git a/spec/services/snippets/update_service_spec.rb b/spec/services/snippets/update_service_spec.rb
index 99bb70a3077..b428897ce27 100644
--- a/spec/services/snippets/update_service_spec.rb
+++ b/spec/services/snippets/update_service_spec.rb
@@ -21,9 +21,8 @@ RSpec.describe Snippets::UpdateService, feature_category: :source_code_managemen
let(:extra_opts) { {} }
let(:options) { base_opts.merge(extra_opts) }
let(:updater) { user }
- let(:spam_params) { double }
- let(:service) { Snippets::UpdateService.new(project: project, current_user: updater, params: options, spam_params: spam_params) }
+ let(:service) { Snippets::UpdateService.new(project: project, current_user: updater, params: options, perform_spam_check: true) }
subject { service.execute(snippet) }
@@ -724,10 +723,6 @@ RSpec.describe Snippets::UpdateService, feature_category: :source_code_managemen
end
end
- before do
- stub_spam_services
- end
-
context 'when Project Snippet' do
let_it_be(:project) { create(:project) }
diff --git a/spec/services/spam/spam_action_service_spec.rb b/spec/services/spam/spam_action_service_spec.rb
index e2cc2ea7ce3..7072f5879ea 100644
--- a/spec/services/spam/spam_action_service_spec.rb
+++ b/spec/services/spam/spam_action_service_spec.rb
@@ -30,11 +30,15 @@ RSpec.describe Spam::SpamActionService, feature_category: :instance_resiliency d
before do
issue.spam = false
personal_snippet.spam = false
+
+ allow_next_instance_of(described_class) do |service|
+ allow(service).to receive(:spam_params).and_return(spam_params)
+ end
end
describe 'constructor argument validation' do
subject do
- described_service = described_class.new(spammable: issue, spam_params: spam_params, user: user, action: :create)
+ described_service = described_class.new(spammable: issue, user: user, action: :create)
described_service.execute
end
@@ -108,7 +112,7 @@ RSpec.describe Spam::SpamActionService, feature_category: :instance_resiliency d
let_it_be(:existing_spam_log) { create(:spam_log, user: user, recaptcha_verified: false) }
subject do
- described_service = described_class.new(spammable: target, spam_params: spam_params, extra_features:
+ described_service = described_class.new(spammable: target, extra_features:
extra_features, user: user, action: :create)
described_service.execute
end
diff --git a/spec/services/tasks_to_be_done/base_service_spec.rb b/spec/services/tasks_to_be_done/base_service_spec.rb
index 3ca9d140197..32b07cab095 100644
--- a/spec/services/tasks_to_be_done/base_service_spec.rb
+++ b/spec/services/tasks_to_be_done/base_service_spec.rb
@@ -35,7 +35,7 @@ RSpec.describe TasksToBeDone::BaseService, feature_category: :team_planning do
expect(Issues::CreateService)
.to receive(:new)
- .with(container: project, current_user: current_user, params: params, spam_params: nil)
+ .with(container: project, current_user: current_user, params: params, perform_spam_check: false)
.and_call_original
expect { service.execute }.to change(Issue, :count).by(1)
diff --git a/spec/services/user_agent_detail_service_spec.rb b/spec/services/user_agent_detail_service_spec.rb
new file mode 100644
index 00000000000..3984ec33716
--- /dev/null
+++ b/spec/services/user_agent_detail_service_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe UserAgentDetailService, feature_category: :instance_resiliency do
+ describe '#create', :request_store do
+ let_it_be(:spammable) { create(:issue) }
+
+ using RSpec::Parameterized::TableSyntax
+
+ where(:perform_spam_check, :spam_params_present, :user_agent, :ip_address, :creates_user_agent_detail) do
+ true | true | 'UA' | 'IP' | true
+ true | false | 'UA' | 'IP' | false
+ false | true | 'UA' | 'IP' | false
+ true | true | '' | 'IP' | false
+ true | true | nil | 'IP' | false
+ true | true | 'UA' | '' | false
+ true | true | 'UA' | nil | false
+ end
+
+ with_them do
+ let(:spam_params) do
+ instance_double('Spam::SpamParams', user_agent: user_agent, ip_address: ip_address) if spam_params_present
+ end
+
+ before do
+ allow(Gitlab::RequestContext.instance).to receive(:spam_params).and_return(spam_params)
+ end
+
+ subject { described_class.new(spammable: spammable, perform_spam_check: perform_spam_check).create } # rubocop:disable Rails/SaveBang
+
+ it 'creates a user agent detail when expected' do
+ if creates_user_agent_detail
+ expect { subject }.to change { UserAgentDetail.count }.by(1)
+ else
+ expect(subject).to be_a ServiceResponse
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/work_items/create_and_link_service_spec.rb b/spec/services/work_items/create_and_link_service_spec.rb
index 00372d460e1..b83492274a3 100644
--- a/spec/services/work_items/create_and_link_service_spec.rb
+++ b/spec/services/work_items/create_and_link_service_spec.rb
@@ -9,7 +9,6 @@ RSpec.describe WorkItems::CreateAndLinkService, feature_category: :portfolio_man
let_it_be(:related_work_item, refind: true) { create(:work_item, project: project) }
let_it_be(:invalid_parent) { create(:work_item, :task, project: project) }
- let(:spam_params) { double }
let(:link_params) { {} }
let(:params) do
@@ -45,11 +44,7 @@ RSpec.describe WorkItems::CreateAndLinkService, feature_category: :portfolio_man
end
describe '#execute' do
- subject(:service_result) { described_class.new(project: project, current_user: user, params: params, spam_params: spam_params, link_params: link_params).execute }
-
- before do
- stub_spam_services
- end
+ subject(:service_result) { described_class.new(project: project, current_user: user, params: params, link_params: link_params).execute }
context 'when work item params are valid' do
it { is_expected.to be_success }
diff --git a/spec/services/work_items/create_from_task_service_spec.rb b/spec/services/work_items/create_from_task_service_spec.rb
index b2f81f1dc54..2ab9209ab05 100644
--- a/spec/services/work_items/create_from_task_service_spec.rb
+++ b/spec/services/work_items/create_from_task_service_spec.rb
@@ -8,7 +8,6 @@ RSpec.describe WorkItems::CreateFromTaskService, feature_category: :team_plannin
let_it_be(:list_work_item, refind: true) { create(:work_item, project: project, description: "- [ ] Item to be converted\n second line\n third line") }
let(:work_item_to_update) { list_work_item }
- let(:spam_params) { double }
let(:link_params) { {} }
let(:current_user) { developer }
let(:params) do
@@ -38,11 +37,7 @@ RSpec.describe WorkItems::CreateFromTaskService, feature_category: :team_plannin
end
describe '#execute' do
- subject(:service_result) { described_class.new(work_item: work_item_to_update, current_user: current_user, work_item_params: params, spam_params: spam_params).execute }
-
- before do
- stub_spam_services
- end
+ subject(:service_result) { described_class.new(work_item: work_item_to_update, current_user: current_user, work_item_params: params).execute }
context 'when work item params are valid' do
it { is_expected.to be_success }
diff --git a/spec/services/work_items/create_service_spec.rb b/spec/services/work_items/create_service_spec.rb
index 46e598c3f11..ec775ba36f3 100644
--- a/spec/services/work_items/create_service_spec.rb
+++ b/spec/services/work_items/create_service_spec.rb
@@ -30,7 +30,7 @@ RSpec.describe WorkItems::CreateService, feature_category: :team_planning do
let_it_be(:user_with_no_access) { create(:user) }
let(:widget_params) { {} }
- let(:spam_params) { double }
+ let(:perform_spam_check) { false }
let(:current_user) { guest }
let(:opts) do
{
@@ -60,17 +60,13 @@ RSpec.describe WorkItems::CreateService, feature_category: :team_planning do
container: container,
current_user: current_user,
params: opts,
- spam_params: spam_params,
+ perform_spam_check: perform_spam_check,
widget_params: widget_params
)
end
subject(:service_result) { service.execute }
- before do
- stub_spam_services
- end
-
context 'when user is not allowed to create a work item in the container' do
let(:current_user) { user_with_no_access }
@@ -151,12 +147,13 @@ RSpec.describe WorkItems::CreateService, feature_category: :team_planning do
end
context 'checking spam' do
+ let(:perform_spam_check) { true }
+
it 'executes SpamActionService' do
expect_next_instance_of(
Spam::SpamActionService,
{
spammable: kind_of(WorkItem),
- spam_params: spam_params,
user: an_instance_of(User),
action: :create
}
@@ -166,6 +163,16 @@ RSpec.describe WorkItems::CreateService, feature_category: :team_planning do
service_result
end
+
+ context 'when `perform_spam_check` is set to `false`' do
+ let(:perform_spam_check) { false }
+
+ it 'does not execute the SpamActionService' do
+ expect(Spam::SpamActionService).not_to receive(:new)
+
+ service_result
+ end
+ end
end
it_behaves_like 'work item widgetable service' do
@@ -180,7 +187,6 @@ RSpec.describe WorkItems::CreateService, feature_category: :team_planning do
container: container,
current_user: current_user,
params: opts,
- spam_params: spam_params,
widget_params: widget_params
)
end
diff --git a/spec/services/work_items/update_service_spec.rb b/spec/services/work_items/update_service_spec.rb
index 2cf52ee853a..6a56e1a72e6 100644
--- a/spec/services/work_items/update_service_spec.rb
+++ b/spec/services/work_items/update_service_spec.rb
@@ -9,7 +9,6 @@ RSpec.describe WorkItems::UpdateService, feature_category: :team_planning do
let_it_be(:parent) { create(:work_item, project: project) }
let_it_be_with_reload(:work_item) { create(:work_item, project: project, assignees: [developer]) }
- let(:spam_params) { double }
let(:widget_params) { {} }
let(:opts) { {} }
let(:current_user) { developer }
@@ -25,17 +24,12 @@ RSpec.describe WorkItems::UpdateService, feature_category: :team_planning do
container: project,
current_user: current_user,
params: opts,
- spam_params: spam_params,
widget_params: widget_params
)
end
subject(:update_work_item) { service.execute(work_item) }
- before do
- stub_spam_services
- end
-
shared_examples 'update service that triggers graphql dates updated subscription' do
it 'triggers graphql subscription issueableDatesUpdated' do
expect(GraphqlTriggers).to receive(:issuable_dates_updated).with(work_item).and_call_original
@@ -176,7 +170,6 @@ RSpec.describe WorkItems::UpdateService, feature_category: :team_planning do
container: project,
current_user: current_user,
params: opts,
- spam_params: spam_params,
widget_params: widget_params
)
end
@@ -392,7 +385,6 @@ RSpec.describe WorkItems::UpdateService, feature_category: :team_planning do
container: project,
current_user: current_user,
params: update_params,
- spam_params: spam_params,
widget_params: widget_params
).execute(work_item)
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index bbf69e75050..41bc2571745 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -173,7 +173,6 @@ RSpec.configure do |config|
config.include SidekiqMiddleware
config.include StubActionCableConnection, type: :channel
config.include StubMemberAccessLevel
- config.include StubSpamServices
config.include SnowplowHelpers
config.include RenderedHelpers
config.include RSpec::Benchmark::Matchers, type: :benchmark
diff --git a/spec/support/helpers/stub_spam_services.rb b/spec/support/helpers/stub_spam_services.rb
deleted file mode 100644
index 841e8366845..00000000000
--- a/spec/support/helpers/stub_spam_services.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: true
-
-module StubSpamServices
- def stub_spam_services
- allow(::Spam::SpamParams).to receive(:new_from_request) do
- ::Spam::SpamParams.new(
- captcha_response: double(:captcha_response),
- spam_log_id: double(:spam_log_id),
- ip_address: double(:ip_address),
- user_agent: double(:user_agent),
- referer: double(:referer)
- )
- end
-
- allow_next_instance_of(::Spam::SpamActionService) do |service|
- allow(service).to receive(:execute)
- end
-
- allow_next_instance_of(::UserAgentDetailService) do |service|
- allow(service).to receive(:create)
- end
- end
-end
diff --git a/spec/support/shared_examples/graphql/mutations/can_mutate_spammable_examples.rb b/spec/support/shared_examples/graphql/mutations/can_mutate_spammable_examples.rb
deleted file mode 100644
index b17e59f0797..00000000000
--- a/spec/support/shared_examples/graphql/mutations/can_mutate_spammable_examples.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.shared_examples 'a mutation which can mutate a spammable' do
- describe "#spam_params" do
- it 'passes spam params to the service constructor' do
- args = [
- project: anything,
- current_user: anything,
- params: anything,
- spam_params: instance_of(::Spam::SpamParams)
- ]
- expect(service).to receive(:new).with(*args).and_call_original
-
- subject
- end
- end
-end
diff --git a/spec/support/shared_examples/models/chat_integration_shared_examples.rb b/spec/support/shared_examples/models/chat_integration_shared_examples.rb
index addd37cde32..0ce54fbc31f 100644
--- a/spec/support/shared_examples/models/chat_integration_shared_examples.rb
+++ b/spec/support/shared_examples/models/chat_integration_shared_examples.rb
@@ -165,7 +165,7 @@ RSpec.shared_examples "chat integration" do |integration_name|
context "with issue events" do
let(:opts) { { title: "Awesome issue", description: "please fix" } }
let(:sample_data) do
- service = Issues::CreateService.new(container: project, current_user: user, params: opts, spam_params: nil)
+ service = Issues::CreateService.new(container: project, current_user: user, params: opts)
issue = service.execute[:issue]
service.hook_data(issue, "open")
end
diff --git a/spec/support/shared_examples/services/rate_limited_service_shared_examples.rb b/spec/support/shared_examples/services/rate_limited_service_shared_examples.rb
index b79f1a332a6..70848044527 100644
--- a/spec/support/shared_examples/services/rate_limited_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/rate_limited_service_shared_examples.rb
@@ -7,7 +7,7 @@
# let(:key) { :issues_create }
# let(:key_scope) { %i[project current_user external_author] }
# let(:application_limit_key) { :issues_create_limit }
-# let(:service) { described_class.new(project: project, current_user: user, params: { title: 'title' }, spam_params: double) }
+# let(:service) { described_class.new(project: project, current_user: user, params: { title: 'title' }) }
# let(:created_model) { Issue }
# end
@@ -29,10 +29,6 @@ RSpec.shared_examples 'rate limited service' do
end
describe '#execute' do
- before do
- stub_spam_services
- end
-
context 'when rate limiting is in effect', :freeze_time, :clean_gitlab_redis_rate_limiting do
let(:user) { create(:user) }
diff --git a/spec/support/shared_examples/services/snippets_shared_examples.rb b/spec/support/shared_examples/services/snippets_shared_examples.rb
index 65893d84798..d8db0f53df5 100644
--- a/spec/support/shared_examples/services/snippets_shared_examples.rb
+++ b/spec/support/shared_examples/services/snippets_shared_examples.rb
@@ -12,7 +12,6 @@ RSpec.shared_examples 'checking spam' do
Spam::SpamActionService,
{
spammable: kind_of(Snippet),
- spam_params: spam_params,
user: an_instance_of(User),
action: action,
extra_features: { files: an_instance_of(Array) }