summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-04-14 12:08:53 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2023-04-14 12:08:53 +0000
commit8a5138ed7d38ccff8b5ca2fe0f7bbb77f8fdaad3 (patch)
tree4c0d373c990fc01cacff9b4093366ab398fcb7d3
parent6d8f30ab0ae82678f10450d2158f24772f0c765c (diff)
downloadgitlab-ce-8a5138ed7d38ccff8b5ca2fe0f7bbb77f8fdaad3.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/CODEOWNERS10
-rw-r--r--.gitlab/ci/package-and-test/main.gitlab-ci.yml54
-rw-r--r--.gitlab/ci/qa.gitlab-ci.yml15
-rw-r--r--.gitlab/ci/rules.gitlab-ci.yml31
-rw-r--r--.rubocop_todo/rspec/factory_bot/avoid_create.yml1
-rw-r--r--.rubocop_todo/rspec/missing_feature_category.yml1
-rw-r--r--CHANGELOG.md12
-rw-r--r--Gemfile1
-rw-r--r--Gemfile.checksum2
-rw-r--r--Gemfile.lock7
-rw-r--r--app/assets/javascripts/diffs/components/diff_file_header.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_area_chart.vue11
-rw-r--r--app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue9
-rw-r--r--app/controllers/profiles/two_factor_auths_controller.rb18
-rw-r--r--app/controllers/profiles_controller.rb3
-rw-r--r--app/controllers/projects/blame_controller.rb26
-rw-r--r--app/models/clusters/agents/authorizations/user_access/group_authorization.rb22
-rw-r--r--app/models/clusters/agents/authorizations/user_access/project_authorization.rb22
-rw-r--r--app/models/members_preloader.rb5
-rw-r--r--app/models/user.rb2
-rw-r--r--app/services/projects/blame_service.rb84
-rw-r--r--app/services/users/approve_service.rb5
-rw-r--r--app/validators/json_schemas/clusters_agents_authorizations_user_access_config.json6
-rw-r--r--app/views/projects/_files.html.haml2
-rw-r--r--app/views/projects/blame/show.html.haml7
-rw-r--r--app/views/projects/blob/_blob.html.haml2
-rw-r--r--config/feature_flags/development/ci_multi_doc_yaml.yml2
-rw-r--r--config/feature_flags/development/webauthn.yml8
-rw-r--r--data/deprecations/15-1-jira-github-enterprise-dvcs.yml7
-rw-r--r--db/docs/agent_user_access_group_authorizations.yml10
-rw-r--r--db/docs/agent_user_access_project_authorizations.yml10
-rw-r--r--db/docs/batched_background_migrations/backfill_admin_mode_scope_for_personal_access_tokens.yml6
-rw-r--r--db/migrate/20230406150254_create_agent_user_access_project_authorizations_table.rb17
-rw-r--r--db/migrate/20230406150354_create_agent_user_access_group_authorizations_table.rb17
-rw-r--r--db/migrate/20230406150454_add_fks_to_agent_user_access_authorizations.rb34
-rw-r--r--db/post_migrate/20221228103133_queue_backfill_admin_mode_scope_for_personal_access_tokens.rb18
-rw-r--r--db/post_migrate/20230406093640_requeue_backfill_admin_mode_scope_for_personal_access_tokens.rb23
-rw-r--r--db/schema_migrations/202304060936401
-rw-r--r--db/schema_migrations/202304061502541
-rw-r--r--db/schema_migrations/202304061503541
-rw-r--r--db/schema_migrations/202304061504541
-rw-r--r--db/structure.sql62
-rw-r--r--doc/administration/auth/oidc.md18
-rw-r--r--doc/api/graphql/reference/index.md1
-rw-r--r--doc/ci/jobs/ci_job_token.md6
-rw-r--r--doc/ci/yaml/includes.md84
-rw-r--r--doc/development/testing_guide/best_practices.md21
-rw-r--r--doc/operations/incident_management/incident_timeline_events.md11
-rw-r--r--doc/update/deprecations.md7
-rw-r--r--doc/user/compliance/license_scanning_of_cyclonedx_files/index.md3
-rw-r--r--doc/user/packages/package_registry/index.md18
-rw-r--r--lib/gitlab/background_migration/backfill_admin_mode_scope_for_personal_access_tokens.rb6
-rw-r--r--lib/gitlab/git/blame_pagination.rb78
-rw-r--r--lib/sidebars/groups/super_sidebar_menus/secure_menu.rb2
-rw-r--r--lib/sidebars/projects/menus/packages_registries_menu.rb15
-rw-r--r--lib/sidebars/projects/super_sidebar_menus/analyze_menu.rb3
-rw-r--r--lib/sidebars/projects/super_sidebar_menus/secure_menu.rb6
-rw-r--r--lib/tasks/gitlab/db.rake2
-rw-r--r--lib/tasks/gitlab/tw/codeowners.rake2
-rw-r--r--locale/gitlab.pot27
-rw-r--r--spec/factories/clusters/agents/authorizations/user_access/group_authorizations.rb10
-rw-r--r--spec/factories/clusters/agents/authorizations/user_access/project_authorizations.rb10
-rw-r--r--spec/features/admin/users/users_spec.rb34
-rw-r--r--spec/features/projects/blobs/blame_spec.rb6
-rw-r--r--spec/features/projects/navbar_spec.rb13
-rw-r--r--spec/lib/gitlab/background_migration/backfill_admin_mode_scope_for_personal_access_tokens_spec.rb9
-rw-r--r--spec/lib/gitlab/git/blame_pagination_spec.rb175
-rw-r--r--spec/lib/sidebars/groups/super_sidebar_menus/secure_menu_spec.rb2
-rw-r--r--spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb22
-rw-r--r--spec/lib/sidebars/projects/super_sidebar_menus/analyze_menu_spec.rb3
-rw-r--r--spec/lib/sidebars/projects/super_sidebar_menus/secure_menu_spec.rb6
-rw-r--r--spec/migrations/queue_backfill_admin_mode_scope_for_personal_access_tokens_spec.rb18
-rw-r--r--spec/migrations/requeue_backfill_admin_mode_scope_for_personal_access_tokens_spec.rb19
-rw-r--r--spec/models/clusters/agents/authorizations/user_access/group_authorization_spec.rb16
-rw-r--r--spec/models/clusters/agents/authorizations/user_access/project_authorization_spec.rb16
-rw-r--r--spec/presenters/issue_email_participant_presenter_spec.rb43
-rw-r--r--spec/presenters/project_clusterable_presenter_spec.rb10
-rw-r--r--spec/services/projects/blame_service_spec.rb132
-rw-r--r--spec/services/users/approve_service_spec.rb18
-rw-r--r--spec/spec_helper.rb1
-rw-r--r--spec/support/helpers/navbar_structure_helper.rb8
-rw-r--r--spec/support/stub_member_access_level.rb46
-rw-r--r--spec/support_specs/stub_member_access_level_spec.rb69
-rw-r--r--spec/tooling/lib/tooling/find_changes_spec.rb157
-rw-r--r--spec/tooling/lib/tooling/helpers/file_handler_spec.rb10
-rw-r--r--spec/tooling/lib/tooling/predictive_tests_spec.rb51
-rwxr-xr-xtooling/bin/find_only_js_changes2
-rwxr-xr-xtooling/lib/tooling/find_changes.rb39
-rw-r--r--tooling/lib/tooling/helpers/file_handler.rb8
-rw-r--r--tooling/lib/tooling/predictive_tests.rb11
90 files changed, 1325 insertions, 527 deletions
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS
index 8d10babc0ab..c826bd0a6c0 100644
--- a/.gitlab/CODEOWNERS
+++ b/.gitlab/CODEOWNERS
@@ -866,20 +866,27 @@ lib/gitlab/checks/** @proglottis @toon
/doc/user/discussions/ @aqualls
/doc/user/enterprise_user/ @jglassman1
/doc/user/feature_flags.md @sselhorn
-/doc/user/group/ @lciutacu
+/doc/user/group/access_and_permissions.md @lciutacu
/doc/user/group/clusters/ @phillipwells
/doc/user/group/compliance_frameworks.md @eread
+/doc/user/group/contribution_analytics/ @lciutacu
/doc/user/group/custom_project_templates.md @eread
+/doc/user/group/devops_adoption/ @lciutacu
/doc/user/group/epics/ @msedlakjakubowski
/doc/user/group/import/ @eread
+/doc/user/group/index.md @lciutacu
+/doc/user/group/insights/ @lciutacu
/doc/user/group/issues_analytics/ @msedlakjakubowski
/doc/user/group/iterations/ @msedlakjakubowski
+/doc/user/group/manage.md @lciutacu
/doc/user/group/planning_hierarchy/ @msedlakjakubowski
/doc/user/group/reporting/ @phillipwells
/doc/user/group/repositories_analytics/ @drcatherinepope
/doc/user/group/roadmap/ @msedlakjakubowski
/doc/user/group/saml_sso/ @jglassman1
/doc/user/group/settings/ @jglassman1
+/doc/user/group/subgroups/ @lciutacu
+/doc/user/group/value_stream_analytics/ @lciutacu
/doc/user/infrastructure/ @phillipwells
/doc/user/infrastructure/clusters/manage/management_project_applications/runner.md @fneill
/doc/user/markdown.md @msedlakjakubowski
@@ -891,6 +898,7 @@ lib/gitlab/checks/** @proglottis @toon
/doc/user/permissions.md @jglassman1
/doc/user/product_analytics/ @lciutacu
/doc/user/profile/account/ @jglassman1
+/doc/user/profile/achievements.md @lciutacu
/doc/user/profile/comment_templates.md @aqualls
/doc/user/profile/contributions_calendar.md @lciutacu
/doc/user/profile/index.md @jglassman1
diff --git a/.gitlab/ci/package-and-test/main.gitlab-ci.yml b/.gitlab/ci/package-and-test/main.gitlab-ci.yml
index 3c9742f59e6..1b2f6b49558 100644
--- a/.gitlab/ci/package-and-test/main.gitlab-ci.yml
+++ b/.gitlab/ci/package-and-test/main.gitlab-ci.yml
@@ -63,6 +63,7 @@ stages:
QA_INTERCEPT_REQUESTS: "true"
GITLAB_LICENSE_MODE: test
GITLAB_QA_ADMIN_ACCESS_TOKEN: $QA_ADMIN_ACCESS_TOKEN
+ GITLAB_QA_OPTS: $EXTRA_GITLAB_QA_OPTS
# todo: remove in 16.1 milestone when not needed for backwards compatibility anymore
EE_LICENSE: $QA_EE_LICENSE
GITHUB_ACCESS_TOKEN: $QA_GITHUB_ACCESS_TOKEN
@@ -233,28 +234,6 @@ _quarantine:
variables:
QA_RSPEC_TAGS: --tag quarantine
-# Temporary test job to support the effort of migrating to Super Sidebar
-# https://gitlab.com/groups/gitlab-org/-/epics/9044
-_super-sidebar-nav:
- extends:
- - .qa
- - .parallel
- variables:
- QA_SCENARIO: Test::Instance::Image
- QA_KNAPSACK_REPORT_NAME: ee-instance
- QA_TESTS: ""
- QA_SUPER_SIDEBAR_ENABLED: "true"
- QA_ALLURE_RESULTS_DIRECTORY: tmp/allure-results-super-sidebar
- QA_EXPORT_TEST_METRICS: "false"
- QA_DISABLE_RSPEC_RETRY: "true"
- GITLAB_QA_OPTS: --set-feature-flags super_sidebar_nav=enabled
- RSPEC_REPORT_OPTS: "--format documentation"
- SKIP_REPORT_IN_ISSUES: "true"
- allow_failure: true
- rules:
- - if: $CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_PATH == "gitlab-org/gitlab" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
- - !reference [.rules:test:manual, rules]
-
# ------------------------------------------
# FF changes
# ------------------------------------------
@@ -325,7 +304,7 @@ decomposition-single-db-selective:
extends: .qa
variables:
QA_SCENARIO: Test::Instance::Image
- GITLAB_QA_OPTS: --omnibus-config decomposition_single_db
+ GITLAB_QA_OPTS: --omnibus-config decomposition_single_db $EXTRA_GITLAB_QA_OPTS
rules:
- !reference [.rules:test:qa-selective, rules]
- if: $QA_SUITES =~ /Test::Instance::All/
@@ -342,7 +321,7 @@ decomposition-multiple-db-selective:
variables:
QA_SCENARIO: Test::Instance::Image
GITLAB_ALLOW_SEPARATE_CI_DATABASE: "true"
- GITLAB_QA_OPTS: --omnibus-config decomposition_multiple_db
+ GITLAB_QA_OPTS: --omnibus-config decomposition_multiple_db $EXTRA_GITLAB_QA_OPTS
rules:
- !reference [.rules:test:qa-selective, rules]
- if: $QA_SUITES =~ /Test::Instance::All/
@@ -359,7 +338,7 @@ object-storage-selective:
variables:
QA_SCENARIO: Test::Instance::Image
QA_RSPEC_TAGS: --tag object_storage
- GITLAB_QA_OPTS: --omnibus-config object_storage
+ GITLAB_QA_OPTS: --omnibus-config object_storage $EXTRA_GITLAB_QA_OPTS
rules:
- !reference [.rules:test:qa-selective, rules]
- if: $QA_SUITES =~ /Test::Instance::ObjectStorage/
@@ -377,7 +356,7 @@ object-storage-aws-selective:
AWS_S3_BUCKET_NAME: $QA_AWS_S3_BUCKET_NAME
AWS_S3_KEY_ID: $QA_AWS_S3_KEY_ID
AWS_S3_REGION: $QA_AWS_S3_REGION
- GITLAB_QA_OPTS: --omnibus-config object_storage_aws
+ GITLAB_QA_OPTS: --omnibus-config object_storage_aws $EXTRA_GITLAB_QA_OPTS
object-storage-aws:
extends: object-storage-aws-selective
parallel: 2
@@ -391,7 +370,7 @@ object-storage-gcs-selective:
GOOGLE_PROJECT: $QA_GOOGLE_PROJECT
GOOGLE_JSON_KEY: $QA_GOOGLE_JSON_KEY
GOOGLE_CLIENT_EMAIL: $QA_GOOGLE_CLIENT_EMAIL
- GITLAB_QA_OPTS: --omnibus-config object_storage_gcs
+ GITLAB_QA_OPTS: --omnibus-config object_storage_gcs $EXTRA_GITLAB_QA_OPTS
object-storage-gcs:
extends: object-storage-gcs-selective
parallel: 2
@@ -403,7 +382,7 @@ packages-selective:
variables:
QA_SCENARIO: Test::Instance::Image
QA_RSPEC_TAGS: --tag packages
- GITLAB_QA_OPTS: --omnibus-config packages
+ GITLAB_QA_OPTS: --omnibus-config packages $EXTRA_GITLAB_QA_OPTS
rules:
- !reference [.rules:test:qa-selective, rules]
- if: $QA_SUITES =~ /Test::Instance::Packages/
@@ -650,7 +629,7 @@ registry-object-storage-tls:
QA_SCENARIO: Test::Integration::RegistryTLS
QA_RSPEC_TAGS: ""
GITLAB_TLS_CERTIFICATE: $QA_GITLAB_TLS_CERTIFICATE
- GITLAB_QA_OPTS: --omnibus-config registry_object_storage
+ GITLAB_QA_OPTS: --omnibus-config registry_object_storage $EXTRA_GITLAB_QA_OPTS
importers:
extends: .qa
@@ -671,27 +650,10 @@ e2e-test-report:
- .rules:report:allure-report
stage: report
variables:
- ALLURE_JOB_NAME: e2e-package-and-test
GITLAB_AUTH_TOKEN: $PROJECT_TOKEN_FOR_CI_SCRIPTS_API_USAGE
ALLURE_PROJECT_PATH: $CI_PROJECT_PATH
ALLURE_MERGE_REQUEST_IID: $CI_MERGE_REQUEST_IID
-# Temporary separate test report for super-sidebar test job
-# TODO: remove once super-sidebar is on by default and enabled in tests
-# https://gitlab.com/groups/gitlab-org/-/epics/9044
-e2e-test-report-super-sidebar:
- extends:
- - .generate-allure-report-base
- stage: report
- needs:
- - _super-sidebar-nav
- variables:
- ALLURE_JOB_NAME: e2e-super-sidebar
- ALLURE_RESULTS_GLOB: gitlab-qa-run-*/**/allure-results-super-sidebar
- rules:
- - if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
- - !reference [.rules:test:manual, rules]
-
upload-knapsack-report:
extends:
- .generate-knapsack-report-base
diff --git a/.gitlab/ci/qa.gitlab-ci.yml b/.gitlab/ci/qa.gitlab-ci.yml
index 2956eef6a9e..cf45c53b226 100644
--- a/.gitlab/ci/qa.gitlab-ci.yml
+++ b/.gitlab/ci/qa.gitlab-ci.yml
@@ -68,6 +68,7 @@ e2e:package-and-test-ee:
RELEASE: "${REGISTRY_HOST}/${REGISTRY_GROUP}/build/omnibus-gitlab-mirror/gitlab-ee:${CI_COMMIT_SHA}"
GITLAB_QA_IMAGE: "${CI_REGISTRY_IMAGE}/gitlab-ee-qa:${CI_COMMIT_SHA}"
RUN_WITH_BUNDLE: "true" # instructs pipeline to install and run gitlab-qa gem via bundler
+ ALLURE_JOB_NAME: e2e-package-and-test
QA_PATH: qa # sets the optional path for bundler to run from
QA_RUN_TYPE: e2e-package-and-test
PIPELINE_NAME: E2E Omnibus GitLab EE
@@ -106,6 +107,20 @@ e2e:package-and-test-ce:
GITLAB_QA_IMAGE: ${CI_REGISTRY_IMAGE}/gitlab-ce-qa:${CI_COMMIT_SHA}
PIPELINE_NAME: E2E Omnibus GitLab CE
+e2e:package-and-test-super-sidebar:
+ extends: e2e:package-and-test-ee
+ variables:
+ QA_SUPER_SIDEBAR_ENABLED: "true"
+ QA_RUN_TYPE: e2e-package-and-test-super-sidebar
+ EXTRA_GITLAB_QA_OPTS: --set-feature-flags super_sidebar_nav=enabled
+ ALLURE_JOB_NAME: e2e-package-and-test-super-sidebar
+ PIPELINE_NAME: E2E Omnibus Super Sidebar
+ rules:
+ - if: $CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_PATH == "gitlab-org/gitlab" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
+ allow_failure: true
+ - when: manual
+ allow_failure: true
+
e2e:test-on-gdk:
extends:
- .qa:rules:e2e:test-on-gdk
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml
index bb9ea30f0d4..704c857760d 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -860,14 +860,27 @@
- <<: *if-not-canonical-namespace
when: never
- <<: *if-merge-request-targeting-stable-branch
+ - <<: *if-ruby2-branch
- <<: *if-merge-request-labels-run-review-app
- <<: *if-merge-request-labels-run-all-e2e
- <<: *if-auto-deploy-branches
- - <<: *if-ruby2-branch
- <<: *if-default-refs
changes: *ci-build-images-patterns
- <<: *if-default-refs
changes: *code-qa-patterns
+ # Rules to support .qa:rules:package-and-test-mrs
+ - <<: *if-merge-request
+ changes: *dependency-patterns
+ - <<: *if-merge-request-labels-run-all-e2e
+ - <<: *if-dot-com-gitlab-org-and-security-merge-request-manual-ff-package-and-e2e
+ changes: *feature-flag-development-config-patterns
+ - <<: *if-merge-request
+ changes: *feature-flag-development-config-patterns
+ - <<: *if-merge-request
+ changes: *nodejs-patterns
+ - <<: *if-merge-request
+ changes: *ci-qa-patterns
+ - <<: *if-force-ci
.build-images:rules:build-assets-image-as-if-foss:
rules:
@@ -1305,7 +1318,7 @@
allow_failure: true
- <<: *if-ruby2-branch
allow_failure: true
- - <<: *if-dot-com-gitlab-org-and-security-merge-request
+ - <<: *if-merge-request
changes: *dependency-patterns
allow_failure: true
variables:
@@ -1316,30 +1329,30 @@
changes: *feature-flag-development-config-patterns
when: manual
allow_failure: true
- - <<: *if-dot-com-gitlab-org-and-security-merge-request
+ - <<: *if-merge-request
changes: *feature-flag-development-config-patterns
allow_failure: true
- - <<: *if-dot-com-gitlab-org-and-security-merge-request
+ - <<: *if-merge-request
changes: *initializers-patterns
allow_failure: true
- - <<: *if-dot-com-gitlab-org-and-security-merge-request
+ - <<: *if-merge-request
changes: *nodejs-patterns
allow_failure: true
- - <<: *if-dot-com-gitlab-org-and-security-merge-request
+ - <<: *if-merge-request
changes: *ci-qa-patterns
allow_failure: true
- - <<: *if-dot-com-gitlab-org-and-security-merge-request
+ - <<: *if-merge-request
changes:
- qa/Gemfile.lock # qa/Gemfile.lock is a part of *qa-patterns, so this rule must be placed before the one with *qa-patterns changes
variables:
UPDATE_QA_CACHE: "true"
- - <<: *if-dot-com-gitlab-org-and-security-merge-request
+ - <<: *if-merge-request
changes: *qa-patterns
allow_failure: true
- <<: *if-dot-com-gitlab-org-and-security-merge-request-and-qa-tests-specified
changes: *code-patterns
allow_failure: true
- - <<: *if-dot-com-gitlab-org-and-security-merge-request
+ - <<: *if-merge-request
changes: *code-patterns
when: manual
allow_failure: true
diff --git a/.rubocop_todo/rspec/factory_bot/avoid_create.yml b/.rubocop_todo/rspec/factory_bot/avoid_create.yml
index 88e0d7c7a85..fe8a1cb6bfa 100644
--- a/.rubocop_todo/rspec/factory_bot/avoid_create.yml
+++ b/.rubocop_todo/rspec/factory_bot/avoid_create.yml
@@ -389,7 +389,6 @@ RSpec/FactoryBot/AvoidCreate:
- 'spec/presenters/packages/pypi/simple_index_presenter_spec.rb'
- 'spec/presenters/packages/pypi/simple_package_versions_presenter_spec.rb'
- 'spec/presenters/pages_domain_presenter_spec.rb'
- - 'spec/presenters/project_clusterable_presenter_spec.rb'
- 'spec/presenters/project_hook_presenter_spec.rb'
- 'spec/presenters/project_presenter_spec.rb'
- 'spec/presenters/projects/import_export/project_export_presenter_spec.rb'
diff --git a/.rubocop_todo/rspec/missing_feature_category.yml b/.rubocop_todo/rspec/missing_feature_category.yml
index c4b8c206433..a0e239e05b5 100644
--- a/.rubocop_todo/rspec/missing_feature_category.yml
+++ b/.rubocop_todo/rspec/missing_feature_category.yml
@@ -5524,7 +5524,6 @@ RSpec/MissingFeatureCategory:
- 'spec/presenters/packages/pypi/simple_index_presenter_spec.rb'
- 'spec/presenters/packages/pypi/simple_package_versions_presenter_spec.rb'
- 'spec/presenters/pages_domain_presenter_spec.rb'
- - 'spec/presenters/project_clusterable_presenter_spec.rb'
- 'spec/presenters/project_hook_presenter_spec.rb'
- 'spec/presenters/project_member_presenter_spec.rb'
- 'spec/presenters/project_presenter_spec.rb'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 098cf10c7fc..f9e4d1b735b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,18 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 15.10.3 (2023-04-14)
+
+### Fixed (3 changes)
+
+- [Backport fixes for broadcast messages](gitlab-org/gitlab@c97c17e31e99f9e93127245cd1f65f7d15cdb0ef) ([merge request](gitlab-org/gitlab!117276))
+- [Fix automatically-retried jobs stuck in pending state](gitlab-org/gitlab@e349581eaf1e050b8bcdee76f9d40f0c182a09f8) ([merge request](gitlab-org/gitlab!117280))
+- [Verify deploy keys settings for protected tags (backport)](gitlab-org/gitlab@4bd6914bd616c1d8dc9ee7cb75e92be13d522ca9) ([merge request](gitlab-org/gitlab!116952))
+
+### Changed (1 change)
+
+- [Change the order of vulnerability creation](gitlab-org/gitlab@4193c4cab75f9472b3804b74b17f4a10f3ae9580) ([merge request](gitlab-org/gitlab!116851)) **GitLab Enterprise Edition**
+
## 15.10.2 (2023-04-05)
### Fixed (3 changes)
diff --git a/Gemfile b/Gemfile
index ff1116f7504..0fc5773ef8d 100644
--- a/Gemfile
+++ b/Gemfile
@@ -249,6 +249,7 @@ gem 're2', '~> 1.6.0'
# Misc
+gem 'semver_dialects', '~> 1.2.1'
gem 'version_sorter', '~> 2.3'
# Export Ruby Regex to Javascript
diff --git a/Gemfile.checksum b/Gemfile.checksum
index 4a0022198f4..40470b66dac 100644
--- a/Gemfile.checksum
+++ b/Gemfile.checksum
@@ -556,6 +556,7 @@
{"name":"sd_notify","version":"0.1.1","platform":"ruby","checksum":"cbc7ac6caa7cedd26b30a72b5eeb6f36050dc0752df263452ea24fb5a4ad3131"},
{"name":"seed-fu","version":"2.3.7","platform":"ruby","checksum":"f19673443e9af799b730e3d4eca6a89b39e5a36825015dffd00d02ea3365cf74"},
{"name":"selenium-webdriver","version":"3.142.7","platform":"ruby","checksum":"dea0993e0e4fdb364f0453144814c0e6099a411d17396807c6cac666d0ddac29"},
+{"name":"semver_dialects","version":"1.2.1","platform":"ruby","checksum":"60a1f67659f79c51a667e8858ec9b089c1e4ce4f6d2a0f0b4ac101916946eb23"},
{"name":"sentry-rails","version":"5.8.0","platform":"ruby","checksum":"c11b2d909de2c2bfda793c45f64180fd784d54c46886338b683ee3f8efa7731b"},
{"name":"sentry-raven","version":"3.1.2","platform":"ruby","checksum":"103d3b122958810d34898ce2e705bcf549ddb9d855a70ce9a3970ee2484f364a"},
{"name":"sentry-ruby","version":"5.8.0","platform":"ruby","checksum":"caeb121433be379fb94e991a45265a287b13a9a9083e7264f539752369d37110"},
@@ -635,6 +636,7 @@
{"name":"train-core","version":"3.4.9","platform":"ruby","checksum":"d7ad8fa9a379c43a30baaaf1141af1cb28349d386c054f7fc81d169a625d6edd"},
{"name":"truncato","version":"0.7.12","platform":"ruby","checksum":"fed9e8a04fa35fd1a64506cd2089761bae4adfe47e756c3ce98a5c43856c9c4c"},
{"name":"tty-color","version":"0.6.0","platform":"ruby","checksum":"6f9c37ca3a4e2367fb2e6d09722762647d6f455c111f05b59f35730eeb24332a"},
+{"name":"tty-command","version":"0.10.1","platform":"ruby","checksum":"0c6c471fcb932d55518734eb4e2e07e9efdd2918713cc39bb7393ba862471192"},
{"name":"tty-cursor","version":"0.7.1","platform":"ruby","checksum":"79534185e6a777888d88628b14b6a1fdf5154a603f285f80b1753e1908e0bf48"},
{"name":"tty-markdown","version":"0.7.2","platform":"ruby","checksum":"1ed81db97028d006ba81e2cfd9fe0a04b0eb28650ad0d4086ed6e5627f4ac511"},
{"name":"tty-prompt","version":"0.23.1","platform":"ruby","checksum":"fcdbce905238993f27eecfdf67597a636bc839d92192f6a0eef22b8166449ec8"},
diff --git a/Gemfile.lock b/Gemfile.lock
index dbd315129ec..73480837892 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1403,6 +1403,10 @@ GEM
selenium-webdriver (3.142.7)
childprocess (>= 0.5, < 4.0)
rubyzip (>= 1.2.2)
+ semver_dialects (1.2.1)
+ pastel (~> 0.8.0)
+ thor (~> 1.2.0)
+ tty-command (~> 0.10.1)
sentry-rails (5.8.0)
railties (>= 5.0)
sentry-ruby (~> 5.8.0)
@@ -1548,6 +1552,8 @@ GEM
htmlentities (~> 4.3.1)
nokogiri (>= 1.7.0, <= 2.0)
tty-color (0.6.0)
+ tty-command (0.10.1)
+ pastel (~> 0.8)
tty-cursor (0.7.1)
tty-markdown (0.7.2)
kramdown (>= 1.16.2, < 3.0)
@@ -1908,6 +1914,7 @@ DEPENDENCIES
sd_notify (~> 0.1.0)
seed-fu (~> 2.3.7)
selenium-webdriver (~> 3.142, >= 3.142.7)
+ semver_dialects (~> 1.2.1)
sentry-rails (~> 5.8.0)
sentry-raven (~> 3.1)
sentry-ruby (~> 5.8.0)
diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue
index 0f1c13f818f..c3a4897ce78 100644
--- a/app/assets/javascripts/diffs/components/diff_file_header.vue
+++ b/app/assets/javascripts/diffs/components/diff_file_header.vue
@@ -376,7 +376,7 @@ export default {
v-if="isReviewable && showLocalFileReviews"
v-gl-tooltip.hover
data-testid="fileReviewCheckbox"
- class="gl-mr-5 gl-display-flex gl-align-items-center"
+ class="gl-mr-5 gl-mb-n3 gl-display-flex gl-align-items-center"
:title="$options.i18n.fileReviewTooltip"
:checked="reviewed"
@change="toggleReview"
diff --git a/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_area_chart.vue b/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_area_chart.vue
index c89e843b660..b4751d51fcb 100644
--- a/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_area_chart.vue
+++ b/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_area_chart.vue
@@ -1,4 +1,5 @@
<script>
+import { v4 as uuidv4 } from 'uuid';
import { GlAreaChart } from '@gitlab/ui/dist/charts';
import { CHART_CONTAINER_HEIGHT } from './constants';
@@ -17,6 +18,15 @@ export default {
required: true,
},
},
+ data: () => ({
+ chartKey: uuidv4(),
+ }),
+ watch: {
+ chartData() {
+ // Re-render area chart when the data changes
+ this.chartKey = uuidv4();
+ },
+ },
chartContainerHeight: CHART_CONTAINER_HEIGHT,
};
</script>
@@ -27,6 +37,7 @@ export default {
</p>
<gl-area-chart
v-bind="$attrs"
+ :key="chartKey"
responsive
width="auto"
:height="$options.chartContainerHeight"
diff --git a/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue b/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue
index 47b96934420..e5d82bad54f 100644
--- a/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue
+++ b/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue
@@ -39,11 +39,10 @@ export default {
</script>
<template>
<div>
- <segmented-control-button-group
- v-model="selectedChart"
- :options="chartRanges"
- class="gl-mb-4"
- />
+ <div class="gl-display-flex gl-flex-wrap-wrap gl-gap-5">
+ <segmented-control-button-group v-model="selectedChart" :options="chartRanges" />
+ <slot name="extend-button-group"></slot>
+ </div>
<ci-cd-analytics-area-chart
v-if="chart"
v-bind="$attrs"
diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb
index 8f482cf6e2f..bc6e67a3a7d 100644
--- a/app/controllers/profiles/two_factor_auths_controller.rb
+++ b/app/controllers/profiles/two_factor_auths_controller.rb
@@ -169,18 +169,6 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
gon.push(webauthn: { options: options, app_id: u2f_app_id })
end
- # Adds delete path to u2f registrations
- # to reduce logic in view template
- def u2f_registrations
- current_user.u2f_registrations.map do |u2f_registration|
- {
- name: u2f_registration.name,
- created_at: u2f_registration.created_at,
- delete_path: profile_u2f_registration_path(u2f_registration)
- }
- end
- end
-
def webauthn_registrations
current_user.webauthn_registrations.map do |webauthn_registration|
{
@@ -235,10 +223,6 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
@qr_code = build_qr_code
@account_string = account_string
- if Feature.enabled?(:webauthn)
- setup_webauthn_registration
- else
- setup_u2f_registration
- end
+ setup_webauthn_registration
end
end
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index 44c8c8f8f44..da15b393e6c 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -10,9 +10,6 @@ class ProfilesController < Profiles::ApplicationController
check_rate_limit!(:profile_update_username, scope: current_user)
end
skip_before_action :require_email, only: [:show, :update]
- before_action do
- push_frontend_feature_flag(:webauthn)
- end
feature_category :user_profile, [:show, :update, :reset_incoming_email_token, :reset_feed_token,
:reset_static_object_token, :update_username]
diff --git a/app/controllers/projects/blame_controller.rb b/app/controllers/projects/blame_controller.rb
index bd5701a3557..cc0d3818e33 100644
--- a/app/controllers/projects/blame_controller.rb
+++ b/app/controllers/projects/blame_controller.rb
@@ -20,28 +20,14 @@ class Projects::BlameController < Projects::ApplicationController
end
load_environment
-
- @blame_mode = Gitlab::Git::BlameMode.new(@commit.project, blame_params)
- blame_service = Projects::BlameService.new(@blob, @commit, @blame_mode, blame_params)
-
- @blame = Gitlab::View::Presenter::Factory.new(blame_service.blame, project: @project, path: @path, page: blame_service.page).fabricate!
-
- @blame_pagination = blame_service.pagination
-
- @blame_per_page = blame_service.per_page
-
- render locals: { total_extra_pages: blame_service.total_extra_pages }
+ load_blame
end
def page
@blob = @repository.blob_at(@commit.id, @path)
load_environment
-
- @blame_mode = Gitlab::Git::BlameMode.new(@commit.project, blame_params)
- blame_service = Projects::BlameService.new(@blob, @commit, @blame_mode, blame_params)
-
- @blame = Gitlab::View::Presenter::Factory.new(blame_service.blame, project: @project, path: @path, page: blame_service.page).fabricate!
+ load_blame
render partial: 'page'
end
@@ -54,6 +40,14 @@ class Projects::BlameController < Projects::ApplicationController
@environment = ::Environments::EnvironmentsByDeploymentsFinder.new(@project, current_user, environment_params).execute.last
end
+ def load_blame
+ @blame_mode = Gitlab::Git::BlameMode.new(@commit.project, blame_params)
+ @blame_pagination = Gitlab::Git::BlamePagination.new(@blob, @blame_mode, blame_params)
+
+ blame = Gitlab::Blame.new(@blob, @commit, range: @blame_pagination.blame_range)
+ @blame = Gitlab::View::Presenter::Factory.new(blame, project: @project, path: @path, page: @blame_pagination.page).fabricate!
+ end
+
def blame_params
params.permit(:page, :no_pagination, :streaming)
end
diff --git a/app/models/clusters/agents/authorizations/user_access/group_authorization.rb b/app/models/clusters/agents/authorizations/user_access/group_authorization.rb
new file mode 100644
index 00000000000..088ce1c0e27
--- /dev/null
+++ b/app/models/clusters/agents/authorizations/user_access/group_authorization.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Clusters
+ module Agents
+ module Authorizations
+ module UserAccess
+ class GroupAuthorization < ApplicationRecord
+ self.table_name = 'agent_user_access_group_authorizations'
+
+ belongs_to :agent, class_name: 'Clusters::Agent', optional: false
+ belongs_to :group, class_name: '::Group', optional: false
+
+ validates :config, json_schema: { filename: 'clusters_agents_authorizations_user_access_config' }
+
+ def config_project
+ agent.project
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/app/models/clusters/agents/authorizations/user_access/project_authorization.rb b/app/models/clusters/agents/authorizations/user_access/project_authorization.rb
new file mode 100644
index 00000000000..22b2c3fea22
--- /dev/null
+++ b/app/models/clusters/agents/authorizations/user_access/project_authorization.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Clusters
+ module Agents
+ module Authorizations
+ module UserAccess
+ class ProjectAuthorization < ApplicationRecord
+ self.table_name = 'agent_user_access_project_authorizations'
+
+ belongs_to :agent, class_name: 'Clusters::Agent', optional: false
+ belongs_to :project, class_name: '::Project', optional: false
+
+ validates :config, json_schema: { filename: 'clusters_agents_authorizations_user_access_config' }
+
+ def config_project
+ agent.project
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/app/models/members_preloader.rb b/app/models/members_preloader.rb
index f6617fa0888..1fef155e6ea 100644
--- a/app/models/members_preloader.rb
+++ b/app/models/members_preloader.rb
@@ -8,15 +8,12 @@ class MembersPreloader
end
def preload_all
- user_associations = [:status]
- user_associations << :webauthn_registrations if Feature.enabled?(:webauthn)
-
ActiveRecord::Associations::Preloader.new(
records: members,
associations: [
:source,
:created_by,
- { user: user_associations }
+ { user: [:status, :webauthn_registrations] }
]
).call
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 738e1eba982..16b4e604a9e 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1087,8 +1087,6 @@ class User < ApplicationRecord
end
def two_factor_webauthn_enabled?
- return false unless Feature.enabled?(:webauthn)
-
(webauthn_registrations.loaded? && webauthn_registrations.any?) || (!webauthn_registrations.loaded? && webauthn_registrations.exists?)
end
diff --git a/app/services/projects/blame_service.rb b/app/services/projects/blame_service.rb
deleted file mode 100644
index d4c01044828..00000000000
--- a/app/services/projects/blame_service.rb
+++ /dev/null
@@ -1,84 +0,0 @@
-# frozen_string_literal: true
-
-# Service class to correctly initialize Gitlab::Blame and Kaminari pagination
-# objects
-module Projects
- class BlameService
- PER_PAGE = 1000
- STREAMING_FIRST_PAGE_SIZE = 200
- STREAMING_PER_PAGE = 2000
-
- def initialize(blob, commit, blame_mode, params)
- @blob = blob
- @commit = commit
- @blame_mode = blame_mode
- @page = extract_page(params)
- @params = params
- end
-
- attr_reader :page
-
- def blame
- Gitlab::Blame.new(blob, commit, range: blame_range)
- end
-
- def pagination
- return unless blame_mode.pagination?
-
- Kaminari.paginate_array([], total_count: blob_lines_count, limit: per_page)
- .tap { |pagination| pagination.max_paginates_per(per_page) }
- .page(page)
- end
-
- def per_page
- blame_mode.streaming? ? STREAMING_PER_PAGE : PER_PAGE
- end
-
- def total_pages
- total = (blob_lines_count.to_f / per_page).ceil
- return total unless blame_mode.streaming?
-
- ([blob_lines_count - STREAMING_FIRST_PAGE_SIZE, 0].max.to_f / per_page).ceil + 1
- end
-
- def total_extra_pages
- [total_pages - 1, 0].max
- end
-
- private
-
- attr_reader :blob, :commit, :blame_mode
-
- def blame_range
- return if blame_mode.full?
-
- first_line = (page - 1) * per_page + 1
-
- if blame_mode.streaming?
- return 1..STREAMING_FIRST_PAGE_SIZE if page == 1
-
- first_line = STREAMING_FIRST_PAGE_SIZE + (page - 2) * per_page + 1
- end
-
- last_line = (first_line + per_page).to_i - 1
-
- first_line..last_line
- end
-
- def extract_page(params)
- page = params.fetch(:page, 1).to_i
-
- return 1 if page < 1 || overlimit?(page)
-
- page
- end
-
- def overlimit?(page)
- page > total_pages
- end
-
- def blob_lines_count
- @blob_lines_count ||= blob.data.lines.count
- end
- end
-end
diff --git a/app/services/users/approve_service.rb b/app/services/users/approve_service.rb
index 353456c545d..53ec37d0ff7 100644
--- a/app/services/users/approve_service.rb
+++ b/app/services/users/approve_service.rb
@@ -17,6 +17,11 @@ module Users
user.accept_pending_invitations! if user.active_for_authentication?
DeviseMailer.user_admin_approval(user).deliver_later
+ if user.created_by_id
+ reset_token = user.generate_reset_token
+ NotificationService.new.new_user(user, reset_token)
+ end
+
log_event(user)
after_approve_hook(user)
success(message: 'Success', http_status: :created)
diff --git a/app/validators/json_schemas/clusters_agents_authorizations_user_access_config.json b/app/validators/json_schemas/clusters_agents_authorizations_user_access_config.json
new file mode 100644
index 00000000000..75624af9e6a
--- /dev/null
+++ b/app/validators/json_schemas/clusters_agents_authorizations_user_access_config.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "description": "Cluster Agent configuration for an authorized project or group through user_access keyword",
+ "type": "object",
+ "additionalProperties": true
+}
diff --git a/app/views/projects/_files.html.haml b/app/views/projects/_files.html.haml
index 5e62d6c5421..161e7aef253 100644
--- a/app/views/projects/_files.html.haml
+++ b/app/views/projects/_files.html.haml
@@ -6,7 +6,7 @@
- if readme_path = @project.repository.readme_path
- add_page_startup_api_call project_blob_path(@project, tree_join(@ref, readme_path), viewer: "rich", format: "json")
-#tree-holder.tree-holder.clearfix.js-per-page{ data: { blame_per_page: Projects::BlameService::PER_PAGE } }
+#tree-holder.tree-holder.clearfix.js-per-page{ data: { blame_per_page: Gitlab::Git::BlamePagination::PAGINATION_PER_PAGE } }
.info-well.gl-display-none.gl-sm-display-flex.project-last-commit.gl-flex-direction-column.gl-mt-5
#js-last-commit.gl-m-auto
= gl_loading_icon(size: 'md')
diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml
index 689dcb3b2cb..a56d398d3a0 100644
--- a/app/views/projects/blame/show.html.haml
+++ b/app/views/projects/blame/show.html.haml
@@ -2,7 +2,7 @@
- add_page_specific_style 'page_bundles/tree'
- blame_streaming_url = blame_pages_streaming_url(@id, @project)
-- if @blame_mode.streaming? && total_extra_pages > 0
+- if @blame_mode.streaming? && @blame_pagination.total_extra_pages > 0
- content_for :startup_js do
= javascript_tag do
:plain
@@ -11,7 +11,7 @@
url.searchParams.set('page', 2);
return fetch(url).then(response => response.body);
})();
-- dataset = { testid: 'blob-content-holder', qa_selector: 'blame_file_content', per_page: @blame_per_page, total_extra_pages: total_extra_pages - 1, pages_url: blame_streaming_url }
+- dataset = { testid: 'blob-content-holder', qa_selector: 'blame_file_content', per_page: @blame_pagination.per_page, total_extra_pages: @blame_pagination.total_extra_pages - 1, pages_url: blame_streaming_url }
#blob-content-holder.tree-holder.js-per-page{ data: dataset }
= render "projects/blob/breadcrumb", blob: @blob, blame: true
@@ -53,5 +53,4 @@
= _('Loading full blame...')
- if @blame_mode.pagination?
- = paginate(@blame_pagination, theme: "gitlab")
-
+ = paginate(@blame_pagination.paginator, theme: "gitlab")
diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml
index d11bf36a610..1a751849b3a 100644
--- a/app/views/projects/blob/_blob.html.haml
+++ b/app/views/projects/blob/_blob.html.haml
@@ -16,7 +16,7 @@
- if project.forked?
#js-fork-info{ data: vue_fork_divergence_data(project, ref) }
-#blob-content-holder.blob-content-holder.js-per-page{ data: { blame_per_page: Projects::BlameService::PER_PAGE } }
+#blob-content-holder.blob-content-holder.js-per-page{ data: { blame_per_page: Gitlab::Git::BlamePagination::PAGINATION_PER_PAGE } }
- if @code_navigation_path
#js-code-navigation{ data: { code_navigation_path: @code_navigation_path, blob_path: blob.path, definition_path_prefix: project_blob_path(@project, @ref) } }
- if !expanded
diff --git a/config/feature_flags/development/ci_multi_doc_yaml.yml b/config/feature_flags/development/ci_multi_doc_yaml.yml
index 4e6289abefa..ec86ecd95c2 100644
--- a/config/feature_flags/development/ci_multi_doc_yaml.yml
+++ b/config/feature_flags/development/ci_multi_doc_yaml.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/388836
milestone: '15.9'
type: development
group: group::pipeline authoring
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/webauthn.yml b/config/feature_flags/development/webauthn.yml
deleted file mode 100644
index 6bd4fc95020..00000000000
--- a/config/feature_flags/development/webauthn.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: webauthn
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/26692
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/232671
-milestone: '13.4'
-type: development
-group: group::authentication and authorization
-default_enabled: true
diff --git a/data/deprecations/15-1-jira-github-enterprise-dvcs.yml b/data/deprecations/15-1-jira-github-enterprise-dvcs.yml
index 8c69dce35fe..34552360141 100644
--- a/data/deprecations/15-1-jira-github-enterprise-dvcs.yml
+++ b/data/deprecations/15-1-jira-github-enterprise-dvcs.yml
@@ -1,10 +1,11 @@
-- title: "Jira GitHub Enterprise DVCS integration" # The name of the feature to be deprecated
+- title: "Jira DVCS connector for Jira Cloud" # The name of the feature to be deprecated
announcement_milestone: "15.1" # The milestone when this feature was first announced as deprecated.
removal_milestone: "16.0" # The milestone when this feature is planned to be removed
breaking_change: true # If this deprecation is a breaking change, set this value to true
body: | # Do not modify this line, instead modify the lines below.
- The [Jira DVCS Connector](https://docs.gitlab.com/ee/integration/jira/dvcs/) (which enables the [Jira Development Panel](https://support.atlassian.com/jira-software-cloud/docs/view-development-information-for-an-issue/)), will no longer support Jira Cloud users starting with GitLab 16.0. The [GitLab for Jira App](https://docs.gitlab.com/ee/integration/jira/connect-app.html) has always been recommended for Jira Cloud users, and it will be required instead of the DVCS connector. If you are a Jira Cloud user, we recommended you begin migrating to the GitLab for Jira App.
- Any Jira Server and Jira Data Center users will need to confirm they are not using the GitHub Enterprise Connector to enable the GitLab DVCS integration, but they may continue to use the [native GitLab DVCS integration](https://docs.gitlab.com/ee/integration/jira/dvcs/) (supported in Jira 8.14 and later).
+ The [Jira DVCS connector](https://docs.gitlab.com/ee/integration/jira/dvcs/) for Jira Cloud has been deprecated and will be removed in GitLab 16.0. If you're using the Jira DVCS connector with Jira Cloud, migrate to the [GitLab for Jira Cloud app](https://docs.gitlab.com/ee/integration/jira/connect-app.html).
+
+ The Jira DVCS connector is also deprecated for Jira 8.13 and earlier. You can only use the Jira DVCS connector with Jira Server or Jira Data Center in Jira 8.14 and later.
# The following items are not published on the docs page, but may be used in the future.
stage: Manage # (optional - may be required in the future) String value of the stage that the feature was created in. e.g., Growth
tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
diff --git a/db/docs/agent_user_access_group_authorizations.yml b/db/docs/agent_user_access_group_authorizations.yml
new file mode 100644
index 00000000000..659b36bd61f
--- /dev/null
+++ b/db/docs/agent_user_access_group_authorizations.yml
@@ -0,0 +1,10 @@
+---
+table_name: agent_user_access_group_authorizations
+classes:
+- Clusters::Agents::Authorizations::UserAccess::GroupAuthorization
+feature_categories:
+- kubernetes_management
+description: Configuration for a group that is authorized to use a particular cluster agent through user_access keyword
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/116901
+milestone: '15.11'
+gitlab_schema: gitlab_main
diff --git a/db/docs/agent_user_access_project_authorizations.yml b/db/docs/agent_user_access_project_authorizations.yml
new file mode 100644
index 00000000000..0f0953da630
--- /dev/null
+++ b/db/docs/agent_user_access_project_authorizations.yml
@@ -0,0 +1,10 @@
+---
+table_name: agent_user_access_project_authorizations
+classes:
+- Clusters::Agents::Authorizations::UserAccess::ProjectAuthorization
+feature_categories:
+- kubernetes_management
+description: Configuration for a project that is authorized to use a particular cluster agent through user_access keyword
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/116901
+milestone: '15.11'
+gitlab_schema: gitlab_main
diff --git a/db/docs/batched_background_migrations/backfill_admin_mode_scope_for_personal_access_tokens.yml b/db/docs/batched_background_migrations/backfill_admin_mode_scope_for_personal_access_tokens.yml
new file mode 100644
index 00000000000..33f3371e294
--- /dev/null
+++ b/db/docs/batched_background_migrations/backfill_admin_mode_scope_for_personal_access_tokens.yml
@@ -0,0 +1,6 @@
+---
+migration_job_name: BackfillAdminModeScopeForPersonalAccessTokens
+description: backfills `admin_mode` scope to personal access tokens associated to administrators
+feature_category: system_access
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/107875
+milestone: 15.8
diff --git a/db/migrate/20230406150254_create_agent_user_access_project_authorizations_table.rb b/db/migrate/20230406150254_create_agent_user_access_project_authorizations_table.rb
new file mode 100644
index 00000000000..1adc3bb001a
--- /dev/null
+++ b/db/migrate/20230406150254_create_agent_user_access_project_authorizations_table.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class CreateAgentUserAccessProjectAuthorizationsTable < Gitlab::Database::Migration[2.1]
+ INDEX_NAME_1 = 'index_agent_user_access_on_project_id'
+ INDEX_NAME_2 = 'index_agent_user_access_on_agent_id_and_project_id'
+
+ def change
+ create_table :agent_user_access_project_authorizations do |t|
+ t.bigint :project_id, null: false
+ t.bigint :agent_id, null: false
+ t.jsonb :config, null: false
+
+ t.index [:project_id], name: INDEX_NAME_1
+ t.index [:agent_id, :project_id], unique: true, name: INDEX_NAME_2
+ end
+ end
+end
diff --git a/db/migrate/20230406150354_create_agent_user_access_group_authorizations_table.rb b/db/migrate/20230406150354_create_agent_user_access_group_authorizations_table.rb
new file mode 100644
index 00000000000..1d4df7d7330
--- /dev/null
+++ b/db/migrate/20230406150354_create_agent_user_access_group_authorizations_table.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class CreateAgentUserAccessGroupAuthorizationsTable < Gitlab::Database::Migration[2.1]
+ INDEX_NAME_1 = 'index_agent_user_access_on_group_id'
+ INDEX_NAME_2 = 'index_agent_user_access_on_agent_id_and_group_id'
+
+ def change
+ create_table :agent_user_access_group_authorizations do |t|
+ t.bigint :group_id, null: false
+ t.bigint :agent_id, null: false
+ t.jsonb :config, null: false
+
+ t.index [:group_id], name: INDEX_NAME_1
+ t.index [:agent_id, :group_id], unique: true, name: INDEX_NAME_2
+ end
+ end
+end
diff --git a/db/migrate/20230406150454_add_fks_to_agent_user_access_authorizations.rb b/db/migrate/20230406150454_add_fks_to_agent_user_access_authorizations.rb
new file mode 100644
index 00000000000..62f00620108
--- /dev/null
+++ b/db/migrate/20230406150454_add_fks_to_agent_user_access_authorizations.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+class AddFksToAgentUserAccessAuthorizations < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key :agent_user_access_project_authorizations, :projects,
+ column: :project_id, on_delete: :cascade
+ add_concurrent_foreign_key :agent_user_access_project_authorizations, :cluster_agents,
+ column: :agent_id, on_delete: :cascade
+ add_concurrent_foreign_key :agent_user_access_group_authorizations, :namespaces,
+ column: :group_id, on_delete: :cascade
+ add_concurrent_foreign_key :agent_user_access_group_authorizations, :cluster_agents,
+ column: :agent_id, on_delete: :cascade
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key_if_exists :agent_user_access_project_authorizations, column: :project_id
+ end
+
+ with_lock_retries do
+ remove_foreign_key_if_exists :agent_user_access_project_authorizations, column: :agent_id
+ end
+
+ with_lock_retries do
+ remove_foreign_key_if_exists :agent_user_access_group_authorizations, column: :group_id
+ end
+
+ with_lock_retries do
+ remove_foreign_key_if_exists :agent_user_access_group_authorizations, column: :agent_id
+ end
+ end
+end
diff --git a/db/post_migrate/20221228103133_queue_backfill_admin_mode_scope_for_personal_access_tokens.rb b/db/post_migrate/20221228103133_queue_backfill_admin_mode_scope_for_personal_access_tokens.rb
index c111d5090e1..577d55f4df6 100644
--- a/db/post_migrate/20221228103133_queue_backfill_admin_mode_scope_for_personal_access_tokens.rb
+++ b/db/post_migrate/20221228103133_queue_backfill_admin_mode_scope_for_personal_access_tokens.rb
@@ -1,21 +1,11 @@
# frozen_string_literal: true
class QueueBackfillAdminModeScopeForPersonalAccessTokens < Gitlab::Database::Migration[2.1]
- MIGRATION = 'BackfillAdminModeScopeForPersonalAccessTokens'
- DELAY_INTERVAL = 2.minutes
-
restrict_gitlab_migration gitlab_schema: :gitlab_main
- def up
- queue_batched_background_migration(
- MIGRATION,
- :personal_access_tokens,
- :id,
- job_interval: DELAY_INTERVAL
- )
- end
+ # no-op as the original migration is rescheduled
+ # in migrations version 20230406093640
+ def up; end
- def down
- delete_batched_background_migration(MIGRATION, :personal_access_tokens, :id, [])
- end
+ def down; end
end
diff --git a/db/post_migrate/20230406093640_requeue_backfill_admin_mode_scope_for_personal_access_tokens.rb b/db/post_migrate/20230406093640_requeue_backfill_admin_mode_scope_for_personal_access_tokens.rb
new file mode 100644
index 00000000000..17ba9edef22
--- /dev/null
+++ b/db/post_migrate/20230406093640_requeue_backfill_admin_mode_scope_for_personal_access_tokens.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+class RequeueBackfillAdminModeScopeForPersonalAccessTokens < Gitlab::Database::Migration[2.1]
+ MIGRATION = 'BackfillAdminModeScopeForPersonalAccessTokens'
+ DELAY_INTERVAL = 2.minutes
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ def up
+ delete_batched_background_migration(MIGRATION, :personal_access_tokens, :id, [])
+
+ queue_batched_background_migration(
+ MIGRATION,
+ :personal_access_tokens,
+ :id,
+ job_interval: DELAY_INTERVAL
+ )
+ end
+
+ def down
+ delete_batched_background_migration(MIGRATION, :personal_access_tokens, :id, [])
+ end
+end
diff --git a/db/schema_migrations/20230406093640 b/db/schema_migrations/20230406093640
new file mode 100644
index 00000000000..3bc9003b2fa
--- /dev/null
+++ b/db/schema_migrations/20230406093640
@@ -0,0 +1 @@
+a49416e1b59ffb29bf2015c96e6bdf92428036862102fbbfa63284cc1da53c82 \ No newline at end of file
diff --git a/db/schema_migrations/20230406150254 b/db/schema_migrations/20230406150254
new file mode 100644
index 00000000000..3e3463a76f9
--- /dev/null
+++ b/db/schema_migrations/20230406150254
@@ -0,0 +1 @@
+2b8aea677f295a0ab8f5ca9fbe7162156a06de89bd30ab5b252eb4460bcc7a2e \ No newline at end of file
diff --git a/db/schema_migrations/20230406150354 b/db/schema_migrations/20230406150354
new file mode 100644
index 00000000000..484af1e53ad
--- /dev/null
+++ b/db/schema_migrations/20230406150354
@@ -0,0 +1 @@
+2f1ef88ab1731b20821a86a74006ed0856d3c7baa5e197f72410aedb15cb2894 \ No newline at end of file
diff --git a/db/schema_migrations/20230406150454 b/db/schema_migrations/20230406150454
new file mode 100644
index 00000000000..f7237bd5ef2
--- /dev/null
+++ b/db/schema_migrations/20230406150454
@@ -0,0 +1 @@
+9966f807ce21016777a87d437355241cd8e5cacf2ccd143258ef0446e6f26e93 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 26d2a9c5787..600cac16e96 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -10858,6 +10858,38 @@ CREATE SEQUENCE agent_project_authorizations_id_seq
ALTER SEQUENCE agent_project_authorizations_id_seq OWNED BY agent_project_authorizations.id;
+CREATE TABLE agent_user_access_group_authorizations (
+ id bigint NOT NULL,
+ group_id bigint NOT NULL,
+ agent_id bigint NOT NULL,
+ config jsonb NOT NULL
+);
+
+CREATE SEQUENCE agent_user_access_group_authorizations_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE agent_user_access_group_authorizations_id_seq OWNED BY agent_user_access_group_authorizations.id;
+
+CREATE TABLE agent_user_access_project_authorizations (
+ id bigint NOT NULL,
+ project_id bigint NOT NULL,
+ agent_id bigint NOT NULL,
+ config jsonb NOT NULL
+);
+
+CREATE SEQUENCE agent_user_access_project_authorizations_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE agent_user_access_project_authorizations_id_seq OWNED BY agent_user_access_project_authorizations.id;
+
CREATE TABLE alert_management_alert_assignees (
id bigint NOT NULL,
user_id bigint NOT NULL,
@@ -24534,6 +24566,10 @@ ALTER TABLE ONLY agent_group_authorizations ALTER COLUMN id SET DEFAULT nextval(
ALTER TABLE ONLY agent_project_authorizations ALTER COLUMN id SET DEFAULT nextval('agent_project_authorizations_id_seq'::regclass);
+ALTER TABLE ONLY agent_user_access_group_authorizations ALTER COLUMN id SET DEFAULT nextval('agent_user_access_group_authorizations_id_seq'::regclass);
+
+ALTER TABLE ONLY agent_user_access_project_authorizations ALTER COLUMN id SET DEFAULT nextval('agent_user_access_project_authorizations_id_seq'::regclass);
+
ALTER TABLE ONLY alert_management_alert_assignees ALTER COLUMN id SET DEFAULT nextval('alert_management_alert_assignees_id_seq'::regclass);
ALTER TABLE ONLY alert_management_alert_metric_images ALTER COLUMN id SET DEFAULT nextval('alert_management_alert_metric_images_id_seq'::regclass);
@@ -26234,6 +26270,12 @@ ALTER TABLE ONLY agent_group_authorizations
ALTER TABLE ONLY agent_project_authorizations
ADD CONSTRAINT agent_project_authorizations_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY agent_user_access_group_authorizations
+ ADD CONSTRAINT agent_user_access_group_authorizations_pkey PRIMARY KEY (id);
+
+ALTER TABLE ONLY agent_user_access_project_authorizations
+ ADD CONSTRAINT agent_user_access_project_authorizations_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY alert_management_alert_assignees
ADD CONSTRAINT alert_management_alert_assignees_pkey PRIMARY KEY (id);
@@ -29409,6 +29451,14 @@ CREATE UNIQUE INDEX index_agent_project_authorizations_on_agent_id_and_project_i
CREATE INDEX index_agent_project_authorizations_on_project_id ON agent_project_authorizations USING btree (project_id);
+CREATE UNIQUE INDEX index_agent_user_access_on_agent_id_and_group_id ON agent_user_access_group_authorizations USING btree (agent_id, group_id);
+
+CREATE UNIQUE INDEX index_agent_user_access_on_agent_id_and_project_id ON agent_user_access_project_authorizations USING btree (agent_id, project_id);
+
+CREATE INDEX index_agent_user_access_on_group_id ON agent_user_access_group_authorizations USING btree (group_id);
+
+CREATE INDEX index_agent_user_access_on_project_id ON agent_user_access_project_authorizations USING btree (project_id);
+
CREATE INDEX index_alert_assignees_on_alert_id ON alert_management_alert_assignees USING btree (alert_id);
CREATE UNIQUE INDEX index_alert_assignees_on_user_id_and_alert_id ON alert_management_alert_assignees USING btree (user_id, alert_id);
@@ -34320,6 +34370,9 @@ ALTER TABLE ONLY epics
ALTER TABLE ONLY environments
ADD CONSTRAINT fk_01a033a308 FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE SET NULL;
+ALTER TABLE ONLY agent_user_access_project_authorizations
+ ADD CONSTRAINT fk_0250c0ad51 FOREIGN KEY (agent_id) REFERENCES cluster_agents(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY incident_management_escalation_rules
ADD CONSTRAINT fk_0314ee86eb FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
@@ -34617,6 +34670,9 @@ ALTER TABLE ONLY alert_management_alerts
ALTER TABLE ONLY path_locks
ADD CONSTRAINT fk_5265c98f24 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY agent_user_access_group_authorizations
+ ADD CONSTRAINT fk_53fd98ccbf FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY clusters_applications_prometheus
ADD CONSTRAINT fk_557e773639 FOREIGN KEY (cluster_id) REFERENCES clusters(id) ON DELETE CASCADE;
@@ -34737,6 +34793,9 @@ ALTER TABLE ONLY vulnerabilities
ALTER TABLE ONLY oauth_openid_requests
ADD CONSTRAINT fk_77114b3b09 FOREIGN KEY (access_grant_id) REFERENCES oauth_access_grants(id) ON DELETE CASCADE;
+ALTER TABLE ONLY agent_user_access_project_authorizations
+ ADD CONSTRAINT fk_78034b05d8 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY users
ADD CONSTRAINT fk_789cd90b35 FOREIGN KEY (accepted_term_id) REFERENCES application_setting_terms(id) ON DELETE CASCADE;
@@ -34854,6 +34913,9 @@ ALTER TABLE ONLY boards_epic_list_user_preferences
ALTER TABLE ONLY issues
ADD CONSTRAINT fk_96b1dd429c FOREIGN KEY (milestone_id) REFERENCES milestones(id) ON DELETE SET NULL;
+ALTER TABLE ONLY agent_user_access_group_authorizations
+ ADD CONSTRAINT fk_97ce8e8284 FOREIGN KEY (agent_id) REFERENCES cluster_agents(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY vulnerability_occurrences
ADD CONSTRAINT fk_97ffe77653 FOREIGN KEY (vulnerability_id) REFERENCES vulnerabilities(id) ON DELETE SET NULL;
diff --git a/doc/administration/auth/oidc.md b/doc/administration/auth/oidc.md
index 359f53c5a2e..afc2f74201a 100644
--- a/doc/administration/auth/oidc.md
+++ b/doc/administration/auth/oidc.md
@@ -40,7 +40,7 @@ The OpenID Connect provides you with a client's details and secret for you to us
```ruby
gitlab_rails['omniauth_providers'] = [
{
- name: "openid_connect",
+ name: "openid_connect", # do not change this parameter
label: "Provider name", # optional label for login button, defaults to "Openid Connect"
icon: "<custom_provider_icon>",
args: {
@@ -66,7 +66,7 @@ The OpenID Connect provides you with a client's details and secret for you to us
For installation from source:
```yaml
- - { name: 'openid_connect',
+ - { name: 'openid_connect', # do not change this parameter
label: 'Provider name', # optional label for login button, defaults to "Openid Connect"
icon: '<custom_provider_icon>',
args: {
@@ -165,7 +165,7 @@ for more details:
```ruby
gitlab_rails['omniauth_providers'] = [
{
- name: "openid_connect",
+ name: "openid_connect", # do not change this parameter
label: "Google OpenID", # optional label for login button, defaults to "Openid Connect"
args: {
name: "openid_connect",
@@ -203,7 +203,7 @@ Example Omnibus configuration block:
```ruby
gitlab_rails['omniauth_providers'] = [
{
- name: "openid_connect",
+ name: "openid_connect", # do not change this parameter
label: "Azure OIDC", # optional label for login button, defaults to "Openid Connect"
args: {
name: "openid_connect",
@@ -335,7 +335,7 @@ but `LocalAccounts` authenticates against local Active Directory accounts. Befor
```ruby
gitlab_rails['omniauth_providers'] = [
{
- name: "openid_connect",
+ name: "openid_connect", # do not change this parameter
label: "Azure B2C OIDC", # optional label for login button, defaults to "Openid Connect"
args: {
name: "openid_connect",
@@ -395,7 +395,7 @@ Example Omnibus configuration block:
```ruby
gitlab_rails['omniauth_providers'] = [
{
- name: "openid_connect",
+ name: "openid_connect", # do not change this parameter
label: "Keycloak", # optional label for login button, defaults to "Openid Connect"
args: {
name: "openid_connect",
@@ -475,7 +475,7 @@ To use symmetric key encryption:
```ruby
gitlab_rails['omniauth_providers'] = [
{
- name: "openid_connect",
+ name: "openid_connect", # do not change this parameter
label: "Keycloak", # optional label for login button, defaults to "Openid Connect"
args: {
name: "openid_connect",
@@ -519,7 +519,7 @@ Example Omnibus GitLab configuration (file path: `/etc/gitlab/gitlab.rb`):
```ruby
gitlab_rails['omniauth_providers'] = [
{
- name: "openid_connect",
+ name: "openid_connect", # do not change this parameter
label: "Casdoor", # optional label for login button, defaults to "Openid Connect"
args: {
name: "openid_connect",
@@ -542,7 +542,7 @@ gitlab_rails['omniauth_providers'] = [
Example installations from source configuration (file path: `config/gitlab.yml`):
```yaml
- - { name: 'openid_connect',
+ - { name: 'openid_connect', # do not change this parameter
label: 'Casdoor', # optional label for login button, defaults to "Openid Connect"
args: {
name: 'openid_connect',
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index dd3da0251d8..61b7f623660 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -26609,6 +26609,7 @@ Attributes for defining a CI/CD variable.
| <a id="complianceviolationinputmergedafter"></a>`mergedAfter` | [`Date`](#date) | Merge requests merged after this date (inclusive). |
| <a id="complianceviolationinputmergedbefore"></a>`mergedBefore` | [`Date`](#date) | Merge requests merged before this date (inclusive). |
| <a id="complianceviolationinputprojectids"></a>`projectIds` | [`[ProjectID!]`](#projectid) | Filter compliance violations by project. |
+| <a id="complianceviolationinputtargetbranch"></a>`targetBranch` | [`String`](#string) | Filter compliance violations by target branch. |
### `DastProfileCadenceInput`
diff --git a/doc/ci/jobs/ci_job_token.md b/doc/ci/jobs/ci_job_token.md
index 40c6a0c1435..2c5e309348c 100644
--- a/doc/ci/jobs/ci_job_token.md
+++ b/doc/ci/jobs/ci_job_token.md
@@ -12,7 +12,7 @@ When a pipeline job is about to run, GitLab generates a unique token and injects
You can use a GitLab CI/CD job token to authenticate with specific API endpoints:
- Packages:
- - [Package Registry](../../user/packages/package_registry/index.md#use-gitlab-cicd-to-build-packages).
+ - [Package Registry](../../user/packages/package_registry/index.md#to-build-packages).
- [Packages API](../../api/packages.md) (project-level).
- [Container Registry](../../user/packages/container_registry/build_and_push_images.md#use-gitlab-cicd)
(the `$CI_REGISTRY_PASSWORD` is `$CI_JOB_TOKEN`).
@@ -76,6 +76,10 @@ be accessed unless projects are explicitly authorized.
There is a proposal to add more strategic control of the access permissions,
see [epic 3559](https://gitlab.com/groups/gitlab-org/-/epics/3559).
+NOTE:
+Because `CI_REGISTRY_TOKEN` uses `CI_JOB_TOKEN` to authenticate, the access configuration
+also applies to `CI_REGISTRY_TOKEN`.
+
### Allow access to your project with a job token
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/346298/) in GitLab 15.9. [Deployed behind the `:inbound_ci_scoped_job_token` feature flag](../../user/feature_flags.md), enabled by default.
diff --git a/doc/ci/yaml/includes.md b/doc/ci/yaml/includes.md
index 252cef0aa97..94148400896 100644
--- a/doc/ci/yaml/includes.md
+++ b/doc/ci/yaml/includes.md
@@ -5,7 +5,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
type: reference
---
-# GitLab CI/CD include examples **(FREE)**
+# Use CI/CD configuration from other files **(FREE)**
You can use [`include`](index.md#include) to include external YAML files in your CI/CD jobs.
@@ -503,6 +503,88 @@ When the pipeline runs, GitLab:
include: 'configs/**/*.yml'
```
+## Define inputs for configuration added with `include` (Beta)
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/391331) in GitLab 15.11 as a Beta feature.
+
+FLAG:
+`spec` and `with` are experimental [Open Beta features](../../policy/alpha-beta-support.md#beta)
+and subject to change without notice.
+
+### Define input parameters with `spec:inputs`
+
+Use `spec:inputs` to define input parameters for CI/CD configuration intended to be added
+to a pipeline with `include`. Use [`include:with`](#set-input-parameter-values-with-includewith)
+to define the values to use when the pipeline runs.
+
+The specs must be declared at the top of the configuration file, in a header section.
+Separate the header from the rest of the configuration with `---`.
+
+Use the interpolation format `$[[ input.input-id ]]` to reference the values outside of the header section.
+The inputs are evaluated and interpolated once, when the configuration is fetched
+during pipeline creation, but before the configuration is merged with the contents of the `.gitlab-ci.yml`.
+
+```yaml
+spec:
+ inputs:
+ environment:
+ job-stage:
+---
+
+scan-website:
+ stage: $[[ inputs.job-stage ]]
+ script: ./scan-website $[[ inputs.environment ]]
+```
+
+When using `spec:inputs`:
+
+- Defined inputs are mandatory by default.
+- Inputs can be made optional by specifying a `default`. Use `default: null` to have no default value.
+- A string containing an interpolation block must not exceed 1 MB.
+- The string inside an interpolation block must not exceed 1 KB.
+
+For example, a `custom_configuration.yml`:
+
+```yaml
+spec:
+ inputs:
+ website:
+ user:
+ default: 'test-user'
+ flags:
+ default: null
+---
+
+# The pipeline configuration would follow...
+```
+
+In this example:
+
+- `website` is mandatory and must be defined.
+- `user` is optional. If not defined, the value is `test-user`.
+- `flags` is optional. If not defined, it has no value.
+
+### Set input parameter values with `include:with`
+
+Use `include:with` to set the values for the parameters when the included configuration
+is added to the pipeline.
+
+For example, to include a `custom_configuration.yml` that has the same specs
+as the [example above](#define-input-parameters-with-specinputs):
+
+```yaml
+include:
+ - local: 'custom_configuration.yml'
+ with:
+ website: "My website"
+```
+
+In this example:
+
+- `website` has a value of `My website` for the included configuration.
+- `user` has a value of `test-user`, because that is the default when not specified.
+- `flags` has no value, because it is optional and has no default when not specified.
+
## Troubleshooting
### `Maximum of 150 nested includes are allowed!` error
diff --git a/doc/development/testing_guide/best_practices.md b/doc/development/testing_guide/best_practices.md
index cd4dc6b21e4..2af9d12a251 100644
--- a/doc/development/testing_guide/best_practices.md
+++ b/doc/development/testing_guide/best_practices.md
@@ -315,6 +315,27 @@ NOTE:
WARNING:
`stub_method` is supposed to be used in factories only. It's strongly discouraged to be used elsewhere. Please consider using [RSpec mocks](https://rspec.info/features/3-12/rspec-mocks/) if available.
+#### Stubbing member access level
+
+To stub [member access level](../../user/permissions.md#roles) for factory stubs like `Project` or `Group` use
+[`stub_member_access_level`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/support/stub_member_access_level.rb):
+
+```ruby
+let(:project) { build_stubbed(:project) }
+let(:maintainer) { build_stubbed(:user) }
+let(:policy) { ProjectPolicy.new(maintainer, project) }
+
+it 'allows admin_project ability' do
+ stub_member_access_level(project, maintainer: maintainer)
+
+ expect(policy).to be_allowed(:admin_project)
+end
+```
+
+NOTE:
+Refrain from using this stub helper if the test code relies on persisting
+`project_authorizations` or `Member` records. Use `Project#add_member` or `Group#add_member` instead.
+
#### Identify slow tests
Running a spec with profiling is a good way to start optimizing a spec. This can
diff --git a/doc/operations/incident_management/incident_timeline_events.md b/doc/operations/incident_management/incident_timeline_events.md
index fdbb4844bb7..d509061eca0 100644
--- a/doc/operations/incident_management/incident_timeline_events.md
+++ b/doc/operations/incident_management/incident_timeline_events.md
@@ -83,6 +83,17 @@ of an incident.
![Incident timeline event for severity change](img/timeline_event_for_severity_change_v15_6.png)
+### When labels change (Experiment)
+
+> [Introduced]([issue-link](https://gitlab.com/gitlab-org/gitlab/-/issues/365489)) in GitLab 15.3 [with a flag](../../administration/feature_flags.md) named `incident_timeline_events_from_labels`. Disabled by default.
+
+FLAG:
+On self-managed GitLab, by default this feature is not available. To make it available per project or for your entire instance, ask an administrator to [enable the feature flag](../../administration/feature_flags.md) named `incident_timeline_events_from_labels`.
+On GitLab.com, this feature is not available.
+This feature is not ready for production use.
+
+A new timeline event is created when someone adds or removes [labels](../../user/project/labels.md) on an incident.
+
## Delete an event
> Ability to delete an event when editing it [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/372265) in GitLab 15.7.
diff --git a/doc/update/deprecations.md b/doc/update/deprecations.md
index 40f482c9fb9..d38e7f59f89 100644
--- a/doc/update/deprecations.md
+++ b/doc/update/deprecations.md
@@ -1043,15 +1043,16 @@ To be prepared for this change, you should do the following before GitLab 16.0:
<div class="deprecation breaking-change" data-milestone="16.0">
-### Jira GitHub Enterprise DVCS integration
+### Jira DVCS connector for Jira Cloud
<div class="deprecation-notes">
- Announced in: GitLab <span class="milestone">15.1</span>
- [Breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/)
</div>
-The [Jira DVCS Connector](https://docs.gitlab.com/ee/integration/jira/dvcs/) (which enables the [Jira Development Panel](https://support.atlassian.com/jira-software-cloud/docs/view-development-information-for-an-issue/)), will no longer support Jira Cloud users starting with GitLab 16.0. The [GitLab for Jira App](https://docs.gitlab.com/ee/integration/jira/connect-app.html) has always been recommended for Jira Cloud users, and it will be required instead of the DVCS connector. If you are a Jira Cloud user, we recommended you begin migrating to the GitLab for Jira App.
-Any Jira Server and Jira Data Center users will need to confirm they are not using the GitHub Enterprise Connector to enable the GitLab DVCS integration, but they may continue to use the [native GitLab DVCS integration](https://docs.gitlab.com/ee/integration/jira/dvcs/) (supported in Jira 8.14 and later).
+The [Jira DVCS connector](https://docs.gitlab.com/ee/integration/jira/dvcs/) for Jira Cloud has been deprecated and will be removed in GitLab 16.0. If you're using the Jira DVCS connector with Jira Cloud, migrate to the [GitLab for Jira Cloud app](https://docs.gitlab.com/ee/integration/jira/connect-app.html).
+
+The Jira DVCS connector is also deprecated for Jira 8.13 and earlier. You can only use the Jira DVCS connector with Jira Server or Jira Data Center in Jira 8.14 and later.
</div>
diff --git a/doc/user/compliance/license_scanning_of_cyclonedx_files/index.md b/doc/user/compliance/license_scanning_of_cyclonedx_files/index.md
index e2b2fc16707..ce0c6451688 100644
--- a/doc/user/compliance/license_scanning_of_cyclonedx_files/index.md
+++ b/doc/user/compliance/license_scanning_of_cyclonedx_files/index.md
@@ -8,8 +8,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# License scanning of CycloneDX files **(ULTIMATE)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/384932) in GitLab 15.9 for GitLab SaaS [with two flags](../../../administration/feature_flags.md) named `license_scanning_sbom_scanner` and `package_metadata_synchronization`. Both flags are disabled by default and both flags must be enabled for this feature to work.
-> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/385173) in GitLab 15.10.
+> - [Enabled](https://gitlab.com/gitlab-org/gitlab/-/issues/385173) in GitLab 15.10 for GitLab SaaS.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/385173) in GitLab 15.10 for self-managed GitLab [with two flags](../../../administration/feature_flags.md) named `license_scanning_sbom_scanner` and `package_metadata_synchronization`, both of which must be enabled for this feature to work. The flags are disabled by default due to the initial performance load when the license database is first imported. Work to improve performance is being tracked in [issue 397670](https://gitlab.com/gitlab-org/gitlab/-/issues/397670).
+> - [Enabled](https://gitlab.com/gitlab-org/gitlab/-/issues/385173) in GitLab 15.11 for self-managed GitLab.
To detect the licenses in use, License Compliance relies on running the
[Dependency Scanning CI Jobs](../../application_security/dependency_scanning/index.md),
diff --git a/doc/user/packages/package_registry/index.md b/doc/user/packages/package_registry/index.md
index cd60a229479..eb3fd082d33 100644
--- a/doc/user/packages/package_registry/index.md
+++ b/doc/user/packages/package_registry/index.md
@@ -71,9 +71,13 @@ NOTE:
If you have not activated the "Package registry" feature for your project at **Settings > General > Visibility, project features, permissions**, you receive a 403 Forbidden response.
Accessing package registry via deploy token is not available when external authorization is enabled.
-## Use GitLab CI/CD to build packages
+## Use GitLab CI/CD
+
+You can use [GitLab CI/CD](../../../ci/index.md) to build or import packages into
+a package registry.
+
+### To build packages
-You can use [GitLab CI/CD](../../../ci/index.md) to build packages.
For Maven, NuGet, npm, Conan, Helm, and PyPI packages, and Composer dependencies, you can
authenticate with GitLab by using the `CI_JOB_TOKEN`.
@@ -97,6 +101,16 @@ when you view the package details:
You can view which pipeline published the package, and the commit and user who triggered it. However, the history is limited to five updates of a given package.
+### To import packages
+
+If you already have packages built in a different registry, you can import them
+into your GitLab package registry with the [Packages Importer](https://gitlab.com/gitlab-org/ci-cd/package-stage/pkgs_importer)
+
+The Packages Importer runs a CI/CD pipeline that [can import these package types](https://gitlab.com/gitlab-org/ci-cd/package-stage/pkgs_importer#formats-supported):
+
+- NPM
+- NuGet
+
## Reduce storage usage
For information on reducing your storage use for the Package Registry, see
diff --git a/lib/gitlab/background_migration/backfill_admin_mode_scope_for_personal_access_tokens.rb b/lib/gitlab/background_migration/backfill_admin_mode_scope_for_personal_access_tokens.rb
index 6f5ddec628d..2127ce5975d 100644
--- a/lib/gitlab/background_migration/backfill_admin_mode_scope_for_personal_access_tokens.rb
+++ b/lib/gitlab/background_migration/backfill_admin_mode_scope_for_personal_access_tokens.rb
@@ -19,7 +19,11 @@ module Gitlab
def perform
each_sub_batch do |sub_batch|
sub_batch.each do |token|
- token.update!(scopes: (YAML.safe_load(token.scopes) + ADMIN_MODE_SCOPE).uniq.to_yaml)
+ existing_scopes = YAML.safe_load(token.scopes, permitted_classes: [Symbol])
+ # making sure scopes are not mixed symbols and strings
+ stringified_scopes = existing_scopes.map(&:to_s)
+
+ token.update!(scopes: (stringified_scopes + ADMIN_MODE_SCOPE).uniq.to_yaml)
end
end
end
diff --git a/lib/gitlab/git/blame_pagination.rb b/lib/gitlab/git/blame_pagination.rb
new file mode 100644
index 00000000000..6bf29859b14
--- /dev/null
+++ b/lib/gitlab/git/blame_pagination.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Git
+ class BlamePagination
+ include Gitlab::Utils::StrongMemoize
+
+ PAGINATION_PER_PAGE = 1000
+ STREAMING_FIRST_PAGE_SIZE = 200
+ STREAMING_PER_PAGE = 2000
+
+ def initialize(blob, blame_mode, params)
+ @blob = blob
+ @blame_mode = blame_mode
+ @params = params
+ end
+
+ def page
+ page = params.fetch(:page, 1).to_i
+
+ return 1 if page < 1
+
+ page
+ end
+ strong_memoize_attr :page
+
+ def per_page
+ blame_mode.streaming? ? STREAMING_PER_PAGE : PAGINATION_PER_PAGE
+ end
+ strong_memoize_attr :per_page
+
+ def total_pages
+ total = (blob_lines_count.to_f / per_page).ceil
+ return total unless blame_mode.streaming?
+
+ ([blob_lines_count - STREAMING_FIRST_PAGE_SIZE, 0].max.to_f / per_page).ceil + 1
+ end
+ strong_memoize_attr :total_pages
+
+ def total_extra_pages
+ [total_pages - 1, 0].max
+ end
+ strong_memoize_attr :total_extra_pages
+
+ def paginator
+ return if blame_mode.streaming? || blame_mode.full?
+
+ Kaminari.paginate_array([], total_count: blob_lines_count, limit: per_page)
+ .tap { |pagination| pagination.max_paginates_per(per_page) }
+ .page(page)
+ end
+
+ def blame_range
+ return if blame_mode.full?
+
+ first_line = ((page - 1) * per_page) + 1
+
+ if blame_mode.streaming?
+ return 1..STREAMING_FIRST_PAGE_SIZE if page == 1
+
+ first_line = STREAMING_FIRST_PAGE_SIZE + ((page - 2) * per_page) + 1
+ end
+
+ last_line = (first_line + per_page).to_i - 1
+
+ first_line..last_line
+ end
+
+ private
+
+ attr_reader :blob, :blame_mode, :params
+
+ def blob_lines_count
+ @blob_lines_count ||= blob.data.lines.count
+ end
+ end
+ end
+end
diff --git a/lib/sidebars/groups/super_sidebar_menus/secure_menu.rb b/lib/sidebars/groups/super_sidebar_menus/secure_menu.rb
index bc4c69c461a..c79e7e379d0 100644
--- a/lib/sidebars/groups/super_sidebar_menus/secure_menu.rb
+++ b/lib/sidebars/groups/super_sidebar_menus/secure_menu.rb
@@ -17,9 +17,9 @@ module Sidebars
override :configure_menu_items
def configure_menu_items
[
- :audit_events,
:security_dashboard,
:vulnerability_report,
+ :audit_events,
:compliance,
:scan_policies
].each { |id| add_item(::Sidebars::NilMenuItem.new(item_id: id)) }
diff --git a/lib/sidebars/projects/menus/packages_registries_menu.rb b/lib/sidebars/projects/menus/packages_registries_menu.rb
index 980f13d81bd..31a1aa56ab5 100644
--- a/lib/sidebars/projects/menus/packages_registries_menu.rb
+++ b/lib/sidebars/projects/menus/packages_registries_menu.rb
@@ -10,6 +10,7 @@ module Sidebars
add_item(container_registry_menu_item)
add_item(infrastructure_registry_menu_item)
add_item(harbor_registry_menu_item)
+ add_item(model_experiments_menu_item)
true
end
@@ -89,6 +90,20 @@ module Sidebars
)
end
+ def model_experiments_menu_item
+ if Feature.disabled?(:ml_experiment_tracking, context.project)
+ return ::Sidebars::NilMenuItem.new(item_id: :model_experiments)
+ end
+
+ ::Sidebars::MenuItem.new(
+ title: _('Model experiments'),
+ link: project_ml_experiments_path(context.project),
+ super_sidebar_parent: Sidebars::Projects::SuperSidebarMenus::AnalyzeMenu,
+ active_routes: { controller: %w[projects/ml/experiments projects/ml/candidates] },
+ item_id: :model_experiments
+ )
+ end
+
def packages_registry_disabled?
!::Gitlab.config.packages.enabled ||
!can?(context.current_user, :read_package, context.project&.packages_policy_subject)
diff --git a/lib/sidebars/projects/super_sidebar_menus/analyze_menu.rb b/lib/sidebars/projects/super_sidebar_menus/analyze_menu.rb
index 2d1b4c0c495..78a988fffaf 100644
--- a/lib/sidebars/projects/super_sidebar_menus/analyze_menu.rb
+++ b/lib/sidebars/projects/super_sidebar_menus/analyze_menu.rb
@@ -25,7 +25,8 @@ module Sidebars
:code_review,
:merge_requests,
:issues,
- :insights
+ :insights,
+ :model_experiments
].each { |id| add_item(::Sidebars::NilMenuItem.new(item_id: id)) }
end
end
diff --git a/lib/sidebars/projects/super_sidebar_menus/secure_menu.rb b/lib/sidebars/projects/super_sidebar_menus/secure_menu.rb
index 8639e3dbb1d..2ca53ba0ce9 100644
--- a/lib/sidebars/projects/super_sidebar_menus/secure_menu.rb
+++ b/lib/sidebars/projects/super_sidebar_menus/secure_menu.rb
@@ -18,13 +18,13 @@ module Sidebars
def configure_menu_items
[
:discover_project_security,
- :audit_events,
:dashboard,
:vulnerability_report,
- :on_demand_scans,
- :scan_policies,
:dependency_list,
:license_compliance,
+ :audit_events,
+ :scan_policies,
+ :on_demand_scans,
:configuration
].each { |id| add_item(::Sidebars::NilMenuItem.new(item_id: id)) }
end
diff --git a/lib/tasks/gitlab/db.rake b/lib/tasks/gitlab/db.rake
index a58eea0d880..963fe23c682 100644
--- a/lib/tasks/gitlab/db.rake
+++ b/lib/tasks/gitlab/db.rake
@@ -109,9 +109,11 @@ namespace :gitlab do
load_database = connection.tables.count <= 1
if load_database
+ puts "Running db:schema:load#{database_name} rake task"
Gitlab::Database.add_post_migrate_path_to_rails(force: true)
Rake::Task["db:schema:load#{database_name}"].invoke
else
+ puts "Running db:migrate#{database_name} rake task"
Rake::Task["db:migrate#{database_name}"].invoke
end
diff --git a/lib/tasks/gitlab/tw/codeowners.rake b/lib/tasks/gitlab/tw/codeowners.rake
index a2417128c48..9d99625140e 100644
--- a/lib/tasks/gitlab/tw/codeowners.rake
+++ b/lib/tasks/gitlab/tw/codeowners.rake
@@ -22,6 +22,7 @@ namespace :tw do
# CodeOwnerRule.new('Activation', ''),
# CodeOwnerRule.new('Acquisition', ''),
# CodeOwnerRule.new('AI Assisted', ''),
+ CodeOwnerRule.new('Analytics Instrumentation', '@lciutacu'),
CodeOwnerRule.new('Anti-Abuse', '@phillipwells'),
CodeOwnerRule.new('Application Performance', '@jglassman1'),
CodeOwnerRule.new('Authentication and Authorization', '@jglassman1'),
@@ -62,7 +63,6 @@ namespace :tw do
CodeOwnerRule.new('Pipeline Execution', '@drcatherinepope'),
CodeOwnerRule.new('Pipeline Security', '@marcel.amirault'),
CodeOwnerRule.new('Product Analytics', '@lciutacu'),
- CodeOwnerRule.new('Product Intelligence', '@lciutacu'),
CodeOwnerRule.new('Product Planning', '@msedlakjakubowski'),
CodeOwnerRule.new('Project Management', '@msedlakjakubowski'),
CodeOwnerRule.new('Provision', '@fneill'),
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index f3b9376b213..2324724a30c 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -13084,9 +13084,18 @@ msgstr ""
msgid "DNS"
msgstr ""
+msgid "DORA4Metrics|Accept testing terms"
+msgstr ""
+
+msgid "DORA4Metrics|Accept testing terms of use?"
+msgstr ""
+
msgid "DORA4Metrics|Average (last %{days}d)"
msgstr ""
+msgid "DORA4Metrics|By enabling this feature, you accept the %{url}"
+msgstr ""
+
msgid "DORA4Metrics|Change Failure Rate"
msgstr ""
@@ -13120,6 +13129,9 @@ msgstr ""
msgid "DORA4Metrics|Failed to load charts"
msgstr ""
+msgid "DORA4Metrics|Forecast"
+msgstr ""
+
msgid "DORA4Metrics|Go to docs"
msgstr ""
@@ -13162,9 +13174,6 @@ msgstr ""
msgid "DORA4Metrics|No merge requests were deployed during this period"
msgstr ""
-msgid "DORA4Metrics|Number of deployments"
-msgstr ""
-
msgid "DORA4Metrics|Number of incidents divided by the number of deployments to a production environment in the given time period."
msgstr ""
@@ -13174,6 +13183,12 @@ msgstr ""
msgid "DORA4Metrics|Percentage of failed deployments"
msgstr ""
+msgid "DORA4Metrics|Predicted number of deployments"
+msgstr ""
+
+msgid "DORA4Metrics|Show forecast"
+msgstr ""
+
msgid "DORA4Metrics|Something went wrong while getting change failure rate data."
msgstr ""
@@ -17457,6 +17472,9 @@ msgstr ""
msgid "Expected documents: %{expected_documents}"
msgstr ""
+msgid "Experiment"
+msgstr ""
+
msgid "Experiments"
msgstr ""
@@ -28355,6 +28373,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Model experiments"
+msgstr ""
+
msgid "Modified"
msgstr ""
diff --git a/spec/factories/clusters/agents/authorizations/user_access/group_authorizations.rb b/spec/factories/clusters/agents/authorizations/user_access/group_authorizations.rb
new file mode 100644
index 00000000000..203aadbd741
--- /dev/null
+++ b/spec/factories/clusters/agents/authorizations/user_access/group_authorizations.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :agent_user_access_group_authorization,
+ class: 'Clusters::Agents::Authorizations::UserAccess::GroupAuthorization' do
+ association :agent, factory: :cluster_agent
+ config { {} }
+ group
+ end
+end
diff --git a/spec/factories/clusters/agents/authorizations/user_access/project_authorizations.rb b/spec/factories/clusters/agents/authorizations/user_access/project_authorizations.rb
new file mode 100644
index 00000000000..8171607f578
--- /dev/null
+++ b/spec/factories/clusters/agents/authorizations/user_access/project_authorizations.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :agent_user_access_project_authorization,
+ class: 'Clusters::Agents::Authorizations::UserAccess::ProjectAuthorization' do
+ association :agent, factory: :cluster_agent
+ config { {} }
+ project
+ end
+end
diff --git a/spec/features/admin/users/users_spec.rb b/spec/features/admin/users/users_spec.rb
index 95f25d5c23d..8e80ce5edd9 100644
--- a/spec/features/admin/users/users_spec.rb
+++ b/spec/features/admin/users/users_spec.rb
@@ -311,6 +311,40 @@ RSpec.describe 'Admin::Users', feature_category: :user_management do
end
end
+ describe 'users pending approval' do
+ it 'sends a welcome email and a password reset email to the user upon admin approval', :sidekiq_inline do
+ user = create(:user, :blocked_pending_approval, created_by_id: current_user.id)
+
+ visit admin_users_path
+
+ click_link 'Pending approval'
+
+ click_user_dropdown_toggle(user.id)
+
+ find('[data-testid="approve"]').click
+
+ expect(page).to have_content("Approve user #{user.name}?")
+
+ within_modal do
+ perform_enqueued_jobs do
+ click_button 'Approve'
+ end
+ end
+
+ expect(page).to have_content('Successfully approved')
+
+ welcome_email = ActionMailer::Base.deliveries.find { |m| m.subject == 'Welcome to GitLab!' }
+ expect(welcome_email.to).to eq([user.email])
+ expect(welcome_email.text_part.body).to have_content('Your GitLab account request has been approved!')
+
+ password_reset_email = ActionMailer::Base.deliveries.find { |m| m.subject == 'Account was created for you' }
+ expect(password_reset_email.to).to eq([user.email])
+ expect(password_reset_email.text_part.body).to have_content('Click here to set your password')
+
+ expect(ActionMailer::Base.deliveries.count).to eq(2)
+ end
+ end
+
describe 'internal users' do
context 'when showing a `Ghost User`' do
let_it_be(:ghost_user) { create(:user, :ghost) }
diff --git a/spec/features/projects/blobs/blame_spec.rb b/spec/features/projects/blobs/blame_spec.rb
index d3558af81b8..6f5bf8ac26e 100644
--- a/spec/features/projects/blobs/blame_spec.rb
+++ b/spec/features/projects/blobs/blame_spec.rb
@@ -44,7 +44,7 @@ RSpec.describe 'File blame', :js, feature_category: :projects do
context 'when blob length is over the blame range limit' do
before do
- stub_const('Projects::BlameService::PER_PAGE', 2)
+ stub_const('Gitlab::Git::BlamePagination::PAGINATION_PER_PAGE', 2)
end
it 'displays two first lines of the file with pagination' do
@@ -112,7 +112,7 @@ RSpec.describe 'File blame', :js, feature_category: :projects do
context 'when streaming is enabled' do
before do
- stub_const('Projects::BlameService::STREAMING_PER_PAGE', 50)
+ stub_const('Gitlab::Git::BlamePagination::STREAMING_PER_PAGE', 50)
end
it_behaves_like 'a full blame page'
@@ -143,7 +143,7 @@ RSpec.describe 'File blame', :js, feature_category: :projects do
context 'when blob length is over global max page limit' do
before do
- stub_const('Projects::BlameService::PER_PAGE', 200)
+ stub_const('Gitlab::Git::BlamePagination::PAGINATION_PER_PAGE', 200)
end
let(:path) { 'files/markdown/ruby-style-guide.md' }
diff --git a/spec/features/projects/navbar_spec.rb b/spec/features/projects/navbar_spec.rb
index 521f633e0e7..31f4e9dcf95 100644
--- a/spec/features/projects/navbar_spec.rb
+++ b/spec/features/projects/navbar_spec.rb
@@ -19,6 +19,7 @@ RSpec.describe 'Project navbar', :with_license, feature_category: :projects do
stub_config(registry: { enabled: false })
stub_feature_flags(harbor_registry_integration: false)
+ stub_feature_flags(ml_experiment_tracking: false)
insert_package_nav(_('Deployments'))
insert_infrastructure_registry_nav
insert_infrastructure_google_cloud_nav
@@ -98,4 +99,16 @@ RSpec.describe 'Project navbar', :with_license, feature_category: :projects do
it_behaves_like 'verified navigation bar'
end
+
+ context 'when models experiments is available' do
+ before do
+ stub_feature_flags(ml_experiment_tracking: true)
+
+ insert_model_experiments_nav(_('Terraform modules'))
+
+ visit project_path(project)
+ end
+
+ it_behaves_like 'verified navigation bar'
+ end
end
diff --git a/spec/lib/gitlab/background_migration/backfill_admin_mode_scope_for_personal_access_tokens_spec.rb b/spec/lib/gitlab/background_migration/backfill_admin_mode_scope_for_personal_access_tokens_spec.rb
index d2da6867773..92fec48454c 100644
--- a/spec/lib/gitlab/background_migration/backfill_admin_mode_scope_for_personal_access_tokens_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_admin_mode_scope_for_personal_access_tokens_spec.rb
@@ -24,8 +24,12 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillAdminModeScopeForPersonalAcc
personal_access_tokens.create!(name: 'admin 4', user_id: admin.id, scopes: "---\n- admin_mode\n")
end
- let!(:pat_admin_2) { personal_access_tokens.create!(name: 'admin 5', user_id: admin.id, scopes: "---\n- read_api\n") }
- let!(:pat_not_in_range) { personal_access_tokens.create!(name: 'admin 6', user_id: admin.id, scopes: "---\n- api\n") }
+ let!(:pat_with_symbol_in_scopes) do
+ personal_access_tokens.create!(name: 'admin 5', user_id: admin.id, scopes: "---\n- :api\n")
+ end
+
+ let!(:pat_admin_2) { personal_access_tokens.create!(name: 'admin 6', user_id: admin.id, scopes: "---\n- read_api\n") }
+ let!(:pat_not_in_range) { personal_access_tokens.create!(name: 'admin 7', user_id: admin.id, scopes: "---\n- api\n") }
subject do
described_class.new(
@@ -47,6 +51,7 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillAdminModeScopeForPersonalAcc
expect(pat_revoked.reload.scopes).to eq("---\n- api\n")
expect(pat_expired.reload.scopes).to eq("---\n- api\n")
expect(pat_admin_mode.reload.scopes).to eq("---\n- admin_mode\n")
+ expect(pat_with_symbol_in_scopes.reload.scopes).to eq("---\n- api\n- admin_mode\n")
expect(pat_admin_2.reload.scopes).to eq("---\n- read_api\n- admin_mode\n")
expect(pat_not_in_range.reload.scopes).to eq("---\n- api\n")
end
diff --git a/spec/lib/gitlab/git/blame_pagination_spec.rb b/spec/lib/gitlab/git/blame_pagination_spec.rb
new file mode 100644
index 00000000000..1f3c0c0342e
--- /dev/null
+++ b/spec/lib/gitlab/git/blame_pagination_spec.rb
@@ -0,0 +1,175 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Git::BlamePagination, feature_category: :source_code_management do
+ subject(:blame_pagination) { described_class.new(blob, blame_mode, params) }
+
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:commit) { project.repository.commit }
+ let_it_be(:blob) { project.repository.blob_at('HEAD', 'README.md') }
+
+ let(:blame_mode) do
+ instance_double(
+ 'Gitlab::Git::BlameMode',
+ 'streaming?' => streaming_mode,
+ 'full?' => full_mode
+ )
+ end
+
+ let(:params) { { page: page } }
+ let(:page) { 1 }
+ let(:streaming_mode) { false }
+ let(:full_mode) { false }
+
+ using RSpec::Parameterized::TableSyntax
+
+ describe '#page' do
+ subject { blame_pagination.page }
+
+ where(:page, :expected_page) do
+ nil | 1
+ 1 | 1
+ 5 | 5
+ -1 | 1
+ 'a' | 1
+ end
+
+ with_them do
+ it { is_expected.to eq(expected_page) }
+ end
+ end
+
+ describe '#per_page' do
+ subject { blame_pagination.per_page }
+
+ it { is_expected.to eq(described_class::PAGINATION_PER_PAGE) }
+
+ context 'when blame mode is streaming' do
+ let(:streaming_mode) { true }
+
+ it { is_expected.to eq(described_class::STREAMING_PER_PAGE) }
+ end
+ end
+
+ describe '#total_pages' do
+ subject { blame_pagination.total_pages }
+
+ before do
+ stub_const("#{described_class.name}::PAGINATION_PER_PAGE", 2)
+ end
+
+ it { is_expected.to eq(2) }
+ end
+
+ describe '#total_extra_pages' do
+ subject { blame_pagination.total_extra_pages }
+
+ before do
+ stub_const("#{described_class.name}::PAGINATION_PER_PAGE", 2)
+ end
+
+ it { is_expected.to eq(1) }
+ end
+
+ describe '#pagination' do
+ subject { blame_pagination.paginator }
+
+ before do
+ stub_const("#{described_class.name}::PAGINATION_PER_PAGE", 2)
+ end
+
+ it 'returns a pagination object' do
+ is_expected.to be_kind_of(Kaminari::PaginatableArray)
+
+ expect(subject.current_page).to eq(1)
+ expect(subject.total_pages).to eq(2)
+ expect(subject.total_count).to eq(4)
+ end
+
+ context 'when user disabled the pagination' do
+ let(:full_mode) { true }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when user chose streaming' do
+ let(:streaming_mode) { true }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when per_page is above the global max per page limit' do
+ before do
+ stub_const("#{described_class.name}::PAGINATION_PER_PAGE", 1000)
+ allow(blob).to receive_message_chain(:data, :lines, :count) { 500 }
+ end
+
+ it 'returns a correct pagination object' do
+ is_expected.to be_kind_of(Kaminari::PaginatableArray)
+
+ expect(subject.current_page).to eq(1)
+ expect(subject.total_pages).to eq(1)
+ expect(subject.total_count).to eq(500)
+ end
+ end
+
+ describe 'Pagination attributes' do
+ where(:page, :current_page, :total_pages) do
+ 1 | 1 | 2
+ 2 | 2 | 2
+ 0 | 1 | 2 # Incorrect
+ end
+
+ with_them do
+ it 'returns the correct pagination attributes' do
+ expect(subject.current_page).to eq(current_page)
+ expect(subject.total_pages).to eq(total_pages)
+ end
+ end
+ end
+ end
+
+ describe '#blame_range' do
+ subject { blame_pagination.blame_range }
+
+ before do
+ stub_const("#{described_class.name}::PAGINATION_PER_PAGE", 2)
+ end
+
+ where(:page, :expected_range) do
+ 1 | (1..2)
+ 2 | (3..4)
+ 0 | (1..2)
+ end
+
+ with_them do
+ it { is_expected.to eq(expected_range) }
+ end
+
+ context 'when user disabled the pagination' do
+ let(:full_mode) { true }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when streaming is enabled' do
+ let(:streaming_mode) { true }
+
+ before do
+ stub_const("#{described_class.name}::STREAMING_FIRST_PAGE_SIZE", 1)
+ stub_const("#{described_class.name}::STREAMING_PER_PAGE", 1)
+ end
+
+ where(:page, :expected_range) do
+ 1 | (1..1)
+ 2 | (2..2)
+ 0 | (1..1)
+ end
+
+ with_them do
+ it { is_expected.to eq(expected_range) }
+ end
+ end
+ end
+end
diff --git a/spec/lib/sidebars/groups/super_sidebar_menus/secure_menu_spec.rb b/spec/lib/sidebars/groups/super_sidebar_menus/secure_menu_spec.rb
index 3640a4d8655..9eb81dda462 100644
--- a/spec/lib/sidebars/groups/super_sidebar_menus/secure_menu_spec.rb
+++ b/spec/lib/sidebars/groups/super_sidebar_menus/secure_menu_spec.rb
@@ -15,9 +15,9 @@ RSpec.describe Sidebars::Groups::SuperSidebarMenus::SecureMenu, feature_category
it 'defines list of NilMenuItem placeholders' do
expect(items.map(&:class).uniq).to eq([Sidebars::NilMenuItem])
expect(items.map(&:item_id)).to eq([
- :audit_events,
:security_dashboard,
:vulnerability_report,
+ :audit_events,
:compliance,
:scan_policies
])
diff --git a/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb b/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb
index 6d7698db743..860206dc6af 100644
--- a/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb
@@ -39,7 +39,7 @@ RSpec.describe Sidebars::Projects::Menus::PackagesRegistriesMenu, feature_catego
before do
stub_container_registry_config(enabled: registry_enabled)
stub_config(packages: { enabled: packages_enabled })
- stub_feature_flags(harbor_registry_integration: false)
+ stub_feature_flags(harbor_registry_integration: false, ml_experiment_tracking: false)
end
context 'when Packages Registry is visible' do
@@ -181,5 +181,25 @@ RSpec.describe Sidebars::Projects::Menus::PackagesRegistriesMenu, feature_catego
end
end
end
+
+ describe 'Model experiments' do
+ let(:item_id) { :model_experiments }
+
+ context 'when :ml_experiment_tracking is enabled' do
+ it 'shows the menu item' do
+ stub_feature_flags(ml_experiment_tracking: true)
+
+ is_expected.not_to be_nil
+ end
+ end
+
+ context 'when :ml_experiment_tracking is disabled' do
+ it 'does not show the menu item' do
+ stub_feature_flags(ml_experiment_tracking: false)
+
+ is_expected.to be_nil
+ end
+ end
+ end
end
end
diff --git a/spec/lib/sidebars/projects/super_sidebar_menus/analyze_menu_spec.rb b/spec/lib/sidebars/projects/super_sidebar_menus/analyze_menu_spec.rb
index b8d74665042..8f07241d2e2 100644
--- a/spec/lib/sidebars/projects/super_sidebar_menus/analyze_menu_spec.rb
+++ b/spec/lib/sidebars/projects/super_sidebar_menus/analyze_menu_spec.rb
@@ -23,7 +23,8 @@ RSpec.describe Sidebars::Projects::SuperSidebarMenus::AnalyzeMenu, feature_categ
:code_review,
:merge_requests,
:issues,
- :insights
+ :insights,
+ :model_experiments
])
end
end
diff --git a/spec/lib/sidebars/projects/super_sidebar_menus/secure_menu_spec.rb b/spec/lib/sidebars/projects/super_sidebar_menus/secure_menu_spec.rb
index b68b33941c3..74ef761332e 100644
--- a/spec/lib/sidebars/projects/super_sidebar_menus/secure_menu_spec.rb
+++ b/spec/lib/sidebars/projects/super_sidebar_menus/secure_menu_spec.rb
@@ -16,13 +16,13 @@ RSpec.describe Sidebars::Projects::SuperSidebarMenus::SecureMenu, feature_catego
expect(items.map(&:class).uniq).to eq([Sidebars::NilMenuItem])
expect(items.map(&:item_id)).to eq([
:discover_project_security,
- :audit_events,
:dashboard,
:vulnerability_report,
- :on_demand_scans,
- :scan_policies,
:dependency_list,
:license_compliance,
+ :audit_events,
+ :scan_policies,
+ :on_demand_scans,
:configuration
])
end
diff --git a/spec/migrations/queue_backfill_admin_mode_scope_for_personal_access_tokens_spec.rb b/spec/migrations/queue_backfill_admin_mode_scope_for_personal_access_tokens_spec.rb
deleted file mode 100644
index 068da23113d..00000000000
--- a/spec/migrations/queue_backfill_admin_mode_scope_for_personal_access_tokens_spec.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-require_migration!
-
-RSpec.describe QueueBackfillAdminModeScopeForPersonalAccessTokens,
- feature_category: :system_access do
- describe '#up' do
- it 'schedules background migration' do
- migrate!
-
- expect(described_class::MIGRATION).to have_scheduled_batched_migration(
- table_name: :personal_access_tokens,
- column_name: :id,
- interval: described_class::DELAY_INTERVAL)
- end
- end
-end
diff --git a/spec/migrations/requeue_backfill_admin_mode_scope_for_personal_access_tokens_spec.rb b/spec/migrations/requeue_backfill_admin_mode_scope_for_personal_access_tokens_spec.rb
new file mode 100644
index 00000000000..b9af6d98beb
--- /dev/null
+++ b/spec/migrations/requeue_backfill_admin_mode_scope_for_personal_access_tokens_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe RequeueBackfillAdminModeScopeForPersonalAccessTokens, feature_category: :system_access do
+ describe '#up' do
+ it 'schedules background migration' do
+ migrate!
+
+ expect(described_class::MIGRATION).to(
+ have_scheduled_batched_migration(
+ table_name: :personal_access_tokens,
+ column_name: :id,
+ interval: described_class::DELAY_INTERVAL)
+ )
+ end
+ end
+end
diff --git a/spec/models/clusters/agents/authorizations/user_access/group_authorization_spec.rb b/spec/models/clusters/agents/authorizations/user_access/group_authorization_spec.rb
new file mode 100644
index 00000000000..d7b4ea2388f
--- /dev/null
+++ b/spec/models/clusters/agents/authorizations/user_access/group_authorization_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Clusters::Agents::Authorizations::UserAccess::GroupAuthorization, feature_category: :kubernetes_management do
+ it { is_expected.to belong_to(:agent).class_name('Clusters::Agent').required }
+ it { is_expected.to belong_to(:group).class_name('::Group').required }
+
+ it { expect(described_class).to validate_jsonb_schema(['config']) }
+
+ describe '#config_project' do
+ let(:record) { create(:agent_user_access_group_authorization) }
+
+ it { expect(record.config_project).to eq(record.agent.project) }
+ end
+end
diff --git a/spec/models/clusters/agents/authorizations/user_access/project_authorization_spec.rb b/spec/models/clusters/agents/authorizations/user_access/project_authorization_spec.rb
new file mode 100644
index 00000000000..a51be15a9d7
--- /dev/null
+++ b/spec/models/clusters/agents/authorizations/user_access/project_authorization_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Clusters::Agents::Authorizations::UserAccess::ProjectAuthorization, feature_category: :kubernetes_management do
+ it { is_expected.to belong_to(:agent).class_name('Clusters::Agent').required }
+ it { is_expected.to belong_to(:project).class_name('Project').required }
+
+ it { expect(described_class).to validate_jsonb_schema(['config']) }
+
+ describe '#config_project' do
+ let(:record) { create(:agent_user_access_project_authorization) }
+
+ it { expect(record.config_project).to eq(record.agent.project) }
+ end
+end
diff --git a/spec/presenters/issue_email_participant_presenter_spec.rb b/spec/presenters/issue_email_participant_presenter_spec.rb
index c270fae3058..993cc9c235b 100644
--- a/spec/presenters/issue_email_participant_presenter_spec.rb
+++ b/spec/presenters/issue_email_participant_presenter_spec.rb
@@ -3,54 +3,49 @@
require 'spec_helper'
RSpec.describe IssueEmailParticipantPresenter, feature_category: :service_desk do
- # See https://gitlab.com/gitlab-org/gitlab/-/issues/389247
- # for details around build_stubbed for access level
- let_it_be(:non_member) { create(:user) } # rubocop:todo RSpec/FactoryBot/AvoidCreate
- let_it_be(:guest) { create(:user) } # rubocop:todo RSpec/FactoryBot/AvoidCreate
- let_it_be(:reporter) { create(:user) } # rubocop:todo RSpec/FactoryBot/AvoidCreate
- let_it_be(:developer) { create(:user) } # rubocop:todo RSpec/FactoryBot/AvoidCreate
- let_it_be(:group) { create(:group) } # rubocop:todo RSpec/FactoryBot/AvoidCreate
- let_it_be(:project) { create(:project, group: group) } # rubocop:todo RSpec/FactoryBot/AvoidCreate
- let_it_be(:issue) { build_stubbed(:issue, project: project) }
- let_it_be(:participant) { build_stubbed(:issue_email_participant, issue: issue, email: 'any@email.com') }
-
- let(:user) { nil }
- let(:presenter) { described_class.new(participant, current_user: user) }
+ let(:user) { build_stubbed(:user) }
+ let(:project) { build_stubbed(:project) }
+ let(:issue) { build_stubbed(:issue, project: project) }
+ let(:participant) { build_stubbed(:issue_email_participant, issue: issue, email: 'any@example.com') }
let(:obfuscated_email) { 'an*****@e*****.c**' }
- let(:email) { 'any@email.com' }
+ let(:email) { 'any@example.com' }
- before_all do
- group.add_guest(guest)
- group.add_reporter(reporter)
- group.add_developer(developer)
- end
+ subject(:presenter) { described_class.new(participant, current_user: user) }
describe '#email' do
subject { presenter.email }
- it { is_expected.to eq(obfuscated_email) }
+ context 'when anonymous' do
+ let(:user) { nil }
+
+ it { is_expected.to eq(obfuscated_email) }
+ end
context 'with signed in user' do
+ before do
+ stub_member_access_level(project, access_level => user) if access_level
+ end
+
context 'when user has no role in project' do
- let(:user) { non_member }
+ let(:access_level) { nil }
it { is_expected.to eq(obfuscated_email) }
end
context 'when user has guest role in project' do
- let(:user) { guest }
+ let(:access_level) { :guest }
it { is_expected.to eq(obfuscated_email) }
end
context 'when user has reporter role in project' do
- let(:user) { reporter }
+ let(:access_level) { :reporter }
it { is_expected.to eq(email) }
end
context 'when user has developer role in project' do
- let(:user) { developer }
+ let(:access_level) { :developer }
it { is_expected.to eq(email) }
end
diff --git a/spec/presenters/project_clusterable_presenter_spec.rb b/spec/presenters/project_clusterable_presenter_spec.rb
index dfe4a191ae5..4727bce02a5 100644
--- a/spec/presenters/project_clusterable_presenter_spec.rb
+++ b/spec/presenters/project_clusterable_presenter_spec.rb
@@ -2,15 +2,15 @@
require 'spec_helper'
-RSpec.describe ProjectClusterablePresenter do
+RSpec.describe ProjectClusterablePresenter, feature_category: :environment_management do
include Gitlab::Routing.url_helpers
let(:presenter) { described_class.new(project) }
- let(:project) { create(:project) }
- let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
+ let(:project) { build_stubbed(:project) }
+ let(:cluster) { build_stubbed(:cluster, :provided_by_gcp, projects: [project]) }
describe '#can_create_cluster?' do
- let(:user) { create(:user) }
+ let(:user) { build_stubbed(:user) }
subject { presenter.can_create_cluster? }
@@ -20,7 +20,7 @@ RSpec.describe ProjectClusterablePresenter do
context 'when user can create' do
before do
- project.add_maintainer(user)
+ stub_member_access_level(project, maintainer: user)
end
it { is_expected.to be_truthy }
diff --git a/spec/services/projects/blame_service_spec.rb b/spec/services/projects/blame_service_spec.rb
deleted file mode 100644
index 7f7f48cf622..00000000000
--- a/spec/services/projects/blame_service_spec.rb
+++ /dev/null
@@ -1,132 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Projects::BlameService, :aggregate_failures, feature_category: :source_code_management do
- subject(:service) { described_class.new(blob, commit, blame_mode, params) }
-
- let_it_be(:project) { create(:project, :repository) }
- let_it_be(:commit) { project.repository.commit }
- let_it_be(:blob) { project.repository.blob_at('HEAD', 'README.md') }
-
- let(:blame_mode) { Gitlab::Git::BlameMode.new(project, params) }
- let(:params) { { page: page } }
- let(:page) { nil }
-
- before do
- stub_const("#{described_class.name}::PER_PAGE", 2)
- end
-
- describe '#blame' do
- subject { service.blame }
-
- it 'returns a correct Gitlab::Blame object' do
- is_expected.to be_kind_of(Gitlab::Blame)
-
- expect(subject.blob).to eq(blob)
- expect(subject.commit).to eq(commit)
- expect(subject.range).to eq(1..2)
- end
-
- describe 'Pagination range calculation' do
- subject { service.blame.range }
-
- context 'with page = 1' do
- let(:page) { 1 }
-
- it { is_expected.to eq(1..2) }
- end
-
- context 'with page = 2' do
- let(:page) { 2 }
-
- it { is_expected.to eq(3..4) }
- end
-
- context 'with page = 3 (overlimit)' do
- let(:page) { 3 }
-
- it { is_expected.to eq(1..2) }
- end
-
- context 'with page = 0 (incorrect)' do
- let(:page) { 0 }
-
- it { is_expected.to eq(1..2) }
- end
-
- context 'when user disabled the pagination' do
- let(:params) { super().merge(no_pagination: 1) }
-
- it { is_expected.to be_nil }
- end
-
- context 'when feature flag disabled' do
- before do
- stub_feature_flags(blame_page_pagination: false)
- end
-
- it { is_expected.to be_nil }
- end
- end
- end
-
- describe '#pagination' do
- subject { service.pagination }
-
- it 'returns a pagination object' do
- is_expected.to be_kind_of(Kaminari::PaginatableArray)
-
- expect(subject.current_page).to eq(1)
- expect(subject.total_pages).to eq(2)
- expect(subject.total_count).to eq(4)
- end
-
- context 'when user disabled the pagination' do
- let(:params) { super().merge(no_pagination: 1) }
-
- it { is_expected.to be_nil }
- end
-
- context 'when feature flag disabled' do
- before do
- stub_feature_flags(blame_page_pagination: false)
- end
-
- it { is_expected.to be_nil }
- end
-
- context 'when per_page is above the global max per page limit' do
- before do
- stub_const("#{described_class.name}::PER_PAGE", 1000)
- allow(blob).to receive_message_chain(:data, :lines, :count) { 500 }
- end
-
- it 'returns a correct pagination object' do
- is_expected.to be_kind_of(Kaminari::PaginatableArray)
-
- expect(subject.current_page).to eq(1)
- expect(subject.total_pages).to eq(1)
- expect(subject.total_count).to eq(500)
- end
- end
-
- describe 'Pagination attributes' do
- using RSpec::Parameterized::TableSyntax
-
- where(:page, :current_page, :total_pages) do
- 1 | 1 | 2
- 2 | 2 | 2
- 3 | 1 | 2 # Overlimit
- 0 | 1 | 2 # Incorrect
- end
-
- with_them do
- it 'returns the correct pagination attributes' do
- expect(subject.current_page).to eq(current_page)
- expect(subject.total_pages).to eq(total_pages)
- end
- end
- end
- end
-end
diff --git a/spec/services/users/approve_service_spec.rb b/spec/services/users/approve_service_spec.rb
index 1b063a9ad1c..09379857c38 100644
--- a/spec/services/users/approve_service_spec.rb
+++ b/spec/services/users/approve_service_spec.rb
@@ -75,6 +75,24 @@ RSpec.describe Users::ApproveService, feature_category: :user_management do
expect { subject }.to have_enqueued_mail(DeviseMailer, :user_admin_approval)
end
+ context 'when the user was created via sign up' do
+ it 'does not send a password reset email' do
+ expect { subject }.not_to have_enqueued_mail(Notify, :new_user_email)
+ end
+ end
+
+ context 'when the user was created by an admin' do
+ let(:user) { create(:user, :blocked_pending_approval, created_by_id: current_user.id) }
+
+ it 'sends a password reset email' do
+ allow(user).to receive(:generate_reset_token).and_return(:reset_token)
+
+ expect(Notify).to receive(:new_user_email).with(user.id, :reset_token).and_call_original
+
+ expect { subject }.to have_enqueued_mail(Notify, :new_user_email)
+ end
+ end
+
context 'email confirmation status' do
context 'user is unconfirmed' do
let(:user) { create(:user, :blocked_pending_approval, :unconfirmed) }
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 42c97d03d21..c3bddf1a6ae 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -172,6 +172,7 @@ RSpec.configure do |config|
config.include RailsHelpers
config.include SidekiqMiddleware
config.include StubActionCableConnection, type: :channel
+ config.include StubMemberAccessLevel
config.include StubSpamServices
config.include SnowplowHelpers
config.include RenderedHelpers
diff --git a/spec/support/helpers/navbar_structure_helper.rb b/spec/support/helpers/navbar_structure_helper.rb
index 15c4b679786..67ea00c6551 100644
--- a/spec/support/helpers/navbar_structure_helper.rb
+++ b/spec/support/helpers/navbar_structure_helper.rb
@@ -114,6 +114,14 @@ module NavbarStructureHelper
)
end
+ def insert_model_experiments_nav(within)
+ insert_after_sub_nav_item(
+ within,
+ within: _('Packages and registries'),
+ new_sub_nav_item_name: _('Model experiments')
+ )
+ end
+
def project_analytics_sub_nav_item
[
_('Value stream'),
diff --git a/spec/support/stub_member_access_level.rb b/spec/support/stub_member_access_level.rb
new file mode 100644
index 00000000000..62e932ee1fc
--- /dev/null
+++ b/spec/support/stub_member_access_level.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module StubMemberAccessLevel
+ # Stubs access level of a member of +object+.
+ #
+ # The following types are supported:
+ # * `Project` - stubs `project.team.max_member_access(user.id)`
+ # * `Group` - stubs `group.max_member_access_for_user(user)`
+ #
+ # @example
+ #
+ # stub_member_access_level(project, maintainer: user)
+ # project.team.max_member_access(user.id) # => Gitlab::Access::MAINTAINER
+ #
+ # stub_member_access_level(group, developer: user)
+ # group.max_member_access_for_user(user) # => Gitlab::Access::DEVELOPER
+ #
+ # stub_member_access_level(project, reporter: user, guest: [guest1, guest2])
+ # project.team.max_member_access(user.id) # => Gitlab::Access::REPORTER
+ # project.team.max_member_access(guests.first.id) # => Gitlab::Access::GUEST
+ # project.team.max_member_access(guests.last.id) # => Gitlab::Access::GUEST
+ #
+ # @param object [Project, Group] Object to be stubbed.
+ # @param access_levels [Hash<Symbol, User>, Hash<Symbol, [User]>] Map of access level to users
+ def stub_member_access_level(object, **access_levels)
+ expectation = case object
+ when Project
+ ->(user) { expect(object.team).to receive(:max_member_access).with(user.id) }
+ when Group
+ ->(user) { expect(object).to receive(:max_member_access_for_user).with(user) }
+ else
+ raise ArgumentError,
+ "Stubbing member access level unsupported for #{object.inspect} (#{object.class})"
+ end
+
+ access_levels.each do |access_level, users|
+ access_level = Gitlab::Access.sym_options_with_owner.fetch(access_level) do
+ raise ArgumentError, "Invalid access level #{access_level.inspect}"
+ end
+
+ Array(users).each do |user|
+ expectation.call(user).at_least(1).times.and_return(access_level)
+ end
+ end
+ end
+end
diff --git a/spec/support_specs/stub_member_access_level_spec.rb b/spec/support_specs/stub_member_access_level_spec.rb
new file mode 100644
index 00000000000..c76bd2ee417
--- /dev/null
+++ b/spec/support_specs/stub_member_access_level_spec.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+require_relative '../support/stub_member_access_level'
+
+RSpec.describe StubMemberAccessLevel, feature_category: :system_access do
+ include described_class
+
+ describe 'stub_member_access_level' do
+ shared_examples 'access level stubs' do
+ let(:guests) { build_stubbed_list(:user, 2) }
+ let(:maintainer) { build_stubbed(:user) }
+ let(:no_access) { build_stubbed(:user) }
+
+ it 'stubs max member access level per user' do
+ stub_member_access_level(object, maintainer: maintainer, guest: guests)
+
+ # Ensure that multple calls are allowed
+ 2.times do
+ expect(access_level_for(maintainer)).to eq(Gitlab::Access::MAINTAINER)
+ expect(access_level_for(guests.first)).to eq(Gitlab::Access::GUEST)
+ expect(access_level_for(guests.last)).to eq(Gitlab::Access::GUEST)
+
+ # Partially stub so we expect a mock error.
+ expect { access_level_for(no_access) }.to raise_error(RSpec::Mocks::MockExpectationError)
+ end
+ end
+
+ it 'fails for unstubbed access' do
+ expect(access_level_for(no_access)).to eq(Gitlab::Access::NO_ACCESS)
+ end
+
+ it 'fails for invalid access level' do
+ expect { stub_member_access_level(object, unknown: :anything) }
+ .to raise_error(ArgumentError, "Invalid access level :unknown")
+ end
+ end
+
+ context 'with project' do
+ let(:object) { build_stubbed(:project) }
+
+ it_behaves_like 'access level stubs' do
+ def access_level_for(user)
+ object.team.max_member_access(user.id)
+ end
+ end
+ end
+
+ context 'with group' do
+ let(:object) { build_stubbed(:group) }
+
+ it_behaves_like 'access level stubs' do
+ def access_level_for(user)
+ object.max_member_access_for_user(user)
+ end
+ end
+ end
+
+ context 'with unsupported object' do
+ let(:object) { :a_symbol }
+
+ it 'raises an error' do
+ expect { stub_member_access_level(object) }
+ .to raise_error(ArgumentError, "Stubbing member access level unsupported for :a_symbol (Symbol)")
+ end
+ end
+ end
+end
diff --git a/spec/tooling/lib/tooling/find_changes_spec.rb b/spec/tooling/lib/tooling/find_changes_spec.rb
index 5932eb5e919..37e590858cf 100644
--- a/spec/tooling/lib/tooling/find_changes_spec.rb
+++ b/spec/tooling/lib/tooling/find_changes_spec.rb
@@ -11,12 +11,17 @@ RSpec.describe Tooling::FindChanges, feature_category: :tooling do
attr_accessor :changed_files_file, :predictive_tests_file, :frontend_fixtures_mapping_file
let(:instance) do
- described_class.new(changed_files_pathname, predictive_tests_pathname, frontend_fixtures_mapping_pathname)
+ described_class.new(
+ changed_files_pathname: changed_files_pathname,
+ predictive_tests_pathname: predictive_tests_pathname,
+ frontend_fixtures_mapping_pathname: frontend_fixtures_mapping_pathname,
+ from: from)
end
let(:changed_files_pathname) { changed_files_file.path }
let(:predictive_tests_pathname) { predictive_tests_file.path }
let(:frontend_fixtures_mapping_pathname) { frontend_fixtures_mapping_file.path }
+ let(:from) { :api }
let(:gitlab_client) { double('GitLab') } # rubocop:disable RSpec/VerifiedDoubles
around do |example|
@@ -45,24 +50,50 @@ RSpec.describe Tooling::FindChanges, feature_category: :tooling do
'CI_MERGE_REQUEST_PROJECT_PATH' => 'dummy-project',
'PROJECT_TOKEN_FOR_CI_SCRIPTS_API_USAGE' => 'dummy-token'
)
+ end
+
+ describe '#initialize' do
+ context 'when fetching changes from unknown' do
+ let(:from) { :unknown }
- allow(instance).to receive(:gitlab).and_return(gitlab_client)
+ it 'raises an ArgumentError' do
+ expect { instance }.to raise_error(
+ ArgumentError, ":from can only be :api or :changed_files"
+ )
+ end
+ end
end
describe '#execute' do
subject { instance.execute }
+ before do
+ allow(instance).to receive(:gitlab).and_return(gitlab_client)
+ end
+
context 'when there is no changed files file' do
let(:changed_files_pathname) { nil }
it 'raises an ArgumentError' do
expect { subject }.to raise_error(
- ArgumentError, "A path to the changed files file must be given as first argument."
+ ArgumentError, "A path to the changed files file must be given as :changed_files_pathname"
)
end
end
- context 'when an changed files file is provided' do
+ context 'when fetching changes from API' do
+ let(:from) { :api }
+
+ it 'calls GitLab API to retrieve the MR diff' do
+ expect(gitlab_client).to receive_message_chain(:merge_request_changes, :changes).and_return([])
+
+ subject
+ end
+ end
+
+ context 'when fetching changes from changed files' do
+ let(:from) { :changed_files }
+
it 'does not call GitLab API to retrieve the MR diff' do
expect(gitlab_client).not_to receive(:merge_request_changes)
@@ -154,68 +185,96 @@ RSpec.describe Tooling::FindChanges, feature_category: :tooling do
describe '#only_js_files_changed' do
subject { instance.only_js_files_changed }
- let(:mr_changes_array) { [] }
+ context 'when fetching changes from changed files' do
+ let(:from) { :changed_files }
- before do
- # The class from the GitLab gem isn't public, so we cannot use verified doubles for it.
- #
- # rubocop:disable RSpec/VerifiedDoubles
- allow(gitlab_client).to receive(:merge_request_changes)
- .with('dummy-project', '1234')
- .and_return(double(changes: mr_changes_array))
- # rubocop:enable RSpec/VerifiedDoubles
- end
+ before do
+ File.write(changed_files_pathname, changed_files_file_content)
+ end
- context 'when a file is passed as an argument' do
- let(:changed_files_pathname) { 'does-not-exist.out' }
+ context 'when changed files contain only *.js changes' do
+ let(:changed_files_file_content) { 'a.js b.js' }
- it 'calls GitLab API' do
- expect(gitlab_client).to receive(:merge_request_changes)
- .with('dummy-project', '1234')
+ it 'returns true' do
+ expect(subject).to be true
+ end
+ end
- subject
+ context 'when changed files contain not only *.js changes' do
+ let(:changed_files_file_content) { 'a.js b.rb' }
+
+ it 'returns false' do
+ expect(subject).to be false
+ end
end
end
- context 'when there are no file changes' do
+ context 'when fetching changes from API' do
+ let(:from) { :api }
+
let(:mr_changes_array) { [] }
- it 'returns false' do
- expect(subject).to be false
+ before do
+ allow(instance).to receive(:gitlab).and_return(gitlab_client)
+
+ # The class from the GitLab gem isn't public, so we cannot use verified doubles for it.
+ #
+ # rubocop:disable RSpec/VerifiedDoubles
+ allow(gitlab_client).to receive(:merge_request_changes)
+ .with('dummy-project', '1234')
+ .and_return(double(changes: mr_changes_array))
+ # rubocop:enable RSpec/VerifiedDoubles
end
- end
- context 'when there are changes to files other than JS files' do
- let(:mr_changes_array) do
- [
- {
- "new_path" => "scripts/gitlab_component_helpers.sh",
- "old_path" => "scripts/gitlab_component_helpers.sh"
- },
- {
- "new_path" => "scripts/test.js",
- "old_path" => "scripts/test.js"
- }
- ]
+ context 'when a file is passed as an argument' do
+ it 'calls GitLab API' do
+ expect(gitlab_client).to receive(:merge_request_changes)
+ .with('dummy-project', '1234')
+
+ subject
+ end
end
- it 'returns false' do
- expect(subject).to be false
+ context 'when there are no file changes' do
+ let(:mr_changes_array) { [] }
+
+ it 'returns false' do
+ expect(subject).to be false
+ end
end
- end
- context 'when there are changes only to JS files' do
- let(:mr_changes_array) do
- [
- {
- "new_path" => "scripts/test.js",
- "old_path" => "scripts/test.js"
- }
- ]
+ context 'when there are changes to files other than JS files' do
+ let(:mr_changes_array) do
+ [
+ {
+ "new_path" => "scripts/gitlab_component_helpers.sh",
+ "old_path" => "scripts/gitlab_component_helpers.sh"
+ },
+ {
+ "new_path" => "scripts/test.js",
+ "old_path" => "scripts/test.js"
+ }
+ ]
+ end
+
+ it 'returns false' do
+ expect(subject).to be false
+ end
end
- it 'returns true' do
- expect(subject).to be true
+ context 'when there are changes only to JS files' do
+ let(:mr_changes_array) do
+ [
+ {
+ "new_path" => "scripts/test.js",
+ "old_path" => "scripts/test.js"
+ }
+ ]
+ end
+
+ it 'returns true' do
+ expect(subject).to be true
+ end
end
end
end
diff --git a/spec/tooling/lib/tooling/helpers/file_handler_spec.rb b/spec/tooling/lib/tooling/helpers/file_handler_spec.rb
index d6f68baeb90..b78f0a3bb6b 100644
--- a/spec/tooling/lib/tooling/helpers/file_handler_spec.rb
+++ b/spec/tooling/lib/tooling/helpers/file_handler_spec.rb
@@ -67,10 +67,10 @@ RSpec.describe Tooling::Helpers::FileHandler, feature_category: :tooling do
end
describe '#write_array_to_file' do
- let(:content_array) { %w[new_entry] }
- let(:overwrite_flag) { false }
+ let(:content_array) { %w[new_entry] }
+ let(:append_flag) { true }
- subject { instance.write_array_to_file(output_file_path, content_array, overwrite: overwrite_flag) }
+ subject { instance.write_array_to_file(output_file_path, content_array, append: append_flag) }
context 'when the output file does not exist' do
let(:non_existing_output_file) { 'tmp/another_file.out' }
@@ -113,8 +113,8 @@ RSpec.describe Tooling::Helpers::FileHandler, feature_category: :tooling do
.to((initial_content.split(' ') + content_array).join(' '))
end
- context 'when the overwrite flag is set to true' do
- let(:overwrite_flag) { true }
+ context 'when the append flag is set to false' do
+ let(:append_flag) { false }
it 'overwrites the previous content' do
expect { subject }.to change { File.read(output_file_path) }
diff --git a/spec/tooling/lib/tooling/predictive_tests_spec.rb b/spec/tooling/lib/tooling/predictive_tests_spec.rb
index 79554037c48..b82364fe6f6 100644
--- a/spec/tooling/lib/tooling/predictive_tests_spec.rb
+++ b/spec/tooling/lib/tooling/predictive_tests_spec.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'tempfile'
+require 'fileutils'
require_relative '../../../../tooling/lib/tooling/predictive_tests'
require_relative '../../../support/helpers/stub_env'
@@ -9,11 +10,14 @@ RSpec.describe Tooling::PredictiveTests, feature_category: :tooling do
let(:instance) { described_class.new }
let(:matching_tests_initial_content) { 'initial_matching_spec' }
+ let(:fixtures_mapping_content) { '{}' }
- attr_accessor :changed_files, :fixtures_mapping, :matching_js_files, :matching_tests, :views_with_partials
+ attr_accessor :changed_files, :changed_files_path, :fixtures_mapping,
+ :matching_js_files, :matching_tests, :views_with_partials
around do |example|
self.changed_files = Tempfile.new('test-folder/changed_files.txt')
+ self.changed_files_path = changed_files.path
self.fixtures_mapping = Tempfile.new('test-folder/fixtures_mapping.txt')
self.matching_js_files = Tempfile.new('test-folder/matching_js_files.txt')
self.matching_tests = Tempfile.new('test-folder/matching_tests.txt')
@@ -22,10 +26,15 @@ RSpec.describe Tooling::PredictiveTests, feature_category: :tooling do
# See https://ruby-doc.org/stdlib-1.9.3/libdoc/tempfile/rdoc/
# Tempfile.html#class-Tempfile-label-Explicit+close
begin
- example.run
- ensure
+ # In practice, we let PredictiveTests create the file, and we just
+ # use its file name.
changed_files.close
changed_files.unlink
+
+ example.run
+ ensure
+ # Since example.run can create the file again, let's remove it again
+ FileUtils.rm_f(changed_files_path)
fixtures_mapping.close
fixtures_mapping.unlink
matching_js_files.close
@@ -39,7 +48,7 @@ RSpec.describe Tooling::PredictiveTests, feature_category: :tooling do
before do
stub_env(
- 'RSPEC_CHANGED_FILES_PATH' => changed_files.path,
+ 'RSPEC_CHANGED_FILES_PATH' => changed_files_path,
'RSPEC_MATCHING_TESTS_PATH' => matching_tests.path,
'RSPEC_VIEWS_INCLUDING_PARTIALS_PATH' => views_with_partials.path,
'FRONTEND_FIXTURES_MAPPING_PATH' => fixtures_mapping.path,
@@ -50,7 +59,9 @@ RSpec.describe Tooling::PredictiveTests, feature_category: :tooling do
# We write some data to later on verify that we only append to this file.
File.write(matching_tests.path, matching_tests_initial_content)
- File.write(fixtures_mapping.path, '{}') # We write valid JSON, so that the file can be processed
+ File.write(fixtures_mapping.path, fixtures_mapping_content)
+
+ allow(Gitlab).to receive(:configure)
end
describe '#execute' do
@@ -73,14 +84,18 @@ RSpec.describe Tooling::PredictiveTests, feature_category: :tooling do
context 'when all ENV variables are provided' do
before do
- File.write(changed_files, changed_files_content)
+ change = double('GitLab::Change') # rubocop:disable RSpec/VerifiedDoubles
+ allow(change).to receive_message_chain(:to_h, :values_at)
+ .and_return([changed_files_content, changed_files_content])
+
+ allow(Gitlab).to receive_message_chain(:merge_request_changes, :changes)
+ .and_return([change])
end
context 'when no files were changed' do
let(:changed_files_content) { '' }
- it 'does not change any files' do
- expect { subject }.not_to change { File.read(changed_files.path) }
+ it 'does not change files other than RSPEC_CHANGED_FILES_PATH' do
expect { subject }.not_to change { File.read(matching_tests.path) }
expect { subject }.not_to change { File.read(views_with_partials.path) }
expect { subject }.not_to change { File.read(fixtures_mapping.path) }
@@ -88,17 +103,27 @@ RSpec.describe Tooling::PredictiveTests, feature_category: :tooling do
end
end
- context 'when some files were changed' do
- let(:changed_files_content) { 'tooling/lib/tooling/predictive_tests.rb' }
+ context 'when some files used for frontend fixtures were changed' do
+ let(:changed_files_content) { 'app/models/todo.rb' }
+ let(:changed_files_matching_test) { 'spec/models/todo_spec.rb' }
+ let(:matching_frontend_fixture) { 'tmp/tests/frontend/fixtures-ee/todos/todos.html' }
+ let(:fixtures_mapping_content) do
+ JSON.dump(changed_files_matching_test => [matching_frontend_fixture]) # rubocop:disable Gitlab/Json
+ end
+
+ it 'writes to RSPEC_CHANGED_FILES_PATH with API contents and appends with matching fixtures' do
+ subject
+
+ expect(File.read(changed_files_path)).to eq("#{changed_files_content} #{matching_frontend_fixture}")
+ end
it 'appends the spec file to RSPEC_MATCHING_TESTS_PATH' do
expect { subject }.to change { File.read(matching_tests.path) }
.from(matching_tests_initial_content)
- .to("#{matching_tests_initial_content} spec/tooling/lib/tooling/predictive_tests_spec.rb")
+ .to("#{matching_tests_initial_content} #{changed_files_matching_test}")
end
- it 'does not change files other than RSPEC_MATCHING_TESTS_PATH' do
- expect { subject }.not_to change { File.read(changed_files.path) }
+ it 'does not change files other than RSPEC_CHANGED_FILES_PATH nor RSPEC_MATCHING_TESTS_PATH' do
expect { subject }.not_to change { File.read(views_with_partials.path) }
expect { subject }.not_to change { File.read(fixtures_mapping.path) }
expect { subject }.not_to change { File.read(matching_js_files.path) }
diff --git a/tooling/bin/find_only_js_changes b/tooling/bin/find_only_js_changes
index 67e3d3c6ff5..a69ee64fe14 100755
--- a/tooling/bin/find_only_js_changes
+++ b/tooling/bin/find_only_js_changes
@@ -3,7 +3,7 @@
require_relative '../lib/tooling/find_changes'
-if Tooling::FindChanges.new.only_js_files_changed
+if Tooling::FindChanges.new(from: :api).only_js_files_changed
puts "Only JS files were changed"
exit 0
else
diff --git a/tooling/lib/tooling/find_changes.rb b/tooling/lib/tooling/find_changes.rb
index b4439bd4abe..d8373d11245 100755
--- a/tooling/lib/tooling/find_changes.rb
+++ b/tooling/lib/tooling/find_changes.rb
@@ -9,8 +9,15 @@ module Tooling
include Helpers::FileHandler
def initialize(
- changed_files_pathname = nil, predictive_tests_pathname = nil, frontend_fixtures_mapping_pathname = nil
+ from:,
+ changed_files_pathname: nil,
+ predictive_tests_pathname: nil,
+ frontend_fixtures_mapping_pathname: nil
)
+
+ raise ArgumentError, ':from can only be :api or :changed_files' unless
+ %i[api changed_files].include?(from)
+
@gitlab_token = ENV['PROJECT_TOKEN_FOR_CI_SCRIPTS_API_USAGE'] || ''
@gitlab_endpoint = ENV['CI_API_V4_URL']
@mr_project_path = ENV['CI_MERGE_REQUEST_PROJECT_PATH']
@@ -18,20 +25,23 @@ module Tooling
@changed_files_pathname = changed_files_pathname
@predictive_tests_pathname = predictive_tests_pathname
@frontend_fixtures_mapping_pathname = frontend_fixtures_mapping_pathname
+ @from = from
end
def execute
if changed_files_pathname.nil?
- raise ArgumentError, "A path to the changed files file must be given as first argument."
+ raise ArgumentError, "A path to the changed files file must be given as :changed_files_pathname"
end
- add_frontend_fixture_files!
- write_array_to_file(changed_files_pathname, file_changes, overwrite: true)
+ case @from
+ when :api
+ write_array_to_file(changed_files_pathname, file_changes + frontend_fixture_files, append: false)
+ else
+ write_array_to_file(changed_files_pathname, frontend_fixture_files, append: true)
+ end
end
def only_js_files_changed
- @changed_files_pathname = nil # We ensure that we'll get the diff from the MR directly, not from a file.
-
file_changes.any? && file_changes.all? { |file| file.end_with?('.js') }
end
@@ -55,25 +65,26 @@ module Tooling
predictive_tests_pathname && frontend_fixtures_mapping_pathname
end
- def add_frontend_fixture_files!
- return unless add_frontend_fixture_files?
-
+ def frontend_fixture_files
# If we have a `test file -> JSON frontend fixture` mapping file, we add the files JSON frontend fixtures
# files to the list of changed files so that Jest can automatically run the dependent tests
# using --findRelatedTests flag.
- test_files.each do |test_file|
- file_changes.concat(frontend_fixtures_mapping[test_file]) if frontend_fixtures_mapping.key?(test_file)
+ empty = [].freeze
+
+ test_files.flat_map do |test_file|
+ frontend_fixtures_mapping[test_file] || empty
end
end
def file_changes
@file_changes ||=
- if changed_files_pathname && File.exist?(changed_files_pathname)
- read_array_from_file(changed_files_pathname)
- else
+ case @from
+ when :api
mr_changes.changes.flat_map do |change|
change.to_h.values_at('old_path', 'new_path')
end.uniq
+ else
+ read_array_from_file(changed_files_pathname)
end
end
diff --git a/tooling/lib/tooling/helpers/file_handler.rb b/tooling/lib/tooling/helpers/file_handler.rb
index 2778bb1ffbc..88248e31df2 100644
--- a/tooling/lib/tooling/helpers/file_handler.rb
+++ b/tooling/lib/tooling/helpers/file_handler.rb
@@ -11,17 +11,17 @@ module Tooling
File.read(file).split(' ')
end
- def write_array_to_file(file, content_array, overwrite: false)
+ def write_array_to_file(file, content_array, append: true)
FileUtils.touch file
# We sort the array to make it easier to read the output file
content_array.sort!
output_content =
- if overwrite
- content_array.join(' ')
+ if append
+ [File.read(file), *content_array].join(' ').lstrip
else
- (File.read(file).split(' ') + content_array).join(' ')
+ content_array.join(' ')
end
File.write(file, output_content)
diff --git a/tooling/lib/tooling/predictive_tests.rb b/tooling/lib/tooling/predictive_tests.rb
index 2691e1ba56d..503426b5520 100644
--- a/tooling/lib/tooling/predictive_tests.rb
+++ b/tooling/lib/tooling/predictive_tests.rb
@@ -32,7 +32,10 @@ module Tooling
end
def execute
- Tooling::FindChanges.new(rspec_changed_files_path).execute
+ Tooling::FindChanges.new(
+ from: :api,
+ changed_files_pathname: rspec_changed_files_path
+ ).execute
Tooling::FindTests.new(rspec_changed_files_path, rspec_matching_tests_path).execute
Tooling::Mappings::PartialToViewsMappings.new(
rspec_changed_files_path, rspec_views_including_partials_path).execute
@@ -41,7 +44,11 @@ module Tooling
Tooling::Mappings::GraphqlBaseTypeMappings.new(rspec_changed_files_path, rspec_matching_tests_path).execute
Tooling::Mappings::ViewToSystemSpecsMappings.new(rspec_changed_files_path, rspec_matching_tests_path).execute
Tooling::FindChanges.new(
- rspec_changed_files_path, rspec_matching_tests_path, frontend_fixtures_mapping_path).execute
+ from: :changed_files,
+ changed_files_pathname: rspec_changed_files_path,
+ predictive_tests_pathname: rspec_matching_tests_path,
+ frontend_fixtures_mapping_pathname: frontend_fixtures_mapping_path
+ ).execute
Tooling::Mappings::ViewToJsMappings.new(rspec_changed_files_path, rspec_matching_js_files_path).execute
end