From 4612d16c2d2dad7ef582bdd6878f02b98e6a84b7 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Fri, 26 Aug 2022 18:12:09 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .gitlab/CODEOWNERS | 78 ++++++++++ .gitlab/ci/rails.gitlab-ci.yml | 2 +- .gitlab/ci/rules.gitlab-ci.yml | 47 +++++-- .gitlab/ci/static-analysis.gitlab-ci.yml | 29 ++-- .../layout/space_inside_block_braces.yml | 74 ---------- .../access_tokens/components/constants.js | 14 +- .../components/content_editor.vue | 6 +- .../components/constants.js | 2 +- .../filtered_search_bar/constants.js | 2 +- .../framework/contextual_sidebar.scss | 4 +- app/assets/stylesheets/framework/header.scss | 4 +- .../stylesheets/framework/variables.scss | 2 +- .../stylesheets/startup/startup-dark.scss | 11 +- .../stylesheets/startup/startup-general.scss | 2 +- app/assets/stylesheets/themes/_dark.scss | 2 - .../themes/dark_mode_overrides.scss | 14 -- app/models/ci/pipeline.rb | 2 +- .../ci_forked_source_public_cost_factor.yml | 8 -- ...n_packages_total_unique_counts_monthly.yml | 4 - ...en_packages_total_unique_counts_weekly.yml | 4 - doc/ci/pipelines/cicd_minutes.md | 7 +- .../merge_request_concepts/index.md | 28 ++-- doc/development/service_ping/implement.md | 5 +- .../service_ping/metrics_instrumentation.md | 2 +- .../reviews/img/mr_summary_comment_v15_3.png | Bin 49186 -> 0 bytes .../reviews/img/mr_summary_comment_v15_4.png | Bin 0 -> 61841 bytes .../project/merge_requests/reviews/index.md | 10 +- ...kfill_vulnerability_reads_cluster_agent.rb | 12 +- ...ntainer_registry_size_batching_strategy.rb | 13 +- ...y_reads_cluster_agent_batching_strategy.rb | 11 +- .../dismissed_vulnerabilities_strategy.rb | 9 +- .../set_correct_vulnerability_state.rb | 7 +- lib/gitlab/database/batch_average_counter.rb | 103 ++++++++++++++ lib/gitlab/database/batch_count.rb | 2 +- lib/gitlab/database/batch_counter.rb | 7 - .../usage_data_counters/hll_redis_counter.rb | 2 +- locale/gitlab.pot | 20 ++- spec/config/settings_spec.rb | 2 +- .../application_settings_controller_spec.rb | 2 +- .../application_controller_spec.rb | 2 +- .../groups/labels_controller_spec.rb | 4 +- .../groups/releases_controller_spec.rb | 4 +- .../import/manifest_controller_spec.rb | 2 +- .../projects/blame_controller_spec.rb | 2 +- .../projects/deploy_keys_controller_spec.rb | 4 +- .../projects/feature_flags_controller_spec.rb | 2 +- .../projects/jobs_controller_spec.rb | 2 +- .../projects/labels_controller_spec.rb | 8 +- .../projects/notes_controller_spec.rb | 2 +- .../projects/releases_controller_spec.rb | 2 +- .../projects/tree_controller_spec.rb | 6 +- .../registrations/welcome_controller_spec.rb | 4 +- .../snippets/notes_controller_spec.rb | 2 +- spec/dependencies/omniauth_saml_spec.rb | 2 +- .../application_experiment_spec.rb | 14 +- spec/finders/ci/jobs_finder_spec.rb | 2 +- spec/finders/ci/runners_finder_spec.rb | 14 +- .../concerns/packages/finder_helper_spec.rb | 6 +- .../container_repositories_finder_spec.rb | 8 +- .../design_management/versions_finder_spec.rb | 4 +- spec/finders/milestones_finder_spec.rb | 2 +- .../packages/group_packages_finder_spec.rb | 4 +- .../packages/npm/package_finder_spec.rb | 2 +- spec/finders/projects_finder_spec.rb | 10 +- .../application_settings_helper_spec.rb | 2 +- spec/helpers/blob_helper_spec.rb | 2 +- spec/helpers/gitlab_script_tag_helper_spec.rb | 4 +- spec/helpers/issuables_helper_spec.rb | 4 +- spec/helpers/projects/pipeline_helper_spec.rb | 2 +- .../routing/pseudonymization_helper_spec.rb | 4 +- spec/helpers/search_helper_spec.rb | 2 +- spec/helpers/wiki_page_version_helper_spec.rb | 4 +- spec/initializers/carrierwave_patch_spec.rb | 6 +- spec/initializers/trusted_proxies_spec.rb | 2 +- ..._vulnerability_reads_cluster_agent_spec.rb | 32 +++-- ...er_registry_size_batching_strategy_spec.rb | 133 +----------------- ...dismissed_vulnerabilities_strategy_spec.rb | 112 --------------- .../set_correct_vulnerability_state_spec.rb | 21 ++- .../database/batch_average_counter_spec.rb | 107 ++++++++++++++ spec/lib/gitlab/database/batch_count_spec.rb | 48 +------ spec/lib/gitlab/usage_data_metrics_spec.rb | 4 + spec/mailers/emails/service_desk_spec.rb | 2 +- ...e_duplicate_project_authorizations_spec.rb | 2 +- ...or_existing_approval_project_rules_spec.rb | 2 +- .../confirm_support_bot_user_spec.rb | 2 +- ...eset_job_token_scope_enabled_again_spec.rb | 4 +- .../reset_job_token_scope_enabled_spec.rb | 4 +- ...set_severity_levels_to_new_default_spec.rb | 10 +- spec/models/ci/pipeline_spec.rb | 16 ++- spec/policies/clusters/agent_policy_spec.rb | 2 +- spec/policies/group_member_policy_spec.rb | 6 +- spec/policies/issue_policy_spec.rb | 2 +- spec/policies/project_policy_spec.rb | 10 +- spec/policies/terraform/state_policy_spec.rb | 2 +- .../terraform/state_version_policy_spec.rb | 2 +- .../composer/packages_presenter_spec.rb | 2 +- .../packages/conan/package_presenter_spec.rb | 2 +- .../nuget/packages_metadata_presenter_spec.rb | 2 +- spec/presenters/project_presenter_spec.rb | 2 +- .../create_table_with_foreign_keys_spec.rb | 2 +- spec/serializers/cluster_entity_spec.rb | 2 +- .../import/provider_repo_serializer_spec.rb | 2 +- .../content_editor_shared_examples.rb | 121 ++++++++++++---- .../user_updates_wiki_page_shared_examples.rb | 11 +- spec/tasks/gitlab/snippets_rake_spec.rb | 2 +- ...distribution_release_file_uploader_spec.rb | 4 +- .../addressable_url_validator_spec.rb | 4 +- .../instance_configuration.html.haml_spec.rb | 2 +- .../layouts/_header_search.html.haml_spec.rb | 2 +- .../_published_experiments.html.haml_spec.rb | 6 +- .../runners/_runner_details.html.haml_spec.rb | 4 +- .../export_request_worker_spec.rb | 2 +- .../cleanup/project_namespace_worker_spec.rb | 2 +- .../packages/helm/extraction_worker_spec.rb | 2 +- spec/workers/pages_worker_spec.rb | 2 +- ...urge_dependency_proxy_cache_worker_spec.rb | 4 +- .../releases/manage_evidence_worker_spec.rb | 2 +- 117 files changed, 699 insertions(+), 707 deletions(-) delete mode 100644 .rubocop_todo/layout/space_inside_block_braces.yml delete mode 100644 config/feature_flags/development/ci_forked_source_public_cost_factor.yml delete mode 100644 doc/user/project/merge_requests/reviews/img/mr_summary_comment_v15_3.png create mode 100644 doc/user/project/merge_requests/reviews/img/mr_summary_comment_v15_4.png create mode 100644 lib/gitlab/database/batch_average_counter.rb create mode 100644 spec/lib/gitlab/database/batch_average_counter_spec.rb diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS index 573ba8de595..a743a601127 100644 --- a/.gitlab/CODEOWNERS +++ b/.gitlab/CODEOWNERS @@ -57,6 +57,84 @@ Dangerfile @gl-quality/eng-prod /ee/lib/ee/gitlab/auth/ldap/ @dblessing @mkozono /lib/gitlab/auth/ldap/ @dblessing @mkozono +^[Verify] +/app/**/ci/ @gitlab-org/maintainers/cicd-verify +/app/controllers/admin/jobs_controller.rb @gitlab-org/maintainers/cicd-verify +/app/controllers/admin/runner_projects_controller.rb @gitlab-org/maintainers/cicd-verify +/app/controllers/admin/runners_controller.rb @gitlab-org/maintainers/cicd-verify +/app/controllers/projects/artifacts_controller.rb @gitlab-org/maintainers/cicd-verify +/app/controllers/projects/build_artifacts_controller.rb @gitlab-org/maintainers/cicd-verify +/app/controllers/projects/builds_controller.rb @gitlab-org/maintainers/cicd-verify +/app/controllers/projects/jobs_controller.rb @gitlab-org/maintainers/cicd-verify +/app/controllers/runner_setup_controller.rb @gitlab-org/maintainers/cicd-verify +/app/controllers/projects/pipeline_schedules_controller.rb @gitlab-org/maintainers/cicd-verify +/app/controllers/projects/pipelines_controller.rb @gitlab-org/maintainers/cicd-verify +/app/controllers/projects/pipelines_settings_controller.rb @gitlab-org/maintainers/cicd-verify +/app/controllers/projects/runner_projects_controller.rb @gitlab-org/maintainers/cicd-verify +/app/controllers/projects/runners_controller.rb @gitlab-org/maintainers/cicd-verify +/app/controllers/projects/triggers_controller.rb @gitlab-org/maintainers/cicd-verify +/app/controllers/projects/usage_quotas_controller.rb @gitlab-org/maintainers/cicd-verify +/app/controllers/projects/variables_controller.rb @gitlab-org/maintainers/cicd-verify +/app/models/commit_status.rb @gitlab-org/maintainers/cicd-verify +/app/models/external_pull_request.rb @gitlab-org/maintainers/cicd-verify +/app/models/generic_commit_status.rb @gitlab-org/maintainers/cicd-verify +/app/models/namespace_ci_cd_setting.rb @gitlab-org/maintainers/cicd-verify +/app/models/project_ci_cd_setting.rb @gitlab-org/maintainers/cicd-verify +/app/presenters/commit_status_presenter.rb @gitlab-org/maintainers/cicd-verify +/app/presenters/generic_commit_status_presenter.rb @gitlab-org/maintainers/cicd-verify +/app/views/projects/artifacts/ @gitlab-org/maintainers/cicd-verify +/app/views/projects/generic_commit_statuses/ @gitlab-org/maintainers/cicd-verify +/app/views/projects/jobs/ @gitlab-org/maintainers/cicd-verify +/app/views/projects/pipeline_schedules/ @gitlab-org/maintainers/cicd-verify +/app/views/projects/pipelines/ @gitlab-org/maintainers/cicd-verify +/app/views/projects/triggers/ @gitlab-org/maintainers/cicd-verify +/app/workers/build_hooks_worker.rb @gitlab-org/maintainers/cicd-verify +/app/workers/build_queue_worker.rb @gitlab-org/maintainers/cicd-verify +/app/workers/build_success_worker.rb @gitlab-org/maintainers/cicd-verify +/app/workers/ci_platform_metrics_update_cron_worker.rb @gitlab-org/maintainers/cicd-verify +/app/workers/create_pipeline_worker.rb @gitlab-org/maintainers/cicd-verify +/app/workers/expire_build_artifacts_worker.rb @gitlab-org/maintainers/cicd-verify +/app/workers/pipeline_hooks_worker.rb @gitlab-org/maintainers/cicd-verify +/app/workers/pipeline_metrics_worker.rb @gitlab-org/maintainers/cicd-verify +/app/workers/pipeline_notification_worker.rb @gitlab-org/maintainers/cicd-verify +/app/workers/pipeline_process_worker.rb @gitlab-org/maintainers/cicd-verify +/app/workers/pipeline_schedule_worker.rb @gitlab-org/maintainers/cicd-verify +/app/workers/run_pipeline_schedule_worker.rb @gitlab-org/maintainers/cicd-verify +/app/workers/stuck_ci_jobs_worker.rb @gitlab-org/maintainers/cicd-verify +/app/workers/update_external_pull_requests_worker.rb @gitlab-org/maintainers/cicd-verify +/lib/**/ci/ @gitlab-org/maintainers/cicd-verify +/lib/api/commit_statuses.rb @gitlab-org/maintainers/cicd-verify +/ee/app/**/ci/ @gitlab-org/maintainers/cicd-verify +/ee/app/**/merge_trains/ @gitlab-org/maintainers/cicd-verify +/ee/app/models/merge_train.rb @gitlab-org/maintainers/cicd-verify +/ee/app/finders/merge_trains_finder.rb @gitlab-org/maintainers/cicd-verify +/ee/app/services/auto_merge/add_to_merge_train_when_pipeline_succeeds_service.rb @gitlab-org/maintainers/cicd-verify +/ee/app/services/auto_merge/merge_train_service.rb @gitlab-org/maintainers/cicd-verify +/ee/app/services/system_notes/merge_train_service.rb @gitlab-org/maintainers/cicd-verify +/ee/app/controllers/ee/admin/runners_controller.rb @gitlab-org/maintainers/cicd-verify +/ee/app/controllers/ee/projects/pipelines_controller.rb @gitlab-org/maintainers/cicd-verify +/ee/app/controllers/projects/pipelines/ @gitlab-org/maintainers/cicd-verify +/ee/app/controllers/projects/subscriptions_controller.rb @gitlab-org/maintainers/cicd-verify +/ee/app/models/merge_train.rb @gitlab-org/maintainers/cicd-verify +/ee/app/helpers/ee/projects/pipeline_helper.rb @gitlab-org/maintainers/cicd-verify +/ee/app/views/ci_minutes_usage_mailer/ @gitlab-org/maintainers/cicd-verify +/ee/app/views/projects/pipelines/ @gitlab-org/maintainers/cicd-verify +/ee/app/views/projects/settings/ci_cd/ @gitlab-org/maintainers/cicd-verify +/ee/app/workers/clear_shared_runners_minutes_worker.rb @gitlab-org/maintainers/cicd-verify +/ee/lib/**/ci/ @gitlab-org/maintainers/cicd-verify +/ee/lib/ee/api/entities/merge_train.rb @gitlab-org/maintainers/cicd-verify +/**/javascripts/jobs/ @pburdette @jivanvl +/**/javascripts/pipelines/ @pburdette @f_caplette @jivanvl @mfluharty @bsandlin @mgandres +/app/assets/javascripts/pipeline_new/ @pburdette @f_caplette @jivanvl @mfluharty @bsandlin @mgandres +/app/assets/javascripts/ci_lint/ @f_caplette @bsandlin @mgandres +/app/assets/javascripts/ci_variable_list/ @pburdette @f_caplette @jivanvl @mfluharty @bsandlin @mgandres +/app/assets/javascripts/pipeline_schedules/ @pburdette @jivanvl +/app/assets/javascripts/pipeline_editor/ @f_caplette @bsandlin @mgandres +/ee/app/assets/javascripts/ci_minutes_usage/ @pburdette @jivanvl +/ee/app/assets/javascripts/usage_quotas/ci_minutes_usage/ @pburdette @jivanvl +/ee/app/assets/javascripts/usage_quotas/pipelines/ @pburdette @jivanvl +/ee/app/assets/javascripts/reports/ @mfluharty + ^[Templates] /lib/gitlab/ci/templates/ @gitlab-org/maintainers/cicd-templates /lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml @DylanGriffith @mayra-cabrera @tkuah diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml index 2d92298cd63..9c06c390da0 100644 --- a/.gitlab/ci/rails.gitlab-ci.yml +++ b/.gitlab/ci/rails.gitlab-ci.yml @@ -620,7 +620,7 @@ rspec:feature-flags: stage: post-test needs: - job: "feature-flags-usage" - - job: "haml-lint foss" + - job: "haml-lint" - job: "haml-lint ee" optional: true script: diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index 3702bec199e..8d0349fa117 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -264,7 +264,7 @@ - "Dockerfile.assets" - "config/**/*.js" - "vendor/assets/**/*" - - "{app/assets,app/helpers,app/presenters,app/views,locale,public,spec/frontend,symbol}/**/*" + - "{app/assets,app/components,app/helpers,app/presenters,app/views,locale,public,spec/frontend,symbol}/**/*" .controllers-patterns: &controllers-patterns - "{,ee/,jh/}{app/controllers}/**/*" @@ -284,7 +284,7 @@ - "Rakefile" - "config.ru" # List explicitly all the app/ dirs that are backend (i.e. all except app/assets). - - "{,ee/,jh/}{app/channels,app/controllers,app/finders,app/graphql,app/helpers,app/mailers,app/models,app/policies,app/presenters,app/serializers,app/services,app/uploaders,app/validators,app/views,app/workers}/**/*" + - "{,ee/,jh/}{app/channels,app/components,app/controllers,app/finders,app/graphql,app/helpers,app/mailers,app/models,app/policies,app/presenters,app/serializers,app/services,app/uploaders,app/validators,app/views,app/workers}/**/*" - "{,ee/,jh/}{bin,config,db,generator_templates,lib}/**/*" - "{,ee/,jh/}spec/**/*" # CI changes @@ -299,7 +299,7 @@ - "{,jh/}Gemfile.lock" - "GITLAB_ELASTICSEARCH_INDEXER_VERSION" # List explicitly all the app/ dirs that are backend (i.e. all except app/assets). - - "{,ee/,jh/}{app/channels,app/controllers,app/finders,app/graphql,app/helpers,app/mailers,app/models,app/policies,app/presenters,app/serializers,app/services,app/uploaders,app/validators,app/views,app/workers}/**/*" + - "{,ee/,jh/}{app/channels,app/components,app/controllers,app/finders,app/graphql,app/helpers,app/mailers,app/models,app/policies,app/presenters,app/serializers,app/services,app/uploaders,app/validators,app/views,app/workers}/**/*" - "{,ee/,jh/}{bin,config,db,generator_templates,lib}/**/*" - "{,ee/,jh/}spec/**/*" @@ -494,9 +494,14 @@ .static-analysis-patterns: &static-analysis-patterns - ".{codeclimate,eslintrc,haml-lint,haml-lint_todo}.yml" - - ".rubocop.yml" - - ".rubocop_todo.yml" + +.rubocop-patterns: &rubocop-patterns + - ".{rubocop,rubocop_todo}.yml" - ".rubocop_todo/**/*.yml" + - "{,ee/,jh/}rubocop/**/*" # We might be changing custom cops + - "{,ee/,jh/}Gemfile.lock" # This should include gitlab-styles, rubocop itself, and any plugins we might be using + - "lib/gitlab_edition.rb" # This is required in RuboCop::CodeReuseHelpers + - ".gitlab/ci/static-analysis.gitlab-ci.yml" .danger-patterns: &danger-patterns - "Dangerfile" @@ -1427,24 +1432,42 @@ # Static analysis rules # ######################### -.static-analysis:rules:ee-and-foss: +.static-analysis:rules:static-analysis: rules: - changes: *code-backstage-qa-patterns - changes: *static-analysis-patterns -.static-analysis:rules:ee-and-foss-qa: +.static-analysis:rules:static-verification-with-database: + rules: + - changes: *code-backstage-qa-patterns + +.static-analysis:rules:rubocop: + rules: + - changes: *rubocop-patterns + variables: + RUN_ALL_RUBOCOP: "true" + - changes: *code-backstage-qa-patterns + +.static-analysis:rules:qa:metadata-lint: rules: - changes: *qa-patterns - - changes: *static-analysis-patterns + - changes: [".gitlab/ci/static-analysis.gitlab-ci.yml"] -.static-analysis:rules:ee: +.static-analysis:rules:haml-lint: + rules: + - changes: *rubocop-patterns + - changes: *static-analysis-patterns + - changes: *code-backstage-qa-patterns + +.static-analysis:rules:haml-lint-ee: rules: - <<: *if-not-ee when: never - - changes: *code-backstage-qa-patterns + - changes: *rubocop-patterns - changes: *static-analysis-patterns + - changes: *code-backstage-qa-patterns -.static-analysis:rules:as-if-foss: +.static-analysis:rules:static-analysis-as-if-foss: rules: - <<: *if-not-ee when: never @@ -1453,7 +1476,7 @@ - <<: *if-security-merge-request changes: *code-backstage-qa-patterns - <<: *if-merge-request - changes: *ci-patterns + changes: [".gitlab/ci/static-analysis.gitlab-ci.yml"] - <<: *if-merge-request changes: *static-analysis-patterns diff --git a/.gitlab/ci/static-analysis.gitlab-ci.yml b/.gitlab/ci/static-analysis.gitlab-ci.yml index b4efd9e49bf..a9e806f12a3 100644 --- a/.gitlab/ci/static-analysis.gitlab-ci.yml +++ b/.gitlab/ci/static-analysis.gitlab-ci.yml @@ -25,7 +25,7 @@ static-analysis: extends: - .static-analysis-base - .static-analysis-cache - - .static-analysis:rules:ee-and-foss + - .static-analysis:rules:static-analysis parallel: 2 script: - run_timed_command "retry yarn install --frozen-lockfile" @@ -34,14 +34,14 @@ static-analysis: static-analysis as-if-foss: extends: - static-analysis - - .static-analysis:rules:as-if-foss + - .static-analysis:rules:static-analysis-as-if-foss - .as-if-foss static-verification-with-database: extends: - .static-analysis-base - .rubocop-job-cache - - .static-analysis:rules:ee-and-foss + - .static-analysis:rules:static-verification-with-database - .use-pg12 script: - bundle exec rake lint:static_verification_with_database @@ -91,11 +91,11 @@ eslint as-if-foss: - .as-if-foss needs: ['generate-apollo-graphql-schema as-if-foss'] -haml-lint foss: +haml-lint: extends: - .static-analysis-base - .ruby-cache - - .static-analysis:rules:ee-and-foss + - .static-analysis:rules:haml-lint script: - run_timed_command "bin/rake 'haml_lint[app/views]'" artifacts: @@ -106,8 +106,8 @@ haml-lint foss: haml-lint ee: extends: - - "haml-lint foss" - - .static-analysis:rules:ee + - "haml-lint" + - .static-analysis:rules:haml-lint-ee script: - run_timed_command "bin/rake 'haml_lint[ee/app/views]'" @@ -115,14 +115,21 @@ rubocop: extends: - .static-analysis-base - .rubocop-job-cache - - .static-analysis:rules:ee-and-foss + - .static-analysis:rules:rubocop + needs: ["detect-tests"] script: - - run_timed_command "bundle exec rubocop --parallel" + - | + # For non-merge request, or when RUN_ALL_RUBOCOP is 'true', run all RuboCop rules + if [ -z "${CI_MERGE_REQUEST_IID}" ] || [ "${RUN_ALL_RUBOCOP}" == "true" ]; then + run_timed_command "bundle exec rubocop --parallel" + else + run_timed_command "bundle exec rubocop --parallel --force-exclusion $(cat tmp/changed_files.txt)" + fi qa:metadata-lint: extends: - .static-analysis-base - - .static-analysis:rules:ee-and-foss-qa + - .static-analysis:rules:qa:metadata-lint before_script: - !reference [.default-before_script, before_script] - cd qa/ @@ -149,7 +156,7 @@ feature-flags-usage: extends: - .static-analysis-base - .rubocop-job-cache - - .static-analysis:rules:ee-and-foss + - .static-analysis:rules:rubocop script: # We need to disable the cache for this cop since it creates files under tmp/feature_flags/*.used, # the cache would prevent these files from being created. diff --git a/.rubocop_todo/layout/space_inside_block_braces.yml b/.rubocop_todo/layout/space_inside_block_braces.yml deleted file mode 100644 index 25fc6dba4d9..00000000000 --- a/.rubocop_todo/layout/space_inside_block_braces.yml +++ /dev/null @@ -1,74 +0,0 @@ ---- -# Cop supports --auto-correct. -Layout/SpaceInsideBlockBraces: - Exclude: - - 'spec/config/settings_spec.rb' - - 'spec/controllers/admin/application_settings_controller_spec.rb' - - 'spec/controllers/application_controller_spec.rb' - - 'spec/controllers/groups/labels_controller_spec.rb' - - 'spec/controllers/groups/releases_controller_spec.rb' - - 'spec/controllers/import/manifest_controller_spec.rb' - - 'spec/controllers/projects/blame_controller_spec.rb' - - 'spec/controllers/projects/deploy_keys_controller_spec.rb' - - 'spec/controllers/projects/feature_flags_controller_spec.rb' - - 'spec/controllers/projects/jobs_controller_spec.rb' - - 'spec/controllers/projects/labels_controller_spec.rb' - - 'spec/controllers/projects/notes_controller_spec.rb' - - 'spec/controllers/projects/releases_controller_spec.rb' - - 'spec/controllers/projects/tree_controller_spec.rb' - - 'spec/controllers/registrations/welcome_controller_spec.rb' - - 'spec/controllers/snippets/notes_controller_spec.rb' - - 'spec/dependencies/omniauth_saml_spec.rb' - - 'spec/experiments/application_experiment_spec.rb' - - 'spec/finders/ci/jobs_finder_spec.rb' - - 'spec/finders/ci/runners_finder_spec.rb' - - 'spec/finders/concerns/packages/finder_helper_spec.rb' - - 'spec/finders/container_repositories_finder_spec.rb' - - 'spec/finders/design_management/versions_finder_spec.rb' - - 'spec/finders/milestones_finder_spec.rb' - - 'spec/finders/packages/group_packages_finder_spec.rb' - - 'spec/finders/packages/npm/package_finder_spec.rb' - - 'spec/finders/projects_finder_spec.rb' - - 'spec/helpers/application_settings_helper_spec.rb' - - 'spec/helpers/blob_helper_spec.rb' - - 'spec/helpers/gitlab_script_tag_helper_spec.rb' - - 'spec/helpers/issuables_helper_spec.rb' - - 'spec/helpers/projects/pipeline_helper_spec.rb' - - 'spec/helpers/routing/pseudonymization_helper_spec.rb' - - 'spec/helpers/search_helper_spec.rb' - - 'spec/helpers/wiki_page_version_helper_spec.rb' - - 'spec/initializers/carrierwave_patch_spec.rb' - - 'spec/initializers/trusted_proxies_spec.rb' - - 'spec/mailers/emails/service_desk_spec.rb' - - 'spec/migrations/20210812013042_remove_duplicate_project_authorizations_spec.rb' - - 'spec/migrations/20210910194952_update_report_type_for_existing_approval_project_rules_spec.rb' - - 'spec/migrations/confirm_support_bot_user_spec.rb' - - 'spec/migrations/reset_job_token_scope_enabled_again_spec.rb' - - 'spec/migrations/reset_job_token_scope_enabled_spec.rb' - - 'spec/migrations/reset_severity_levels_to_new_default_spec.rb' - - 'spec/policies/clusters/agent_policy_spec.rb' - - 'spec/policies/group_member_policy_spec.rb' - - 'spec/policies/issue_policy_spec.rb' - - 'spec/policies/project_policy_spec.rb' - - 'spec/policies/terraform/state_policy_spec.rb' - - 'spec/policies/terraform/state_version_policy_spec.rb' - - 'spec/presenters/packages/composer/packages_presenter_spec.rb' - - 'spec/presenters/packages/conan/package_presenter_spec.rb' - - 'spec/presenters/packages/nuget/packages_metadata_presenter_spec.rb' - - 'spec/presenters/project_presenter_spec.rb' - - 'spec/rubocop/cop/migration/create_table_with_foreign_keys_spec.rb' - - 'spec/serializers/cluster_entity_spec.rb' - - 'spec/serializers/import/provider_repo_serializer_spec.rb' - - 'spec/tasks/gitlab/snippets_rake_spec.rb' - - 'spec/uploaders/packages/debian/distribution_release_file_uploader_spec.rb' - - 'spec/validators/addressable_url_validator_spec.rb' - - 'spec/views/help/instance_configuration.html.haml_spec.rb' - - 'spec/views/layouts/_header_search.html.haml_spec.rb' - - 'spec/views/layouts/_published_experiments.html.haml_spec.rb' - - 'spec/views/shared/runners/_runner_details.html.haml_spec.rb' - - 'spec/workers/bulk_imports/export_request_worker_spec.rb' - - 'spec/workers/clusters/cleanup/project_namespace_worker_spec.rb' - - 'spec/workers/packages/helm/extraction_worker_spec.rb' - - 'spec/workers/pages_worker_spec.rb' - - 'spec/workers/purge_dependency_proxy_cache_worker_spec.rb' - - 'spec/workers/releases/manage_evidence_worker_spec.rb' diff --git a/app/assets/javascripts/access_tokens/components/constants.js b/app/assets/javascripts/access_tokens/components/constants.js index 84e50bc099f..9cd7cb5bb3a 100644 --- a/app/assets/javascripts/access_tokens/components/constants.js +++ b/app/assets/javascripts/access_tokens/components/constants.js @@ -12,8 +12,6 @@ export const FIELDS = [ key: 'name', label: __('Token name'), sortable: true, - tdClass: `gl-text-black-normal`, - thClass: `gl-text-black-normal`, }, { formatter(scopes) { @@ -22,40 +20,30 @@ export const FIELDS = [ key: 'scopes', label: __('Scopes'), sortable: true, - tdClass: `gl-text-black-normal`, - thClass: `gl-text-black-normal`, }, { key: 'createdAt', label: s__('AccessTokens|Created'), sortable: true, - tdClass: `gl-text-black-normal`, - thClass: `gl-text-black-normal`, }, { key: 'lastUsedAt', label: __('Last Used'), sortable: true, - tdClass: `gl-text-black-normal`, - thClass: `gl-text-black-normal`, }, { key: 'expiresAt', label: __('Expires'), sortable: true, - tdClass: `gl-text-black-normal`, - thClass: `gl-text-black-normal`, }, { key: 'role', label: __('Role'), - tdClass: `gl-text-black-normal`, - thClass: `gl-text-black-normal`, sortable: true, }, { key: 'action', label: __('Action'), - thClass: `gl-text-black-normal`, + tdClass: 'gl-py-3!', }, ]; diff --git a/app/assets/javascripts/content_editor/components/content_editor.vue b/app/assets/javascripts/content_editor/components/content_editor.vue index c24186ae406..05d6f468d23 100644 --- a/app/assets/javascripts/content_editor/components/content_editor.vue +++ b/app/assets/javascripts/content_editor/components/content_editor.vue @@ -105,7 +105,11 @@ export default { - + diff --git a/app/assets/javascripts/security_configuration/components/constants.js b/app/assets/javascripts/security_configuration/components/constants.js index 5a9ef832e05..77216408c39 100644 --- a/app/assets/javascripts/security_configuration/components/constants.js +++ b/app/assets/javascripts/security_configuration/components/constants.js @@ -157,7 +157,7 @@ export const SCANNER_NAMES_MAP = { COVERAGE_FUZZING: COVERAGE_FUZZING_NAME, SECRET_DETECTION: SECRET_DETECTION_NAME, DEPENDENCY_SCANNING: DEPENDENCY_SCANNING_NAME, - GENERIC: s__('ciReport|Manually Added'), + GENERIC: s__('ciReport|Manually added'), }; export const securityFeatures = [ diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js b/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js index 5d7f4ae2a01..ffe09634a3b 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js @@ -46,7 +46,7 @@ export const SortDirection = { export const FILTERED_SEARCH_LABELS = 'labels'; export const FILTERED_SEARCH_TERM = 'filtered-search-term'; -export const TOKEN_TITLE_ASSIGNEE = __('Assignee'); +export const TOKEN_TITLE_ASSIGNEE = s__('SearchToken|Assignee'); export const TOKEN_TITLE_AUTHOR = __('Author'); export const TOKEN_TITLE_CONFIDENTIAL = __('Confidential'); export const TOKEN_TITLE_CONTACT = s__('Crm|Contact'); diff --git a/app/assets/stylesheets/framework/contextual_sidebar.scss b/app/assets/stylesheets/framework/contextual_sidebar.scss index ad0036df607..34c7ffa58fe 100644 --- a/app/assets/stylesheets/framework/contextual_sidebar.scss +++ b/app/assets/stylesheets/framework/contextual_sidebar.scss @@ -149,7 +149,7 @@ margin: $sidebar-top-item-tb-margin $sidebar-top-item-lr-margin; &:hover { - background-color: $indigo-900-alpha-008; + background-color: $nav-active-bg; } } @@ -275,7 +275,7 @@ &:not(.fly-out-top-item) { > a:not(.has-sub-items) { - background-color: $indigo-900-alpha-008; + background-color: $nav-active-bg; } } } diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index ffefc5fead5..48223bd5416 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -560,7 +560,7 @@ } .frequent-items-list-item-container > a:hover { - background-color: $nav-active-bg; + background-color: $nav-active-bg !important; } } @@ -577,7 +577,7 @@ .top-nav-menu-item { &.active, &:hover { - background-color: $nav-active-bg; + background-color: $nav-active-bg !important; } .gl-icon { diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index e9ad930ef2b..bd32a817d5d 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -364,7 +364,7 @@ $well-expand-item: #e8f2f7 !default; $well-inner-border: #eef0f2 !default; $well-light-border: #f1f1f1; $well-light-text-color: #5b6169; -$nav-active-bg: var(--nav-active-bg, rgba($black, 0.08)) !important; +$nav-active-bg: rgba($black, 0.08); /* * Text diff --git a/app/assets/stylesheets/startup/startup-dark.scss b/app/assets/stylesheets/startup/startup-dark.scss index d4304abe241..71c99883297 100644 --- a/app/assets/stylesheets/startup/startup-dark.scss +++ b/app/assets/stylesheets/startup/startup-dark.scss @@ -18,7 +18,6 @@ body.gl-dark { --gl-text-color: #fafafa; --border-color: #4f4f4f; --black: #fff; - --nav-active-bg: rgba(255, 255, 255, 0.08); } :root { --white: #333; @@ -1128,7 +1127,7 @@ kbd { font-weight: 600; } .nav-sidebar li.active:not(.fly-out-top-item) > a:not(.has-sub-items) { - background-color: rgba(41, 41, 97, 0.08); + background-color: rgba(255, 255, 255, 0.08); } .nav-sidebar ul { padding-left: 0; @@ -1781,7 +1780,6 @@ body.gl-dark { --white: #333; --black: #fff; --svg-status-bg: #333; - --nav-active-bg: rgba(255, 255, 255, 0.08); } .nav-sidebar, .toggle-sidebar-button, @@ -1796,12 +1794,6 @@ body.gl-dark { .nav-sidebar li a { color: var(--gray-600); } -.nav-sidebar li.active { - box-shadow: none; -} -.nav-sidebar li.active:not(.fly-out-top-item) > a:not(.has-sub-items) { - background-color: var(--nav-active-bg); -} body.gl-dark { --gl-theme-accent: #868686; } @@ -2029,7 +2021,6 @@ body.gl-dark { --white: #333; --black: #fff; --svg-status-bg: #333; - --nav-active-bg: rgba(255, 255, 255, 0.08); } .tab-width-8 { tab-size: 8; diff --git a/app/assets/stylesheets/startup/startup-general.scss b/app/assets/stylesheets/startup/startup-general.scss index d08ea63ffed..d517fcf58ed 100644 --- a/app/assets/stylesheets/startup/startup-general.scss +++ b/app/assets/stylesheets/startup/startup-general.scss @@ -1107,7 +1107,7 @@ kbd { font-weight: 600; } .nav-sidebar li.active:not(.fly-out-top-item) > a:not(.has-sub-items) { - background-color: rgba(41, 41, 97, 0.08); + background-color: rgba(0, 0, 0, 0.08); } .nav-sidebar ul { padding-left: 0; diff --git a/app/assets/stylesheets/themes/_dark.scss b/app/assets/stylesheets/themes/_dark.scss index eeb4604f32a..4b74e449e06 100644 --- a/app/assets/stylesheets/themes/_dark.scss +++ b/app/assets/stylesheets/themes/_dark.scss @@ -101,7 +101,6 @@ $white-dark: #444; $theme-indigo-50: #1a1a40; $border-color: #4f4f4f; -$nav-active-bg: rgba(255, 255, 255, 0.08); :root { color-scheme: dark; @@ -206,7 +205,6 @@ body.gl-dark { --black: #{$black}; --svg-status-bg: #{$white}; - --nav-active-bg: #{$nav-active-bg}; .gl-button.gl-button, .gl-button.gl-button.btn-block { diff --git a/app/assets/stylesheets/themes/dark_mode_overrides.scss b/app/assets/stylesheets/themes/dark_mode_overrides.scss index 92740aaf89e..9348216c2ac 100644 --- a/app/assets/stylesheets/themes/dark_mode_overrides.scss +++ b/app/assets/stylesheets/themes/dark_mode_overrides.scss @@ -64,20 +64,6 @@ a { color: var(--gray-600); } - - > a:hover { - background-color: var(--nav-active-bg); - } - - &.active { - box-shadow: none; - - &:not(.fly-out-top-item) { - > a:not(.has-sub-items) { - background-color: var(--nav-active-bg); - } - } - } } .sidebar-sub-level-items.fly-out-list { diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 621d198d341..bf7eed9543d 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -473,7 +473,7 @@ module Ci end def uses_needs? - builds.where(scheduling_type: :dag).any? + processables.where(scheduling_type: :dag).any? end def stages_count diff --git a/config/feature_flags/development/ci_forked_source_public_cost_factor.yml b/config/feature_flags/development/ci_forked_source_public_cost_factor.yml deleted file mode 100644 index 6e5a6c1e1f1..00000000000 --- a/config/feature_flags/development/ci_forked_source_public_cost_factor.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: ci_forked_source_public_cost_factor -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/94870 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/370147 -milestone: '15.4' -type: development -group: group::pipeline execution -default_enabled: false diff --git a/config/metrics/counts_28d/20210216184850_deploy_token_packages_total_unique_counts_monthly.yml b/config/metrics/counts_28d/20210216184850_deploy_token_packages_total_unique_counts_monthly.yml index 720165425fa..6b6c40495a6 100644 --- a/config/metrics/counts_28d/20210216184850_deploy_token_packages_total_unique_counts_monthly.yml +++ b/config/metrics/counts_28d/20210216184850_deploy_token_packages_total_unique_counts_monthly.yml @@ -16,17 +16,13 @@ options: events: - i_package_composer_deploy_token - i_package_conan_deploy_token - - i_package_container_deploy_token - - i_package_debian_deploy_token - i_package_generic_deploy_token - - i_package_golang_deploy_token - i_package_helm_deploy_token - i_package_maven_deploy_token - i_package_npm_deploy_token - i_package_nuget_deploy_token - i_package_pypi_deploy_token - i_package_rubygems_deploy_token - - i_package_tag_deploy_token - i_package_terraform_module_deploy_token distribution: - ee diff --git a/config/metrics/counts_7d/20210216184848_deploy_token_packages_total_unique_counts_weekly.yml b/config/metrics/counts_7d/20210216184848_deploy_token_packages_total_unique_counts_weekly.yml index 671b8a1785a..28adf6d6d01 100644 --- a/config/metrics/counts_7d/20210216184848_deploy_token_packages_total_unique_counts_weekly.yml +++ b/config/metrics/counts_7d/20210216184848_deploy_token_packages_total_unique_counts_weekly.yml @@ -15,17 +15,13 @@ options: events: - i_package_composer_deploy_token - i_package_conan_deploy_token - - i_package_container_deploy_token - - i_package_debian_deploy_token - i_package_generic_deploy_token - - i_package_golang_deploy_token - i_package_helm_deploy_token - i_package_maven_deploy_token - i_package_npm_deploy_token - i_package_nuget_deploy_token - i_package_pypi_deploy_token - i_package_rubygems_deploy_token - - i_package_tag_deploy_token - i_package_terraform_module_deploy_token distribution: - ee diff --git a/doc/ci/pipelines/cicd_minutes.md b/doc/ci/pipelines/cicd_minutes.md index 4b7d3845361..7487bd3190c 100644 --- a/doc/ci/pipelines/cicd_minutes.md +++ b/doc/ci/pipelines/cicd_minutes.md @@ -201,9 +201,12 @@ can be higher than the end-to-end duration of a pipeline. The cost factors for jobs running on shared runners on GitLab.com are: -- `0.008` for public projects, and projects in the [GitLab for Open Source program](../../subscriptions/index.md#gitlab-for-open-source). - For every 125 minutes of job execution time, you use 1 CI/CD minute. - `1` for internal and private projects. +- `0.5` for public projects in the [GitLab for Open Source program](../../subscriptions/index.md#gitlab-for-open-source). +- `0.008` for public forks of public projects. For every 125 minutes of job execution time, + you use 1 CI/CD minute. +- `0.04` for other public projects, after September 1, 2022 (previously `0.008`). + For every 25 minutes of job execution time, you use 1 CI/CD minute. - Calculated differently for [community contributions to GitLab projects](#cost-factor-for-community-contributions-to-gitlab-projects). The cost factors on self-managed instances are: diff --git a/doc/development/merge_request_concepts/index.md b/doc/development/merge_request_concepts/index.md index 331f0e01579..d463f6ba290 100644 --- a/doc/development/merge_request_concepts/index.md +++ b/doc/development/merge_request_concepts/index.md @@ -10,7 +10,7 @@ info: "See the Technical Writers assigned to Development Guidelines: https://abo NOTE: The documentation below is the single source of truth for the merge request terminology and functionality. -The merge request is made up of several different key components and ideas that encompass the overall merge request experience. These concepts sometimes have competing and confusing terminology or overlap with other concepts. The concepts this will cover are: +The merge request is made up of several different key components and ideas that encompass the overall merge request experience. These concepts sometimes have competing and confusing terminology or overlap with other concepts. This page covers the following concepts: 1. Merge widget 1. Report widgets @@ -40,15 +40,17 @@ Reports are widgets within the merge request that report information about chang ## Merge checks -Merge checks are statuses that can either pass or fail and conditionally control the availability of the merge button being available within a merge request. The key distinguishing factor in a merge check is that users **do not** interact with the merge checks inside of the merge request, but are able to influence whether or not the check passes or fails. Results from the check are processed as true/false to determine whether or not a merge request can be merged. Examples include: +Merge checks are statuses that can either pass or fail and conditionally control the availability of the merge button being available within a merge request. The key distinguishing factor in a merge check is that users **do not** interact with the merge checks inside of the merge request, but are able to influence whether or not the check passes or fails. Results from the check are processed as true/false to determine whether or not a merge request can be merged. -- Merge conflicts. -- Pipeline success. -- Threads resolution. -- [External status checks](../../user/project/merge_requests/status_checks.md). -- Required approvals. +Examples of merge checks include: -When all of the required merge checks are satisfied a merge request becomes mergeable. +- Merge conflicts +- Pipeline success +- Threads resolution +- [External status checks](../../user/project/merge_requests/status_checks.md) +- Required approvals + +A merge request can be merged only when all of the required merge checks are satisfied. ## Approvals @@ -58,8 +60,8 @@ Additionally, approval settings provide configuration options to define how thos Examples of approval rules and settings include: -1. [merge request approval rules](../../user/project/merge_requests/approvals/rules.md) -1. [code owner approvals](../../user/project/code_owners.md) -1. [security approvals](../../user/application_security/index.md#security-approvals-in-merge-requests) -1. [prevent editing approval rules](../../user/project/merge_requests/approvals/settings.md#prevent-editing-approval-rules-in-merge-requests) -1. [remove all approvals when commits are added](../../user/project/merge_requests/approvals/settings.md#remove-all-approvals-when-commits-are-added-to-the-source-branch) +- [Merge request approval rules](../../user/project/merge_requests/approvals/rules.md) +- [Code owner approvals](../../user/project/code_owners.md) +- [Security approvals](../../user/application_security/index.md#security-approvals-in-merge-requests) +- [Prevent editing approval rules](../../user/project/merge_requests/approvals/settings.md#prevent-editing-approval-rules-in-merge-requests) +- [Remove all approvals when commits are added](../../user/project/merge_requests/approvals/settings.md#remove-all-approvals-when-commits-are-added-to-the-source-branch) diff --git a/doc/development/service_ping/implement.md b/doc/development/service_ping/implement.md index 0ebc58dd669..5ddf4e8fa73 100644 --- a/doc/development/service_ping/implement.md +++ b/doc/development/service_ping/implement.md @@ -127,7 +127,7 @@ Examples using `usage_data.rb` have been [deprecated](usage_data.md). We recomme #### Grouping and batch operations -The `count`, `distinct_count`, `sum`, and `average` batch counters can accept an `ActiveRecord::Relation` +The `count`, `distinct_count` and `sum` batch counters can accept an `ActiveRecord::Relation` object, which groups by a specified column. With a grouped relation, the methods do batch counting, handle errors, and returns a hash table of key-value pairs. @@ -142,9 +142,6 @@ distinct_count(Project.group(:visibility_level), :creator_id) sum(Issue.group(:state_id), :weight)) # returns => {1=>3542, 2=>6820} - -average(Issue.group(:state_id), :weight)) -# returns => {1=>3.5, 2=>2.5} ``` #### Add operation diff --git a/doc/development/service_ping/metrics_instrumentation.md b/doc/development/service_ping/metrics_instrumentation.md index bde838df940..ee0d701a5bb 100644 --- a/doc/development/service_ping/metrics_instrumentation.md +++ b/doc/development/service_ping/metrics_instrumentation.md @@ -337,7 +337,7 @@ To create a stub instrumentation for a Service Ping metric, you can use a dedica The generator takes the class name as an argument and the following options: - `--type=TYPE` Required. Indicates the metric type. It must be one of: `database`, `generic`, `redis`, `numbers`. -- `--operation` Required for `database` & `numebers` type. +- `--operation` Required for `database` & `numbers` type. - For `database` it must be one of: `count`, `distinct_count`, `estimate_batch_distinct_count`, `sum`, `average`. - For `numbers` it must be: `add`. - `--ee` Indicates if the metric is for EE. diff --git a/doc/user/project/merge_requests/reviews/img/mr_summary_comment_v15_3.png b/doc/user/project/merge_requests/reviews/img/mr_summary_comment_v15_3.png deleted file mode 100644 index 38e181158030749d9dfb28db467eb9463bd8a7a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49186 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 hMD&3l-~sC{fv({q8#6AU?< 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�g7LDjO`9meclwV7i>B?LY~bSV0t> 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 Bu`d7s literal 0 HcmV?d00001 diff --git a/doc/user/project/merge_requests/reviews/index.md b/doc/user/project/merge_requests/reviews/index.md index 27b223c48ec..5a788f595ea 100644 --- a/doc/user/project/merge_requests/reviews/index.md +++ b/doc/user/project/merge_requests/reviews/index.md @@ -56,12 +56,11 @@ displays next to your name. You can submit your completed review in multiple ways: - Use the `/submit_review` [quick action](../../quick_actions.md) in the text of a non-review comment. -- Select **Finish review** and then **Submit review** in the footer at the bottom of the screen. +- Select **Finish review**, then select **Submit review** at the bottom of the modal window. + In the modal window, you can supply a **Summary comment**, approve the merge request, and + include quick actions: -Selecting **Finish review** opens a modal window to add an optional comment to summarize your review. -You can also include quick actions: - -![Finish review with comment](img/mr_summary_comment_v15_3.png) + ![Finish review with comment](img/mr_summary_comment_v15_4.png) When you submit your review, GitLab: @@ -69,6 +68,7 @@ When you submit your review, GitLab: - Sends a single email to every notifiable user of the merge request, with your review comments attached. Replying to this email creates a new comment on the merge request. - Perform any quick actions you added to your review comments. +- Optional. Approves the merge request. ### Resolve or unresolve thread with a comment diff --git a/lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent.rb b/lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent.rb index 728b60f7a0e..0c41d6af209 100644 --- a/lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent.rb +++ b/lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent.rb @@ -10,16 +10,12 @@ module Gitlab vulnerability_reads.project_id = cluster_agents.project_id SQL - RELATION = ->(relation) do - relation - .where(report_type: 7) - end + CLUSTER_IMAGE_SCANNING_REPORT_TYPE = 7 + + scope_to ->(relation) { relation.where(report_type: CLUSTER_IMAGE_SCANNING_REPORT_TYPE) } def perform - each_sub_batch( - operation_name: :update_all, - batching_scope: RELATION - ) do |sub_batch| + each_sub_batch(operation_name: :update_all) do |sub_batch| sub_batch .joins(CLUSTER_AGENTS_JOIN) .update_all('casted_cluster_agent_id = CAST(vulnerability_reads.cluster_agent_id AS bigint)') diff --git a/lib/gitlab/background_migration/batching_strategies/backfill_project_statistics_with_container_registry_size_batching_strategy.rb b/lib/gitlab/background_migration/batching_strategies/backfill_project_statistics_with_container_registry_size_batching_strategy.rb index 9ad119310f7..72da2b5a2b7 100644 --- a/lib/gitlab/background_migration/batching_strategies/backfill_project_statistics_with_container_registry_size_batching_strategy.rb +++ b/lib/gitlab/background_migration/batching_strategies/backfill_project_statistics_with_container_registry_size_batching_strategy.rb @@ -3,18 +3,9 @@ module Gitlab module BackgroundMigration module BatchingStrategies - # Batching class to use for back-filling project_statistic's container_registry_size. - # Batches will be scoped to records where the project_ids are migrated - # - # If no more batches exist in the table, returns nil. + # Used to apply additional filters to the batching table, migrated to + # use BatchedMigrationJob#filter_batch with https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93771 class BackfillProjectStatisticsWithContainerRegistrySizeBatchingStrategy < PrimaryKeyBatchingStrategy - MIGRATION_PHASE_1_ENDED_AT = Date.new(2022, 01, 23).freeze - - def apply_additional_filters(relation, job_arguments: [], job_class: nil) - relation.where(created_at: MIGRATION_PHASE_1_ENDED_AT..).or( - relation.where(migration_state: 'import_done') - ).select(:project_id).distinct - end end end end diff --git a/lib/gitlab/background_migration/batching_strategies/backfill_vulnerability_reads_cluster_agent_batching_strategy.rb b/lib/gitlab/background_migration/batching_strategies/backfill_vulnerability_reads_cluster_agent_batching_strategy.rb index f0d015198dc..c2fa00f66de 100644 --- a/lib/gitlab/background_migration/batching_strategies/backfill_vulnerability_reads_cluster_agent_batching_strategy.rb +++ b/lib/gitlab/background_migration/batching_strategies/backfill_vulnerability_reads_cluster_agent_batching_strategy.rb @@ -3,16 +3,9 @@ module Gitlab module BackgroundMigration module BatchingStrategies - # Batching class to use for back-filling vulnerability_read's casted_cluster_agent_id from cluster_agent_id. - # Batches will be scoped to records where the report_type belongs to cluster_image_scanning. - # - # If no more batches exist in the table, returns nil. + # Used to apply additional filters to the batching table, migrated to + # use BatchedMigrationJob#filter_batch with https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93771 class BackfillVulnerabilityReadsClusterAgentBatchingStrategy < PrimaryKeyBatchingStrategy - CLUSTER_IMAGE_SCANNING_REPORT_TYPE = 7 - - def apply_additional_filters(relation, job_arguments: [], job_class: nil) - relation.where(report_type: CLUSTER_IMAGE_SCANNING_REPORT_TYPE) - end end end end diff --git a/lib/gitlab/background_migration/batching_strategies/dismissed_vulnerabilities_strategy.rb b/lib/gitlab/background_migration/batching_strategies/dismissed_vulnerabilities_strategy.rb index e1855b6cfee..9504d4eec11 100644 --- a/lib/gitlab/background_migration/batching_strategies/dismissed_vulnerabilities_strategy.rb +++ b/lib/gitlab/background_migration/batching_strategies/dismissed_vulnerabilities_strategy.rb @@ -3,14 +3,9 @@ module Gitlab module BackgroundMigration module BatchingStrategies - # Batching class to use for setting state in vulnerabilitites table. - # Batches will be scoped to records where the dismissed_at is set. - # - # If no more batches exist in the table, returns nil. + # Used to apply additional filters to the batching table, migrated to + # use BatchedMigrationJob#filter_batch with https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93771 class DismissedVulnerabilitiesStrategy < PrimaryKeyBatchingStrategy - def apply_additional_filters(relation, job_arguments: [], job_class: nil) - relation.where.not(dismissed_at: nil) - end end end end diff --git a/lib/gitlab/background_migration/set_correct_vulnerability_state.rb b/lib/gitlab/background_migration/set_correct_vulnerability_state.rb index fd6cbcb8d05..a0cfeed618a 100644 --- a/lib/gitlab/background_migration/set_correct_vulnerability_state.rb +++ b/lib/gitlab/background_migration/set_correct_vulnerability_state.rb @@ -6,11 +6,10 @@ module Gitlab class SetCorrectVulnerabilityState < BatchedMigrationJob DISMISSED_STATE = 2 + scope_to ->(relation) { relation.where.not(dismissed_at: nil) } + def perform - each_sub_batch( - operation_name: :update_vulnerabilities_state, - batching_scope: -> (relation) { relation.where.not(dismissed_at: nil) } - ) do |sub_batch| + each_sub_batch(operation_name: :update_vulnerabilities_state) do |sub_batch| sub_batch.update_all(state: DISMISSED_STATE) end end diff --git a/lib/gitlab/database/batch_average_counter.rb b/lib/gitlab/database/batch_average_counter.rb new file mode 100644 index 00000000000..9cb1e34ab67 --- /dev/null +++ b/lib/gitlab/database/batch_average_counter.rb @@ -0,0 +1,103 @@ +# frozen_string_literal: true + +module Gitlab + module Database + class BatchAverageCounter + COLUMN_FALLBACK = 0 + DEFAULT_BATCH_SIZE = 1_000 + FALLBACK = -1 + MAX_ALLOWED_LOOPS = 10_000 + OFFSET_BY_ONE = 1 + SLEEP_TIME_IN_SECONDS = 0.01 # 10 msec sleep + + attr_reader :relation, :column + + def initialize(relation, column) + @relation = relation + @column = wrap_column(relation, column) + end + + def count(batch_size: nil) + raise 'BatchAverageCounter can not be run inside a transaction' if transaction_open? + + batch_size = batch_size.presence || DEFAULT_BATCH_SIZE + + start = column_start + finish = column_finish + + total_sum = 0 + total_records = 0 + + batch_start = start + + while batch_start < finish + begin + batch_end = [batch_start + batch_size, finish].min + batch_relation = build_relation_batch(batch_start, batch_end) + + # We use `sum` and `count` instead of `average` here to not run into an "average of averages" + # problem as batches will have different sizes, so we are essentially summing up the values for + # each batch separately, and then dividing that result on the total number of records. + batch_sum, batch_count = batch_relation.pick(column.sum, column.count) + + total_sum += batch_sum.to_i + total_records += batch_count + + batch_start = batch_end + rescue ActiveRecord::QueryCanceled => error # rubocop:disable Database/RescueQueryCanceled + # retry with a safe batch size & warmer cache + if batch_size >= 2 * DEFAULT_BATCH_SIZE + batch_size /= 2 + else + log_canceled_batch_fetch(batch_start, batch_relation.to_sql, error) + + return FALLBACK + end + end + + sleep(SLEEP_TIME_IN_SECONDS) + end + + return FALLBACK if total_records == 0 + + total_sum.to_f / total_records + end + + private + + def column_start + relation.unscope(:group, :having).minimum(column) || COLUMN_FALLBACK + end + + def column_finish + (relation.unscope(:group, :having).maximum(column) || COLUMN_FALLBACK) + OFFSET_BY_ONE + end + + def build_relation_batch(start, finish) + relation.where(column.between(start...finish)) + end + + def log_canceled_batch_fetch(batch_start, query, error) + Gitlab::AppJsonLogger + .error( + event: 'batch_count', + relation: relation.table_name, + operation: 'average', + start: batch_start, + query: query, + message: "Query has been canceled with message: #{error.message}" + ) + end + + def transaction_open? + relation.connection.transaction_open? + end + + def wrap_column(relation, column) + return column if column.is_a?(Arel::Attributes::Attribute) + + relation.arel_table[column] + end + end + end +end diff --git a/lib/gitlab/database/batch_count.rb b/lib/gitlab/database/batch_count.rb index b33f39e3cbe..7a064fb4005 100644 --- a/lib/gitlab/database/batch_count.rb +++ b/lib/gitlab/database/batch_count.rb @@ -48,7 +48,7 @@ module Gitlab end def batch_average(relation, column, batch_size: nil, start: nil, finish: nil) - BatchCounter.new(relation, column: nil, operation: :average, operation_args: [column]).count(batch_size: batch_size, start: start, finish: finish) + BatchAverageCounter.new(relation, column).count(batch_size: batch_size) end class << self diff --git a/lib/gitlab/database/batch_counter.rb b/lib/gitlab/database/batch_counter.rb index aa4abf15755..abb62140503 100644 --- a/lib/gitlab/database/batch_counter.rb +++ b/lib/gitlab/database/batch_counter.rb @@ -6,7 +6,6 @@ module Gitlab FALLBACK = -1 MIN_REQUIRED_BATCH_SIZE = 1_250 DEFAULT_SUM_BATCH_SIZE = 1_000 - DEFAULT_AVERAGE_BATCH_SIZE = 1_000 MAX_ALLOWED_LOOPS = 10_000 SLEEP_TIME_IN_SECONDS = 0.01 # 10 msec sleep ALLOWED_MODES = [:itself, :distinct].freeze @@ -27,7 +26,6 @@ module Gitlab def unwanted_configuration?(finish, batch_size, start) (@operation == :count && batch_size <= MIN_REQUIRED_BATCH_SIZE) || (@operation == :sum && batch_size < DEFAULT_SUM_BATCH_SIZE) || - (@operation == :average && batch_size < DEFAULT_AVERAGE_BATCH_SIZE) || (finish - start) / batch_size >= MAX_ALLOWED_LOOPS || start >= finish end @@ -110,7 +108,6 @@ module Gitlab def batch_size_for_mode_and_operation(mode, operation) return DEFAULT_SUM_BATCH_SIZE if operation == :sum - return DEFAULT_AVERAGE_BATCH_SIZE if operation == :average mode == :distinct ? DEFAULT_DISTINCT_BATCH_SIZE : DEFAULT_BATCH_SIZE end @@ -148,10 +145,6 @@ module Gitlab message: "Query has been canceled with message: #{error.message}" ) end - - def not_group_by_query? - !@relation.is_a?(ActiveRecord::Relation) || @relation.group_values.blank? - end end end end diff --git a/lib/gitlab/usage_data_counters/hll_redis_counter.rb b/lib/gitlab/usage_data_counters/hll_redis_counter.rb index a5db8ba4dcc..51dc3689dfd 100644 --- a/lib/gitlab/usage_data_counters/hll_redis_counter.rb +++ b/lib/gitlab/usage_data_counters/hll_redis_counter.rb @@ -22,7 +22,6 @@ module Gitlab analytics code_review compliance - deploy_token_packages ecosystem epic_boards_usage epics_usage @@ -37,6 +36,7 @@ module Gitlab CATEGORIES_COLLECTED_FROM_METRICS_DEFINITIONS = %w[ ci_users + deploy_token_packages error_tracking ide_edit importer diff --git a/locale/gitlab.pot b/locale/gitlab.pot index fea9aa2529c..e30601d3011 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -218,6 +218,11 @@ msgid_plural "%d contributions" msgstr[0] "" msgstr[1] "" +msgid "%d contributor" +msgid_plural "%d contributors" +msgstr[0] "" +msgstr[1] "" + msgid "%d day" msgid_plural "%d days" msgstr[0] "" @@ -368,6 +373,11 @@ msgid_plural "%d projects selected" msgstr[0] "" msgstr[1] "" +msgid "%d push" +msgid_plural "%d pushes" +msgstr[0] "" +msgstr[1] "" + msgid "%d remaining" msgid_plural "%d remaining" msgstr[0] "" @@ -10418,7 +10428,7 @@ msgstr "" msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed." msgstr "" -msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors." +msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}." msgstr "" msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}" @@ -13104,6 +13114,12 @@ msgstr "" msgid "DeploymentApproval|Manual job: %{jobName}" msgstr "" +msgid "DeploymentApproval|Rejected %{time}" +msgstr "" + +msgid "DeploymentApproval|Rejected by you %{time}" +msgstr "" + msgid "DeploymentTarget|GitLab Pages" msgstr "" @@ -46175,7 +46191,7 @@ msgstr "" msgid "ciReport|Manage licenses" msgstr "" -msgid "ciReport|Manually Added" +msgid "ciReport|Manually added" msgstr "" msgid "ciReport|New" diff --git a/spec/config/settings_spec.rb b/spec/config/settings_spec.rb index 1de0e7e6c26..10eeb3f5371 100644 --- a/spec/config/settings_spec.rb +++ b/spec/config/settings_spec.rb @@ -116,7 +116,7 @@ RSpec.describe Settings do describe '.cron_for_service_ping' do it 'returns correct crontab for some manually calculated example' do allow(Gitlab::CurrentSettings) - .to receive(:uuid) { 'd9e2f4e8-db1f-4e51-b03d-f427e1965c4a'} + .to receive(:uuid) { 'd9e2f4e8-db1f-4e51-b03d-f427e1965c4a' } expect(described_class.send(:cron_for_service_ping)).to eq('44 10 * * 4') end diff --git a/spec/controllers/admin/application_settings_controller_spec.rb b/spec/controllers/admin/application_settings_controller_spec.rb index cb25011fd96..ab0cad989cb 100644 --- a/spec/controllers/admin/application_settings_controller_spec.rb +++ b/spec/controllers/admin/application_settings_controller_spec.rb @@ -9,7 +9,7 @@ RSpec.describe Admin::ApplicationSettingsController, :do_not_mock_admin_mode_set let(:group) { create(:group) } let(:project) { create(:project, namespace: group) } let(:admin) { create(:admin) } - let(:user) { create(:user)} + let(:user) { create(:user) } before do stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 1e28ef4ba93..f1adb9020fa 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -1006,7 +1006,7 @@ RSpec.describe ApplicationController do end describe '.endpoint_id_for_action' do - controller(described_class) { } + controller(described_class) {} it 'returns an expected endpoint id' do expect(controller.class.endpoint_id_for_action('hello')).to eq('AnonymousController#hello') diff --git a/spec/controllers/groups/labels_controller_spec.rb b/spec/controllers/groups/labels_controller_spec.rb index 90da40cd5f0..37db26096d3 100644 --- a/spec/controllers/groups/labels_controller_spec.rb +++ b/spec/controllers/groups/labels_controller_spec.rb @@ -20,7 +20,7 @@ RSpec.describe Groups::LabelsController do it 'returns group and project labels by default' do get :index, params: { group_id: group }, format: :json - label_ids = json_response.map {|label| label['title']} + label_ids = json_response.map { |label| label['title'] } expect(label_ids).to match_array([label_1.title, group_label_1.title]) end @@ -36,7 +36,7 @@ RSpec.describe Groups::LabelsController do params = { group_id: subgroup, only_group_labels: true } get :index, params: params, format: :json - label_ids = json_response.map {|label| label['title']} + label_ids = json_response.map { |label| label['title'] } expect(label_ids).to match_array([group_label_1.title, subgroup_label_1.title]) end end diff --git a/spec/controllers/groups/releases_controller_spec.rb b/spec/controllers/groups/releases_controller_spec.rb index 9d372114d62..7dd0bc6206a 100644 --- a/spec/controllers/groups/releases_controller_spec.rb +++ b/spec/controllers/groups/releases_controller_spec.rb @@ -42,7 +42,7 @@ RSpec.describe Groups::ReleasesController do end it 'does not return any releases' do - expect(json_response.map {|r| r['tag'] } ).to be_empty + expect(json_response.map { |r| r['tag'] } ).to be_empty end it 'returns OK' do @@ -56,7 +56,7 @@ RSpec.describe Groups::ReleasesController do index - expect(json_response.map {|r| r['tag'] } ).to match_array(%w(p2 p1 v2 v1)) + expect(json_response.map { |r| r['tag'] } ).to match_array(%w(p2 p1 v2 v1)) end end diff --git a/spec/controllers/import/manifest_controller_spec.rb b/spec/controllers/import/manifest_controller_spec.rb index 0111ad9501f..6f805b44e89 100644 --- a/spec/controllers/import/manifest_controller_spec.rb +++ b/spec/controllers/import/manifest_controller_spec.rb @@ -6,7 +6,7 @@ RSpec.describe Import::ManifestController, :clean_gitlab_redis_shared_state do include ImportSpecHelper let_it_be(:user) { create(:user) } - let_it_be(:group) { create(:group)} + let_it_be(:group) { create(:group) } before(:all) do group.add_maintainer(user) diff --git a/spec/controllers/projects/blame_controller_spec.rb b/spec/controllers/projects/blame_controller_spec.rb index bf475f6135a..62a544bb3fc 100644 --- a/spec/controllers/projects/blame_controller_spec.rb +++ b/spec/controllers/projects/blame_controller_spec.rb @@ -41,7 +41,7 @@ RSpec.describe Projects::BlameController do end context "invalid branch, valid file" do - let(:id) { 'invalid-branch/files/ruby/missing_file.rb'} + let(:id) { 'invalid-branch/files/ruby/missing_file.rb' } it { is_expected.to respond_with(:not_found) } end diff --git a/spec/controllers/projects/deploy_keys_controller_spec.rb b/spec/controllers/projects/deploy_keys_controller_spec.rb index 821f7fca73d..308146ce792 100644 --- a/spec/controllers/projects/deploy_keys_controller_spec.rb +++ b/spec/controllers/projects/deploy_keys_controller_spec.rb @@ -27,8 +27,8 @@ RSpec.describe Projects::DeployKeysController do end context 'when json requested' do - let(:project2) { create(:project, :internal)} - let(:project_private) { create(:project, :private)} + let(:project2) { create(:project, :internal) } + let(:project_private) { create(:project, :private) } let(:deploy_key_internal) { create(:deploy_key) } let(:deploy_key_actual) { create(:deploy_key) } diff --git a/spec/controllers/projects/feature_flags_controller_spec.rb b/spec/controllers/projects/feature_flags_controller_spec.rb index fd95aa44568..d7850cc5f33 100644 --- a/spec/controllers/projects/feature_flags_controller_spec.rb +++ b/spec/controllers/projects/feature_flags_controller_spec.rb @@ -208,7 +208,7 @@ RSpec.describe Projects::FeatureFlagsController do end context 'when feature flag is not found' do - let!(:feature_flag) { } + let!(:feature_flag) {} let(:params) do { diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb index e4e3151dd12..0793e790301 100644 --- a/spec/controllers/projects/jobs_controller_spec.rb +++ b/spec/controllers/projects/jobs_controller_spec.rb @@ -700,7 +700,7 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do expect(response).to match_response_schema('job/build_trace') expect(json_response['id']).to eq job.id expect(json_response['status']).to eq job.status - expect(json_response['lines'].flat_map {|l| l['content'].map { |c| c['text'] } }).to include("ヾ(´༎ຶД༎ຶ`)ノ") + expect(json_response['lines'].flat_map { |l| l['content'].map { |c| c['text'] } }).to include("ヾ(´༎ຶД༎ຶ`)ノ") end end diff --git a/spec/controllers/projects/labels_controller_spec.rb b/spec/controllers/projects/labels_controller_spec.rb index 776ed9774b1..a5259522fe2 100644 --- a/spec/controllers/projects/labels_controller_spec.rb +++ b/spec/controllers/projects/labels_controller_spec.rb @@ -25,10 +25,10 @@ RSpec.describe Projects::LabelsController do let_it_be(:group_label_3) { create(:group_label, group: group, title: 'Group Label 3') } let_it_be(:group_label_4) { create(:group_label, group: group, title: 'Group Label 4') } - let_it_be(:group_labels) { [group_label_3, group_label_4]} - let_it_be(:project_labels) { [label_4, label_5]} - let_it_be(:group_priority_labels) { [group_label_1, group_label_2]} - let_it_be(:project_priority_labels) { [label_1, label_2, label_3]} + let_it_be(:group_labels) { [group_label_3, group_label_4] } + let_it_be(:project_labels) { [label_4, label_5] } + let_it_be(:group_priority_labels) { [group_label_1, group_label_2] } + let_it_be(:project_priority_labels) { [label_1, label_2, label_3] } before do create(:label_priority, project: project, label: group_label_1, priority: 3) diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb index 9050765afd6..1f8e96258ca 100644 --- a/spec/controllers/projects/notes_controller_spec.rb +++ b/spec/controllers/projects/notes_controller_spec.rb @@ -659,7 +659,7 @@ RSpec.describe Projects::NotesController do context 'when target_id and noteable_id do not match' do let(:locked_issue) { create(:issue, :locked, project: project) } - let(:issue) {create(:issue, project: project)} + let(:issue) { create(:issue, project: project) } it 'uses target_id and ignores noteable_id' do request_params = { diff --git a/spec/controllers/projects/releases_controller_spec.rb b/spec/controllers/projects/releases_controller_spec.rb index ad6682601f3..b307bb357fa 100644 --- a/spec/controllers/projects/releases_controller_spec.rb +++ b/spec/controllers/projects/releases_controller_spec.rb @@ -312,7 +312,7 @@ RSpec.describe Projects::ReleasesController do end context 'suffix path abuse' do - let(:suffix_path) { 'downloads/zips/../../../../../../../robots.txt'} + let(:suffix_path) { 'downloads/zips/../../../../../../../robots.txt' } it 'raises attack error' do expect do diff --git a/spec/controllers/projects/tree_controller_spec.rb b/spec/controllers/projects/tree_controller_spec.rb index 143516e4712..9bc3065b6da 100644 --- a/spec/controllers/projects/tree_controller_spec.rb +++ b/spec/controllers/projects/tree_controller_spec.rb @@ -163,8 +163,8 @@ RSpec.describe Projects::TreeController do end context 'successful creation' do - let(:path) { 'files/new_dir'} - let(:branch_name) { 'master-test'} + let(:path) { 'files/new_dir' } + let(:branch_name) { 'master-test' } it 'redirects to the new directory' do expect(subject) @@ -175,7 +175,7 @@ RSpec.describe Projects::TreeController do context 'unsuccessful creation' do let(:path) { 'README.md' } - let(:branch_name) { 'master'} + let(:branch_name) { 'master' } it 'does not allow overwriting of existing files' do expect(subject) diff --git a/spec/controllers/registrations/welcome_controller_spec.rb b/spec/controllers/registrations/welcome_controller_spec.rb index 8a5a8490a23..14e88d469ba 100644 --- a/spec/controllers/registrations/welcome_controller_spec.rb +++ b/spec/controllers/registrations/welcome_controller_spec.rb @@ -36,7 +36,7 @@ RSpec.describe Registrations::WelcomeController do sign_in(user) end - it { is_expected.to redirect_to(dashboard_projects_path)} + it { is_expected.to redirect_to(dashboard_projects_path) } end context 'when role is set and setup_for_company is not set' do @@ -78,7 +78,7 @@ RSpec.describe Registrations::WelcomeController do sign_in(user) end - it { is_expected.to redirect_to(dashboard_projects_path)} + it { is_expected.to redirect_to(dashboard_projects_path) } context 'when the new user already has any accepted group membership' do let!(:member1) { create(:group_member, user: user) } diff --git a/spec/controllers/snippets/notes_controller_spec.rb b/spec/controllers/snippets/notes_controller_spec.rb index 8e85e283b31..00d99b46d0b 100644 --- a/spec/controllers/snippets/notes_controller_spec.rb +++ b/spec/controllers/snippets/notes_controller_spec.rb @@ -312,7 +312,7 @@ RSpec.describe Snippets::NotesController do describe 'POST toggle_award_emoji' do let(:note) { create(:note_on_personal_snippet, noteable: public_snippet) } - let(:emoji_name) { 'thumbsup'} + let(:emoji_name) { 'thumbsup' } before do sign_in(user) diff --git a/spec/dependencies/omniauth_saml_spec.rb b/spec/dependencies/omniauth_saml_spec.rb index 8956fa44b7a..470b1512a83 100644 --- a/spec/dependencies/omniauth_saml_spec.rb +++ b/spec/dependencies/omniauth_saml_spec.rb @@ -17,7 +17,7 @@ RSpec.describe 'processing of SAMLResponse in dependencies' do allow_next_instance_of(OneLogin::RubySaml::Response) do |instance| allow(instance).to receive(:is_valid?).and_return(true) end - saml_strategy.send(:handle_response, mock_saml_response, {}, settings ) { } + saml_strategy.send(:handle_response, mock_saml_response, {}, settings ) {} end it 'can extract AuthnContextClassRef from SAMLResponse param' do diff --git a/spec/experiments/application_experiment_spec.rb b/spec/experiments/application_experiment_spec.rb index a39890dd530..20050fae1cb 100644 --- a/spec/experiments/application_experiment_spec.rb +++ b/spec/experiments/application_experiment_spec.rb @@ -21,7 +21,7 @@ RSpec.describe ApplicationExperiment, :experiment do # them optional there. expect(experiment(:example)).to register_behavior(:control).with(nil) - expect { experiment(:example) { } }.not_to raise_error + expect { experiment(:example) {} }.not_to raise_error end describe "#publish" do @@ -289,11 +289,11 @@ RSpec.describe ApplicationExperiment, :experiment do end it "doesn't raise an exception" do - expect { experiment(:top) { |e| e.control { experiment(:nested) { } } } }.not_to raise_error + expect { experiment(:top) { |e| e.control { experiment(:nested) {} } } }.not_to raise_error end it "tracks an event", :snowplow do - experiment(:top) { |e| e.control { experiment(:nested) { } } } + experiment(:top) { |e| e.control { experiment(:nested) {} } } expect(Gitlab::Tracking).to have_received(:event).with( # rubocop:disable RSpec/ExpectGitlabTracking 'top', @@ -311,8 +311,8 @@ RSpec.describe ApplicationExperiment, :experiment do cache.clear(key: application_experiment.name) - application_experiment.control { } - application_experiment.candidate { } + application_experiment.control {} + application_experiment.candidate {} end it "caches the variant determined by the variant resolver" do @@ -378,7 +378,7 @@ RSpec.describe ApplicationExperiment, :experiment do it "doesn't warn on non dev/test environments" do allow(Gitlab).to receive(:dev_or_test_env?).and_return(false) - expect { experiment(:example) { |e| e.use { } } }.not_to raise_error + expect { experiment(:example) { |e| e.use {} } }.not_to raise_error expect(ActiveSupport::Deprecation).not_to have_received(:new).with(anything, 'Gitlab::Experiment') end @@ -387,7 +387,7 @@ RSpec.describe ApplicationExperiment, :experiment do # This will eventually raise an ActiveSupport::Deprecation exception, # it's ok to change it when that happens. - expect { experiment(:example) { |e| e.use { } } }.not_to raise_error + expect { experiment(:example) { |e| e.use {} } }.not_to raise_error expect(ActiveSupport::Deprecation).to have_received(:new).with(anything, 'Gitlab::Experiment') end diff --git a/spec/finders/ci/jobs_finder_spec.rb b/spec/finders/ci/jobs_finder_spec.rb index 45e8cf5a582..dd3ba9721e4 100644 --- a/spec/finders/ci/jobs_finder_spec.rb +++ b/spec/finders/ci/jobs_finder_spec.rb @@ -56,7 +56,7 @@ RSpec.describe Ci::JobsFinder, '#execute' do context 'scope is an array' do let(:jobs) { [pending_job, running_job, successful_job, canceled_job] } - let(:params) {{ scope: %w'running success' }} + let(:params) { { scope: %w'running success' } } it 'filters by the job statuses in the scope' do expect(subject).to contain_exactly(running_job, successful_job) diff --git a/spec/finders/ci/runners_finder_spec.rb b/spec/finders/ci/runners_finder_spec.rb index 96412c1e371..8d3c375385a 100644 --- a/spec/finders/ci/runners_finder_spec.rb +++ b/spec/finders/ci/runners_finder_spec.rb @@ -260,13 +260,13 @@ RSpec.describe Ci::RunnersFinder do let_it_be(:runner_sub_group_2) { create(:ci_runner, :group, contacted_at: 10.minutes.ago) } let_it_be(:runner_sub_group_3) { create(:ci_runner, :group, contacted_at: 9.minutes.ago) } let_it_be(:runner_sub_group_4) { create(:ci_runner, :group, contacted_at: 8.minutes.ago) } - let_it_be(:runner_project_1) { create(:ci_runner, :project, contacted_at: 7.minutes.ago, projects: [project])} - let_it_be(:runner_project_2) { create(:ci_runner, :project, contacted_at: 6.minutes.ago, projects: [project_2])} - let_it_be(:runner_project_3) { create(:ci_runner, :project, contacted_at: 5.minutes.ago, description: 'runner_project_search', projects: [project, project_2])} - let_it_be(:runner_project_4) { create(:ci_runner, :project, contacted_at: 4.minutes.ago, projects: [project_3])} - let_it_be(:runner_project_5) { create(:ci_runner, :project, contacted_at: 3.minutes.ago, tag_list: %w[runner_tag], projects: [project_4])} - let_it_be(:runner_project_6) { create(:ci_runner, :project, contacted_at: 2.minutes.ago, projects: [project_5])} - let_it_be(:runner_project_7) { create(:ci_runner, :project, contacted_at: 1.minute.ago, projects: [project_6])} + let_it_be(:runner_project_1) { create(:ci_runner, :project, contacted_at: 7.minutes.ago, projects: [project]) } + let_it_be(:runner_project_2) { create(:ci_runner, :project, contacted_at: 6.minutes.ago, projects: [project_2]) } + let_it_be(:runner_project_3) { create(:ci_runner, :project, contacted_at: 5.minutes.ago, description: 'runner_project_search', projects: [project, project_2]) } + let_it_be(:runner_project_4) { create(:ci_runner, :project, contacted_at: 4.minutes.ago, projects: [project_3]) } + let_it_be(:runner_project_5) { create(:ci_runner, :project, contacted_at: 3.minutes.ago, tag_list: %w[runner_tag], projects: [project_4]) } + let_it_be(:runner_project_6) { create(:ci_runner, :project, contacted_at: 2.minutes.ago, projects: [project_5]) } + let_it_be(:runner_project_7) { create(:ci_runner, :project, contacted_at: 1.minute.ago, projects: [project_6]) } let(:target_group) { nil } let(:membership) { nil } diff --git a/spec/finders/concerns/packages/finder_helper_spec.rb b/spec/finders/concerns/packages/finder_helper_spec.rb index e8648d131ff..94bcec6163e 100644 --- a/spec/finders/concerns/packages/finder_helper_spec.rb +++ b/spec/finders/concerns/packages/finder_helper_spec.rb @@ -24,7 +24,7 @@ RSpec.describe ::Packages::FinderHelper do subject { finder.execute(project1) } - it { is_expected.to eq [package1]} + it { is_expected.to eq [package1] } end describe '#packages_visible_to_user' do @@ -61,7 +61,7 @@ RSpec.describe ::Packages::FinderHelper do end shared_examples 'returning package1' do - it { is_expected.to eq [package1]} + it { is_expected.to eq [package1] } end shared_examples 'returning no packages' do @@ -165,7 +165,7 @@ RSpec.describe ::Packages::FinderHelper do end shared_examples 'returning project1' do - it { is_expected.to eq [project1]} + it { is_expected.to eq [project1] } end shared_examples 'returning no project' do diff --git a/spec/finders/container_repositories_finder_spec.rb b/spec/finders/container_repositories_finder_spec.rb index 5d449d1b811..472c39d1f23 100644 --- a/spec/finders/container_repositories_finder_spec.rb +++ b/spec/finders/container_repositories_finder_spec.rb @@ -28,9 +28,9 @@ RSpec.describe ContainerRepositoriesFinder do context "with name set to #{name}" do let(:params) { { name: name } } - it { is_expected.to contain_exactly(project_repository)} + it { is_expected.to contain_exactly(project_repository) } - it { is_expected.not_to include(not_searched_repository)} + it { is_expected.not_to include(not_searched_repository) } end end end @@ -50,7 +50,7 @@ RSpec.describe ContainerRepositoriesFinder do context "with sort set to #{order}" do let(:params) { { sort: order } } - it { is_expected.to eq([sort_repository2, sort_repository])} + it { is_expected.to eq([sort_repository2, sort_repository]) } end end @@ -58,7 +58,7 @@ RSpec.describe ContainerRepositoriesFinder do context "with sort set to #{order}" do let(:params) { { sort: order } } - it { is_expected.to eq([sort_repository, sort_repository2])} + it { is_expected.to eq([sort_repository, sort_repository2]) } end end end diff --git a/spec/finders/design_management/versions_finder_spec.rb b/spec/finders/design_management/versions_finder_spec.rb index 0d606ef46f1..7dafdcfda97 100644 --- a/spec/finders/design_management/versions_finder_spec.rb +++ b/spec/finders/design_management/versions_finder_spec.rb @@ -71,13 +71,13 @@ RSpec.describe DesignManagement::VersionsFinder do describe 'returning versions earlier or equal to a version' do context 'when argument is the first version' do - let(:params) { { earlier_or_equal_to: version_1 }} + let(:params) { { earlier_or_equal_to: version_1 } } it { is_expected.to eq([version_1]) } end context 'when argument is the second version' do - let(:params) { { earlier_or_equal_to: version_2 }} + let(:params) { { earlier_or_equal_to: version_2 } } it { is_expected.to contain_exactly(version_1, version_2) } end diff --git a/spec/finders/milestones_finder_spec.rb b/spec/finders/milestones_finder_spec.rb index 8b26599cbfa..8dd83df3a28 100644 --- a/spec/finders/milestones_finder_spec.rb +++ b/spec/finders/milestones_finder_spec.rb @@ -28,7 +28,7 @@ RSpec.describe MilestonesFinder do end context 'milestones for groups and project' do - let(:extra_params) {{}} + let(:extra_params) { {} } let(:result) do described_class.new({ project_ids: [project_1.id, project_2.id], group_ids: group.id, state: 'all' }.merge(extra_params)).execute end diff --git a/spec/finders/packages/group_packages_finder_spec.rb b/spec/finders/packages/group_packages_finder_spec.rb index 90a8cd3c57f..f78a356b13d 100644 --- a/spec/finders/packages/group_packages_finder_spec.rb +++ b/spec/finders/packages/group_packages_finder_spec.rb @@ -217,7 +217,7 @@ RSpec.describe Packages::GroupPackagesFinder do context 'group is nil' do subject { described_class.new(user, nil).execute } - it { is_expected.to be_empty} + it { is_expected.to be_empty } end context 'package type is nil' do @@ -225,7 +225,7 @@ RSpec.describe Packages::GroupPackagesFinder do subject { described_class.new(user, group, package_type: nil).execute } - it { is_expected.to match_array([package1])} + it { is_expected.to match_array([package1]) } end context 'with invalid package_type' do diff --git a/spec/finders/packages/npm/package_finder_spec.rb b/spec/finders/packages/npm/package_finder_spec.rb index 7fabb3eed86..8c9149a5a2d 100644 --- a/spec/finders/packages/npm/package_finder_spec.rb +++ b/spec/finders/packages/npm/package_finder_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' RSpec.describe ::Packages::Npm::PackageFinder do - let_it_be_with_reload(:project) { create(:project)} + let_it_be_with_reload(:project) { create(:project) } let_it_be_with_refind(:package) { create(:npm_package, project: project) } let(:project) { package.project } diff --git a/spec/finders/projects_finder_spec.rb b/spec/finders/projects_finder_spec.rb index 3bef4d85b33..1fa2a975ec3 100644 --- a/spec/finders/projects_finder_spec.rb +++ b/spec/finders/projects_finder_spec.rb @@ -62,7 +62,7 @@ RSpec.describe ProjectsFinder do describe 'with id_after' do context 'only returns projects with a project id greater than given' do - let(:params) { { id_after: internal_project.id }} + let(:params) { { id_after: internal_project.id } } it { is_expected.to eq([public_project]) } end @@ -70,7 +70,7 @@ RSpec.describe ProjectsFinder do describe 'with id_before' do context 'only returns projects with a project id less than given' do - let(:params) { { id_before: public_project.id }} + let(:params) { { id_before: public_project.id } } it { is_expected.to eq([internal_project]) } end @@ -79,7 +79,7 @@ RSpec.describe ProjectsFinder do describe 'with both id_before and id_after' do context 'only returns projects with a project id less than given' do let!(:projects) { create_list(:project, 5, :public) } - let(:params) { { id_after: projects.first.id, id_before: projects.last.id }} + let(:params) { { id_after: projects.first.id, id_before: projects.last.id } } it { is_expected.to contain_exactly(*projects[1..-2]) } end @@ -89,7 +89,7 @@ RSpec.describe ProjectsFinder do context 'only returns projects with a project id less than given and matching search' do subject { finder.execute.joins(:route) } - let(:params) { { id_before: public_project.id }} + let(:params) { { id_before: public_project.id } } it { is_expected.to eq([internal_project]) } end @@ -97,7 +97,7 @@ RSpec.describe ProjectsFinder do context 'only returns projects with a project id greater than given and matching search' do subject { finder.execute.joins(:route) } - let(:params) { { id_after: internal_project.id }} + let(:params) { { id_after: internal_project.id } } it { is_expected.to eq([public_project]) } end diff --git a/spec/helpers/application_settings_helper_spec.rb b/spec/helpers/application_settings_helper_spec.rb index ba6caf7cf17..fab1fed797c 100644 --- a/spec/helpers/application_settings_helper_spec.rb +++ b/spec/helpers/application_settings_helper_spec.rb @@ -313,7 +313,7 @@ RSpec.describe ApplicationSettingsHelper do allow(helper).to receive(:can?).with(user, :read_cluster, instance_of(Clusters::Instance)).and_return(true) end - it { is_expected.to be_truthy} + it { is_expected.to be_truthy } context ':certificate_based_clusters feature flag is disabled' do before do diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb index 65e46b61882..fe652e905cc 100644 --- a/spec/helpers/blob_helper_spec.rb +++ b/spec/helpers/blob_helper_spec.rb @@ -357,7 +357,7 @@ RSpec.describe BlobHelper do describe '#ide_merge_request_path' do let_it_be(:project) { create(:project, :repository) } - let_it_be(:merge_request) { create(:merge_request, source_project: project)} + let_it_be(:merge_request) { create(:merge_request, source_project: project) } it 'returns IDE path for the given MR if MR is not merged' do expect(helper.ide_merge_request_path(merge_request)).to eq("/-/ide/project/#{project.full_path}/merge_requests/#{merge_request.iid}") diff --git a/spec/helpers/gitlab_script_tag_helper_spec.rb b/spec/helpers/gitlab_script_tag_helper_spec.rb index 9d71e25286e..cfe7b349cec 100644 --- a/spec/helpers/gitlab_script_tag_helper_spec.rb +++ b/spec/helpers/gitlab_script_tag_helper_spec.rb @@ -27,8 +27,8 @@ RSpec.describe GitlabScriptTagHelper do end describe 'inline script tag' do - let(:tag_with_nonce) {""} - let(:tag_with_nonce_and_type) {""} + let(:tag_with_nonce) { "" } + let(:tag_with_nonce_and_type) { "" } it 'returns a script tag with a nonce using block syntax' do expect(helper.javascript_tag { 'alert(1)' }.to_s).to eq tag_with_nonce diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb index 069465c2fec..18a21b59409 100644 --- a/spec/helpers/issuables_helper_spec.rb +++ b/spec/helpers/issuables_helper_spec.rb @@ -448,7 +448,7 @@ RSpec.describe IssuablesHelper do allow(merge_request).to receive(:can_be_merged_by?).and_return(can_merge) end - it { is_expected.to include({ can_merge: can_merge })} + it { is_expected.to include({ can_merge: can_merge }) } end end end @@ -480,7 +480,7 @@ RSpec.describe IssuablesHelper do allow(merge_request).to receive(:can_be_merged_by?).and_return(can_merge) end - it { is_expected.to include({ can_merge: can_merge })} + it { is_expected.to include({ can_merge: can_merge }) } end end end diff --git a/spec/helpers/projects/pipeline_helper_spec.rb b/spec/helpers/projects/pipeline_helper_spec.rb index 1231dcef693..59fc278543f 100644 --- a/spec/helpers/projects/pipeline_helper_spec.rb +++ b/spec/helpers/projects/pipeline_helper_spec.rb @@ -8,7 +8,7 @@ RSpec.describe Projects::PipelineHelper do let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project, :repository) } let_it_be(:raw_pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id) } - let_it_be(:pipeline) { Ci::PipelinePresenter.new(raw_pipeline, current_user: user)} + let_it_be(:pipeline) { Ci::PipelinePresenter.new(raw_pipeline, current_user: user) } describe '#js_pipeline_tabs_data' do before do diff --git a/spec/helpers/routing/pseudonymization_helper_spec.rb b/spec/helpers/routing/pseudonymization_helper_spec.rb index dd4cc55ed2b..eb2cb548f35 100644 --- a/spec/helpers/routing/pseudonymization_helper_spec.rb +++ b/spec/helpers/routing/pseudonymization_helper_spec.rb @@ -70,7 +70,7 @@ RSpec.describe ::Routing::PseudonymizationHelper do end context 'with controller for groups with subgroups and project' do - let(:masked_url) { "http://localhost/namespace#{subgroup.id}/project#{subproject.id}"} + let(:masked_url) { "http://localhost/namespace#{subgroup.id}/project#{subproject.id}" } let(:group) { subgroup } let(:project) { subproject } let(:request) do @@ -94,7 +94,7 @@ RSpec.describe ::Routing::PseudonymizationHelper do end context 'with controller for groups and subgroups' do - let(:masked_url) { "http://localhost/groups/namespace#{subgroup.id}/-/shared"} + let(:masked_url) { "http://localhost/groups/namespace#{subgroup.id}/-/shared" } let(:group) { subgroup } let(:request) do double(:Request, diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb index 513e2865ee3..ad0705e4fbf 100644 --- a/spec/helpers/search_helper_spec.rb +++ b/spec/helpers/search_helper_spec.rb @@ -625,7 +625,7 @@ RSpec.describe SearchHelper do false | false end - let(:params) {{ confidential: confidential }} + let(:params) { { confidential: confidential } } with_them do it 'transforms confidentiality param' do diff --git a/spec/helpers/wiki_page_version_helper_spec.rb b/spec/helpers/wiki_page_version_helper_spec.rb index bc500c28c5a..a792e5df035 100644 --- a/spec/helpers/wiki_page_version_helper_spec.rb +++ b/spec/helpers/wiki_page_version_helper_spec.rb @@ -6,8 +6,8 @@ RSpec.describe WikiPageVersionHelper do let_it_be(:project) { create(:project, :public, :repository) } let_it_be(:user) { create(:user, username: 'foo') } - let(:commit_with_user) { create(:commit, project: project, author: user)} - let(:commit_without_user) { create(:commit, project: project, author_name: 'Foo', author_email: 'foo@example.com')} + let(:commit_with_user) { create(:commit, project: project, author: user) } + let(:commit_without_user) { create(:commit, project: project, author_name: 'Foo', author_email: 'foo@example.com') } let(:wiki_page_version) { Gitlab::Git::WikiPageVersion.new(commit, nil) } describe '#wiki_page_version_author_url' do diff --git a/spec/initializers/carrierwave_patch_spec.rb b/spec/initializers/carrierwave_patch_spec.rb index b0f337935ef..0910342f10f 100644 --- a/spec/initializers/carrierwave_patch_spec.rb +++ b/spec/initializers/carrierwave_patch_spec.rb @@ -8,7 +8,7 @@ RSpec.describe 'CarrierWave::Storage::Fog::File' do let(:storage) { CarrierWave::Storage::Fog.new(uploader) } let(:bucket_name) { 'some-bucket' } let(:connection) { ::Fog::Storage.new(connection_options) } - let(:bucket) { connection.directories.new(key: bucket_name )} + let(:bucket) { connection.directories.new(key: bucket_name ) } let(:test_filename) { 'test' } let(:test_data) { File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) } @@ -33,7 +33,7 @@ RSpec.describe 'CarrierWave::Storage::Fog::File' do end describe '#copy_to' do - let(:dest_filename) { 'copied.txt'} + let(:dest_filename) { 'copied.txt' } it 'copies the file' do fog_file = subject.send(:file) @@ -67,7 +67,7 @@ RSpec.describe 'CarrierWave::Storage::Fog::File' do end describe '#copy_to' do - let(:dest_filename) { 'copied.txt'} + let(:dest_filename) { 'copied.txt' } it 'copies the file' do result = subject.copy_to(dest_filename) diff --git a/spec/initializers/trusted_proxies_spec.rb b/spec/initializers/trusted_proxies_spec.rb index 2786f034969..63c96ce17d1 100644 --- a/spec/initializers/trusted_proxies_spec.rb +++ b/spec/initializers/trusted_proxies_spec.rb @@ -58,7 +58,7 @@ RSpec.describe 'trusted_proxies' do end def stub_request(headers = {}) - ActionDispatch::RemoteIp.new(proc { }, false, Rails.application.config.action_dispatch.trusted_proxies).call(headers) + ActionDispatch::RemoteIp.new(proc {}, false, Rails.application.config.action_dispatch.trusted_proxies).call(headers) ActionDispatch::Request.new(headers) end diff --git a/spec/lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent_spec.rb b/spec/lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent_spec.rb index 79699375a8d..f642ec8c20d 100644 --- a/spec/lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent_spec.rb +++ b/spec/lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent_spec.rb @@ -23,8 +23,6 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillVulnerabilityReadsClusterAge let(:sub_batch_size) { 1_000 } let(:pause_ms) { 0 } - subject(:perform_migration) { migration.perform } - before do users_table.create!(id: 1, name: 'John Doe', email: 'test@example.com', projects_limit: 5) @@ -49,20 +47,30 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillVulnerabilityReadsClusterAge add_vulnerability_read!(11, project_id: 1, cluster_agent_id: 1, report_type: 7) end - it 'backfills `casted_cluster_agent_id` for the selected records', :aggregate_failures do - queries = ActiveRecord::QueryRecorder.new do - perform_migration + describe '#filter_batch' do + it 'pick only vulnerability reads where report_type = 7' do + expect(migration.filter_batch(vulnerability_reads_table).pluck(:id)).to contain_exactly(1, 3, 7, 9, 10, 11) end - - expect(queries.count).to eq(3) - expect(vulnerability_reads_table.where.not(casted_cluster_agent_id: nil).count).to eq 3 - expect(vulnerability_reads_table.where.not(casted_cluster_agent_id: nil).pluck(:id)).to match_array([1, 9, 10]) end - it 'tracks timings of queries' do - expect(migration.batch_metrics.timings).to be_empty + describe '#perform' do + subject(:perform_migration) { migration.perform } - expect { perform_migration }.to change { migration.batch_metrics.timings } + it 'backfills `casted_cluster_agent_id` for the selected records', :aggregate_failures do + queries = ActiveRecord::QueryRecorder.new do + perform_migration + end + + expect(queries.count).to eq(3) + expect(vulnerability_reads_table.where.not(casted_cluster_agent_id: nil).count).to eq 3 + expect(vulnerability_reads_table.where.not(casted_cluster_agent_id: nil).pluck(:id)).to match_array([1, 9, 10]) + end + + it 'tracks timings of queries' do + expect(migration.batch_metrics.timings).to be_empty + + expect { perform_migration }.to change { migration.batch_metrics.timings } + end end private diff --git a/spec/lib/gitlab/background_migration/batching_strategies/backfill_project_statistics_with_container_registry_size_batching_strategy_spec.rb b/spec/lib/gitlab/background_migration/batching_strategies/backfill_project_statistics_with_container_registry_size_batching_strategy_spec.rb index 94e9bcf9207..7076e82ea34 100644 --- a/spec/lib/gitlab/background_migration/batching_strategies/backfill_project_statistics_with_container_registry_size_batching_strategy_spec.rb +++ b/spec/lib/gitlab/background_migration/batching_strategies/backfill_project_statistics_with_container_registry_size_batching_strategy_spec.rb @@ -2,137 +2,6 @@ require 'spec_helper' -RSpec.describe Gitlab::BackgroundMigration::BatchingStrategies::BackfillProjectStatisticsWithContainerRegistrySizeBatchingStrategy, '#next_batch' do # rubocop:disable Layout/LineLength - let(:batching_strategy) { described_class.new(connection: ActiveRecord::Base.connection) } - let(:namespace) { table(:namespaces) } - let(:project) { table(:projects) } - let(:container_repositories) { table(:container_repositories) } - - let!(:group) do - namespace.create!( - name: 'namespace1', type: 'Group', path: 'space1' - ) - end - - let!(:proj_namespace1) do - namespace.create!( - name: 'proj1', path: 'proj1', type: 'Project', parent_id: group.id - ) - end - - let!(:proj_namespace2) do - namespace.create!( - name: 'proj2', path: 'proj2', type: 'Project', parent_id: group.id - ) - end - - let!(:proj_namespace3) do - namespace.create!( - name: 'proj3', path: 'proj3', type: 'Project', parent_id: group.id - ) - end - - let!(:proj1) do - project.create!( - name: 'proj1', path: 'proj1', namespace_id: group.id, project_namespace_id: proj_namespace1.id - ) - end - - let!(:proj2) do - project.create!( - name: 'proj2', path: 'proj2', namespace_id: group.id, project_namespace_id: proj_namespace2.id - ) - end - - let!(:proj3) do - project.create!( - name: 'proj3', path: 'proj3', namespace_id: group.id, project_namespace_id: proj_namespace3.id - ) - end - - let!(:con1) do - container_repositories.create!( - project_id: proj1.id, - name: "ContReg_#{proj1.id}:1", - migration_state: 'import_done', - created_at: Date.new(2022, 01, 20) - ) - end - - let!(:con2) do - container_repositories.create!( - project_id: proj1.id, - name: "ContReg_#{proj1.id}:2", - migration_state: 'import_done', - created_at: Date.new(2022, 01, 20) - ) - end - - let!(:con3) do - container_repositories.create!( - project_id: proj2.id, - name: "ContReg_#{proj2.id}:1", - migration_state: 'import_done', - created_at: Date.new(2022, 01, 20) - ) - end - - let!(:con4) do - container_repositories.create!( - project_id: proj3.id, - name: "ContReg_#{proj3.id}:1", - migration_state: 'default', - created_at: Date.new(2022, 02, 20) - ) - end - - let!(:con5) do - container_repositories.create!( - project_id: proj3.id, - name: "ContReg_#{proj3.id}:2", - migration_state: 'default', - created_at: Date.new(2022, 02, 20) - ) - end - +RSpec.describe Gitlab::BackgroundMigration::BatchingStrategies::BackfillProjectStatisticsWithContainerRegistrySizeBatchingStrategy do # rubocop:disable Layout/LineLength it { expect(described_class).to be < Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy } - - context 'when starting on the first batch' do - it 'returns the bounds of the next batch' do - batch_bounds = batching_strategy.next_batch( - :container_repositories, - :project_id, - batch_min_value: con1.project_id, - batch_size: 3, - job_arguments: [] - ) - expect(batch_bounds).to eq([con1.project_id, con4.project_id]) - end - end - - context 'when additional batches remain' do - it 'returns the bounds of the next batch' do - batch_bounds = batching_strategy.next_batch( - :container_repositories, - :project_id, - batch_min_value: con3.project_id, - batch_size: 3, - job_arguments: [] - ) - - expect(batch_bounds).to eq([con3.project_id, con5.project_id]) - end - end - - context 'when no additional batches remain' do - it 'returns nil' do - batch_bounds = batching_strategy.next_batch(:container_repositories, - :project_id, - batch_min_value: con5.project_id + 1, - batch_size: 1, job_arguments: [] - ) - - expect(batch_bounds).to be_nil - end - end end diff --git a/spec/lib/gitlab/background_migration/batching_strategies/dismissed_vulnerabilities_strategy_spec.rb b/spec/lib/gitlab/background_migration/batching_strategies/dismissed_vulnerabilities_strategy_spec.rb index f96c7de50f2..e4bef81e0bd 100644 --- a/spec/lib/gitlab/background_migration/batching_strategies/dismissed_vulnerabilities_strategy_spec.rb +++ b/spec/lib/gitlab/background_migration/batching_strategies/dismissed_vulnerabilities_strategy_spec.rb @@ -3,117 +3,5 @@ require 'spec_helper' RSpec.describe Gitlab::BackgroundMigration::BatchingStrategies::DismissedVulnerabilitiesStrategy, '#next_batch' do - let(:batching_strategy) { described_class.new(connection: ActiveRecord::Base.connection) } - let(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') } - let(:users) { table(:users) } - let(:user) { create_user! } - let(:project) do - table(:projects).create!( - namespace_id: namespace.id, - project_namespace_id: namespace.id, - packages_enabled: false) - end - - let(:vulnerabilities) { table(:vulnerabilities) } - - let!(:vulnerability1) do - create_vulnerability!( - project_id: project.id, - author_id: user.id, - dismissed_at: Time.current - ) - end - - let!(:vulnerability2) do - create_vulnerability!( - project_id: project.id, - author_id: user.id, - dismissed_at: Time.current - ) - end - - let!(:vulnerability3) do - create_vulnerability!( - project_id: project.id, - author_id: user.id, - dismissed_at: Time.current - ) - end - - let!(:vulnerability4) do - create_vulnerability!( - project_id: project.id, - author_id: user.id, - dismissed_at: nil - ) - end - it { expect(described_class).to be < Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy } - - context 'when starting on the first batch' do - it 'returns the bounds of the next batch' do - batch_bounds = batching_strategy.next_batch( - :vulnerabilities, - :id, - batch_min_value: vulnerability1.id, - batch_size: 2, - job_arguments: [] - ) - expect(batch_bounds).to eq([vulnerability1.id, vulnerability2.id]) - end - end - - context 'when additional batches remain' do - it 'returns the bounds of the next batch and skips the records that do not have `dismissed_at` set' do - batch_bounds = batching_strategy.next_batch( - :vulnerabilities, - :id, - batch_min_value: vulnerability3.id, - batch_size: 2, - job_arguments: [] - ) - - expect(batch_bounds).to eq([vulnerability3.id, vulnerability3.id]) - end - end - - context 'when no additional batches remain' do - it 'returns nil' do - batch_bounds = batching_strategy.next_batch( - :vulnerabilities, - :id, - batch_min_value: vulnerability4.id + 1, - batch_size: 1, - job_arguments: [] - ) - - expect(batch_bounds).to be_nil - end - end - - private - - def create_vulnerability!( - project_id:, author_id:, title: 'test', severity: 7, confidence: 7, report_type: 0, state: 1, dismissed_at: nil - ) - vulnerabilities.create!( - project_id: project_id, - author_id: author_id, - title: title, - severity: severity, - confidence: confidence, - report_type: report_type, - state: state, - dismissed_at: dismissed_at - ) - end - - def create_user!(name: "Example User", email: "user@example.com", user_type: nil) - users.create!( - name: name, - email: email, - username: name, - projects_limit: 10 - ) - end end diff --git a/spec/lib/gitlab/background_migration/set_correct_vulnerability_state_spec.rb b/spec/lib/gitlab/background_migration/set_correct_vulnerability_state_spec.rb index d5b98e49a31..2372ce21c4c 100644 --- a/spec/lib/gitlab/background_migration/set_correct_vulnerability_state_spec.rb +++ b/spec/lib/gitlab/background_migration/set_correct_vulnerability_state_spec.rb @@ -34,7 +34,7 @@ RSpec.describe Gitlab::BackgroundMigration::SetCorrectVulnerabilityState do let(:detected_state) { 1 } let(:dismissed_state) { 2 } - subject(:perform_migration) do + let(:migration_job) do described_class.new(start_id: vulnerability_with_dismissed_at.id, end_id: vulnerability_without_dismissed_at.id, batch_table: :vulnerabilities, @@ -42,15 +42,24 @@ RSpec.describe Gitlab::BackgroundMigration::SetCorrectVulnerabilityState do sub_batch_size: 1, pause_ms: 0, connection: ActiveRecord::Base.connection) - .perform end - it 'changes vulnerability state to `dismissed` when dismissed_at is not nil' do - expect { perform_migration }.to change { vulnerability_with_dismissed_at.reload.state }.to(dismissed_state) + describe '#filter_batch' do + it 'filters out vulnerabilities where dismissed_at is null' do + expect(migration_job.filter_batch(vulnerabilities)).to contain_exactly(vulnerability_with_dismissed_at) + end end - it 'does not change the state when dismissed_at is nil' do - expect { perform_migration }.not_to change { vulnerability_without_dismissed_at.reload.state } + describe '#perform' do + subject(:perform_migration) { migration_job.perform } + + it 'changes vulnerability state to `dismissed` when dismissed_at is not nil' do + expect { perform_migration }.to change { vulnerability_with_dismissed_at.reload.state }.to(dismissed_state) + end + + it 'does not change the state when dismissed_at is nil' do + expect { perform_migration }.not_to change { vulnerability_without_dismissed_at.reload.state } + end end private diff --git a/spec/lib/gitlab/database/batch_average_counter_spec.rb b/spec/lib/gitlab/database/batch_average_counter_spec.rb new file mode 100644 index 00000000000..43c7a1554f7 --- /dev/null +++ b/spec/lib/gitlab/database/batch_average_counter_spec.rb @@ -0,0 +1,107 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Database::BatchAverageCounter do + let(:model) { Issue } + let(:column) { :weight } + + let(:in_transaction) { false } + + before do + allow(model.connection).to receive(:transaction_open?).and_return(in_transaction) + end + + describe '#count' do + before do + create_list(:issue, 2, weight: 4) + create_list(:issue, 2, weight: 2) + create_list(:issue, 2, weight: 3) + end + + subject(:batch_average_counter) { described_class.new(model, column) } + + it 'returns correct average of weights' do + expect(subject.count).to eq(3.0) + end + + it 'does no raise an exception if transaction is not open' do + expect { subject.count }.not_to raise_error + end + + context 'when transaction is open' do + let(:in_transaction) { true } + + it 'raises an error' do + expect { subject.count }.to raise_error('BatchAverageCounter can not be run inside a transaction') + end + end + + context 'when batch size is small' do + let(:batch_size) { 2 } + + it 'returns correct average of weights' do + expect(subject.count(batch_size: batch_size)).to eq(3.0) + end + end + + context 'when column passed is an Arel attribute' do + let(:column) { model.arel_table[:weight] } + + it 'returns correct average of weights' do + expect(subject.count).to eq(3.0) + end + end + + context 'when column has total count of zero' do + before do + Issue.update_all(weight: nil) + end + + it 'returns the fallback value' do + expect(subject.count).to eq(-1) + end + end + + context 'when one batch has nil weights (no average)' do + before do + issues = Issue.where(weight: 4) + issues.update_all(weight: nil) + end + + let(:batch_size) { 2 } + + it 'calculates average of weights with no errors' do + expect(subject.count(batch_size: batch_size)).to eq(2.5) + end + end + + context 'when batch fetch query is cancelled' do + let(:batch_size) { 22_000 } + let(:relation) { instance_double(ActiveRecord::Relation, to_sql: batch_average_query) } + + context 'when all retries fail' do + let(:batch_average_query) { 'SELECT AVG(weight) FROM issues WHERE weight BETWEEN 2 and 5' } + let(:query_timed_out_exception) { ActiveRecord::QueryCanceled.new('query timed out') } + + before do + allow(model).to receive(:where).and_return(relation) + allow(relation).to receive(:pick).and_raise(query_timed_out_exception) + end + + it 'logs failing query' do + expect(Gitlab::AppJsonLogger).to receive(:error).with( + event: 'batch_count', + relation: model.table_name, + operation: 'average', + start: 2, + query: batch_average_query, + message: 'Query has been canceled with message: query timed out' + ) + + expect(subject.count(batch_size: batch_size)).to eq(-1) + end + end + end + end +end diff --git a/spec/lib/gitlab/database/batch_count_spec.rb b/spec/lib/gitlab/database/batch_count_spec.rb index 72a4af692b4..a87b0c1a3a8 100644 --- a/spec/lib/gitlab/database/batch_count_spec.rb +++ b/spec/lib/gitlab/database/batch_count_spec.rb @@ -493,56 +493,18 @@ RSpec.describe Gitlab::Database::BatchCount do end describe '#batch_average' do - let(:model) { Issue } let(:column) { :weight } before do - Issue.update_all(weight: 2) + allow_next_instance_of(Gitlab::Database::BatchAverageCounter) do |instance| + allow(instance).to receive(:count).and_return + end end - it 'returns the average of values in the given column' do - expect(described_class.batch_average(model, column)).to eq(2) - end - - it 'works when given an Arel column' do - expect(described_class.batch_average(model, model.arel_table[column])).to eq(2) - end - - it 'works with a batch size of 50K' do - expect(described_class.batch_average(model, column, batch_size: 50_000)).to eq(2) - end - - it 'works with start and finish provided' do - expect(described_class.batch_average(model, column, start: model.minimum(:id), finish: model.maximum(:id))).to eq(2) - end - - it "defaults the batch size to #{Gitlab::Database::BatchCounter::DEFAULT_AVERAGE_BATCH_SIZE}" do - min_id = model.minimum(:id) - relation = instance_double(ActiveRecord::Relation) - allow(model).to receive_message_chain(:select, public_send: relation) - batch_end_id = min_id + calculate_batch_size(Gitlab::Database::BatchCounter::DEFAULT_AVERAGE_BATCH_SIZE) - - expect(relation).to receive(:where).with("id" => min_id..batch_end_id).and_return(double(send: 1)) + it 'calls BatchAverageCounter' do + expect(Gitlab::Database::BatchAverageCounter).to receive(:new).with(model, column).and_call_original described_class.batch_average(model, column) end - - it_behaves_like 'when a transaction is open' do - subject { described_class.batch_average(model, column) } - end - - it_behaves_like 'disallowed configurations', :batch_average do - let(:args) { [model, column] } - let(:default_batch_size) { Gitlab::Database::BatchCounter::DEFAULT_AVERAGE_BATCH_SIZE } - let(:small_batch_size) { Gitlab::Database::BatchCounter::DEFAULT_AVERAGE_BATCH_SIZE - 1 } - end - - it_behaves_like 'when batch fetch query is canceled' do - let(:mode) { :itself } - let(:operation) { :average } - let(:operation_args) { [column] } - - subject { described_class.method(:batch_average) } - end end end diff --git a/spec/lib/gitlab/usage_data_metrics_spec.rb b/spec/lib/gitlab/usage_data_metrics_spec.rb index 485f2131d87..1968523dc4a 100644 --- a/spec/lib/gitlab/usage_data_metrics_spec.rb +++ b/spec/lib/gitlab/usage_data_metrics_spec.rb @@ -16,6 +16,10 @@ RSpec.describe Gitlab::UsageDataMetrics do allow_next_instance_of(Gitlab::Database::BatchCounter) do |batch_counter| allow(batch_counter).to receive(:transaction_open?).and_return(false) end + + allow_next_instance_of(Gitlab::Database::BatchAverageCounter) do |instance| + allow(instance).to receive(:transaction_open?).and_return(false) + end end context 'with instrumentation_class' do diff --git a/spec/mailers/emails/service_desk_spec.rb b/spec/mailers/emails/service_desk_spec.rb index 28011456a66..1523d9b986b 100644 --- a/spec/mailers/emails/service_desk_spec.rb +++ b/spec/mailers/emails/service_desk_spec.rb @@ -76,7 +76,7 @@ RSpec.describe Emails::ServiceDesk do shared_examples 'read template from repository' do |template_key| let(:template_content) { 'custom text' } - let(:issue) { create(:issue, project: project)} + let(:issue) { create(:issue, project: project) } before do issue.issue_email_participants.create!(email: email) diff --git a/spec/migrations/20210812013042_remove_duplicate_project_authorizations_spec.rb b/spec/migrations/20210812013042_remove_duplicate_project_authorizations_spec.rb index f734456b0b6..c88f94c6426 100644 --- a/spec/migrations/20210812013042_remove_duplicate_project_authorizations_spec.rb +++ b/spec/migrations/20210812013042_remove_duplicate_project_authorizations_spec.rb @@ -48,7 +48,7 @@ RSpec.describe RemoveDuplicateProjectAuthorizations, :migration do project_authorizations.create! project_id: project_1.id, user_id: user_1.id, access_level: Gitlab::Access::REPORTER end - it { expect { subject }.to change { ProjectAuthorization.count}.from(3).to(1) } + it { expect { subject }.to change { ProjectAuthorization.count }.from(3).to(1) } it 'retains the highest access level' do subject diff --git a/spec/migrations/20210910194952_update_report_type_for_existing_approval_project_rules_spec.rb b/spec/migrations/20210910194952_update_report_type_for_existing_approval_project_rules_spec.rb index c90eabbe4eb..69ee10eb0d1 100644 --- a/spec/migrations/20210910194952_update_report_type_for_existing_approval_project_rules_spec.rb +++ b/spec/migrations/20210910194952_update_report_type_for_existing_approval_project_rules_spec.rb @@ -39,7 +39,7 @@ RSpec.describe UpdateReportTypeForExistingApprovalProjectRules, :migration do end context 'with the rule name set to another value (e.g., Test Rule)' do - let(:rule_name) { 'Test Rule'} + let(:rule_name) { 'Test Rule' } it 'does not update report_type' do expect { migrate! }.not_to change { approval_project_rule.reload.report_type } diff --git a/spec/migrations/confirm_support_bot_user_spec.rb b/spec/migrations/confirm_support_bot_user_spec.rb index f6bcab4aa7d..c60c7fe45f7 100644 --- a/spec/migrations/confirm_support_bot_user_spec.rb +++ b/spec/migrations/confirm_support_bot_user_spec.rb @@ -52,7 +52,7 @@ RSpec.describe ConfirmSupportBotUser, :migration do end it 'does not change the `created_at` attribute' do - expect { migrate!}.not_to change { support_bot.reload.created_at }.from(nil) + expect { migrate! }.not_to change { support_bot.reload.created_at }.from(nil) end end diff --git a/spec/migrations/reset_job_token_scope_enabled_again_spec.rb b/spec/migrations/reset_job_token_scope_enabled_again_spec.rb index da6817f6f21..8f9e12852e1 100644 --- a/spec/migrations/reset_job_token_scope_enabled_again_spec.rb +++ b/spec/migrations/reset_job_token_scope_enabled_again_spec.rb @@ -9,8 +9,8 @@ RSpec.describe ResetJobTokenScopeEnabledAgain do let(:projects) { table(:projects) } let(:namespaces) { table(:namespaces) } let(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') } - let(:project_1) { projects.create!(name: 'proj-1', path: 'gitlab-org', namespace_id: namespace.id)} - let(:project_2) { projects.create!(name: 'proj-2', path: 'gitlab-org', namespace_id: namespace.id)} + let(:project_1) { projects.create!(name: 'proj-1', path: 'gitlab-org', namespace_id: namespace.id) } + let(:project_2) { projects.create!(name: 'proj-2', path: 'gitlab-org', namespace_id: namespace.id) } before do settings.create!(id: 1, project_id: project_1.id, job_token_scope_enabled: true) diff --git a/spec/migrations/reset_job_token_scope_enabled_spec.rb b/spec/migrations/reset_job_token_scope_enabled_spec.rb index 40dfe4de34b..fb7bd78c11f 100644 --- a/spec/migrations/reset_job_token_scope_enabled_spec.rb +++ b/spec/migrations/reset_job_token_scope_enabled_spec.rb @@ -9,8 +9,8 @@ RSpec.describe ResetJobTokenScopeEnabled do let(:projects) { table(:projects) } let(:namespaces) { table(:namespaces) } let(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') } - let(:project_1) { projects.create!(name: 'proj-1', path: 'gitlab-org', namespace_id: namespace.id)} - let(:project_2) { projects.create!(name: 'proj-2', path: 'gitlab-org', namespace_id: namespace.id)} + let(:project_1) { projects.create!(name: 'proj-1', path: 'gitlab-org', namespace_id: namespace.id) } + let(:project_2) { projects.create!(name: 'proj-2', path: 'gitlab-org', namespace_id: namespace.id) } before do settings.create!(id: 1, project_id: project_1.id, job_token_scope_enabled: true) diff --git a/spec/migrations/reset_severity_levels_to_new_default_spec.rb b/spec/migrations/reset_severity_levels_to_new_default_spec.rb index 18dc001db16..c352f1f3cee 100644 --- a/spec/migrations/reset_severity_levels_to_new_default_spec.rb +++ b/spec/migrations/reset_severity_levels_to_new_default_spec.rb @@ -6,10 +6,10 @@ require_migration! RSpec.describe ResetSeverityLevelsToNewDefault do let(:approval_project_rules) { table(:approval_project_rules) } - let(:projects) { table(:projects)} - let(:namespaces) { table(:namespaces)} - let(:namespace) { namespaces.create!(name: 'namespace', path: 'namespace')} - let(:project) { projects.create!(name: 'project', path: 'project', namespace_id: namespace.id)} + let(:projects) { table(:projects) } + let(:namespaces) { table(:namespaces) } + let(:namespace) { namespaces.create!(name: 'namespace', path: 'namespace') } + let(:project) { projects.create!(name: 'project', path: 'project', namespace_id: namespace.id) } let(:approval_project_rule) { approval_project_rules.create!(name: 'rule', project_id: project.id, severity_levels: severity_levels) } context 'without having all severity levels selected' do @@ -27,7 +27,7 @@ RSpec.describe ResetSeverityLevelsToNewDefault do it 'changes severity_levels to the default value' do expect(approval_project_rule.severity_levels).to eq(severity_levels) - expect { migrate! }.to change {approval_project_rule.reload.severity_levels}.from(severity_levels).to(default_levels) + expect { migrate! }.to change { approval_project_rule.reload.severity_levels }.from(severity_levels).to(default_levels) end end end diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 6fbff95f504..cf80eafd137 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -4482,10 +4482,20 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do let_it_be(:pipeline) { create(:ci_pipeline) } context 'when the scheduling type is `dag`' do - it 'returns true' do - create(:ci_build, pipeline: pipeline, scheduling_type: :dag) + context 'when the processable is a bridge' do + it 'returns true' do + create(:ci_bridge, pipeline: pipeline, scheduling_type: :dag) - expect(pipeline.uses_needs?).to eq(true) + expect(pipeline.uses_needs?).to eq(true) + end + end + + context 'when the processable is a build' do + it 'returns true' do + create(:ci_build, pipeline: pipeline, scheduling_type: :dag) + + expect(pipeline.uses_needs?).to eq(true) + end end end diff --git a/spec/policies/clusters/agent_policy_spec.rb b/spec/policies/clusters/agent_policy_spec.rb index 307d751b78b..8f778d318ed 100644 --- a/spec/policies/clusters/agent_policy_spec.rb +++ b/spec/policies/clusters/agent_policy_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Clusters::AgentPolicy do - let(:cluster_agent) { create(:cluster_agent, name: 'agent' )} + let(:cluster_agent) { create(:cluster_agent, name: 'agent' ) } let(:user) { create(:admin) } let(:policy) { described_class.new(user, cluster_agent) } let(:project) { cluster_agent.project } diff --git a/spec/policies/group_member_policy_spec.rb b/spec/policies/group_member_policy_spec.rb index 50774313aae..27ce683861c 100644 --- a/spec/policies/group_member_policy_spec.rb +++ b/spec/policies/group_member_policy_spec.rb @@ -128,7 +128,7 @@ RSpec.describe GroupMemberPolicy do context 'with the group parent' do let(:current_user) { create :user } - let(:subgroup) { create(:group, :private, parent: group)} + let(:subgroup) { create(:group, :private, parent: group) } before do group.add_owner(owner) @@ -143,7 +143,7 @@ RSpec.describe GroupMemberPolicy do context 'without group parent' do let(:current_user) { create :user } - let(:subgroup) { create(:group, :private)} + let(:subgroup) { create(:group, :private) } before do subgroup.add_owner(current_user) @@ -158,7 +158,7 @@ RSpec.describe GroupMemberPolicy do context 'without group parent with two owners' do let(:current_user) { create :user } let(:other_user) { create :user } - let(:subgroup) { create(:group, :private)} + let(:subgroup) { create(:group, :private) } before do subgroup.add_owner(current_user) diff --git a/spec/policies/issue_policy_spec.rb b/spec/policies/issue_policy_spec.rb index 7ca4baddb79..4d492deb54c 100644 --- a/spec/policies/issue_policy_spec.rb +++ b/spec/policies/issue_policy_spec.rb @@ -398,7 +398,7 @@ RSpec.describe IssuePolicy do context 'with a hidden issue' do let(:user) { create(:user) } let(:banned_user) { create(:user, :banned) } - let(:admin) { create(:user, :admin)} + let(:admin) { create(:user, :admin) } let(:hidden_issue) { create(:issue, project: project, author: banned_user) } it 'does not allow non-admin user to read the issue' do diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index e8fdf9a8e25..9bf96475fc1 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -777,13 +777,13 @@ RSpec.describe ProjectPolicy do project.add_developer(user) end - it { is_expected.not_to be_allowed(:project_bot_access)} + it { is_expected.not_to be_allowed(:project_bot_access) } end context "when project bot and not part of the project" do let(:current_user) { project_bot } - it { is_expected.not_to be_allowed(:project_bot_access)} + it { is_expected.not_to be_allowed(:project_bot_access) } end context "when project bot and part of the project" do @@ -793,7 +793,7 @@ RSpec.describe ProjectPolicy do project.add_developer(project_bot) end - it { is_expected.to be_allowed(:project_bot_access)} + it { is_expected.to be_allowed(:project_bot_access) } end end @@ -804,7 +804,7 @@ RSpec.describe ProjectPolicy do project.add_maintainer(project_bot) end - it { is_expected.not_to be_allowed(:create_resource_access_tokens)} + it { is_expected.not_to be_allowed(:create_resource_access_tokens) } end end @@ -946,7 +946,7 @@ RSpec.describe ProjectPolicy do context 'with anonymous' do let(:current_user) { anonymous } - it { is_expected.to be_disallowed(:metrics_dashboard)} + it { is_expected.to be_disallowed(:metrics_dashboard) } end end diff --git a/spec/policies/terraform/state_policy_spec.rb b/spec/policies/terraform/state_policy_spec.rb index 82152920997..d75e20a2c66 100644 --- a/spec/policies/terraform/state_policy_spec.rb +++ b/spec/policies/terraform/state_policy_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.describe Terraform::StatePolicy do let_it_be(:project) { create(:project) } - let_it_be(:terraform_state) { create(:terraform_state, project: project)} + let_it_be(:terraform_state) { create(:terraform_state, project: project) } subject { described_class.new(user, terraform_state) } diff --git a/spec/policies/terraform/state_version_policy_spec.rb b/spec/policies/terraform/state_version_policy_spec.rb index 6614e073332..4d41dd44455 100644 --- a/spec/policies/terraform/state_version_policy_spec.rb +++ b/spec/policies/terraform/state_version_policy_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.describe Terraform::StateVersionPolicy do let_it_be(:project) { create(:project) } - let_it_be(:terraform_state) { create(:terraform_state, :with_version, project: project)} + let_it_be(:terraform_state) { create(:terraform_state, :with_version, project: project) } subject { described_class.new(user, terraform_state.latest_version) } diff --git a/spec/presenters/packages/composer/packages_presenter_spec.rb b/spec/presenters/packages/composer/packages_presenter_spec.rb index 1f638e5b935..ae88acea61d 100644 --- a/spec/presenters/packages/composer/packages_presenter_spec.rb +++ b/spec/presenters/packages/composer/packages_presenter_spec.rb @@ -50,7 +50,7 @@ RSpec.describe ::Packages::Composer::PackagesPresenter do end describe '#provider' do - subject { presenter.provider} + subject { presenter.provider } let(:expected_json) do { diff --git a/spec/presenters/packages/conan/package_presenter_spec.rb b/spec/presenters/packages/conan/package_presenter_spec.rb index d35137cd820..9b74d2e637e 100644 --- a/spec/presenters/packages/conan/package_presenter_spec.rb +++ b/spec/presenters/packages/conan/package_presenter_spec.rb @@ -7,7 +7,7 @@ RSpec.describe ::Packages::Conan::PackagePresenter do let_it_be(:package) { create(:conan_package) } let_it_be(:project) { package.project } let_it_be(:package_file_pending_destruction) { create(:package_file, :pending_destruction, package: package) } - let_it_be(:conan_package_reference) { '123456789'} + let_it_be(:conan_package_reference) { '123456789' } let(:params) { { package_scope: :instance } } let(:presenter) { described_class.new(package, user, project, params) } diff --git a/spec/presenters/packages/nuget/packages_metadata_presenter_spec.rb b/spec/presenters/packages/nuget/packages_metadata_presenter_spec.rb index b2bcdf8f03d..39682a3311c 100644 --- a/spec/presenters/packages/nuget/packages_metadata_presenter_spec.rb +++ b/spec/presenters/packages/nuget/packages_metadata_presenter_spec.rb @@ -12,7 +12,7 @@ RSpec.describe Packages::Nuget::PackagesMetadataPresenter do describe '#count' do subject { presenter.count } - it {is_expected.to eq 1} + it { is_expected.to eq 1 } end describe '#items' do diff --git a/spec/presenters/project_presenter_spec.rb b/spec/presenters/project_presenter_spec.rb index df3e4b985ab..5eeaea8de06 100644 --- a/spec/presenters/project_presenter_spec.rb +++ b/spec/presenters/project_presenter_spec.rb @@ -613,7 +613,7 @@ RSpec.describe ProjectPresenter do end context 'empty repo' do - let(:project) { create(:project, :stubbed_repository)} + let(:project) { create(:project, :stubbed_repository) } context 'for a guest user' do it 'orders the items correctly' do diff --git a/spec/rubocop/cop/migration/create_table_with_foreign_keys_spec.rb b/spec/rubocop/cop/migration/create_table_with_foreign_keys_spec.rb index 39bb4fc208f..072edb5827b 100644 --- a/spec/rubocop/cop/migration/create_table_with_foreign_keys_spec.rb +++ b/spec/rubocop/cop/migration/create_table_with_foreign_keys_spec.rb @@ -192,7 +192,7 @@ RSpec.describe RuboCop::Cop::Migration::CreateTableWithForeignKeys do include_context 'when there is a target to a high traffic table', :foreign_key do let(:explicit_target_opts) { ", to_table: :#{table_name}" } - let(:implicit_target_opts) { } + let(:implicit_target_opts) {} end end end diff --git a/spec/serializers/cluster_entity_spec.rb b/spec/serializers/cluster_entity_spec.rb index 7c4c146575d..2de27deeffe 100644 --- a/spec/serializers/cluster_entity_spec.rb +++ b/spec/serializers/cluster_entity_spec.rb @@ -45,7 +45,7 @@ RSpec.describe ClusterEntity do context 'when no application has been installed' do let(:cluster) { create(:cluster, :instance) } - subject { described_class.new(cluster, request: request).as_json[:applications]} + subject { described_class.new(cluster, request: request).as_json[:applications] } it 'contains helm as not_installable' do expect(subject).not_to be_empty diff --git a/spec/serializers/import/provider_repo_serializer_spec.rb b/spec/serializers/import/provider_repo_serializer_spec.rb index 430bad151d3..905685c75e3 100644 --- a/spec/serializers/import/provider_repo_serializer_spec.rb +++ b/spec/serializers/import/provider_repo_serializer_spec.rb @@ -23,7 +23,7 @@ RSpec.describe Import::ProviderRepoSerializer do end it 'raises an error if invalid provider supplied' do - expect { described_class.new.represent({}, { provider: :invalid })}.to raise_error { NotImplementedError } + expect { described_class.new.represent({}, { provider: :invalid }) }.to raise_error { NotImplementedError } end end end diff --git a/spec/support/shared_examples/features/content_editor_shared_examples.rb b/spec/support/shared_examples/features/content_editor_shared_examples.rb index 3fa7beea97e..21f264a8b6a 100644 --- a/spec/support/shared_examples/features/content_editor_shared_examples.rb +++ b/spec/support/shared_examples/features/content_editor_shared_examples.rb @@ -1,22 +1,87 @@ # frozen_string_literal: true RSpec.shared_examples 'edits content using the content editor' do - content_editor_testid = '[data-testid="content-editor"] [contenteditable].ProseMirror' + let(:content_editor_testid) { '[data-testid="content-editor"] [contenteditable].ProseMirror' } + + def switch_to_content_editor + find('[data-testid="toggle-editing-mode-button"] label', text: 'Rich text').click + end + + def type_in_content_editor(keys) + find(content_editor_testid).send_keys keys + end + + def open_insert_media_dropdown + page.find('svg[data-testid="media-icon"]').click + end + + def set_source_editor_content(content) + find('.js-gfm-input').set content + end + + def expect_formatting_menu_to_be_visible + expect(page).to have_css('[data-testid="formatting-bubble-menu"]') + end + + def expect_formatting_menu_to_be_hidden + expect(page).not_to have_css('[data-testid="formatting-bubble-menu"]') + end + + def expect_media_bubble_menu_to_be_visible + expect(page).to have_css('[data-testid="media-bubble-menu"]') + end + + def upload_asset(fixture_name) + attach_file('content_editor_image', Rails.root.join('spec', 'fixtures', fixture_name), make_visible: true) + end describe 'formatting bubble menu' do - it 'shows a formatting bubble menu for a regular paragraph' do + it 'shows a formatting bubble menu for a regular paragraph and headings' do + switch_to_content_editor + expect(page).to have_css(content_editor_testid) - find(content_editor_testid).send_keys 'Typing text in the content editor' - find(content_editor_testid).send_keys [:shift, :left] + type_in_content_editor 'Typing text in the content editor' + type_in_content_editor [:shift, :left] - expect(page).to have_css('[data-testid="formatting-bubble-menu"]') + expect_formatting_menu_to_be_visible + + type_in_content_editor [:right, :right, :enter, '## Heading'] + + expect_formatting_menu_to_be_hidden + + type_in_content_editor [:shift, :left] + + expect_formatting_menu_to_be_visible + end + end + + describe 'media elements bubble menu' do + before do + switch_to_content_editor + + open_insert_media_dropdown end - it 'does not show a formatting bubble menu for code blocks' do - find(content_editor_testid).send_keys '```js ' + def test_displays_media_bubble_menu(media_element_selector, fixture_file) + upload_asset fixture_file - expect(page).not_to have_css('[data-testid="formatting-bubble-menu"]') + wait_for_requests + + expect(page).to have_css(media_element_selector) + + page.find(media_element_selector).click + + expect_formatting_menu_to_be_hidden + expect_media_bubble_menu_to_be_visible + end + + it 'displays correct media bubble menu for images', :js do + test_displays_media_bubble_menu '[data-testid="content_editor_editablebox"] img[src]', 'dk.png' + end + + it 'displays correct media bubble menu for video', :js do + test_displays_media_bubble_menu '[data-testid="content_editor_editablebox"] video', 'video_sample.mp4' end end @@ -30,45 +95,50 @@ RSpec.shared_examples 'edits content using the content editor' do page.go_back refresh + switch_to_content_editor end it 'applies theme classes to code blocks' do expect(page).not_to have_css('.content-editor-code-block.code.highlight.dark') - find(content_editor_testid).send_keys [:enter, :enter] - find(content_editor_testid).send_keys '```js ' # trigger input rule - find(content_editor_testid).send_keys 'var a = 0' + type_in_content_editor [:enter, :enter] + type_in_content_editor '```js ' # trigger input rule + type_in_content_editor 'var a = 0' expect(page).to have_css('.content-editor-code-block.code.highlight.dark') end end describe 'code block bubble menu' do + before do + switch_to_content_editor + end + it 'shows a code block bubble menu for a code block' do - find(content_editor_testid).send_keys [:enter, :enter] + type_in_content_editor [:enter, :enter] - find(content_editor_testid).send_keys '```js ' # trigger input rule - find(content_editor_testid).send_keys 'var a = 0' - find(content_editor_testid).send_keys [:shift, :left] + type_in_content_editor '```js ' # trigger input rule + type_in_content_editor 'var a = 0' + type_in_content_editor [:shift, :left] - expect(page).not_to have_css('[data-testid="formatting-bubble-menu"]') + expect_formatting_menu_to_be_hidden expect(page).to have_css('[data-testid="code-block-bubble-menu"]') end it 'sets code block type to "javascript" for `js`' do - find(content_editor_testid).send_keys [:enter, :enter] + type_in_content_editor [:enter, :enter] - find(content_editor_testid).send_keys '```js ' - find(content_editor_testid).send_keys 'var a = 0' + type_in_content_editor '```js ' + type_in_content_editor 'var a = 0' expect(find('[data-testid="code-block-bubble-menu"]')).to have_text('Javascript') end it 'sets code block type to "Custom (nomnoml)" for `nomnoml`' do - find(content_editor_testid).send_keys [:enter, :enter] + type_in_content_editor [:enter, :enter] - find(content_editor_testid).send_keys '```nomnoml ' - find(content_editor_testid).send_keys 'test' + type_in_content_editor '```nomnoml ' + type_in_content_editor 'test' expect(find('[data-testid="code-block-bubble-menu"]')).to have_text('Custom (nomnoml)') end @@ -76,10 +146,11 @@ RSpec.shared_examples 'edits content using the content editor' do describe 'mermaid diagram' do before do - find(content_editor_testid).send_keys [:enter, :enter] + switch_to_content_editor - find(content_editor_testid).send_keys '```mermaid ' - find(content_editor_testid).send_keys ['graph TD;', :enter, ' JohnDoe12 --> HelloWorld34'] + type_in_content_editor [:enter, :enter] + type_in_content_editor '```mermaid ' + type_in_content_editor ['graph TD;', :enter, ' JohnDoe12 --> HelloWorld34'] end it 'renders and updates the diagram correctly in a sandboxed iframe' do diff --git a/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb index 87067336a36..5c63d6a973d 100644 --- a/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb +++ b/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb @@ -137,16 +137,7 @@ RSpec.shared_examples 'User updates wiki page' do end end - context 'when using the content editor' do - context 'with feature flag on' do - before do - find('[data-testid="toggle-editing-mode-button"] label', text: 'Rich text').click - end - - it_behaves_like 'edits content using the content editor' - end - end - + it_behaves_like 'edits content using the content editor' it_behaves_like 'autocompletes items' end diff --git a/spec/tasks/gitlab/snippets_rake_spec.rb b/spec/tasks/gitlab/snippets_rake_spec.rb index c55bded1d5a..c50b04b4600 100644 --- a/spec/tasks/gitlab/snippets_rake_spec.rb +++ b/spec/tasks/gitlab/snippets_rake_spec.rb @@ -3,7 +3,7 @@ require 'rake_helper' RSpec.describe 'gitlab:snippets namespace rake task', :silence_stdout do - let_it_be(:user) { create(:user)} + let_it_be(:user) { create(:user) } let_it_be(:migrated) { create(:personal_snippet, :repository, author: user) } let(:non_migrated) { create_list(:personal_snippet, 3, author: user) } diff --git a/spec/uploaders/packages/debian/distribution_release_file_uploader_spec.rb b/spec/uploaders/packages/debian/distribution_release_file_uploader_spec.rb index 203a453bcdd..dbbf69e3c8d 100644 --- a/spec/uploaders/packages/debian/distribution_release_file_uploader_spec.rb +++ b/spec/uploaders/packages/debian/distribution_release_file_uploader_spec.rb @@ -49,12 +49,12 @@ RSpec.describe Packages::Debian::DistributionReleaseFileUploader do end describe '#filename' do - it { expect(subject.filename).to eq('Release')} + it { expect(subject.filename).to eq('Release') } context 'with signed_file' do let(:uploader) { described_class.new(distribution, :signed_file) } - it { expect(subject.filename).to eq('InRelease')} + it { expect(subject.filename).to eq('InRelease') } end end end diff --git a/spec/validators/addressable_url_validator_spec.rb b/spec/validators/addressable_url_validator_spec.rb index b3a4459db30..9109a899881 100644 --- a/spec/validators/addressable_url_validator_spec.rb +++ b/spec/validators/addressable_url_validator_spec.rb @@ -245,7 +245,7 @@ RSpec.describe AddressableUrlValidator do end context 'when enforce_user is' do - let(:url) { 'http://$user@example.com'} + let(:url) { 'http://$user@example.com' } let(:validator) { described_class.new(attributes: [:link_url], enforce_user: enforce_user) } context 'true' do @@ -274,7 +274,7 @@ RSpec.describe AddressableUrlValidator do end context 'when ascii_only is' do - let(:url) { 'https://𝕘itⅼαƄ.com/foo/foo.bar'} + let(:url) { 'https://𝕘itⅼαƄ.com/foo/foo.bar' } let(:validator) { described_class.new(attributes: [:link_url], ascii_only: ascii_only) } context 'true' do diff --git a/spec/views/help/instance_configuration.html.haml_spec.rb b/spec/views/help/instance_configuration.html.haml_spec.rb index fbf84a5d272..4461eadf1a3 100644 --- a/spec/views/help/instance_configuration.html.haml_spec.rb +++ b/spec/views/help/instance_configuration.html.haml_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.describe 'help/instance_configuration' do describe 'General Sections:' do - let(:instance_configuration) { build(:instance_configuration)} + let(:instance_configuration) { build(:instance_configuration) } let(:settings) { instance_configuration.settings } let(:ssh_settings) { settings[:ssh_algorithms_hashes] } diff --git a/spec/views/layouts/_header_search.html.haml_spec.rb b/spec/views/layouts/_header_search.html.haml_spec.rb index 3ab4ae6a483..3a21bb3a92c 100644 --- a/spec/views/layouts/_header_search.html.haml_spec.rb +++ b/spec/views/layouts/_header_search.html.haml_spec.rb @@ -8,7 +8,7 @@ RSpec.describe 'layouts/_header_search' do let(:scope) { nil } let(:ref) { nil } let(:code_search) { false } - let(:for_snippets) { false} + let(:for_snippets) { false } let(:header_search_context) do { diff --git a/spec/views/layouts/_published_experiments.html.haml_spec.rb b/spec/views/layouts/_published_experiments.html.haml_spec.rb index 84894554bd9..072e4f2074e 100644 --- a/spec/views/layouts/_published_experiments.html.haml_spec.rb +++ b/spec/views/layouts/_published_experiments.html.haml_spec.rb @@ -13,10 +13,10 @@ RSpec.describe 'layouts/_published_experiments', :experiment do test_variant: :variant_name ) - experiment(:test_control) { } + experiment(:test_control) {} experiment(:test_excluded) { |e| e.exclude! } - experiment(:test_candidate) { |e| e.candidate { } } - experiment(:test_variant) { |e| e.variant(:variant_name) { } } + experiment(:test_candidate) { |e| e.candidate {} } + experiment(:test_variant) { |e| e.variant(:variant_name) {} } experiment(:test_published_only).publish render diff --git a/spec/views/shared/runners/_runner_details.html.haml_spec.rb b/spec/views/shared/runners/_runner_details.html.haml_spec.rb index cdf5ec563d0..978750c8435 100644 --- a/spec/views/shared/runners/_runner_details.html.haml_spec.rb +++ b/spec/views/shared/runners/_runner_details.html.haml_spec.rb @@ -113,14 +113,14 @@ RSpec.describe 'shared/runners/_runner_details.html.haml' do describe 'Tags value' do context 'when runner does not have tags' do it { is_expected.to have_content('Tags') } - it { is_expected.not_to have_selector('span.gl-badge.badge.badge-info')} + it { is_expected.not_to have_selector('span.gl-badge.badge.badge-info') } end context 'when runner have tags' do let(:runner) { create(:ci_runner, tag_list: %w(tag2 tag3 tag1)) } it { is_expected.to have_content('Tags tag1 tag2 tag3') } - it { is_expected.to have_selector('span.gl-badge.badge.badge-info')} + it { is_expected.to have_selector('span.gl-badge.badge.badge-info') } end end diff --git a/spec/workers/bulk_imports/export_request_worker_spec.rb b/spec/workers/bulk_imports/export_request_worker_spec.rb index 846df63a4d7..a7f7aaa7dba 100644 --- a/spec/workers/bulk_imports/export_request_worker_spec.rb +++ b/spec/workers/bulk_imports/export_request_worker_spec.rb @@ -60,7 +60,7 @@ RSpec.describe BulkImports::ExportRequestWorker do context 'when entity is group' do let(:entity) { create(:bulk_import_entity, :group_entity, source_full_path: 'foo/bar', bulk_import: bulk_import) } - let(:expected) { '/groups/foo%2Fbar/export_relations'} + let(:expected) { '/groups/foo%2Fbar/export_relations' } include_examples 'requests relations export for api resource' end diff --git a/spec/workers/clusters/cleanup/project_namespace_worker_spec.rb b/spec/workers/clusters/cleanup/project_namespace_worker_spec.rb index b9219586a0b..c24ca71eb35 100644 --- a/spec/workers/clusters/cleanup/project_namespace_worker_spec.rb +++ b/spec/workers/clusters/cleanup/project_namespace_worker_spec.rb @@ -18,7 +18,7 @@ RSpec.describe Clusters::Cleanup::ProjectNamespaceWorker do end context 'when exceeded the execution limit' do - subject { worker_instance.perform(cluster.id, worker_instance.send(:execution_limit))} + subject { worker_instance.perform(cluster.id, worker_instance.send(:execution_limit)) } it 'logs the error' do expect(logger).to receive(:error) diff --git a/spec/workers/packages/helm/extraction_worker_spec.rb b/spec/workers/packages/helm/extraction_worker_spec.rb index daebbda3077..70a090d6989 100644 --- a/spec/workers/packages/helm/extraction_worker_spec.rb +++ b/spec/workers/packages/helm/extraction_worker_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.describe Packages::Helm::ExtractionWorker, type: :worker do describe '#perform' do - let_it_be(:package) { create(:helm_package, without_package_files: true, status: 'processing')} + let_it_be(:package) { create(:helm_package, without_package_files: true, status: 'processing') } let!(:package_file) { create(:helm_package_file, without_loaded_metadatum: true, package: package) } let(:package_file_id) { package_file.id } diff --git a/spec/workers/pages_worker_spec.rb b/spec/workers/pages_worker_spec.rb index 5ddfd5b43b9..ad714d8d11e 100644 --- a/spec/workers/pages_worker_spec.rb +++ b/spec/workers/pages_worker_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.describe PagesWorker, :sidekiq_inline do let(:project) { create(:project) } - let(:ci_build) { create(:ci_build, project: project)} + let(:ci_build) { create(:ci_build, project: project) } it 'calls UpdatePagesService' do expect_next_instance_of(Projects::UpdatePagesService, project, ci_build) do |service| diff --git a/spec/workers/purge_dependency_proxy_cache_worker_spec.rb b/spec/workers/purge_dependency_proxy_cache_worker_spec.rb index 3de59670f8d..84315fd6ee9 100644 --- a/spec/workers/purge_dependency_proxy_cache_worker_spec.rb +++ b/spec/workers/purge_dependency_proxy_cache_worker_spec.rb @@ -4,9 +4,9 @@ require 'spec_helper' RSpec.describe PurgeDependencyProxyCacheWorker do let_it_be(:user) { create(:admin) } - let_it_be_with_refind(:blob) { create(:dependency_proxy_blob )} + let_it_be_with_refind(:blob) { create(:dependency_proxy_blob ) } let_it_be_with_reload(:group) { blob.group } - let_it_be_with_refind(:manifest) { create(:dependency_proxy_manifest, group: group )} + let_it_be_with_refind(:manifest) { create(:dependency_proxy_manifest, group: group ) } let_it_be(:group_id) { group.id } subject { described_class.new.perform(user.id, group_id) } diff --git a/spec/workers/releases/manage_evidence_worker_spec.rb b/spec/workers/releases/manage_evidence_worker_spec.rb index 2fbfb6c9dc1..886fcd346eb 100644 --- a/spec/workers/releases/manage_evidence_worker_spec.rb +++ b/spec/workers/releases/manage_evidence_worker_spec.rb @@ -29,7 +29,7 @@ RSpec.describe Releases::ManageEvidenceWorker do context 'when evidence has already been created' do let(:release) { create(:release, project: project, released_at: 1.hour.since) } - let!(:evidence) { create(:evidence, release: release )} + let!(:evidence) { create(:evidence, release: release ) } it_behaves_like 'does not create a new Evidence record' end