diff options
56 files changed, 1146 insertions, 1204 deletions
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS index ddcb8f2ffaf..56816fe6b2b 100644 --- a/.gitlab/CODEOWNERS +++ b/.gitlab/CODEOWNERS @@ -2,12 +2,14 @@ # project here: https://gitlab.com/gitlab-org/gitlab/-/project_members # As described in https://docs.gitlab.com/ee/user/project/code_owners.html -* @gitlab-org/maintainers/rails-backend @gitlab-org/maintainers/frontend @gitlab-org/maintainers/database @gl-quality/qe-maintainers @gl-quality/tooling-maintainers @gitlab-org/delivery @gitlab-org/maintainers/cicd-templates @nolith @gitlab-org/tw-leadership +# In order to support release automation, each required approval group MUST +# include @gitlab-bot +* @gitlab-bot @gitlab-org/maintainers/rails-backend @gitlab-org/maintainers/frontend @gitlab-org/maintainers/database @gl-quality/qe-maintainers @gl-quality/tooling-maintainers @gitlab-org/delivery @gitlab-org/maintainers/cicd-templates @nolith @gitlab-org/tw-leadership -.gitlab/CODEOWNERS @gitlab-org/development-leaders @gitlab-org/tw-leadership +.gitlab/CODEOWNERS @gitlab-bot @gitlab-org/development-leaders @gitlab-org/tw-leadership ## Allows release tooling to update the Gitaly Version -GITALY_SERVER_VERSION @project_278964_bot6 @gitlab-org/maintainers/rails-backend @gitlab-org/delivery +GITALY_SERVER_VERSION @gitlab-bot @project_278964_bot6 @gitlab-org/maintainers/rails-backend @gitlab-org/delivery ## Files that are excluded from required approval ## These rules override the * rule above, so that changes to docs and templates @@ -23,13 +25,13 @@ GITALY_SERVER_VERSION @project_278964_bot6 @gitlab-org/maintainers/rails-backend ## Technical writing files that do not need `*` rule approval, ## but still require an approval from a TW team DRI for each file. -/data/deprecations/templates/ @marcel.amirault @sarahgerman @gitlab-org/tw-leadership -.markdownlint.yml @marcel.amirault @eread @aqualls @gitlab-org/tw-leadership -/doc/.markdownlint/ @marcel.amirault @eread @aqualls @gitlab-org/tw-leadership -/doc/.vale/ @marcel.amirault @eread @aqualls @gitlab-org/tw-leadership -/lib/tasks/gitlab/tw/codeowners.rake @aqualls @gitlab-org/tw-leadership -/scripts/lint-doc.sh @marcel.amirault @eread @axil @sarahgerman @gitlab-org/tw-leadership -/scripts/lint-docs-metadata.sh @marcel.amirault @eread @axil @sarahgerman @gitlab-org/tw-leadership +/data/deprecations/templates/ @gitlab-bot @marcel.amirault @sarahgerman @gitlab-org/tw-leadership +.markdownlint.yml @gitlab-bot @marcel.amirault @eread @aqualls @gitlab-org/tw-leadership +/doc/.markdownlint/ @gitlab-bot @marcel.amirault @eread @aqualls @gitlab-org/tw-leadership +/doc/.vale/ @gitlab-bot @marcel.amirault @eread @aqualls @gitlab-org/tw-leadership +/lib/tasks/gitlab/tw/codeowners.rake @gitlab-bot @aqualls @gitlab-org/tw-leadership +/scripts/lint-doc.sh @gitlab-bot @marcel.amirault @eread @axil @sarahgerman @gitlab-org/tw-leadership +/scripts/lint-docs-metadata.sh @gitlab-bot @marcel.amirault @eread @axil @sarahgerman @gitlab-org/tw-leadership ^[Source code editing] .solargraph.yml.example @igor.drozdov @@ -48,13 +50,13 @@ GITALY_SERVER_VERSION @project_278964_bot6 @gitlab-org/maintainers/rails-backend /spec/frontend_integration/ /ee/spec/frontend_integration/ -[Clickhouse] @gitlab-org/maintainers/clickhouse +[Clickhouse] @gitlab-bot @gitlab-org/maintainers/clickhouse /db/click_house/ /ee/db/click_house/ /**/click(_|-)?house/ ## We list db/ subfolders explicitly as we don't want to match Clickhouse files -[Database] @gitlab-org/maintainers/database +[Database] @gitlab-bot @gitlab-org/maintainers/database /db/database_connections/ /ee/db/database_connections/ /db/docs/ @@ -79,31 +81,31 @@ GITALY_SERVER_VERSION @project_278964_bot6 @gitlab-org/maintainers/rails-backend /ee/app/finders/ /rubocop/rubocop-migrations.yml -[Pipeline configuration] @gl-quality/eng-prod +[Pipeline configuration] @gitlab-bot @gl-quality/eng-prod /.gitlab-ci.yml /.gitlab/ci/ -/.gitlab/ci/docs.gitlab-ci.yml @gl-quality/eng-prod @gl-docsteam -/.gitlab/ci/frontend.gitlab-ci.yml @gl-quality/eng-prod @gitlab-org/maintainers/frontend -/.gitlab/ci/package-and-test/ @gl-quality/eng-prod @gl-quality/qe-maintainers -/.gitlab/ci/qa.gitlab-ci.yml @gl-quality/eng-prod @gl-quality/qe-maintainers -/.gitlab/ci/qa-common/ @gl-quality/eng-prod @gl-quality/qe-maintainers -/.gitlab/ci/releases.gitlab-ci.yml @gl-quality/eng-prod @gitlab-org/delivery -/.gitlab/ci/reports.gitlab-ci.yml @gl-quality/eng-prod @gitlab-com/gl-security/appsec -/.gitlab/ci/review-apps/qa.gitlab-ci.yml @gl-quality/eng-prod @gl-quality/qe-maintainers -/.gitlab/ci/test-on-gdk/ @gl-quality/eng-prod @gl-quality/qe-maintainers +/.gitlab/ci/docs.gitlab-ci.yml @gitlab-bot @gl-quality/eng-prod @gl-docsteam +/.gitlab/ci/frontend.gitlab-ci.yml @gitlab-bot @gl-quality/eng-prod @gitlab-org/maintainers/frontend +/.gitlab/ci/package-and-test/ @gitlab-bot @gl-quality/eng-prod @gl-quality/qe-maintainers +/.gitlab/ci/qa.gitlab-ci.yml @gitlab-bot @gl-quality/eng-prod @gl-quality/qe-maintainers +/.gitlab/ci/qa-common/ @gitlab-bot @gl-quality/eng-prod @gl-quality/qe-maintainers +/.gitlab/ci/releases.gitlab-ci.yml @gitlab-bot @gl-quality/eng-prod @gitlab-org/delivery +/.gitlab/ci/reports.gitlab-ci.yml @gitlab-bot @gl-quality/eng-prod @gitlab-com/gl-security/appsec +/.gitlab/ci/review-apps/qa.gitlab-ci.yml @gitlab-bot @gl-quality/eng-prod @gl-quality/qe-maintainers +/.gitlab/ci/test-on-gdk/ @gitlab-bot @gl-quality/eng-prod @gl-quality/qe-maintainers /gems/gem.gitlab-ci.yml -[Tooling] @gl-quality/eng-prod +[Tooling] @gitlab-bot @gl-quality/eng-prod Dangerfile /danger/ /tooling/danger/ /scripts/ -/scripts/**/*.rb @gl-quality/eng-prod @gitlab-org/maintainers/rails-backend -/scripts/**/glfm/**/* @gl-quality/eng-prod @gitlab-org/plan-stage/backend-engineers -/scripts/**/*.js @gl-quality/eng-prod @gitlab-org/maintainers/frontend -/scripts/frontend/ @gl-quality/eng-prod @gitlab-org/maintainers/frontend -/scripts/remote_development/ @gitlab-org/maintainers/remote-development/backend -/scripts/review_apps/seed-dast-test-data.sh @gl-quality/eng-prod @dappelt @ngeorge1 +/scripts/**/*.rb @gitlab-bot @gl-quality/eng-prod @gitlab-org/maintainers/rails-backend +/scripts/**/glfm/**/* @gitlab-bot @gl-quality/eng-prod @gitlab-org/plan-stage/backend-engineers +/scripts/**/*.js @gitlab-bot @gl-quality/eng-prod @gitlab-org/maintainers/frontend +/scripts/frontend/ @gitlab-bot @gl-quality/eng-prod @gitlab-org/maintainers/frontend +/scripts/remote_development/ @gitlab-bot @gitlab-org/maintainers/remote-development/backend +/scripts/review_apps/seed-dast-test-data.sh @gitlab-bot @gl-quality/eng-prod @dappelt @ngeorge1 /.codeclimate.yml /.dockerignore /.editorconfig @@ -415,7 +417,7 @@ Dangerfile ^[Workhorse] @gitlab-org/maintainers/gitlab-workhorse /workhorse/ -[Application Security] @gitlab-com/gl-security/appsec +[Application Security] @gitlab-bot @gitlab-com/gl-security/appsec /app/assets/javascripts/lib/dompurify.js /app/assets/javascripts/gfm_auto_complete.js /ee/app/assets/javascripts/gfm_auto_complete.js @@ -1024,7 +1026,7 @@ lib/gitlab/checks/** /doc/user/workspace/ @ashrafkhamis # End rake-managed-docs-block -[Authentication and Authorization] @gitlab-org/manage/authentication-and-authorization/approvers +[Authentication and Authorization] @gitlab-bot @gitlab-org/manage/authentication-and-authorization/approvers /app/assets/javascripts/access_tokens/ /app/assets/javascripts/alerts_settings/graphql/mutations/reset_http_token.mutation.graphql /app/assets/javascripts/authentication/ @@ -1270,7 +1272,7 @@ lib/gitlab/checks/** /lib/tasks/gitlab/password.rake /lib/tasks/tokens.rake -[Verify] @gitlab-org/maintainers/cicd-verify @shinya.maeda @stanhu @ayufan +[Verify] @gitlab-bot @gitlab-org/maintainers/cicd-verify @shinya.maeda @stanhu @ayufan # With these catch-all rules we will require backend approval and use it as an # opportunity to refine specific rules defined in this section. # Note that frontend, CI templates and other concerns should be kept within @@ -1341,93 +1343,93 @@ lib/gitlab/checks/** /ee/lib/ee/api/entities/merge_train.rb # Overrides for Verify. These files below require approval from teams outside Verify. -/**/lib/**/ci/reports/**/ @gitlab-org/maintainers/rails-backend -/**/lib/**/ci/parsers/**/ @gitlab-org/maintainers/rails-backend -/ee/lib/gitlab/ci/parsers/license_compliance/ @gitlab-org/secure/composition-analysis-be -/ee/lib/gitlab/ci/parsers/security/ @gitlab-org/govern/threat-insights-backend-team -/ee/lib/gitlab/ci/reports/coverage_fuzzing/ @gitlab-org/secure/fuzzing-be -/ee/lib/gitlab/ci/reports/dependency_list/ @gitlab-org/secure/composition-analysis-be -/ee/lib/gitlab/ci/reports/license_scanning/ @gitlab-org/secure/composition-analysis-be -/ee/lib/gitlab/ci/reports/security/ @gitlab-org/govern/threat-insights-backend-team +/**/lib/**/ci/reports/**/ @gitlab-bot @gitlab-org/maintainers/rails-backend +/**/lib/**/ci/parsers/**/ @gitlab-bot @gitlab-org/maintainers/rails-backend +/ee/lib/gitlab/ci/parsers/license_compliance/ @gitlab-bot @gitlab-org/secure/composition-analysis-be +/ee/lib/gitlab/ci/parsers/security/ @gitlab-bot @gitlab-org/govern/threat-insights-backend-team +/ee/lib/gitlab/ci/reports/coverage_fuzzing/ @gitlab-bot @gitlab-org/secure/fuzzing-be +/ee/lib/gitlab/ci/reports/dependency_list/ @gitlab-bot @gitlab-org/secure/composition-analysis-be +/ee/lib/gitlab/ci/reports/license_scanning/ @gitlab-bot @gitlab-org/secure/composition-analysis-be +/ee/lib/gitlab/ci/reports/security/ @gitlab-bot @gitlab-org/govern/threat-insights-backend-team # Verify frontend -/**/javascripts/ci/ @gitlab-org/ci-cd/verify/frontend -/**/javascripts/token_access/ @gitlab-org/ci-cd/verify/frontend -/**/javascripts/admin/application_settings/runner_token_expiration/ @gitlab-org/ci-cd/verify/frontend -/**/javascripts/usage_quotas/pipelines/ @gitlab-org/ci-cd/verify/frontend @sheldonled @aalakkad @kpalchyk +/**/javascripts/ci/ @gitlab-bot @gitlab-org/ci-cd/verify/frontend +/**/javascripts/token_access/ @gitlab-bot @gitlab-org/ci-cd/verify/frontend +/**/javascripts/admin/application_settings/runner_token_expiration/ @gitlab-bot @gitlab-org/ci-cd/verify/frontend +/**/javascripts/usage_quotas/pipelines/ @gitlab-bot @gitlab-org/ci-cd/verify/frontend @sheldonled @aalakkad @kpalchyk ## Verify:Runner Fleet Backend -/app/controllers/admin/runner*.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/app/controllers/concerns/runner*.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/app/controllers/groups/runner*.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/app/controllers/projects/runner*.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/app/controllers/runner*.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/app/finders/ci/runner*.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/app/graphql/mutations/ci/runner/ @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/app/graphql/resolvers/ci/*_runners_resolver.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/app/graphql/resolvers/ci/runner*.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/app/graphql/types/ci/runner_*.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/app/graphql/types/namespace/shared_runners_setting_enum.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/app/graphql/types/permission_types/ci/runner*.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/app/models/ci/build_runner_session.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/app/models/ci/runner*.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/app/models/concerns/ci/has_runner_executor.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/app/models/concerns/runner*.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/app/models/preloaders/runner*.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/app/policies/ci/runner*.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/app/presenters/ci/runner_*.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/app/serializers/runner*.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/app/services/groups/update_shared_runners_service.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/app/services/ci/runners/ @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/app/workers/ci/runners/ @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/db/docs/ci_runner*.yml @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/ee/app/controllers/ee/admin/runner*.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/ee/app/controllers/ee/groups/runner*.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/ee/app/graphql/ee/mutations/ci/runner/ @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/ee/app/graphql/ee/types/ci/runner*.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/ee/app/graphql/resolvers/ci/runner*.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/ee/app/models/ee/ci/runner*.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/ee/app/policies/ee/ci/runner*.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/ee/app/services/audit_events/*runner*.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/ee/app/services/ci/runners/ @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/ee/app/services/ee/ci/runners/ @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/ee/app/workers/ci/runners/ @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/lib/api/ci/helpers/runner.rb @gitlab-org/maintainers/cicd-verify @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/lib/api/ci/runner*.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/lib/api/ci/runner.rb @gitlab-org/maintainers/cicd-verify @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/lib/api/entities/ci/runner*.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/lib/gitlab/audit/ci_runner_token_author.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/lib/gitlab/ci/runner*.rb @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/lib/gitlab/seeders/ci/runner/ @gitlab-org/ci-cd/runner-fleet-team/backend-approvers -/lib/tasks/gitlab/seed/runner_fleet.rake @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/app/controllers/admin/runner*.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/app/controllers/concerns/runner*.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/app/controllers/groups/runner*.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/app/controllers/projects/runner*.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/app/controllers/runner*.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/app/finders/ci/runner*.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/app/graphql/mutations/ci/runner/ @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/app/graphql/resolvers/ci/*_runners_resolver.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/app/graphql/resolvers/ci/runner*.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/app/graphql/types/ci/runner_*.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/app/graphql/types/namespace/shared_runners_setting_enum.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/app/graphql/types/permission_types/ci/runner*.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/app/models/ci/build_runner_session.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/app/models/ci/runner*.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/app/models/concerns/ci/has_runner_executor.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/app/models/concerns/runner*.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/app/models/preloaders/runner*.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/app/policies/ci/runner*.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/app/presenters/ci/runner_*.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/app/serializers/runner*.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/app/services/groups/update_shared_runners_service.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/app/services/ci/runners/ @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/app/workers/ci/runners/ @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/db/docs/ci_runner*.yml @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/ee/app/controllers/ee/admin/runner*.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/ee/app/controllers/ee/groups/runner*.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/ee/app/graphql/ee/mutations/ci/runner/ @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/ee/app/graphql/ee/types/ci/runner*.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/ee/app/graphql/resolvers/ci/runner*.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/ee/app/models/ee/ci/runner*.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/ee/app/policies/ee/ci/runner*.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/ee/app/services/audit_events/*runner*.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/ee/app/services/ci/runners/ @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/ee/app/services/ee/ci/runners/ @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/ee/app/workers/ci/runners/ @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/lib/api/ci/helpers/runner.rb @gitlab-bot @gitlab-org/maintainers/cicd-verify @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/lib/api/ci/runner*.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/lib/api/ci/runner.rb @gitlab-bot @gitlab-org/maintainers/cicd-verify @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/lib/api/entities/ci/runner*.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/lib/gitlab/audit/ci_runner_token_author.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/lib/gitlab/ci/runner*.rb @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/lib/gitlab/seeders/ci/runner/ @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers +/lib/tasks/gitlab/seed/runner_fleet.rake @gitlab-bot @gitlab-org/ci-cd/runner-fleet-team/backend-approvers # CI/CD templates require approval from specific owners. -/lib/gitlab/ci/templates/ @gitlab-org/maintainers/cicd-templates -/lib/gitlab/ci/templates/Security/ @gonzoyumo @twoodham @amarpatel -/lib/gitlab/ci/templates/Security/API-Fuzzing.*.yml @gitlab-org/secure/dynamic-analysis -/lib/gitlab/ci/templates/Security/Container-Scanning.*.yml @gitlab-org/secure/composition-analysis-be -/lib/gitlab/ci/templates/Security/Coverage-Fuzzing.*.yml @gitlab-org/secure/dynamic-analysis -/lib/gitlab/ci/templates/Security/DAST.*.yml @gitlab-org/secure/dynamic-analysis -/lib/gitlab/ci/templates/Security/Dependency-Scanning.*.yml @gitlab-org/secure/composition-analysis-be -/lib/gitlab/ci/templates/Security/License-Scanning.*.yml @gitlab-org/secure/composition-analysis-be -/lib/gitlab/ci/templates/Security/SAST.*.yml @gitlab-org/secure/static-analysis -/lib/gitlab/ci/templates/Security/Secret-Detection.*.yml @gitlab-org/secure/static-analysis -/lib/gitlab/ci/templates/Security/Secure-Binaries.*.yml @gitlab-org/secure/static-analysis @gitlab-org/secure/composition-analysis-be @gitlab-org/secure/dynamic-analysis +/lib/gitlab/ci/templates/ @gitlab-bot @gitlab-org/maintainers/cicd-templates +/lib/gitlab/ci/templates/Security/ @gitlab-bot @gonzoyumo @twoodham @amarpatel +/lib/gitlab/ci/templates/Security/API-Fuzzing.*.yml @gitlab-bot @gitlab-org/secure/dynamic-analysis +/lib/gitlab/ci/templates/Security/Container-Scanning.*.yml @gitlab-bot @gitlab-org/secure/composition-analysis-be +/lib/gitlab/ci/templates/Security/Coverage-Fuzzing.*.yml @gitlab-bot @gitlab-org/secure/dynamic-analysis +/lib/gitlab/ci/templates/Security/DAST.*.yml @gitlab-bot @gitlab-org/secure/dynamic-analysis +/lib/gitlab/ci/templates/Security/Dependency-Scanning.*.yml @gitlab-bot @gitlab-org/secure/composition-analysis-be +/lib/gitlab/ci/templates/Security/License-Scanning.*.yml @gitlab-bot @gitlab-org/secure/composition-analysis-be +/lib/gitlab/ci/templates/Security/SAST.*.yml @gitlab-bot @gitlab-org/secure/static-analysis +/lib/gitlab/ci/templates/Security/Secret-Detection.*.yml @gitlab-bot @gitlab-org/secure/static-analysis +/lib/gitlab/ci/templates/Security/Secure-Binaries.*.yml @gitlab-bot @gitlab-org/secure/static-analysis @gitlab-org/secure/composition-analysis-be @gitlab-org/secure/dynamic-analysis # Note: The `Fortify-FoD-sast.gitlab-ci.yml` template is provided and maintained by Fortify, an official Technology Partner with GitLab. -/lib/gitlab/ci/templates/Jobs/Container-Scanning.*.yml @gitlab-org/secure/composition-analysis-be -/lib/gitlab/ci/templates/Jobs/Dependency-Scanning.*.yml @gitlab-org/secure/composition-analysis-be -/lib/gitlab/ci/templates/Jobs/License-Scanning.*.yml @gitlab-org/secure/composition-analysis-be -/lib/gitlab/ci/templates/Jobs/SAST.*.yml @gitlab-org/secure/static-analysis -/lib/gitlab/ci/templates/Jobs/Secret-Detection.*.yml @gitlab-org/secure/static-analysis +/lib/gitlab/ci/templates/Jobs/Container-Scanning.*.yml @gitlab-bot @gitlab-org/secure/composition-analysis-be +/lib/gitlab/ci/templates/Jobs/Dependency-Scanning.*.yml @gitlab-bot @gitlab-org/secure/composition-analysis-be +/lib/gitlab/ci/templates/Jobs/License-Scanning.*.yml @gitlab-bot @gitlab-org/secure/composition-analysis-be +/lib/gitlab/ci/templates/Jobs/SAST.*.yml @gitlab-bot @gitlab-org/secure/static-analysis +/lib/gitlab/ci/templates/Jobs/Secret-Detection.*.yml @gitlab-bot @gitlab-org/secure/static-analysis -[Data Stores::Tenant Scale] @abdwdd @alexpooley @manojmj +[Data Stores::Tenant Scale] @gitlab-bot @abdwdd @alexpooley @manojmj lib/api/entities/basic_project_details.rb lib/api/entities/project_with_access.rb lib/api/entities/project_identity.rb lib/api/entities/project.rb ee/lib/ee/api/entities/project.rb -[Compliance] @gitlab-org/govern/compliance +[Compliance] @gitlab-bot @gitlab-org/govern/compliance /app/services/audit_events/build_service.rb /ee/app/services/ee/audit_events/build_service.rb /ee/spec/services/audit_events/custom_audit_event_service_spec.rb @@ -1490,21 +1492,22 @@ ee/lib/ee/api/entities/project.rb /app/workers/releases/create_evidence_worker.rb /app/workers/releases/manage_evidence_worker.rb -^[Fulfillment::Utilization] @sheldonled @aalakkad @kpalchyk +^[Fulfillment::Utilization] @gitlab-bot @sheldonled @aalakkad @kpalchyk /ee/app/assets/javascripts/usage_quotas/components/ /ee/app/assets/javascripts/usage_quotas/seats/ /ee/app/assets/javascripts/usage_quotas/storage/ -[Manage::Foundations] @gitlab-org/manage/foundations/engineering +[Manage::Foundations] @gitlab-bot @gitlab-org/manage/foundations/engineering /lib/sidebars/ /ee/lib/sidebars/ -[Global Search] @gitlab-org/search-team/migration-maintainers +[Global Search] @gitlab-bot @gitlab-org/search-team/migration-maintainers /ee/elastic/migrate/ /ee/spec/elastic/migrate/ /ee/spec/support/elastic.rb -[Create::IDE - Remote Development Backend] @gitlab-org/maintainers/remote-development/backend +[Create::IDE - Remote Development Backend] @gitlab-bot @gitlab-org/maintainers/remote-development/backend + /ee/app/models/remote_development/ /ee/app/policies/remote_development/ /ee/app/finders/remote_development/ @@ -1519,7 +1522,7 @@ ee/lib/ee/api/entities/project.rb /ee/spec/policies/remote_development/ /ee/spec/requests/api/graphql/mutations/remote_development/ /ee/spec/requests/api/graphql/remote_development/ -/ee/spec/features/remote_development/ @gitlab-org/maintainers/remote-development/backend @gitlab-org/maintainers/remote-development/frontend +/ee/spec/features/remote_development/ @gitlab-bot @gitlab-org/maintainers/remote-development/backend @gitlab-org/maintainers/remote-development/frontend /ee/spec/finders/remote_development/ /ee/spec/support/shared_contexts/remote_development/ /ee/spec/graphql/types/remote_development/ @@ -1528,9 +1531,9 @@ ee/lib/ee/api/entities/project.rb /ee/spec/fixtures/remote_development/ /ee/spec/controllers/remote_development/ /ee/spec/services/remote_development/ -/qa/qa/specs/features/**/remote_development/ @gitlab-org/maintainers/remote-development/backend @gl-quality/qe-maintainers +/qa/qa/specs/features/**/remote_development/ @gitlab-bot @gitlab-org/maintainers/remote-development/backend @gl-quality/qe-maintainers -[Create::IDE - Remote Development Frontend] @gitlab-org/maintainers/remote-development/frontend +[Create::IDE - Remote Development Frontend] @gitlab-bot @gitlab-org/maintainers/remote-development/frontend /ee/app/assets/remote_development/ /ee/app/assets/**/remote_development/ /ee/app/views/remote_development/ diff --git a/app/assets/javascripts/ci/job_details/components/job_header.vue b/app/assets/javascripts/ci/job_details/components/job_header.vue index 7f4dea05464..13f3eebd447 100644 --- a/app/assets/javascripts/ci/job_details/components/job_header.vue +++ b/app/assets/javascripts/ci/job_details/components/job_header.vue @@ -7,13 +7,6 @@ import { __, sprintf } from '~/locale'; import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue'; import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; -/** - * Renders header component for job and pipeline page based on UI mockups - * - * Used in: - * - job show page - * - pipeline show page - */ export default { components: { CiBadgeLink, @@ -33,33 +26,21 @@ export default { type: Object, required: true, }, - itemName: { + name: { type: String, required: true, }, - itemId: { - type: String, - required: false, - default: '', - }, time: { type: String, required: true, }, user: { type: Object, - required: false, - default: () => ({}), - }, - hasSidebarButton: { - type: Boolean, - required: false, - default: false, + required: true, }, shouldRenderTriggeredLabel: { type: Boolean, - required: false, - default: true, + required: true, }, }, @@ -92,13 +73,6 @@ export default { message() { return this.user?.status?.message; }, - item() { - if (this.itemId) { - return `${this.itemName} #${this.itemId}`; - } - - return this.itemName; - }, userId() { return isGid(this.user?.id) ? getIdFromGraphQLId(this.user?.id) : this.user?.id; }, @@ -121,7 +95,7 @@ export default { <section class="header-main-content gl-mr-3"> <ci-badge-link class="gl-mr-3" :status="status" /> - <strong data-testid="job-header-item-text">{{ item }}</strong> + <strong data-testid="job-name">{{ name }}</strong> <template v-if="shouldRenderTriggeredLabel">{{ __('started') }}</template> <template v-else>{{ __('created') }}</template> @@ -165,7 +139,6 @@ export default { <slot></slot> </section> <gl-button - v-if="hasSidebarButton" class="gl-md-display-none gl-ml-auto gl-align-self-start js-sidebar-build-toggle" icon="chevron-double-lg-left" :aria-label="__('Toggle sidebar')" diff --git a/app/assets/javascripts/ci/job_details/job_app.vue b/app/assets/javascripts/ci/job_details/job_app.vue index 474190fe50e..5137ebfeaa8 100644 --- a/app/assets/javascripts/ci/job_details/job_app.vue +++ b/app/assets/javascripts/ci/job_details/job_app.vue @@ -129,7 +129,7 @@ export default { return Boolean(this.job.retry_path); }, - itemName() { + jobName() { return sprintf(__('Job %{jobName}'), { jobName: this.job.name }); }, }, @@ -229,9 +229,8 @@ export default { :status="job.status" :time="headerTime" :user="job.user" - :has-sidebar-button="true" :should-render-triggered-label="shouldRenderTriggeredLabel" - :item-name="itemName" + :name="jobName" @clickedSidebarButton="toggleSidebar" /> </div> diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb index 0fe31d74ae8..5479154f667 100644 --- a/app/controllers/concerns/issuable_collections.rb +++ b/app/controllers/concerns/issuable_collections.rb @@ -23,7 +23,7 @@ module IssuableCollections end def set_pagination - row_count = finder.row_count + row_count = request.format.atom? ? -1 : finder.row_count @issuables = @issuables.page(params[:page]) @issuables = per_page_for_relative_position if params[:sort] == 'relative_position' diff --git a/app/graphql/types/ci/job_trace_type.rb b/app/graphql/types/ci/job_trace_type.rb index b959d37f327..405c640115d 100644 --- a/app/graphql/types/ci/job_trace_type.rb +++ b/app/graphql/types/ci/job_trace_type.rb @@ -5,17 +5,24 @@ module Types module Ci class JobTraceType < BaseObject graphql_name 'CiJobTrace' + MAX_SIZE_KB = 16 + MAX_SIZE_B = MAX_SIZE_KB * 1024 field :html_summary, GraphQL::Types::String, null: false, alpha: { milestone: '15.11' }, - description: 'HTML summary that contains the tail lines of the trace.' do + description: 'HTML summary that contains the tail lines of the trace. ' \ + "Returns at most #{MAX_SIZE_KB}KB of raw bytes from the trace. " \ + 'The returned string might start with an unexpected invalid UTF-8 code point due to truncation.' do argument :last_lines, Integer, required: false, default_value: 10, description: 'Number of tail lines to return, up to a maximum of 100 lines.' end def html_summary(last_lines:) - object.html(last_lines: last_lines.clamp(1, 100)).html_safe + object.html( + last_lines: last_lines.clamp(1, 100), + max_size: Feature.enabled?(:graphql_job_trace_html_summary_max_size) ? MAX_SIZE_B : nil + ).html_safe end end end diff --git a/app/models/bulk_imports/entity.rb b/app/models/bulk_imports/entity.rb index d81286cd2d2..437118c36e8 100644 --- a/app/models/bulk_imports/entity.rb +++ b/app/models/bulk_imports/entity.rb @@ -196,6 +196,10 @@ class BulkImports::Entity < ApplicationRecord update!(has_failures: true) end + def source_version + @source_version ||= bulk_import.source_version_info + end + private def validate_parent_is_a_group diff --git a/app/models/pages/lookup_path.rb b/app/models/pages/lookup_path.rb index 2ffb2e84cbf..e8becc833ca 100644 --- a/app/models/pages/lookup_path.rb +++ b/app/models/pages/lookup_path.rb @@ -35,7 +35,7 @@ module Pages { type: 'zip', path: deployment.file.url_or_file_path( - expire_at: ::Gitlab::Pages::CacheControl::DEPLOYMENT_EXPIRATION.from_now + expire_at: ::Gitlab::Pages::DEPLOYMENT_EXPIRATION.from_now ), global_id: global_id, sha256: deployment.file_sha256, diff --git a/app/models/pages/virtual_domain.rb b/app/models/pages/virtual_domain.rb index aebfbd2b490..0a64e91bf60 100644 --- a/app/models/pages/virtual_domain.rb +++ b/app/models/pages/virtual_domain.rb @@ -2,9 +2,8 @@ module Pages class VirtualDomain - def initialize(projects:, cache: nil, trim_prefix: nil, domain: nil) + def initialize(projects:, trim_prefix: nil, domain: nil) @projects = projects - @cache = cache @trim_prefix = trim_prefix @domain = domain end @@ -25,14 +24,9 @@ module Pages .reverse end - # cache_key is required by #present_cached in ::API::Internal::Pages - def cache_key - @cache_key ||= cache&.cache_key - end - private - attr_reader :projects, :trim_prefix, :domain, :cache + attr_reader :projects, :trim_prefix, :domain def lookup_paths_for(project) Pages::LookupPath.new(project, trim_prefix: trim_prefix, domain: domain) diff --git a/app/services/bulk_imports/create_pipeline_trackers_service.rb b/app/services/bulk_imports/create_pipeline_trackers_service.rb deleted file mode 100644 index 7fa62e0ce8a..00000000000 --- a/app/services/bulk_imports/create_pipeline_trackers_service.rb +++ /dev/null @@ -1,72 +0,0 @@ -# frozen_string_literal: true - -module BulkImports - class CreatePipelineTrackersService - def initialize(entity) - @entity = entity - end - - def execute! - entity.class.transaction do - entity.pipelines.each do |pipeline| - status = skip_pipeline?(pipeline) ? -2 : 0 - - entity.trackers.create!( - stage: pipeline[:stage], - pipeline_name: pipeline[:pipeline], - status: status - ) - end - end - end - - private - - attr_reader :entity - - def skip_pipeline?(pipeline) - return false unless source_version.valid? - - minimum_version, maximum_version = pipeline.values_at(:minimum_source_version, :maximum_source_version) - - if minimum_version && non_patch_source_version < Gitlab::VersionInfo.parse(minimum_version) - log_skipped_pipeline(pipeline, minimum_version, maximum_version) - return true - end - - if maximum_version && non_patch_source_version > Gitlab::VersionInfo.parse(maximum_version) - log_skipped_pipeline(pipeline, minimum_version, maximum_version) - return true - end - - false - end - - def source_version - @source_version ||= entity.bulk_import.source_version_info - end - - def non_patch_source_version - source_version.without_patch - end - - def log_skipped_pipeline(pipeline, minimum_version, maximum_version) - logger.info( - message: 'Pipeline skipped as source instance version not compatible with pipeline', - bulk_import_entity_id: entity.id, - bulk_import_id: entity.bulk_import_id, - bulk_import_entity_type: entity.source_type, - source_full_path: entity.source_full_path, - pipeline_name: pipeline[:pipeline], - minimum_source_version: minimum_version, - maximum_source_version: maximum_version, - source_version: source_version.to_s, - importer: 'gitlab_migration' - ) - end - - def logger - @logger ||= Gitlab::Import::Logger.build - end - end -end diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index 8bc33fb46be..6ef7447b9da 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -3369,15 +3369,6 @@ :weight: 1 :idempotent: false :tags: [] -- :name: pages_invalidate_domain_cache - :worker_name: Pages::InvalidateDomainCacheWorker - :feature_category: :pages - :has_external_dependencies: false - :urgency: :low - :resource_boundary: :unknown - :weight: 1 - :idempotent: true - :tags: [] - :name: post_receive :worker_name: PostReceive :feature_category: :source_code_management diff --git a/app/workers/bulk_import_worker.rb b/app/workers/bulk_import_worker.rb index 6bce13c5ff0..83b881ee525 100644 --- a/app/workers/bulk_import_worker.rb +++ b/app/workers/bulk_import_worker.rb @@ -22,7 +22,7 @@ class BulkImportWorker # rubocop:disable Scalability/IdempotentWorker @bulk_import.start! if @bulk_import.created? created_entities.first(next_batch_size).each do |entity| - BulkImports::CreatePipelineTrackersService.new(entity).execute! + create_tracker(entity) entity.start! @@ -51,7 +51,7 @@ class BulkImportWorker # rubocop:disable Scalability/IdempotentWorker end def all_entities_failed? - entities.all? { |entity| entity.failed? } + entities.all?(&:failed?) end # A new BulkImportWorker job is enqueued to either @@ -72,4 +72,55 @@ class BulkImportWorker # rubocop:disable Scalability/IdempotentWorker def next_batch_size [DEFAULT_BATCH_SIZE - started_entities.count, 0].max end + + def create_tracker(entity) + entity.class.transaction do + entity.pipelines.each do |pipeline| + status = skip_pipeline?(pipeline, entity) ? :skipped : :created + + entity.trackers.create!( + stage: pipeline[:stage], + pipeline_name: pipeline[:pipeline], + status: BulkImports::Tracker.state_machine.states[status].value + ) + end + end + end + + def skip_pipeline?(pipeline, entity) + return false unless entity.source_version.valid? + + minimum_version, maximum_version = pipeline.values_at(:minimum_source_version, :maximum_source_version) + + if source_version_out_of_range?(minimum_version, maximum_version, entity.source_version.without_patch) + log_skipped_pipeline(pipeline, entity, minimum_version, maximum_version) + return true + end + + false + end + + def source_version_out_of_range?(minimum_version, maximum_version, non_patch_source_version) + (minimum_version && non_patch_source_version < Gitlab::VersionInfo.parse(minimum_version)) || + (maximum_version && non_patch_source_version > Gitlab::VersionInfo.parse(maximum_version)) + end + + def log_skipped_pipeline(pipeline, entity, minimum_version, maximum_version) + logger.info( + message: 'Pipeline skipped as source instance version not compatible with pipeline', + bulk_import_entity_id: entity.id, + bulk_import_id: entity.bulk_import_id, + bulk_import_entity_type: entity.source_type, + source_full_path: entity.source_full_path, + pipeline_name: pipeline[:pipeline], + minimum_source_version: minimum_version, + maximum_source_version: maximum_version, + source_version: entity.source_version.to_s, + importer: 'gitlab_migration' + ) + end + + def logger + @logger ||= Gitlab::Import::Logger.build + end end diff --git a/app/workers/pages/invalidate_domain_cache_worker.rb b/app/workers/pages/invalidate_domain_cache_worker.rb deleted file mode 100644 index 1700b681b94..00000000000 --- a/app/workers/pages/invalidate_domain_cache_worker.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true - -module Pages - class InvalidateDomainCacheWorker - include Gitlab::EventStore::Subscriber - - idempotent! - - feature_category :pages - - def handle_event(event) - domain_ids(event).each do |domain_id| - ::Gitlab::Pages::CacheControl - .for_domain(domain_id) - .clear_cache - end - - event.data.values_at( - :root_namespace_id, - :old_root_namespace_id, - :new_root_namespace_id - ).compact.uniq.each do |namespace_id| - ::Gitlab::Pages::CacheControl - .for_namespace(namespace_id) - .clear_cache - end - end - - def domain_ids(event) - ids = PagesDomain.ids_for_project(event.data[:project_id]) - - ids << event.data[:domain_id] if event.data[:domain_id] - - ids - end - end -end diff --git a/config/feature_flags/development/api_keyset_pagination_multi_order.yml b/config/feature_flags/development/api_keyset_pagination_multi_order.yml new file mode 100644 index 00000000000..6fa174e9814 --- /dev/null +++ b/config/feature_flags/development/api_keyset_pagination_multi_order.yml @@ -0,0 +1,8 @@ +--- +name: api_keyset_pagination_multi_order +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/130019 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/422999 +milestone: '16.5' +type: development +group: group::authentication and authorization +default_enabled: false diff --git a/config/feature_flags/development/cache_pages_domain_api.yml b/config/feature_flags/development/graphql_job_trace_html_summary_max_size.yml index 409497aa22d..88e99afddf3 100644 --- a/config/feature_flags/development/cache_pages_domain_api.yml +++ b/config/feature_flags/development/graphql_job_trace_html_summary_max_size.yml @@ -1,8 +1,7 @@ ---- -name: cache_pages_domain_api -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88956 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/364127 -milestone: '15.2' +name: graphql_job_trace_html_summary_max_size +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/130984 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/425196 +milestone: '16.5' +group: group::pipeline execution type: development -group: group::editor default_enabled: false diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml index 4957e5f4979..d14c20426df 100644 --- a/config/sidekiq_queues.yml +++ b/config/sidekiq_queues.yml @@ -481,8 +481,6 @@ - 1 - - pages_domain_verification - 1 -- - pages_invalidate_domain_cache - - 1 - - personal_access_tokens - 1 - - pipeline_background diff --git a/doc/administration/backup_restore/restore_gitlab.md b/doc/administration/backup_restore/restore_gitlab.md index d641d50b33d..c7439a88661 100644 --- a/doc/administration/backup_restore/restore_gitlab.md +++ b/doc/administration/backup_restore/restore_gitlab.md @@ -450,3 +450,19 @@ For more information, see: - [Having different owners](https://www.postgresql.org/message-id/2039.1177339749@sss.pgh.pa.us). - Stack Overflow: [Resulting errors](https://stackoverflow.com/questions/4368789/error-must-be-owner-of-language-plpgsql). + +### Restoring fails due to Git server hook + +While restoring from backup, you can encounter an error when the following are true: + +- A Git Server Hook (`custom_hook`) is configured using the method for [GitLab version 15.10 and earlier](../server_hooks.md) +- Your GitLab version is on version 15.11 and later +- You created symlinks to a directory outside of the GitLab-managed locations + +The error looks like: + +```plaintext +{"level":"fatal","msg":"restore: pipeline: 1 failures encountered:\n - @hashed/path/to/hashed_repository.git (path/to_project): manager: restore custom hooks, \"@hashed/path/to/hashed_repository/<BackupTimestamp>_<GitLabVersion>-ee/001.custom_hooks.tar\": rpc error: code = Internal desc = setting custom hooks: generating prepared vote: walking directory: copying file to hash: read /mnt/gitlab-app/git-data/repositories/+gitaly/tmp/default-repositories.old.<timestamp>.<temporaryfolder>/custom_hooks/compliance-triggers.d: is a directory\n","pid":3256017,"time":"2023-08-10T20:09:44.395Z"} +``` + +To resolve this, you can update the Git [server hooks](../server_hooks.md) for GitLab version 15.11 and later, and create a new backup. diff --git a/doc/administration/raketasks/maintenance.md b/doc/administration/raketasks/maintenance.md index e8c45df08c9..cdb70ca715b 100644 --- a/doc/administration/raketasks/maintenance.md +++ b/doc/administration/raketasks/maintenance.md @@ -176,13 +176,15 @@ Running? ... yes Checking Sidekiq ... Finished -Checking GitLab ... +Checking GitLab App... Database config exists? ... yes Database is SQLite ... no All migrations up? ... yes GitLab config exists? ... yes -GitLab config outdated? ... no +GitLab config up to date? ... no +Cable config exists? ... yes +Resque config exists? ... yes Log directory writable? ... yes Tmp directory writable? ... yes Init script exists? ... yes diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index bbf780907fe..0f9cdf7860c 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -14389,7 +14389,7 @@ CI/CD variables for a GitLab instance. ##### `CiJobTrace.htmlSummary` -HTML summary that contains the tail lines of the trace. +HTML summary that contains the tail lines of the trace. Returns at most 16KB of raw bytes from the trace. The returned string might start with an unexpected invalid UTF-8 code point due to truncation. WARNING: **Introduced** in 15.11. diff --git a/doc/api/rest/index.md b/doc/api/rest/index.md index 15bfbef78f1..17da691b720 100644 --- a/doc/api/rest/index.md +++ b/doc/api/rest/index.md @@ -540,6 +540,7 @@ options: | [Project jobs](../jobs.md#list-project-jobs) | `order_by=id`, `sort=desc` only | Authenticated users only. | | [Project audit events](../audit_events.md#retrieve-all-project-audit-events) | `order_by=id`, `sort=desc` only | Authenticated users only. | | [Projects](../projects.md) | `order_by=id` only | Authenticated and unauthenticated users. | +| [Users](../users.md) | `order_by=id`, `order_by=name`, `order_by=username` | Authenticated and unauthenticated users. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/419556) in GitLab 15.4 [with a flag](../../user/feature_flags.md)) named `api_keyset_pagination_multi_order`. Disabled by default. | ### Pagination response headers diff --git a/doc/api/users.md b/doc/api/users.md index e5e8f7be759..0652742c557 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -58,6 +58,8 @@ GET /users ] ``` +This endpoint supports [keyset pagination](rest/index.md#keyset-based-pagination). Keyset pagination [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/419556) in GitLab 16.5 [with a flag](../user/feature_flags.md) named `api_keyset_pagination_multi_order`. Disabled by default. + You can also use `?search=` to search for users by name, username, or public email. For example, `/users?search=John`. When you search for a: - Public email, you must use the full email address to get an exact match. A search might return a partial match. For example, if you search for the email `on@example.com`, the search can return both `on@example.com` and `jon@example.com`. diff --git a/doc/ci/review_apps/img/enable_review_app_v16.png b/doc/ci/review_apps/img/enable_review_app_v16.png Binary files differnew file mode 100644 index 00000000000..00e305d6a6b --- /dev/null +++ b/doc/ci/review_apps/img/enable_review_app_v16.png diff --git a/doc/ci/review_apps/index.md b/doc/ci/review_apps/index.md index f6607d0080f..f831c53c024 100644 --- a/doc/ci/review_apps/index.md +++ b/doc/ci/review_apps/index.md @@ -78,12 +78,12 @@ To use the review apps template: 1. On the left sidebar, select **Search or go to** and find the project you want to create a review app job for. -1. Select **Build > Environments**. +1. Select **Operate > Environments**. 1. Select **Enable review apps**. 1. Copy the provided code snippet and paste it into your `.gitlab-ci.yml` file: - ![enable review apps modal](img/enable_review_app_v12_8.png) + ![enable review apps modal](img/enable_review_app_v16.png) You can edit this template as needed. diff --git a/lib/api/internal/pages.rb b/lib/api/internal/pages.rb index 5664a3df589..971b76279a1 100644 --- a/lib/api/internal/pages.rb +++ b/lib/api/internal/pages.rb @@ -36,16 +36,7 @@ module API virtual_domain = ::Gitlab::Pages::VirtualHostFinder.new(params[:host]).execute no_content! unless virtual_domain - if virtual_domain.cache_key.present? - # Cache context is not added to make it easier to expire the cache with - # Gitlab::Pages::CacheControl - present_cached virtual_domain, - cache_context: nil, - with: Entities::Internal::Pages::VirtualDomain, - expires_in: ::Gitlab::Pages::CacheControl::EXPIRE - else - present virtual_domain, with: Entities::Internal::Pages::VirtualDomain - end + present virtual_domain, with: Entities::Internal::Pages::VirtualDomain end end end diff --git a/lib/api/users.rb b/lib/api/users.rb index f23865c6d90..a01ace3a9c3 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -141,7 +141,11 @@ module API users = users.preload(:user_detail) - present paginate(users), options + if Feature.enabled?(:api_keyset_pagination_multi_order) + present paginate_with_strategies(users), options + else + present paginate(users), options + end end # rubocop: enable CodeReuse/ActiveRecord diff --git a/lib/gitlab/ci/trace.rb b/lib/gitlab/ci/trace.rb index 494ba75a57d..f4ba9100812 100644 --- a/lib/gitlab/ci/trace.rb +++ b/lib/gitlab/ci/trace.rb @@ -30,9 +30,9 @@ module Gitlab @job = job end - def html(last_lines: nil) + def html(last_lines: nil, max_size: nil) read do |stream| - stream.html(last_lines: last_lines) + stream.html(last_lines: last_lines, max_size: max_size) end end diff --git a/lib/gitlab/ci/trace/stream.rb b/lib/gitlab/ci/trace/stream.rb index dd435ba05b7..ef494a79d9a 100644 --- a/lib/gitlab/ci/trace/stream.rb +++ b/lib/gitlab/ci/trace/stream.rb @@ -53,18 +53,20 @@ module Gitlab append(data, 0) end - def raw(last_lines: nil) + def raw(last_lines: nil, max_size: nil) return unless valid? - if last_lines.to_i > 0 + if max_size.to_i > 0 + read_last_lines_with_max_size(last_lines, max_size) + elsif last_lines.to_i > 0 read_last_lines(last_lines) else stream.read end.force_encoding(Encoding.default_external) end - def html(last_lines: nil) - text = raw(last_lines: last_lines) + def html(last_lines: nil, max_size: nil) + text = raw(last_lines: last_lines, max_size: max_size) buffer = StringIO.new(text) ::Gitlab::Ci::Ansi2html.convert(buffer).html end @@ -117,6 +119,37 @@ module Gitlab to_enum(:reverse_line).first(limit).reverse.join end + def read_last_lines_with_max_size(limit, max_size) + linesleft = limit + result = '' + + reverse_line_with_max_size(max_size) do |line| + result = line + result + unless linesleft.nil? + linesleft -= 1 + break if linesleft <= 0 + end + end + + result + end + + def reverse_line_with_max_size(max_size) + stream.seek(0, IO::SEEK_END) + debris = '' + sizeleft = max_size + + until sizeleft <= 0 || (buf = read_backward([BUFFER_SIZE, sizeleft].min)).empty? + sizeleft -= buf.bytesize + debris, *lines = (buf + debris).each_line.to_a + lines.reverse_each do |line| + yield(line.force_encoding(Encoding.default_external)) + end + end + + yield(debris.force_encoding(Encoding.default_external)) unless debris.empty? + end + def reverse_line stream.seek(0, IO::SEEK_END) debris = '' diff --git a/lib/gitlab/database/tables_truncate.rb b/lib/gitlab/database/tables_truncate.rb index 7ae1981fa2b..f91146fff3d 100644 --- a/lib/gitlab/database/tables_truncate.rb +++ b/lib/gitlab/database/tables_truncate.rb @@ -5,7 +5,7 @@ module Gitlab class TablesTruncate GITLAB_SCHEMAS_TO_IGNORE = %i[gitlab_geo gitlab_embedding].freeze - def initialize(database_name:, min_batch_size:, logger: nil, until_table: nil, dry_run: false) + def initialize(database_name:, min_batch_size: 5, logger: nil, until_table: nil, dry_run: false) @database_name = database_name @min_batch_size = min_batch_size @logger = logger @@ -19,19 +19,6 @@ module Gitlab logger&.info "DRY RUN:" if dry_run - schemas_for_connection = Gitlab::Database.gitlab_schemas_for_connection(connection) - tables_to_truncate = Gitlab::Database::GitlabSchema.tables_to_schema.reject do |_, schema_name| - GITLAB_SCHEMAS_TO_IGNORE.union(schemas_for_connection).include?(schema_name) - end.keys - - Gitlab::Database::SharedModel.using_connection(connection) do - Postgresql::DetachedPartition.find_each do |detached_partition| - next if GITLAB_SCHEMAS_TO_IGNORE.union(schemas_for_connection).include?(detached_partition.table_schema) - - tables_to_truncate << detached_partition.fully_qualified_table_name - end - end - tables_sorted = Gitlab::Database::TablesSortedByForeignKeys.new(connection, tables_to_truncate).execute # Checking if all the tables have the write-lock triggers # to make sure we are deleting the right tables on the right database. @@ -63,10 +50,41 @@ module Gitlab truncate_tables_in_batches(tables_sorted) end + def needs_truncation? + return false if single_database_setup? + + sql = tables_to_truncate.map { |table_name| "(SELECT EXISTS( SELECT * FROM #{table_name} ))" }.join("\nUNION\n") + + result = with_suppressed_query_analyzers do + connection.execute(sql).to_a + end + + result.to_a.any? { |row| row['exists'] == true } + end + private attr_accessor :database_name, :min_batch_size, :logger, :dry_run, :until_table + def tables_to_truncate + @tables_to_truncate ||= begin + schemas_for_connection = Gitlab::Database.gitlab_schemas_for_connection(connection) + tables = Gitlab::Database::GitlabSchema.tables_to_schema.reject do |_, schema_name| + GITLAB_SCHEMAS_TO_IGNORE.union(schemas_for_connection).include?(schema_name) + end.keys + + Gitlab::Database::SharedModel.using_connection(connection) do + Postgresql::DetachedPartition.find_each do |detached_partition| + next if GITLAB_SCHEMAS_TO_IGNORE.union(schemas_for_connection).include?(detached_partition.table_schema) + + tables << detached_partition.fully_qualified_table_name + end + end + + tables + end + end + def connection @connection ||= Gitlab::Database.database_base_models[database_name].connection end @@ -133,6 +151,12 @@ module Gitlab ci_base_model = Gitlab::Database.database_base_models[:ci] !!Gitlab::Database.db_config_share_with(ci_base_model.connection_db_config) end + + def with_suppressed_query_analyzers(&block) + Gitlab::Database::QueryAnalyzers::GitlabSchemasValidateConnection.with_suppressed do + Gitlab::Database::QueryAnalyzers::Ci::PartitioningRoutingAnalyzer.with_suppressed(&block) + end + end end end end diff --git a/lib/gitlab/event_store.rb b/lib/gitlab/event_store.rb index 474aac8073e..1e397b52ddf 100644 --- a/lib/gitlab/event_store.rb +++ b/lib/gitlab/event_store.rb @@ -36,26 +36,6 @@ module Gitlab store.subscribe ::MergeRequests::UpdateHeadPipelineWorker, to: ::Ci::PipelineCreatedEvent store.subscribe ::Namespaces::UpdateRootStatisticsWorker, to: ::Projects::ProjectDeletedEvent - store.subscribe ::Pages::InvalidateDomainCacheWorker, to: ::Pages::PageDeployedEvent - store.subscribe ::Pages::InvalidateDomainCacheWorker, to: ::Pages::PageDeletedEvent - store.subscribe ::Pages::InvalidateDomainCacheWorker, to: ::Projects::ProjectDeletedEvent - store.subscribe ::Pages::InvalidateDomainCacheWorker, to: ::Projects::ProjectCreatedEvent - store.subscribe ::Pages::InvalidateDomainCacheWorker, to: ::Projects::ProjectPathChangedEvent - store.subscribe ::Pages::InvalidateDomainCacheWorker, to: ::Projects::ProjectArchivedEvent - store.subscribe ::Pages::InvalidateDomainCacheWorker, to: ::Projects::ProjectTransferedEvent - store.subscribe ::Pages::InvalidateDomainCacheWorker, - to: ::Projects::ProjectAttributesChangedEvent, - if: -> (event) { event.pages_related? } - store.subscribe ::Pages::InvalidateDomainCacheWorker, - to: ::Projects::ProjectFeaturesChangedEvent, - if: -> (event) { event.pages_related? } - store.subscribe ::Pages::InvalidateDomainCacheWorker, to: ::Groups::GroupTransferedEvent - store.subscribe ::Pages::InvalidateDomainCacheWorker, to: ::Groups::GroupPathChangedEvent - store.subscribe ::Pages::InvalidateDomainCacheWorker, to: ::Groups::GroupDeletedEvent - store.subscribe ::Pages::InvalidateDomainCacheWorker, to: ::PagesDomains::PagesDomainDeletedEvent - store.subscribe ::Pages::InvalidateDomainCacheWorker, to: ::PagesDomains::PagesDomainUpdatedEvent - store.subscribe ::Pages::InvalidateDomainCacheWorker, to: ::PagesDomains::PagesDomainCreatedEvent - store.subscribe ::MergeRequests::CreateApprovalEventWorker, to: ::MergeRequests::ApprovedEvent store.subscribe ::MergeRequests::CreateApprovalNoteWorker, to: ::MergeRequests::ApprovedEvent store.subscribe ::MergeRequests::ResolveTodosAfterApprovalWorker, to: ::MergeRequests::ApprovedEvent diff --git a/lib/gitlab/pages.rb b/lib/gitlab/pages.rb index 700a39c6b41..defe054d631 100644 --- a/lib/gitlab/pages.rb +++ b/lib/gitlab/pages.rb @@ -5,6 +5,7 @@ module Gitlab VERSION = File.read(Rails.root.join("GITLAB_PAGES_VERSION")).strip.freeze INTERNAL_API_REQUEST_HEADER = 'Gitlab-Pages-Api-Request' MAX_SIZE = 1.terabyte + DEPLOYMENT_EXPIRATION = 24.hours include JwtAuthenticatable diff --git a/lib/gitlab/pages/virtual_host_finder.rb b/lib/gitlab/pages/virtual_host_finder.rb index 88ee0e44c00..e9ac86f0d47 100644 --- a/lib/gitlab/pages/virtual_host_finder.rb +++ b/lib/gitlab/pages/virtual_host_finder.rb @@ -38,15 +38,9 @@ module Gitlab return if namespace.blank? - cache = if Feature.enabled?(:cache_pages_domain_api, namespace) - ::Gitlab::Pages::CacheControl.for_namespace(namespace.id) - end - ::Pages::VirtualDomain.new( trim_prefix: namespace.full_path, - projects: namespace.all_projects_with_pages, - cache: cache - ) + projects: namespace.all_projects_with_pages) end def by_custom_domain(host) @@ -54,15 +48,7 @@ module Gitlab return unless domain&.pages_deployed? - cache = if Feature.enabled?(:cache_pages_domain_api, domain.project.root_namespace) - ::Gitlab::Pages::CacheControl.for_domain(domain.id) - end - - ::Pages::VirtualDomain.new( - projects: [domain.project], - domain: domain, - cache: cache - ) + ::Pages::VirtualDomain.new(projects: [domain.project], domain: domain) end end end diff --git a/lib/gitlab/pagination/cursor_based_keyset.rb b/lib/gitlab/pagination/cursor_based_keyset.rb index ee8259cc671..592f635c14e 100644 --- a/lib/gitlab/pagination/cursor_based_keyset.rb +++ b/lib/gitlab/pagination/cursor_based_keyset.rb @@ -10,6 +10,20 @@ module Gitlab ::Packages::BuildInfo => { id: :desc } }.freeze + SUPPORTED_MULTI_ORDERING = { + Group => { name: [:asc] }, + AuditEvent => { id: [:desc] }, + User => { + id: [:asc, :desc], + name: [:asc, :desc], + username: [:asc, :desc], + created_at: [:asc, :desc], + updated_at: [:asc, :desc] + }, + ::Ci::Build => { id: [:desc] }, + ::Packages::BuildInfo => { id: [:desc] } + }.freeze + # Relation types that are enforced in this list # enforce the use of keyset pagination, thus erroring out requests # made with offset pagination above a certain limit. @@ -19,7 +33,11 @@ module Gitlab ENFORCED_TYPES = [Group].freeze def self.available_for_type?(relation) - SUPPORTED_ORDERING.key?(relation.klass) + if Feature.enabled?(:api_keyset_pagination_multi_order) + SUPPORTED_MULTI_ORDERING.key?(relation.klass) + else + SUPPORTED_ORDERING.key?(relation.klass) + end end def self.available?(cursor_based_request_context, relation) @@ -32,9 +50,16 @@ module Gitlab end def self.order_satisfied?(relation, cursor_based_request_context) - order_by_from_request = cursor_based_request_context.order_by + if Feature.enabled?(:api_keyset_pagination_multi_order) + order_by_from_request = cursor_based_request_context.order + sort_from_request = cursor_based_request_context.sort + + SUPPORTED_MULTI_ORDERING[relation.klass][order_by_from_request]&.include?(sort_from_request) + else + order_by_from_request = cursor_based_request_context.order_by - SUPPORTED_ORDERING[relation.klass] == order_by_from_request + SUPPORTED_ORDERING[relation.klass] == order_by_from_request + end end private_class_method :order_satisfied? end diff --git a/lib/gitlab/pagination/keyset/cursor_based_request_context.rb b/lib/gitlab/pagination/keyset/cursor_based_request_context.rb index 41b90846345..f90574c86ab 100644 --- a/lib/gitlab/pagination/keyset/cursor_based_request_context.rb +++ b/lib/gitlab/pagination/keyset/cursor_based_request_context.rb @@ -32,6 +32,14 @@ module Gitlab def order_by { (params[:order_by]&.to_sym || DEFAULT_SORT_COLUMN) => (params[:sort]&.to_sym || DEFAULT_SORT_DIRECTION) } end + + def order + params[:order_by]&.to_sym || DEFAULT_SORT_COLUMN + end + + def sort + params[:sort]&.to_sym || DEFAULT_SORT_DIRECTION + end end end end diff --git a/lib/system_check/app/table_truncate_check.rb b/lib/system_check/app/table_truncate_check.rb new file mode 100644 index 00000000000..57b5950c21d --- /dev/null +++ b/lib/system_check/app/table_truncate_check.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module SystemCheck + module App + class TableTruncateCheck < SystemCheck::BaseCheck + set_name 'Tables are truncated?' + + def skip? + Gitlab::Database.database_mode != Gitlab::Database::MODE_MULTIPLE_DATABASES + end + + def check? + @rake_tasks = [] + Gitlab::Database.database_base_models_with_gitlab_shared.keys.each_with_object({}) do |database_name, _h| + if Gitlab::Database::TablesTruncate.new(database_name: database_name).needs_truncation? + @rake_tasks << "gitlab:db:truncate_legacy_tables:#{database_name}" + end + end + + @rake_tasks.empty? + end + + def show_error + try_fixing_it( + sudo_gitlab("bundle exec rake #{@rake_tasks.join(' ')}") + ) + for_more_information( + "doc/development/database/multiple_databases.md in section 'Truncating tables'" + ) + fix_and_rerun + end + end + end +end diff --git a/lib/system_check/rake_task/app_task.rb b/lib/system_check/rake_task/app_task.rb index 20332d4b24b..8e711138856 100644 --- a/lib/system_check/rake_task/app_task.rb +++ b/lib/system_check/rake_task/app_task.rb @@ -13,6 +13,7 @@ module SystemCheck def self.checks [ SystemCheck::App::DatabaseConfigExistsCheck, + SystemCheck::App::TableTruncateCheck, SystemCheck::App::MigrationsAreUpCheck, SystemCheck::App::OrphanedGroupMembersCheck, SystemCheck::App::GitlabConfigExistsCheck, diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 9957b040482..9d495689696 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -12367,6 +12367,9 @@ msgstr "" msgid "ComplianceStandardsAdherence|The following features help satisfy this requirement." msgstr "" +msgid "ComplianceStandardsAdherence|Unable to load the standards adherence report. Refresh the page and try again." +msgstr "" + msgid "ComplianceStandardsAdherence|Update approval settings in the project's merge request settings to satisfy this requirement." msgstr "" diff --git a/qa/qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb deleted file mode 100644 index 7e73d09a148..00000000000 --- a/qa/qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: true - -module QA - RSpec.describe 'Verify', :runner, product_group: :pipeline_execution do - describe 'Code coverage statistics' do - let(:executor) { "qa-runner-#{Time.now.to_i}" } - let(:runner) { create(:project_runner, name: executor, tags: ['e2e-test']) } - - let(:merge_request) do - Resource::MergeRequest.fabricate_via_api! do |mr| - mr.project = runner.project - mr.file_name = '.gitlab-ci.yml' - mr.file_content = <<~EOF - test: - tags: [e2e-test] - coverage: '/\\d+\\.\\d+% covered/' - script: - - echo '66.67% covered' - EOF - end - end - - before do - Flow::Login.sign_in - end - - after do - runner.remove_via_api! - end - - it 'creates an MR with code coverage statistics', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348068' do - merge_request.visit! - - Page::MergeRequest::Show.perform do |mr_widget| - mr_widget.has_pipeline_status?('passed') - expect(mr_widget).to have_content('Test coverage 66.67%') - end - end - end - end -end diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 057146b31dd..d4f04105605 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -120,11 +120,11 @@ RSpec.describe Projects::IssuesController, :request_store, feature_category: :te allow(Kaminari.config).to receive(:default_per_page).and_return(1) end - it 'redirects to last page when out of bounds on non-html requests' do + it 'does not redirect when out of bounds on non-html requests' do get :index, params: params.merge(page: last_page + 1), format: 'atom' - expect(response).to have_gitlab_http_status(:redirect) - expect(response).to redirect_to(action: 'index', format: 'atom', page: last_page, state: 'opened') + expect(response).to have_gitlab_http_status(:ok) + expect(assigns(:issues).size).to eq(0) end end diff --git a/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb b/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb index edaa675c35a..e052d06c158 100644 --- a/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb +++ b/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb @@ -264,8 +264,6 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', before do forked_project.add_maintainer(user2) - stub_feature_flags(auto_merge_labels_mr_widget: false) - visit project_merge_request_path(project, merge_request) page.within('.merge-request-tabs') do diff --git a/spec/features/merge_request/user_sees_merge_widget_spec.rb b/spec/features/merge_request/user_sees_merge_widget_spec.rb index e64b6a5b672..1db09790e1c 100644 --- a/spec/features/merge_request/user_sees_merge_widget_spec.rb +++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb @@ -958,4 +958,21 @@ RSpec.describe 'Merge request > User sees merge widget', :js, feature_category: end end end + + context 'views MR when pipeline has code coverage enabled' do + let!(:pipeline) { create(:ci_pipeline, status: 'success', project: project, ref: merge_request.source_branch) } + let!(:build) { create(:ci_build, :success, :coverage, pipeline: pipeline) } + + before do + merge_request.update!(head_pipeline: pipeline) + + visit project_merge_request_path(project, merge_request) + end + + it 'shows the coverage' do + within '.ci-widget' do + expect(find_by_testid('pipeline-coverage')).to have_content('Test coverage 99.90% ') + end + end + end end diff --git a/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb b/spec/features/merge_request/user_sets_to_auto_merge_spec.rb index ebec8a6d2ea..4dc0c03aedc 100644 --- a/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb +++ b/spec/features/merge_request/user_sets_to_auto_merge_spec.rb @@ -2,7 +2,9 @@ require 'spec_helper' -RSpec.describe 'Merge request > User merges when pipeline succeeds', :js, feature_category: :code_review_workflow do +RSpec.describe 'Merge request > User sets to auto-merge', :js, feature_category: :code_review_workflow do + include ContentEditorHelpers + let(:project) { create(:project, :public, :repository) } let(:user) { project.creator } let(:merge_request) do @@ -32,22 +34,23 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js, featur context 'when there is active pipeline for merge request' do before do create(:ci_build, pipeline: pipeline) - stub_feature_flags(auto_merge_labels_mr_widget: true) sign_in(user) visit project_merge_request_path(project, merge_request) end - describe 'enabling Merge when pipeline succeeds' do + describe 'setting to auto-merge when pipeline succeeds' do shared_examples 'Set to auto-merge activator' do - it 'activates the Merge when pipeline succeeds feature', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/410055' do + it 'activates auto-merge feature', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/410055' do + close_rich_text_promo_popover_if_present + expect(page).to have_content 'Set to auto-merge' click_button "Set to auto-merge" + wait_for_requests expect(page).to have_content "Set by #{user.name} to be merged automatically when the pipeline succeeds" expect(page).to have_content "Source branch will not be deleted" expect(page).to have_selector ".js-cancel-auto-merge" - visit project_merge_request_path(project, merge_request) # Needed to refresh the page - expect(page).to have_content /enabled an automatic merge when the pipeline for \h{8} succeeds/i + expect(page).to have_content(/enabled an automatic merge when the pipeline for \h{8} succeeds/i) end end @@ -57,6 +60,7 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js, featur context 'when enabled after it was previously canceled' do before do + close_rich_text_promo_popover_if_present click_button "Set to auto-merge" wait_for_requests @@ -64,14 +68,12 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js, featur click_button "Cancel auto-merge" wait_for_requests - - expect(page).to have_content 'Set to auto-merge' end it_behaves_like 'Set to auto-merge activator' end - context 'when it was enabled and then canceled' do + context 'when it is enabled and then canceled' do let(:merge_request) do create( :merge_request_with_diffs, @@ -94,7 +96,7 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js, featur end end - context 'when merge when pipeline succeeds is enabled' do + context 'when there is an active pipeline' do let(:merge_request) do create( :merge_request_with_diffs, @@ -112,12 +114,13 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js, featur end before do - stub_feature_flags(auto_merge_labels_mr_widget: true) sign_in user visit project_merge_request_path(project, merge_request) end - it 'allows to cancel the automatic merge', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/410494' do + it 'allows to cancel the auto-merge', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/410055' do + close_rich_text_promo_popover_if_present + click_button "Cancel auto-merge" expect(page).to have_button "Set to auto-merge" @@ -128,22 +131,13 @@ RSpec.describe 'Merge request > User merges when pipeline succeeds', :js, featur end end - context 'when pipeline is not active' do - it 'does not allow to enable merge when pipeline succeeds' do - stub_feature_flags(auto_merge_labels_mr_widget: false) - - visit project_merge_request_path(project, merge_request) - - expect(page).not_to have_link 'Merge when pipeline succeeds' + context 'when there is no active pipeline' do + before do + sign_in user + visit project_merge_request_path(project, merge_request.reload) end - end - - context 'when pipeline is not active and auto_merge_labels_mr_widget on' do - it 'does not allow to enable merge when pipeline succeeds' do - stub_feature_flags(auto_merge_labels_mr_widget: true) - - visit project_merge_request_path(project, merge_request) + it 'does not allow to set to auto-merge' do expect(page).not_to have_link 'Set to auto-merge' end end diff --git a/spec/frontend/ci/job_details/components/job_header_spec.js b/spec/frontend/ci/job_details/components/job_header_spec.js index 923f463086a..6fc55732353 100644 --- a/spec/frontend/ci/job_details/components/job_header_spec.js +++ b/spec/frontend/ci/job_details/components/job_header_spec.js @@ -16,6 +16,7 @@ describe('Header CI Component', () => { text: 'failed', details_path: 'path', }, + name: 'Job build_job', time: '2017-05-08T14:57:39.781Z', user: { id: 1234, @@ -25,7 +26,7 @@ describe('Header CI Component', () => { email: 'foo@bar.com', avatar_url: 'link', }, - hasSidebarButton: true, + shouldRenderTriggeredLabel: true, }; const findCiBadgeLink = () => wrapper.findComponent(CiBadgeLink); @@ -34,7 +35,7 @@ describe('Header CI Component', () => { const findSidebarToggleBtn = () => wrapper.findComponent(GlButton); const findStatusTooltip = () => wrapper.findComponent(GlTooltip); const findActionButtons = () => wrapper.findByTestId('job-header-action-buttons'); - const findHeaderItemText = () => wrapper.findByTestId('job-header-item-text'); + const findJobName = () => wrapper.findByTestId('job-name'); const createComponent = (props, slots) => { wrapper = extendedWrapper( @@ -50,7 +51,7 @@ describe('Header CI Component', () => { describe('render', () => { beforeEach(() => { - createComponent({ itemName: 'Pipeline' }); + createComponent(); }); it('should render status badge', () => { @@ -72,7 +73,7 @@ describe('Header CI Component', () => { describe('user avatar', () => { beforeEach(() => { - createComponent({ itemName: 'Pipeline' }); + createComponent(); }); it('contains the username', () => { @@ -93,7 +94,6 @@ describe('Header CI Component', () => { beforeEach(() => { createComponent({ - itemName: 'Pipeline', user: { ...defaultProps.user, status: { message: STATUS_MESSAGE } }, }); }); @@ -108,7 +108,6 @@ describe('Header CI Component', () => { beforeEach(() => { createComponent({ - itemName: 'Pipeline', user: { ...defaultProps.user, id: `gid://gitlab/User/${1}` }, }); }); @@ -125,29 +124,19 @@ describe('Header CI Component', () => { }); }); - describe('with item id', () => { + describe('job name', () => { beforeEach(() => { - createComponent({ itemName: 'Pipeline', itemId: '123' }); + createComponent(); }); - it('should render item name and id', () => { - expect(findHeaderItemText().text()).toBe('Pipeline #123'); - }); - }); - - describe('without item id', () => { - beforeEach(() => { - createComponent({ itemName: 'Job build_job' }); - }); - - it('should render item name', () => { - expect(findHeaderItemText().text()).toBe('Job build_job'); + it('should render the job name', () => { + expect(findJobName().text()).toBe('Job build_job'); }); }); describe('slot', () => { it('should render header action buttons', () => { - createComponent({ itemName: 'Job build_job' }, { slots: { default: 'Test Actions' } }); + createComponent({}, { slots: { default: 'Test Actions' } }); expect(findActionButtons().exists()).toBe(true); expect(findActionButtons().text()).toBe('Test Actions'); @@ -156,7 +145,7 @@ describe('Header CI Component', () => { describe('shouldRenderTriggeredLabel', () => { it('should render created keyword when the shouldRenderTriggeredLabel is false', () => { - createComponent({ shouldRenderTriggeredLabel: false, itemName: 'Job build_job' }); + createComponent({ shouldRenderTriggeredLabel: false }); expect(wrapper.text()).toContain('created'); expect(wrapper.text()).not.toContain('started'); diff --git a/spec/graphql/types/ci/job_trace_type_spec.rb b/spec/graphql/types/ci/job_trace_type_spec.rb index 5f4f3097ace..69123445b8b 100644 --- a/spec/graphql/types/ci/job_trace_type_spec.rb +++ b/spec/graphql/types/ci/job_trace_type_spec.rb @@ -26,7 +26,7 @@ RSpec.describe GitlabSchema.types['CiJobTrace'], feature_category: :continuous_i it 'shows the correct trace contents' do expect_next_instance_of(Gitlab::Ci::Trace) do |trace| - expect(trace).to receive(:html).with(last_lines: 10).and_call_original + expect(trace).to receive(:html).with(last_lines: 10, max_size: 16384).and_call_original end is_expected.to eq('<span>BUILD TRACE</span>') @@ -48,7 +48,7 @@ RSpec.describe GitlabSchema.types['CiJobTrace'], feature_category: :continuous_i it 'shows the last 10 lines of trace contents' do expect_next_instance_of(Gitlab::Ci::Trace) do |trace| - expect(trace).to receive(:html).with(last_lines: 10).and_call_original + expect(trace).to receive(:html).with(last_lines: 10, max_size: 16384).and_call_original end is_expected.to eq expected_html_trace_contents(10) @@ -60,7 +60,7 @@ RSpec.describe GitlabSchema.types['CiJobTrace'], feature_category: :continuous_i it 'shows the last line of trace contents' do expect_next_instance_of(Gitlab::Ci::Trace) do |trace| - expect(trace).to receive(:html).with(last_lines: 1).and_call_original + expect(trace).to receive(:html).with(last_lines: 1, max_size: 16384).and_call_original end is_expected.to eq expected_html_trace_contents(1) @@ -72,7 +72,7 @@ RSpec.describe GitlabSchema.types['CiJobTrace'], feature_category: :continuous_i it 'shows the correct trace contents' do expect_next_instance_of(Gitlab::Ci::Trace) do |trace| - expect(trace).to receive(:html).with(last_lines: 10).and_call_original + expect(trace).to receive(:html).with(last_lines: 10, max_size: 16384).and_call_original end is_expected.to eq expected_html_trace_contents(10) @@ -84,12 +84,116 @@ RSpec.describe GitlabSchema.types['CiJobTrace'], feature_category: :continuous_i it 'shows the last 100 lines of trace contents' do expect_next_instance_of(Gitlab::Ci::Trace) do |trace| - expect(trace).to receive(:html).with(last_lines: 100).and_call_original + expect(trace).to receive(:html).with(last_lines: 100, max_size: 16384).and_call_original end is_expected.to eq expected_html_trace_contents(100) end end end + + context 'when trace contains long lines' do + before do + # Creates lines of "aaaaaaaa...aaaaaaaa" + job.trace.set((1..20).map { (1..1024).map { "a" }.join("") }.join("\n")) + end + + context 'when last_lines is lower than 16KB' do + let(:args) { {} } + + it 'shows the whole lines' do + expect_next_instance_of(Gitlab::Ci::Trace) do |trace| + expect(trace).to receive(:html).with(last_lines: 10, max_size: 16384).and_call_original + end + + is_expected.to eq "<span>#{(1..10).map { (1..1024).map { 'a' }.join('') }.join('<br/>')}</span>" + end + end + + context 'when last_lines is higher than 16KB' do + let(:args) { { last_lines: 20 } } + + it 'shows only the latest byte' do + expect_next_instance_of(Gitlab::Ci::Trace) do |trace| + expect(trace).to receive(:html).with(last_lines: 20, max_size: 16384).and_call_original + end + + is_expected.to eq "<span>#{(1..1009).map { 'a' }.join('')}<br/>" \ + "#{(1..15).map { (1..1024).map { 'a' }.join('') }.join('<br/>')}</span>" + end + end + + context 'when FF graphql_job_trace_html_summary_max_size is disabled' do + before do + stub_feature_flags(graphql_job_trace_html_summary_max_size: false) + end + + let(:args) { { last_lines: 20 } } + + it 'does not limit the read size from the raw trace' do + expect_next_instance_of(Gitlab::Ci::Trace) do |trace| + expect(trace).to receive(:html).with(last_lines: 20, max_size: nil).and_call_original + end + + is_expected.to eq "<span>#{(1..20).map { (1..1024).map { 'a' }.join('') }.join('<br/>')}</span>" + end + end + + context 'when trace is cut in middle of a line' do + let(:args) { {} } + + before do + stub_const('Types::Ci::JobTraceType::MAX_SIZE_B', 1536) + end + + it 'shows only the latest byte' do + expect_next_instance_of(Gitlab::Ci::Trace) do |trace| + expect(trace).to receive(:html).with(last_lines: 10, max_size: 1536).and_call_original + end + + is_expected.to eq "<span>#{(1..511).map { 'a' }.join('')}<br/>#{(1..1024).map { 'a' }.join('')}</span>" + end + end + + context 'when trace is cut at end of a line' do + let(:args) { {} } + + before do + stub_const('Types::Ci::JobTraceType::MAX_SIZE_B', 2050) + end + + it 'shows only the latest byte' do + expect_next_instance_of(Gitlab::Ci::Trace) do |trace| + expect(trace).to receive(:html).with(last_lines: 10, max_size: 2050).and_call_original + end + + is_expected.to eq "<span><br/>#{(1..2).map { (1..1024).map { 'a' }.join('') }.join('<br/>')}</span>" + end + end + end + + context 'when trace contains multi-bytes UTF-8' do + before do + # Creates lines of 4 pound symbol, pound symbol is 2 byte wise in UTF-8 + # Append an "a" (1 byte character) at the end to cut in the middle of UTF-8 + job.trace.set((1..20).map { (1..4).map { "£" }.join("") }.join("\n")) + end + + context 'when cut in the middle of a codepoint' do + before do + stub_const('Types::Ci::JobTraceType::MAX_SIZE_B', 5) + end + + let(:args) { {} } + + it 'shows a single "invalid utf-8" symbol' do + expect_next_instance_of(Gitlab::Ci::Trace) do |trace| + expect(trace).to receive(:html).with(last_lines: 10, max_size: 5).and_call_original + end + + is_expected.to eq "<span>�££</span>" + end + end + end end end diff --git a/spec/lib/gitlab/ci/trace/stream_spec.rb b/spec/lib/gitlab/ci/trace/stream_spec.rb index d65b6fb41f6..9439d29aa11 100644 --- a/spec/lib/gitlab/ci/trace/stream_spec.rb +++ b/spec/lib/gitlab/ci/trace/stream_spec.rb @@ -243,6 +243,56 @@ RSpec.describe Gitlab::Ci::Trace::Stream, :clean_gitlab_redis_cache do expect(result.encoding).to eq(Encoding.default_external) end end + + context 'limit max size' do + before do + # specifying BUFFER_SIZE forces to seek backwards + allow(described_class).to receive(:BUFFER_SIZE) + .and_return(2) + end + + it 'returns every lines with respect of the size' do + all_lines = lines.join + max_size = all_lines.bytesize.div(2) + result = stream.raw(max_size: max_size) + + expect(result.bytes).to eq(all_lines.bytes[-max_size..]) + expect(result.lines.count).to be > 1 + expect(result.encoding).to eq(Encoding.default_external) + end + + it 'returns everything if trying to get too many bytes' do + all_lines = lines.join + result = stream.raw(max_size: all_lines.bytesize * 2) + + expect(result).to eq(all_lines) + expect(result.encoding).to eq(Encoding.default_external) + end + end + + context 'limit max lines and max size' do + before do + # specifying BUFFER_SIZE forces to seek backwards + allow(described_class).to receive(:BUFFER_SIZE) + .and_return(2) + end + + it 'returns max lines if max size is greater' do + result = stream.raw(last_lines: 2, max_size: lines.join.bytesize * 2) + + expect(result).to eq(lines.last(2).join) + expect(result.encoding).to eq(Encoding.default_external) + end + + it 'returns max size if max lines is greater' do + all_lines = lines.join + max_size = all_lines.bytesize.div(2) + result = stream.raw(last_lines: lines.size * 2, max_size: max_size) + + expect(result.bytes).to eq(all_lines.bytes[-max_size..]) + expect(result.encoding).to eq(Encoding.default_external) + end + end end let(:path) { __FILE__ } diff --git a/spec/lib/gitlab/database/tables_truncate_spec.rb b/spec/lib/gitlab/database/tables_truncate_spec.rb index 04bec50088d..e41c7d34378 100644 --- a/spec/lib/gitlab/database/tables_truncate_spec.rb +++ b/spec/lib/gitlab/database/tables_truncate_spec.rb @@ -9,6 +9,7 @@ RSpec.describe Gitlab::Database::TablesTruncate, :reestablished_active_record_ba let(:min_batch_size) { 1 } let(:main_connection) { ApplicationRecord.connection } let(:ci_connection) { Ci::ApplicationRecord.connection } + let(:logger) { instance_double(Logger) } # Main Database let(:main_db_main_item_model) { table("_test_gitlab_main_items", database: "main") } @@ -32,8 +33,123 @@ RSpec.describe Gitlab::Database::TablesTruncate, :reestablished_active_record_ba table("gitlab_partitions_dynamic._test_gitlab_hook_logs_202201", database: "ci") end + before do + skip_if_shared_database(:ci) + + # Creating some test tables on the main database + main_tables_sql = <<~SQL + CREATE TABLE _test_gitlab_main_items (id serial NOT NULL PRIMARY KEY); + + CREATE TABLE _test_gitlab_main_references ( + id serial NOT NULL PRIMARY KEY, + item_id BIGINT NOT NULL, + CONSTRAINT fk_constrained_1 FOREIGN KEY(item_id) REFERENCES _test_gitlab_main_items(id) + ); + + CREATE TABLE _test_gitlab_hook_logs ( + id bigserial not null, + created_at timestamptz not null, + item_id BIGINT NOT NULL, + PRIMARY KEY (id, created_at), + CONSTRAINT fk_constrained_1 FOREIGN KEY(item_id) REFERENCES _test_gitlab_main_items(id) + ) PARTITION BY RANGE(created_at); + + CREATE TABLE gitlab_partitions_dynamic._test_gitlab_hook_logs_202201 + PARTITION OF _test_gitlab_hook_logs + FOR VALUES FROM ('20220101') TO ('20220131'); + + CREATE TABLE gitlab_partitions_dynamic._test_gitlab_hook_logs_202202 + PARTITION OF _test_gitlab_hook_logs + FOR VALUES FROM ('20220201') TO ('20220228'); + + ALTER TABLE _test_gitlab_hook_logs DETACH PARTITION gitlab_partitions_dynamic._test_gitlab_hook_logs_202201; + SQL + + execute_on_each_database(main_tables_sql) + + ci_tables_sql = <<~SQL + CREATE TABLE _test_gitlab_ci_items (id serial NOT NULL PRIMARY KEY); + + CREATE TABLE _test_gitlab_ci_references ( + id serial NOT NULL PRIMARY KEY, + item_id BIGINT NOT NULL, + CONSTRAINT fk_constrained_1 FOREIGN KEY(item_id) REFERENCES _test_gitlab_ci_items(id) + ); + SQL + + execute_on_each_database(ci_tables_sql) + + internal_tables_sql = <<~SQL + CREATE TABLE _test_gitlab_shared_items (id serial NOT NULL PRIMARY KEY); + SQL + + execute_on_each_database(internal_tables_sql) + + # Filling the tables + 5.times do |i| + # Main Database + main_db_main_item_model.create!(id: i) + main_db_main_reference_model.create!(item_id: i) + main_db_ci_item_model.create!(id: i) + main_db_ci_reference_model.create!(item_id: i) + main_db_shared_item_model.create!(id: i) + main_db_partitioned_item.create!(item_id: i, created_at: '2022-02-02 02:00') + main_db_partitioned_item_detached.create!(item_id: i, created_at: '2022-01-01 01:00') + # CI Database + ci_db_main_item_model.create!(id: i) + ci_db_main_reference_model.create!(item_id: i) + ci_db_ci_item_model.create!(id: i) + ci_db_ci_reference_model.create!(item_id: i) + ci_db_shared_item_model.create!(id: i) + ci_db_partitioned_item.create!(item_id: i, created_at: '2022-02-02 02:00') + ci_db_partitioned_item_detached.create!(item_id: i, created_at: '2022-01-01 01:00') + end + + Gitlab::Database::SharedModel.using_connection(main_connection) do + Postgresql::DetachedPartition.create!( + table_name: '_test_gitlab_hook_logs_202201', + drop_after: Time.current + ) + end + + Gitlab::Database::SharedModel.using_connection(ci_connection) do + Postgresql::DetachedPartition.create!( + table_name: '_test_gitlab_hook_logs_202201', + drop_after: Time.current + ) + end + + allow(Gitlab::Database::GitlabSchema).to receive(:tables_to_schema).and_return( + { + "_test_gitlab_main_items" => :gitlab_main, + "_test_gitlab_main_references" => :gitlab_main, + "_test_gitlab_hook_logs" => :gitlab_main, + "_test_gitlab_ci_items" => :gitlab_ci, + "_test_gitlab_ci_references" => :gitlab_ci, + "_test_gitlab_shared_items" => :gitlab_shared, + "_test_gitlab_geo_items" => :gitlab_geo + } + ) + + allow(Gitlab::Database::GitlabSchema).to receive(:views_and_tables_to_schema).and_return( + { + "_test_gitlab_main_items" => :gitlab_main, + "_test_gitlab_main_references" => :gitlab_main, + "_test_gitlab_hook_logs" => :gitlab_main, + "_test_gitlab_ci_items" => :gitlab_ci, + "_test_gitlab_ci_references" => :gitlab_ci, + "_test_gitlab_shared_items" => :gitlab_shared, + "_test_gitlab_geo_items" => :gitlab_geo, + "detached_partitions" => :gitlab_shared, + "postgres_foreign_keys" => :gitlab_shared, + "postgres_partitions" => :gitlab_shared + } + ) + + allow(logger).to receive(:info).with(any_args) + end + shared_examples 'truncating legacy tables on a database' do - let(:logger) { instance_double(Logger) } let(:dry_run) { false } let(:until_table) { nil } @@ -47,122 +163,6 @@ RSpec.describe Gitlab::Database::TablesTruncate, :reestablished_active_record_ba ).execute end - before do - skip_if_shared_database(:ci) - - # Creating some test tables on the main database - main_tables_sql = <<~SQL - CREATE TABLE _test_gitlab_main_items (id serial NOT NULL PRIMARY KEY); - - CREATE TABLE _test_gitlab_main_references ( - id serial NOT NULL PRIMARY KEY, - item_id BIGINT NOT NULL, - CONSTRAINT fk_constrained_1 FOREIGN KEY(item_id) REFERENCES _test_gitlab_main_items(id) - ); - - CREATE TABLE _test_gitlab_hook_logs ( - id bigserial not null, - created_at timestamptz not null, - item_id BIGINT NOT NULL, - PRIMARY KEY (id, created_at), - CONSTRAINT fk_constrained_1 FOREIGN KEY(item_id) REFERENCES _test_gitlab_main_items(id) - ) PARTITION BY RANGE(created_at); - - CREATE TABLE gitlab_partitions_dynamic._test_gitlab_hook_logs_202201 - PARTITION OF _test_gitlab_hook_logs - FOR VALUES FROM ('20220101') TO ('20220131'); - - CREATE TABLE gitlab_partitions_dynamic._test_gitlab_hook_logs_202202 - PARTITION OF _test_gitlab_hook_logs - FOR VALUES FROM ('20220201') TO ('20220228'); - - ALTER TABLE _test_gitlab_hook_logs DETACH PARTITION gitlab_partitions_dynamic._test_gitlab_hook_logs_202201; - SQL - - execute_on_each_database(main_tables_sql) - - ci_tables_sql = <<~SQL - CREATE TABLE _test_gitlab_ci_items (id serial NOT NULL PRIMARY KEY); - - CREATE TABLE _test_gitlab_ci_references ( - id serial NOT NULL PRIMARY KEY, - item_id BIGINT NOT NULL, - CONSTRAINT fk_constrained_1 FOREIGN KEY(item_id) REFERENCES _test_gitlab_ci_items(id) - ); - SQL - - execute_on_each_database(ci_tables_sql) - - internal_tables_sql = <<~SQL - CREATE TABLE _test_gitlab_shared_items (id serial NOT NULL PRIMARY KEY); - SQL - - execute_on_each_database(internal_tables_sql) - - # Filling the tables - 5.times do |i| - # Main Database - main_db_main_item_model.create!(id: i) - main_db_main_reference_model.create!(item_id: i) - main_db_ci_item_model.create!(id: i) - main_db_ci_reference_model.create!(item_id: i) - main_db_shared_item_model.create!(id: i) - main_db_partitioned_item.create!(item_id: i, created_at: '2022-02-02 02:00') - main_db_partitioned_item_detached.create!(item_id: i, created_at: '2022-01-01 01:00') - # CI Database - ci_db_main_item_model.create!(id: i) - ci_db_main_reference_model.create!(item_id: i) - ci_db_ci_item_model.create!(id: i) - ci_db_ci_reference_model.create!(item_id: i) - ci_db_shared_item_model.create!(id: i) - ci_db_partitioned_item.create!(item_id: i, created_at: '2022-02-02 02:00') - ci_db_partitioned_item_detached.create!(item_id: i, created_at: '2022-01-01 01:00') - end - - Gitlab::Database::SharedModel.using_connection(main_connection) do - Postgresql::DetachedPartition.create!( - table_name: '_test_gitlab_hook_logs_202201', - drop_after: Time.current - ) - end - - Gitlab::Database::SharedModel.using_connection(ci_connection) do - Postgresql::DetachedPartition.create!( - table_name: '_test_gitlab_hook_logs_202201', - drop_after: Time.current - ) - end - - allow(Gitlab::Database::GitlabSchema).to receive(:tables_to_schema).and_return( - { - "_test_gitlab_main_items" => :gitlab_main, - "_test_gitlab_main_references" => :gitlab_main, - "_test_gitlab_hook_logs" => :gitlab_main, - "_test_gitlab_ci_items" => :gitlab_ci, - "_test_gitlab_ci_references" => :gitlab_ci, - "_test_gitlab_shared_items" => :gitlab_shared, - "_test_gitlab_geo_items" => :gitlab_geo - } - ) - - allow(Gitlab::Database::GitlabSchema).to receive(:views_and_tables_to_schema).and_return( - { - "_test_gitlab_main_items" => :gitlab_main, - "_test_gitlab_main_references" => :gitlab_main, - "_test_gitlab_hook_logs" => :gitlab_main, - "_test_gitlab_ci_items" => :gitlab_ci, - "_test_gitlab_ci_references" => :gitlab_ci, - "_test_gitlab_shared_items" => :gitlab_shared, - "_test_gitlab_geo_items" => :gitlab_geo, - "detached_partitions" => :gitlab_shared, - "postgres_foreign_keys" => :gitlab_shared, - "postgres_partitions" => :gitlab_shared - } - ) - - allow(logger).to receive(:info).with(any_args) - end - context 'when the truncated tables are not locked for writes' do it 'raises an error that the tables are not locked for writes' do error_message = /is not locked for writes. Run the rake task gitlab:db:lock_writes first/ @@ -348,6 +348,50 @@ RSpec.describe Gitlab::Database::TablesTruncate, :reestablished_active_record_ba end end + describe '#needs_truncation?' do + let(:database_name) { 'ci' } + + subject { described_class.new(database_name: database_name).needs_truncation? } + + context 'when running in a single database mode' do + before do + skip_if_multiple_databases_are_setup(:ci) + end + + it { is_expected.to eq(false) } + end + + context 'when running in a multiple database mode' do + before do + skip_if_shared_database(:ci) + end + + context 'with main data in ci database' do + it { is_expected.to eq(true) } + end + + context 'with no main data in ci datatabase' do + before do + # Remove 'main' data in ci database + ci_connection.truncate_tables([:_test_gitlab_main_items, :_test_gitlab_main_references]) + end + + it { is_expected.to eq(false) } + + it 'supresses some QueryAnalyzers' do + expect( + Gitlab::Database::QueryAnalyzers::GitlabSchemasValidateConnection + ).to receive(:with_suppressed).and_call_original + expect( + Gitlab::Database::QueryAnalyzers::Ci::PartitioningRoutingAnalyzer + ).to receive(:with_suppressed).and_call_original + + subject + end + end + end + end + def geo_configured? !!ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, name: 'geo') end diff --git a/spec/lib/gitlab/pages/cache_control_spec.rb b/spec/lib/gitlab/pages/cache_control_spec.rb deleted file mode 100644 index 72240f52580..00000000000 --- a/spec/lib/gitlab/pages/cache_control_spec.rb +++ /dev/null @@ -1,88 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Gitlab::Pages::CacheControl, feature_category: :pages do - RSpec.shared_examples 'cache_control' do |type| - it { expect(subject.cache_key).to match(/pages_domain_for_#{type}_1_*/) } - - describe '#clear_cache', :use_clean_rails_redis_caching do - before do - Rails.cache.write("pages_domain_for_#{type}_1", ['settings-hash']) - Rails.cache.write("pages_domain_for_#{type}_1_settings-hash", 'payload') - end - - it 'clears the cache' do - cached_keys = [ - "pages_domain_for_#{type}_1_settings-hash", - "pages_domain_for_#{type}_1" - ] - - expect(::Gitlab::AppLogger) - .to receive(:info) - .with( - message: 'clear pages cache', - pages_keys: cached_keys, - pages_type: type, - pages_id: 1 - ) - - expect(Rails.cache) - .to receive(:delete_multi) - .with(cached_keys) - - subject.clear_cache - end - end - end - - describe '.for_namespace' do - subject(:cache_control) { described_class.for_namespace(1) } - - it_behaves_like 'cache_control', :namespace - end - - describe '.for_domain' do - subject(:cache_control) { described_class.for_domain(1) } - - it_behaves_like 'cache_control', :domain - end - - describe '#cache_key' do - it 'does not change the pages config' do - expect { described_class.new(type: :domain, id: 1).cache_key } - .not_to change(Gitlab.config, :pages) - end - - it 'is based on pages settings' do - access_control = Gitlab.config.pages.access_control - cache_key = described_class.new(type: :domain, id: 1).cache_key - - stub_config(pages: { access_control: !access_control }) - - expect(described_class.new(type: :domain, id: 1).cache_key).not_to eq(cache_key) - end - - it 'is based on the force_pages_access_control settings' do - force_pages_access_control = ::Gitlab::CurrentSettings.force_pages_access_control - cache_key = described_class.new(type: :domain, id: 1).cache_key - - ::Gitlab::CurrentSettings.force_pages_access_control = !force_pages_access_control - - expect(described_class.new(type: :domain, id: 1).cache_key).not_to eq(cache_key) - end - - it 'caches the application settings hash' do - expect(Rails.cache) - .to receive(:write) - .with('pages_domain_for_domain_1', kind_of(Set)) - - described_class.new(type: :domain, id: 1).cache_key - end - end - - it 'fails with invalid type' do - expect { described_class.new(type: :unknown, id: nil) } - .to raise_error(ArgumentError, 'type must be :namespace or :domain') - end -end diff --git a/spec/lib/gitlab/pages/virtual_host_finder_spec.rb b/spec/lib/gitlab/pages/virtual_host_finder_spec.rb index 49eee772f8d..8c34968bbfc 100644 --- a/spec/lib/gitlab/pages/virtual_host_finder_spec.rb +++ b/spec/lib/gitlab/pages/virtual_host_finder_spec.rb @@ -40,23 +40,9 @@ RSpec.describe Gitlab::Pages::VirtualHostFinder, feature_category: :pages do it 'returns the virual domain when there are pages deployed for the project' do expect(virtual_domain).to be_an_instance_of(Pages::VirtualDomain) - expect(virtual_domain.cache_key).to match(/pages_domain_for_domain_#{pages_domain.id}_/) expect(virtual_domain.lookup_paths.length).to eq(1) expect(virtual_domain.lookup_paths.first.project_id).to eq(project.id) end - - context 'when :cache_pages_domain_api is disabled' do - before do - stub_feature_flags(cache_pages_domain_api: false) - end - - it 'returns the virual domain when there are pages deployed for the project' do - expect(virtual_domain).to be_an_instance_of(Pages::VirtualDomain) - expect(virtual_domain.cache_key).to be_nil - expect(virtual_domain.lookup_paths.length).to eq(1) - expect(virtual_domain.lookup_paths.first.project_id).to eq(project.id) - end - end end end @@ -76,23 +62,8 @@ RSpec.describe Gitlab::Pages::VirtualHostFinder, feature_category: :pages do virtual_domain = described_class.new("#{project.namespace.path}.example.com").execute expect(virtual_domain).to be_an_instance_of(Pages::VirtualDomain) - expect(virtual_domain.cache_key).to match(/pages_domain_for_namespace_#{project.namespace.id}_/) expect(virtual_domain.lookup_paths.length).to eq(0) end - - context 'when :cache_pages_domain_api is disabled' do - before do - stub_feature_flags(cache_pages_domain_api: false) - end - - it 'returns the virual domain with no lookup_paths' do - virtual_domain = described_class.new("#{project.namespace.path}.example.com".downcase).execute - - expect(virtual_domain).to be_an_instance_of(Pages::VirtualDomain) - expect(virtual_domain.cache_key).to be_nil - expect(virtual_domain.lookup_paths.length).to eq(0) - end - end end context 'when there are pages deployed for the project' do @@ -111,7 +82,6 @@ RSpec.describe Gitlab::Pages::VirtualHostFinder, feature_category: :pages do virtual_domain = described_class.new("#{project.namespace.path}.example.com").execute expect(virtual_domain).to be_an_instance_of(Pages::VirtualDomain) - expect(virtual_domain.cache_key).to match(/pages_domain_for_namespace_#{project.namespace.id}_/) expect(virtual_domain.lookup_paths.length).to eq(1) expect(virtual_domain.lookup_paths.first.project_id).to eq(project.id) end @@ -120,25 +90,9 @@ RSpec.describe Gitlab::Pages::VirtualHostFinder, feature_category: :pages do virtual_domain = described_class.new("#{project.namespace.path}.Example.com").execute expect(virtual_domain).to be_an_instance_of(Pages::VirtualDomain) - expect(virtual_domain.cache_key).to match(/pages_domain_for_namespace_#{project.namespace.id}_/) expect(virtual_domain.lookup_paths.length).to eq(1) expect(virtual_domain.lookup_paths.first.project_id).to eq(project.id) end - - context 'when :cache_pages_domain_api is disabled' do - before_all do - stub_feature_flags(cache_pages_domain_api: false) - end - - it 'returns the virual domain when there are pages deployed for the project' do - virtual_domain = described_class.new("#{project.namespace.path}.example.com").execute - - expect(virtual_domain).to be_an_instance_of(Pages::VirtualDomain) - expect(virtual_domain.cache_key).to be_nil - expect(virtual_domain.lookup_paths.length).to eq(1) - expect(virtual_domain.lookup_paths.first.project_id).to eq(project.id) - end - end end end @@ -187,18 +141,6 @@ RSpec.describe Gitlab::Pages::VirtualHostFinder, feature_category: :pages do expect(virtual_domain.lookup_paths.first.project_id).to eq(project.id) end end - - context 'when :cache_pages_domain_api is disabled' do - before do - stub_feature_flags(cache_pages_domain_api: false) - end - - it 'returns the virual domain when there are pages deployed for the project' do - expect(virtual_domain).to be_an_instance_of(Pages::VirtualDomain) - expect(virtual_domain.lookup_paths.length).to eq(1) - expect(virtual_domain.lookup_paths.first.project_id).to eq(project.id) - end - end end end diff --git a/spec/lib/gitlab/pagination/cursor_based_keyset_spec.rb b/spec/lib/gitlab/pagination/cursor_based_keyset_spec.rb index 7cee65c13f7..4128f745ce7 100644 --- a/spec/lib/gitlab/pagination/cursor_based_keyset_spec.rb +++ b/spec/lib/gitlab/pagination/cursor_based_keyset_spec.rb @@ -6,20 +6,52 @@ RSpec.describe Gitlab::Pagination::CursorBasedKeyset do subject { described_class } describe '.available_for_type?' do - it 'returns true for Group' do - expect(subject.available_for_type?(Group.all)).to be_truthy - end + context 'with api_keyset_pagination_multi_order FF disabled' do + before do + stub_feature_flags(api_keyset_pagination_multi_order: false) + end - it 'returns true for Ci::Build' do - expect(subject.available_for_type?(Ci::Build.all)).to be_truthy - end + it 'returns true for Group' do + expect(subject.available_for_type?(Group.all)).to be_truthy + end - it 'returns true for Packages::BuildInfo' do - expect(subject.available_for_type?(Packages::BuildInfo.all)).to be_truthy + it 'returns true for Ci::Build' do + expect(subject.available_for_type?(Ci::Build.all)).to be_truthy + end + + it 'returns true for Packages::BuildInfo' do + expect(subject.available_for_type?(Packages::BuildInfo.all)).to be_truthy + end + + it 'return false for User' do + expect(subject.available_for_type?(User.all)).to be_falsey + end end - it 'return false for other types of relations' do - expect(subject.available_for_type?(User.all)).to be_falsey + context 'with api_keyset_pagination_multi_order FF enabled' do + before do + stub_feature_flags(api_keyset_pagination_multi_order: true) + end + + it 'returns true for Group' do + expect(subject.available_for_type?(Group.all)).to be_truthy + end + + it 'returns true for Ci::Build' do + expect(subject.available_for_type?(Ci::Build.all)).to be_truthy + end + + it 'returns true for Packages::BuildInfo' do + expect(subject.available_for_type?(Packages::BuildInfo.all)).to be_truthy + end + + it 'returns true for User' do + expect(subject.available_for_type?(User.all)).to be_truthy + end + + it 'return false for other types of relations' do + expect(subject.available_for_type?(Issue.all)).to be_falsey + end end end @@ -58,7 +90,7 @@ RSpec.describe Gitlab::Pagination::CursorBasedKeyset do end it 'return false for other types of relations' do - expect(subject.available?(cursor_based_request_context, User.all)).to be_falsey + expect(subject.available?(cursor_based_request_context, Issue.all)).to be_falsey expect(subject.available?(cursor_based_request_context, Ci::Build.all)).to be_falsey expect(subject.available?(cursor_based_request_context, Packages::BuildInfo.all)).to be_falsey end @@ -68,16 +100,48 @@ RSpec.describe Gitlab::Pagination::CursorBasedKeyset do let(:order_by) { :id } let(:sort) { :desc } - it 'returns true for Ci::Build' do - expect(subject.available?(cursor_based_request_context, Ci::Build.all)).to be_truthy - end + context 'with api_keyset_pagination_multi_order FF disabled' do + before do + stub_feature_flags(api_keyset_pagination_multi_order: false) + end + + it 'returns true for Ci::Build' do + expect(subject.available?(cursor_based_request_context, Ci::Build.all)).to be_truthy + end + + it 'returns true for AuditEvent' do + expect(subject.available?(cursor_based_request_context, AuditEvent.all)).to be_truthy + end - it 'returns true for AuditEvent' do - expect(subject.available?(cursor_based_request_context, AuditEvent.all)).to be_truthy + it 'returns true for Packages::BuildInfo' do + expect(subject.available?(cursor_based_request_context, Packages::BuildInfo.all)).to be_truthy + end + + it 'returns false for User' do + expect(subject.available?(cursor_based_request_context, User.all)).to be_falsey + end end - it 'returns true for Packages::BuildInfo' do - expect(subject.available?(cursor_based_request_context, Packages::BuildInfo.all)).to be_truthy + context 'with api_keyset_pagination_multi_order FF enabled' do + before do + stub_feature_flags(api_keyset_pagination_multi_order: true) + end + + it 'returns true for Ci::Build' do + expect(subject.available?(cursor_based_request_context, Ci::Build.all)).to be_truthy + end + + it 'returns true for AuditEvent' do + expect(subject.available?(cursor_based_request_context, AuditEvent.all)).to be_truthy + end + + it 'returns true for Packages::BuildInfo' do + expect(subject.available?(cursor_based_request_context, Packages::BuildInfo.all)).to be_truthy + end + + it 'returns true for User' do + expect(subject.available?(cursor_based_request_context, User.all)).to be_truthy + end end end @@ -90,7 +154,7 @@ RSpec.describe Gitlab::Pagination::CursorBasedKeyset do end it 'return false for other types of relations' do - expect(subject.available?(cursor_based_request_context, User.all)).to be_falsey + expect(subject.available?(cursor_based_request_context, Issue.all)).to be_falsey end end end diff --git a/spec/lib/system_check/app/table_truncate_check_spec.rb b/spec/lib/system_check/app/table_truncate_check_spec.rb new file mode 100644 index 00000000000..673365f3e5e --- /dev/null +++ b/spec/lib/system_check/app/table_truncate_check_spec.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe SystemCheck::App::TableTruncateCheck, feature_category: :cell do + context 'when running on single databases' do + before do + skip_if_database_exists(:ci) + end + + describe '#skip?' do + subject { described_class.new.skip? } + + it { is_expected.to eq(true) } + end + end + + context 'when running on multiple databases' do + let(:needs_truncation) { true } + + before do + skip_if_shared_database(:ci) + + allow_next_instances_of(Gitlab::Database::TablesTruncate, 2) do |instance| + allow(instance).to receive(:needs_truncation?).and_return(needs_truncation) + end + end + + describe '#skip?' do + subject { described_class.new.skip? } + + it { is_expected.to eq(false) } + end + + describe '#check?' do + subject { described_class.new.check? } + + context 'when TableTruncate returns false' do + let(:needs_truncation) { false } + + it { is_expected.to eq(true) } + end + + context 'when TableTruncate returns true' do + let(:needs_truncation) { true } + + it { is_expected.to eq(false) } + end + end + + describe '#show_error' do + let(:needs_truncation) { true } + let(:checker) { described_class.new } + + before do + checker.check? + end + + subject(:show_error) { checker.show_error } + + it 'outputs error information' do + expected = %r{ + Try\sfixing\sit:\s+ + sudo\s-u\s.+?\s-H\sbundle\sexec\srake\sgitlab:db:truncate_legacy_tables:main\s + gitlab:db:truncate_legacy_tables:ci\s+ + For\smore\sinformation\ssee:\s+ + doc/development/database/multiple_databases.md\sin\ssection\s'Truncating\stables'\s+ + Please\sfix\sthe\serror\sabove\sand\srerun\sthe\schecks.\s+ + }x + + expect { show_error }.to output(expected).to_stdout + end + end + end +end diff --git a/spec/models/bulk_imports/entity_spec.rb b/spec/models/bulk_imports/entity_spec.rb index da1eb12e9b8..3e98ba0973e 100644 --- a/spec/models/bulk_imports/entity_spec.rb +++ b/spec/models/bulk_imports/entity_spec.rb @@ -438,4 +438,12 @@ RSpec.describe BulkImports::Entity, type: :model, feature_category: :importers d end end end + + describe '#source_version' do + subject { build(:bulk_import_entity, :group_entity) } + + it 'pulls the source version from the associated BulkImport' do + expect(subject.source_version).to eq(subject.bulk_import.source_version_info) + end + end end diff --git a/spec/models/pages/virtual_domain_spec.rb b/spec/models/pages/virtual_domain_spec.rb index 560fe7febd6..02e3fd67f2d 100644 --- a/spec/models/pages/virtual_domain_spec.rb +++ b/spec/models/pages/virtual_domain_spec.rb @@ -73,19 +73,4 @@ RSpec.describe Pages::VirtualDomain, feature_category: :pages do end end end - - describe '#cache_key' do - it 'returns the cache key based in the given cache_control' do - cache_control = instance_double(::Gitlab::Pages::CacheControl, cache_key: 'cache_key') - virtual_domain = described_class.new(projects: [instance_double(Project)], cache: cache_control) - - expect(virtual_domain.cache_key).to eq('cache_key') - end - - it 'returns nil when no cache_control is given' do - virtual_domain = described_class.new(projects: [instance_double(Project)]) - - expect(virtual_domain.cache_key).to be_nil - end - end end diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index de2a58d106e..5973649a9d7 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' RSpec.describe API::Users, :aggregate_failures, feature_category: :user_profile do include WorkhorseHelpers + include KeysetPaginationHelpers let_it_be(:admin) { create(:admin) } let_it_be(:user, reload: true) { create(:user, username: 'user.withdot') } @@ -258,6 +259,48 @@ RSpec.describe API::Users, :aggregate_failures, feature_category: :user_profile end.not_to exceed_all_query_limit(control_count) end end + + context 'when api_keyset_pagination_multi_order FF is enabled' do + before do + stub_feature_flags(api_keyset_pagination_multi_order: true) + end + + it_behaves_like 'an endpoint with keyset pagination', invalid_order: nil do + let(:first_record) { user } + let(:second_record) { admin } + let(:api_call) { api(path, user) } + end + + it 'still supports offset pagination when keyset pagination params are not provided' do + get api(path, user) + + expect(response).to include_pagination_headers + end + end + + context 'when api_keyset_pagination_multi_order FF is disabled' do + before do + stub_feature_flags(api_keyset_pagination_multi_order: false) + end + + it 'paginates the records correctly using offset pagination' do + get api(path, user), params: { pagination: 'keyset', per_page: 1 } + + params_for_next_page = pagination_params_from_next_url(response) + expect(response).to include_pagination_headers + expect(params_for_next_page).not_to include('cursor') + end + + context 'on making requests with unsupported ordering structure' do + it 'does not return error' do + get api(path, user), + params: { pagination: 'keyset', per_page: 1, order_by: 'created_at', sort: 'asc' } + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to include_pagination_headers + end + end + end end end diff --git a/spec/services/bulk_imports/create_pipeline_trackers_service_spec.rb b/spec/services/bulk_imports/create_pipeline_trackers_service_spec.rb deleted file mode 100644 index 9a74f5ca07a..00000000000 --- a/spec/services/bulk_imports/create_pipeline_trackers_service_spec.rb +++ /dev/null @@ -1,176 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe BulkImports::CreatePipelineTrackersService, feature_category: :importers do - describe '#execute!' do - context 'when entity is group' do - it 'creates trackers for group entity' do - bulk_import = create(:bulk_import) - entity = create(:bulk_import_entity, :group_entity, bulk_import: bulk_import) - - described_class.new(entity).execute! - - expect(entity.trackers.to_a).to include( - have_attributes( - stage: 0, status_name: :created, relation: BulkImports::Groups::Pipelines::GroupPipeline.to_s - ), - have_attributes( - stage: 1, status_name: :created, relation: BulkImports::Groups::Pipelines::GroupAttributesPipeline.to_s - ) - ) - end - end - - context 'when entity is project' do - it 'creates trackers for project entity' do - bulk_import = create(:bulk_import) - entity = create(:bulk_import_entity, :project_entity, bulk_import: bulk_import) - - described_class.new(entity).execute! - - expect(entity.trackers.to_a).to include( - have_attributes( - stage: 0, status_name: :created, relation: BulkImports::Projects::Pipelines::ProjectPipeline.to_s - ), - have_attributes( - stage: 1, status_name: :created, relation: BulkImports::Projects::Pipelines::RepositoryPipeline.to_s - ) - ) - end - end - - context 'when tracker configuration has a minimum version defined' do - before do - allow_next_instance_of(BulkImports::Groups::Stage) do |stage| - allow(stage).to receive(:config).and_return( - { - pipeline1: { pipeline: 'PipelineClass1', stage: 0 }, - pipeline2: { pipeline: 'PipelineClass2', stage: 1, minimum_source_version: '14.10.0' }, - pipeline3: { pipeline: 'PipelineClass3', stage: 1, minimum_source_version: '15.0.0' }, - pipeline5: { pipeline: 'PipelineClass4', stage: 1, minimum_source_version: '15.1.0' }, - pipeline6: { pipeline: 'PipelineClass5', stage: 1, minimum_source_version: '16.0.0' } - } - ) - end - end - - context 'when the source instance version is older than the tracker mininum version' do - let_it_be(:bulk_import) { create(:bulk_import, source_version: '15.0.0') } - let_it_be(:entity) { create(:bulk_import_entity, :group_entity, bulk_import: bulk_import) } - - it 'creates trackers as skipped if version requirement does not meet' do - described_class.new(entity).execute! - - expect(entity.trackers.collect { |tracker| [tracker.status_name, tracker.relation] }).to contain_exactly( - [:created, 'PipelineClass1'], - [:created, 'PipelineClass2'], - [:created, 'PipelineClass3'], - [:skipped, 'PipelineClass4'], - [:skipped, 'PipelineClass5'] - ) - end - - it 'logs an info message for the skipped pipelines' do - expect_next_instance_of(Gitlab::Import::Logger) do |logger| - expect(logger).to receive(:info).with({ - message: 'Pipeline skipped as source instance version not compatible with pipeline', - bulk_import_entity_id: entity.id, - bulk_import_id: entity.bulk_import_id, - bulk_import_entity_type: entity.source_type, - source_full_path: entity.source_full_path, - importer: 'gitlab_migration', - pipeline_name: 'PipelineClass4', - minimum_source_version: '15.1.0', - maximum_source_version: nil, - source_version: '15.0.0' - }) - - expect(logger).to receive(:info).with({ - message: 'Pipeline skipped as source instance version not compatible with pipeline', - bulk_import_entity_id: entity.id, - bulk_import_id: entity.bulk_import_id, - bulk_import_entity_type: entity.source_type, - source_full_path: entity.source_full_path, - importer: 'gitlab_migration', - pipeline_name: 'PipelineClass5', - minimum_source_version: '16.0.0', - maximum_source_version: nil, - source_version: '15.0.0' - }) - end - - described_class.new(entity).execute! - end - end - - context 'when the source instance version is undefined' do - it 'creates trackers as created' do - bulk_import = create(:bulk_import, source_version: nil) - entity = create(:bulk_import_entity, :group_entity, bulk_import: bulk_import) - - described_class.new(entity).execute! - - expect(entity.trackers.collect { |tracker| [tracker.status_name, tracker.relation] }).to contain_exactly( - [:created, 'PipelineClass1'], - [:created, 'PipelineClass2'], - [:created, 'PipelineClass3'], - [:created, 'PipelineClass4'], - [:created, 'PipelineClass5'] - ) - end - end - end - - context 'when tracker configuration has a maximum version defined' do - before do - allow_next_instance_of(BulkImports::Groups::Stage) do |stage| - allow(stage).to receive(:config).and_return( - { - pipeline1: { pipeline: 'PipelineClass1', stage: 0 }, - pipeline2: { pipeline: 'PipelineClass2', stage: 1, maximum_source_version: '14.10.0' }, - pipeline3: { pipeline: 'PipelineClass3', stage: 1, maximum_source_version: '15.0.0' }, - pipeline5: { pipeline: 'PipelineClass4', stage: 1, maximum_source_version: '15.1.0' }, - pipeline6: { pipeline: 'PipelineClass5', stage: 1, maximum_source_version: '16.0.0' } - } - ) - end - end - - context 'when the source instance version is older than the tracker maximum version' do - it 'creates trackers as skipped if version requirement does not meet' do - bulk_import = create(:bulk_import, source_version: '15.0.0') - entity = create(:bulk_import_entity, :group_entity, bulk_import: bulk_import) - - described_class.new(entity).execute! - - expect(entity.trackers.collect { |tracker| [tracker.status_name, tracker.relation] }).to contain_exactly( - [:created, 'PipelineClass1'], - [:skipped, 'PipelineClass2'], - [:created, 'PipelineClass3'], - [:created, 'PipelineClass4'], - [:created, 'PipelineClass5'] - ) - end - end - - context 'when the source instance version is a patch version' do - it 'creates trackers with the same status as the non-patch source version' do - bulk_import_1 = create(:bulk_import, source_version: '15.0.1') - entity_1 = create(:bulk_import_entity, :group_entity, bulk_import: bulk_import_1) - - bulk_import_2 = create(:bulk_import, source_version: '15.0.0') - entity_2 = create(:bulk_import_entity, :group_entity, bulk_import: bulk_import_2) - - described_class.new(entity_1).execute! - described_class.new(entity_2).execute! - - trackers_1 = entity_1.trackers.collect { |tracker| [tracker.status_name, tracker.relation] } - trackers_2 = entity_2.trackers.collect { |tracker| [tracker.status_name, tracker.relation] } - - expect(trackers_1).to eq(trackers_2) - end - end - end - end -end diff --git a/spec/support/shared_examples/features/protected_branches_access_control_ce_shared_examples.rb b/spec/support/shared_examples/features/protected_branches_access_control_ce_shared_examples.rb index 39db3b701d9..fc717fbac20 100644 --- a/spec/support/shared_examples/features/protected_branches_access_control_ce_shared_examples.rb +++ b/spec/support/shared_examples/features/protected_branches_access_control_ce_shared_examples.rb @@ -49,7 +49,8 @@ RSpec.shared_examples "protected branches > access control > CE" do expect(ProtectedBranch.last.push_access_levels.map(&:access_level)).to include(access_type_id) end - it "allows updating protected branches so that #{access_type_name} can merge to them" do + it "allows updating protected branches so that #{access_type_name} can merge to them", + quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/425080' do visit project_protected_branches_path(project) show_add_form diff --git a/spec/support/shared_examples/requests/api_keyset_pagination_shared_examples.rb b/spec/support/shared_examples/requests/api_keyset_pagination_shared_examples.rb index 60f77e31ae9..85db36013dd 100644 --- a/spec/support/shared_examples/requests/api_keyset_pagination_shared_examples.rb +++ b/spec/support/shared_examples/requests/api_keyset_pagination_shared_examples.rb @@ -40,9 +40,11 @@ RSpec.shared_examples 'an endpoint with keyset pagination' do |invalid_order: 'n context 'on making requests with unsupported ordering structure' do let(:additional_params) { { order_by: invalid_order, sort: invalid_sort } } - it 'returns error', :aggregate_failures do - is_expected.to have_gitlab_http_status(:method_not_allowed) - expect(json_response['error']).to eq('Keyset pagination is not yet available for this type of request') + if invalid_order + it 'returns error', :aggregate_failures do + is_expected.to have_gitlab_http_status(:method_not_allowed) + expect(json_response['error']).to eq('Keyset pagination is not yet available for this type of request') + end end end end diff --git a/spec/workers/bulk_import_worker_spec.rb b/spec/workers/bulk_import_worker_spec.rb index ec8550bb3bc..c96e5ace124 100644 --- a/spec/workers/bulk_import_worker_spec.rb +++ b/spec/workers/bulk_import_worker_spec.rb @@ -137,5 +137,174 @@ RSpec.describe BulkImportWorker, feature_category: :importers do end end end + + context 'when importing a group' do + it 'creates trackers for group entity' do + bulk_import = create(:bulk_import) + entity = create(:bulk_import_entity, :group_entity, bulk_import: bulk_import) + + subject.perform(bulk_import.id) + + expect(entity.trackers.to_a).to include( + have_attributes( + stage: 0, status_name: :created, relation: BulkImports::Groups::Pipelines::GroupPipeline.to_s + ), + have_attributes( + stage: 1, status_name: :created, relation: BulkImports::Groups::Pipelines::GroupAttributesPipeline.to_s + ) + ) + end + end + + context 'when importing a project' do + it 'creates trackers for project entity' do + bulk_import = create(:bulk_import) + entity = create(:bulk_import_entity, :project_entity, bulk_import: bulk_import) + + subject.perform(bulk_import.id) + + expect(entity.trackers.to_a).to include( + have_attributes( + stage: 0, status_name: :created, relation: BulkImports::Projects::Pipelines::ProjectPipeline.to_s + ), + have_attributes( + stage: 1, status_name: :created, relation: BulkImports::Projects::Pipelines::RepositoryPipeline.to_s + ) + ) + end + end + + context 'when tracker configuration has a minimum version defined' do + before do + allow_next_instance_of(BulkImports::Groups::Stage) do |stage| + allow(stage).to receive(:config).and_return( + { + pipeline1: { pipeline: 'PipelineClass1', stage: 0 }, + pipeline2: { pipeline: 'PipelineClass2', stage: 1, minimum_source_version: '14.10.0' }, + pipeline3: { pipeline: 'PipelineClass3', stage: 1, minimum_source_version: '15.0.0' }, + pipeline5: { pipeline: 'PipelineClass4', stage: 1, minimum_source_version: '15.1.0' }, + pipeline6: { pipeline: 'PipelineClass5', stage: 1, minimum_source_version: '16.0.0' } + } + ) + end + end + + context 'when the source instance version is older than the tracker mininum version' do + let_it_be(:bulk_import) { create(:bulk_import, source_version: '15.0.0') } + let_it_be(:entity) { create(:bulk_import_entity, :group_entity, bulk_import: bulk_import) } + + it 'creates trackers as skipped if version requirement does not meet' do + subject.perform(bulk_import.id) + + expect(entity.trackers.collect { |tracker| [tracker.status_name, tracker.relation] }).to contain_exactly( + [:created, 'PipelineClass1'], + [:created, 'PipelineClass2'], + [:created, 'PipelineClass3'], + [:skipped, 'PipelineClass4'], + [:skipped, 'PipelineClass5'] + ) + end + + it 'logs an info message for the skipped pipelines' do + expect_next_instance_of(Gitlab::Import::Logger) do |logger| + expect(logger).to receive(:info).with({ + message: 'Pipeline skipped as source instance version not compatible with pipeline', + bulk_import_entity_id: entity.id, + bulk_import_id: entity.bulk_import_id, + bulk_import_entity_type: entity.source_type, + source_full_path: entity.source_full_path, + importer: 'gitlab_migration', + pipeline_name: 'PipelineClass4', + minimum_source_version: '15.1.0', + maximum_source_version: nil, + source_version: '15.0.0' + }) + + expect(logger).to receive(:info).with({ + message: 'Pipeline skipped as source instance version not compatible with pipeline', + bulk_import_entity_id: entity.id, + bulk_import_id: entity.bulk_import_id, + bulk_import_entity_type: entity.source_type, + source_full_path: entity.source_full_path, + importer: 'gitlab_migration', + pipeline_name: 'PipelineClass5', + minimum_source_version: '16.0.0', + maximum_source_version: nil, + source_version: '15.0.0' + }) + end + + subject.perform(bulk_import.id) + end + end + + context 'when the source instance version is undefined' do + it 'creates trackers as created' do + bulk_import = create(:bulk_import, source_version: nil) + entity = create(:bulk_import_entity, :group_entity, bulk_import: bulk_import) + + subject.perform(bulk_import.id) + + expect(entity.trackers.collect { |tracker| [tracker.status_name, tracker.relation] }).to contain_exactly( + [:created, 'PipelineClass1'], + [:created, 'PipelineClass2'], + [:created, 'PipelineClass3'], + [:created, 'PipelineClass4'], + [:created, 'PipelineClass5'] + ) + end + end + end + + context 'when tracker configuration has a maximum version defined' do + before do + allow_next_instance_of(BulkImports::Groups::Stage) do |stage| + allow(stage).to receive(:config).and_return( + { + pipeline1: { pipeline: 'PipelineClass1', stage: 0 }, + pipeline2: { pipeline: 'PipelineClass2', stage: 1, maximum_source_version: '14.10.0' }, + pipeline3: { pipeline: 'PipelineClass3', stage: 1, maximum_source_version: '15.0.0' }, + pipeline5: { pipeline: 'PipelineClass4', stage: 1, maximum_source_version: '15.1.0' }, + pipeline6: { pipeline: 'PipelineClass5', stage: 1, maximum_source_version: '16.0.0' } + } + ) + end + end + + context 'when the source instance version is older than the tracker maximum version' do + it 'creates trackers as skipped if version requirement does not meet' do + bulk_import = create(:bulk_import, source_version: '15.0.0') + entity = create(:bulk_import_entity, :group_entity, bulk_import: bulk_import) + + subject.perform(bulk_import.id) + + expect(entity.trackers.collect { |tracker| [tracker.status_name, tracker.relation] }).to contain_exactly( + [:created, 'PipelineClass1'], + [:skipped, 'PipelineClass2'], + [:created, 'PipelineClass3'], + [:created, 'PipelineClass4'], + [:created, 'PipelineClass5'] + ) + end + end + + context 'when the source instance version is a patch version' do + it 'creates trackers with the same status as the non-patch source version' do + bulk_import_1 = create(:bulk_import, source_version: '15.0.1') + entity_1 = create(:bulk_import_entity, :group_entity, bulk_import: bulk_import_1) + + bulk_import_2 = create(:bulk_import, source_version: '15.0.0') + entity_2 = create(:bulk_import_entity, :group_entity, bulk_import: bulk_import_2) + + described_class.perform_inline(bulk_import_1.id) + described_class.perform_inline(bulk_import_2.id) + + trackers_1 = entity_1.trackers.collect { |tracker| [tracker.status_name, tracker.relation] } + trackers_2 = entity_2.trackers.collect { |tracker| [tracker.status_name, tracker.relation] } + + expect(trackers_1).to eq(trackers_2) + end + end + end end end diff --git a/spec/workers/pages/invalidate_domain_cache_worker_spec.rb b/spec/workers/pages/invalidate_domain_cache_worker_spec.rb deleted file mode 100644 index b3c81b25a93..00000000000 --- a/spec/workers/pages/invalidate_domain_cache_worker_spec.rb +++ /dev/null @@ -1,267 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Pages::InvalidateDomainCacheWorker, feature_category: :pages do - shared_examples 'clears caches with' do |event_class:, event_data:, caches:| - include AfterNextHelpers - - let(:event) { event_class.new(data: event_data) } - - subject { consume_event(subscriber: described_class, event: event) } - - it_behaves_like 'subscribes to event' - - it 'clears the cache with Gitlab::Pages::CacheControl' do - caches.each do |cache| - expect_next(Gitlab::Pages::CacheControl, type: cache[:type], id: cache[:id]) - .to receive(:clear_cache) - end - - subject - end - end - - context 'when a project have multiple domains' do - include AfterNextHelpers - - let_it_be(:project) { create(:project) } - let_it_be(:pages_domain) { create(:pages_domain, project: project) } - let_it_be(:pages_domain2) { create(:pages_domain, project: project) } - - let(:event) do - Pages::PageDeployedEvent.new( - data: { - project_id: project.id, - namespace_id: project.namespace_id, - root_namespace_id: project.root_ancestor.id - } - ) - end - - subject { consume_event(subscriber: described_class, event: event) } - - it 'clears the cache with Gitlab::Pages::CacheControl' do - expect_next(Gitlab::Pages::CacheControl, type: :namespace, id: project.namespace_id) - .to receive(:clear_cache) - expect_next(Gitlab::Pages::CacheControl, type: :domain, id: pages_domain.id) - .to receive(:clear_cache) - expect_next(Gitlab::Pages::CacheControl, type: :domain, id: pages_domain2.id) - .to receive(:clear_cache) - - subject - end - end - - it_behaves_like 'clears caches with', - event_class: Pages::PageDeployedEvent, - event_data: { project_id: 1, namespace_id: 2, root_namespace_id: 3 }, - caches: [ - { type: :namespace, id: 3 } - ] - - it_behaves_like 'clears caches with', - event_class: Pages::PageDeletedEvent, - event_data: { project_id: 1, namespace_id: 2, root_namespace_id: 3 }, - caches: [ - { type: :namespace, id: 3 } - ] - - it_behaves_like 'clears caches with', - event_class: Projects::ProjectDeletedEvent, - event_data: { project_id: 1, namespace_id: 2, root_namespace_id: 3 }, - caches: [ - { type: :namespace, id: 3 } - ] - - it_behaves_like 'clears caches with', - event_class: Projects::ProjectCreatedEvent, - event_data: { project_id: 1, namespace_id: 2, root_namespace_id: 3 }, - caches: [ - { type: :namespace, id: 3 } - ] - - it_behaves_like 'clears caches with', - event_class: Projects::ProjectArchivedEvent, - event_data: { project_id: 1, namespace_id: 2, root_namespace_id: 3 }, - caches: [ - { type: :namespace, id: 3 } - ] - - it_behaves_like 'clears caches with', - event_class: Projects::ProjectPathChangedEvent, - event_data: { - project_id: 1, - namespace_id: 2, - root_namespace_id: 3, - old_path: 'old_path', - new_path: 'new_path' - }, - caches: [ - { type: :namespace, id: 3 } - ] - - it_behaves_like 'clears caches with', - event_class: Projects::ProjectTransferedEvent, - event_data: { - project_id: 1, - old_namespace_id: 2, - old_root_namespace_id: 3, - new_namespace_id: 4, - new_root_namespace_id: 5 - }, - caches: [ - { type: :namespace, id: 3 }, - { type: :namespace, id: 5 } - ] - - it_behaves_like 'clears caches with', - event_class: Groups::GroupTransferedEvent, - event_data: { - group_id: 1, - old_root_namespace_id: 3, - new_root_namespace_id: 5 - }, - caches: [ - { type: :namespace, id: 3 }, - { type: :namespace, id: 5 } - ] - - it_behaves_like 'clears caches with', - event_class: Groups::GroupPathChangedEvent, - event_data: { - group_id: 1, - root_namespace_id: 2, - old_path: 'old_path', - new_path: 'new_path' - }, - caches: [ - { type: :namespace, id: 2 } - ] - - it_behaves_like 'clears caches with', - event_class: Groups::GroupDeletedEvent, - event_data: { - group_id: 1, - root_namespace_id: 3 - }, - caches: [ - { type: :namespace, id: 3 } - ] - - it_behaves_like 'clears caches with', - event_class: PagesDomains::PagesDomainDeletedEvent, - event_data: { - project_id: 1, - namespace_id: 2, - root_namespace_id: 3, - domain_id: 4, - domain: 'somedomain.com' - }, - caches: [ - { type: :domain, id: 4 }, - { type: :namespace, id: 3 } - ] - - it_behaves_like 'clears caches with', - event_class: PagesDomains::PagesDomainUpdatedEvent, - event_data: { - project_id: 1, - namespace_id: 2, - root_namespace_id: 3, - domain_id: 4, - domain: 'somedomain.com' - }, - caches: [ - { type: :domain, id: 4 }, - { type: :namespace, id: 3 } - ] - - it_behaves_like 'clears caches with', - event_class: PagesDomains::PagesDomainCreatedEvent, - event_data: { - project_id: 1, - namespace_id: 2, - root_namespace_id: 3, - domain_id: 4, - domain: 'somedomain.com' - }, - caches: [ - { type: :domain, id: 4 }, - { type: :namespace, id: 3 } - ] - - context 'when project attributes change' do - Projects::ProjectAttributesChangedEvent::PAGES_RELATED_ATTRIBUTES.each do |attribute| - it_behaves_like 'clears caches with', - event_class: Projects::ProjectAttributesChangedEvent, - event_data: { - project_id: 1, - namespace_id: 2, - root_namespace_id: 3, - domain_id: 4, - attributes: [attribute] - }, - caches: [ - { type: :domain, id: 4 }, - { type: :namespace, id: 3 } - ] - end - - it_behaves_like 'ignores the published event' do - let(:event) do - Projects::ProjectAttributesChangedEvent.new( - data: { - project_id: 1, - namespace_id: 2, - root_namespace_id: 3, - attributes: ['unknown'] - } - ) - end - end - end - - context 'when project features change' do - it_behaves_like 'clears caches with', - event_class: Projects::ProjectFeaturesChangedEvent, - event_data: { - project_id: 1, - namespace_id: 2, - root_namespace_id: 3, - features: ['pages_access_level'] - }, - caches: [ - { type: :namespace, id: 3 } - ] - - it_behaves_like 'ignores the published event' do - let(:event) do - Projects::ProjectFeaturesChangedEvent.new( - data: { - project_id: 1, - namespace_id: 2, - root_namespace_id: 3, - features: ['unknown'] - } - ) - end - end - end - - context 'when namespace based cache keys are duplicated' do - # de-dups namespace cache keys - it_behaves_like 'clears caches with', - event_class: Projects::ProjectTransferedEvent, - event_data: { - project_id: 1, - old_namespace_id: 2, - old_root_namespace_id: 5, - new_namespace_id: 4, - new_root_namespace_id: 5 - }, - caches: [ - { type: :namespace, id: 5 } - ] - end -end |