diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6624d31a51d3c8037f45f4936e3439850a0e082f..e8f3ac2813ff6704346004f55b915ca1a70db009 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -154,6 +154,7 @@ variables: KNAPSACK_RSPEC_SUITE_REPORT_PATH: knapsack/report-master.json RSPEC_CHANGED_FILES_PATH: rspec/changed_files.txt RSPEC_FOSS_IMPACT_PIPELINE_TEMPLATE_YML: .gitlab/ci/rails/rspec-foss-impact.gitlab-ci.yml.erb + RSPEC_PREDICTIVE_PIPELINE_TEMPLATE_YML: .gitlab/ci/rails/rspec-predictive.gitlab-ci.yml.erb RSPEC_LAST_RUN_RESULTS_FILE: rspec/rspec_last_run_results.txt RSPEC_MATCHING_JS_FILES_PATH: rspec/js_matching_files.txt RSPEC_VIEWS_INCLUDING_PARTIALS_PATH: rspec/views_including_partials.txt diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml index 89445fb931565548d78a490e8edf4e9b6a1f1e0c..d5480999d87bed13be28cba2833e906c3141d189 100644 --- a/.gitlab/ci/rails.gitlab-ci.yml +++ b/.gitlab/ci/rails.gitlab-ci.yml @@ -74,12 +74,6 @@ rspec migration pg13: - .rails:rules:ee-and-foss-migration - .rspec-migration-parallel -rspec migration pg13 predictive: - extends: - - rspec migration pg13 - - .predictive-rspec-tests - - .rails:rules:ee-and-foss-migration:predictive - rspec background_migration pg13: extends: - .rspec-base-pg13 @@ -87,12 +81,6 @@ rspec background_migration pg13: - .rails:rules:ee-and-foss-background-migration - .rspec-background-migration-parallel -rspec background_migration pg13 predictive: - extends: - - rspec background_migration pg13 - - .predictive-rspec-tests - - .rails:rules:ee-and-foss-background-migration:predictive - rspec migration pg13 single-db: extends: - rspec migration pg13 @@ -135,12 +123,6 @@ rspec unit pg13: - .rails:rules:ee-and-foss-unit - .rspec-unit-parallel -rspec unit pg13 predictive: - extends: - - rspec unit pg13 - - .predictive-rspec-tests - - .rails:rules:ee-and-foss-unit:predictive - rspec unit pg13 single-db: extends: - rspec unit pg13 @@ -165,12 +147,6 @@ rspec integration pg13: - .rails:rules:ee-and-foss-integration - .rspec-integration-parallel -rspec integration pg13 predictive: - extends: - - rspec integration pg13 - - .predictive-rspec-tests - - .rails:rules:ee-and-foss-integration:predictive - rspec integration pg13 single-db: extends: - rspec integration pg13 @@ -197,12 +173,6 @@ rspec system pg13: variables: DEBUG_GITLAB_TRANSACTION_STACK: "true" -rspec system pg13 predictive: - extends: - - rspec system pg13 - - .predictive-rspec-tests - - .rails:rules:ee-and-foss-system:predictive - rspec system pg13 single-db: extends: - rspec system pg13 @@ -305,24 +275,12 @@ rspec:coverage: - rspec unit pg13 - rspec integration pg13 - rspec system pg13 - # FOSS/EE predictive jobs - - rspec migration pg13 predictive - - rspec background_migration pg13 predictive - - rspec unit pg13 predictive - - rspec integration pg13 predictive - - rspec system pg13 predictive # EE jobs - rspec-ee migration pg13 - rspec-ee background_migration pg13 - rspec-ee unit pg13 - rspec-ee integration pg13 - rspec-ee system pg13 - # EE predictive jobs - - rspec-ee migration pg13 predictive - - rspec-ee background_migration pg13 predictive - - rspec-ee unit pg13 predictive - - rspec-ee integration pg13 predictive - - rspec-ee system pg13 predictive # Memory jobs - memory-on-boot script: @@ -402,6 +360,56 @@ rspec:flaky-tests-report: ################################################## # EE: default refs (MRs, default branch, schedules) jobs # +rspec-predictive:pipeline-generate: + extends: + - .rails:rules:rspec-predictive + stage: prepare + needs: ["detect-tests", "retrieve-tests-metadata"] + script: + - scripts/generate_rspec_pipeline.rb -t "${RSPEC_PREDICTIVE_PIPELINE_TEMPLATE_YML}" -k "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" -f "${RSPEC_MATCHING_TESTS_FOSS_PATH}" -o "${RSPEC_PREDICTIVE_PIPELINE_TEMPLATE_YML}.yml" + - scripts/generate_rspec_pipeline.rb -t "${RSPEC_PREDICTIVE_PIPELINE_TEMPLATE_YML}" -k "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" -f "${RSPEC_MATCHING_TESTS_EE_PATH}" -o "${RSPEC_PREDICTIVE_PIPELINE_TEMPLATE_YML}-ee.yml" -p "ee/" + - echo "Content of ${RSPEC_PREDICTIVE_PIPELINE_TEMPLATE_YML}.yml:" + - cat "${RSPEC_PREDICTIVE_PIPELINE_TEMPLATE_YML}.yml" + - echo "\n================================================\n" + - echo "Content of ${RSPEC_PREDICTIVE_PIPELINE_TEMPLATE_YML}-ee.yml:" + - cat "${RSPEC_PREDICTIVE_PIPELINE_TEMPLATE_YML}-ee.yml" + artifacts: + expire_in: 1 day + paths: + - "${RSPEC_PREDICTIVE_PIPELINE_TEMPLATE_YML}.yml" + - "${RSPEC_PREDICTIVE_PIPELINE_TEMPLATE_YML}-ee.yml" + +rspec:predictive:trigger: + extends: + - .rails:rules:rspec-predictive + stage: test + needs: + - job: "setup-test-env" + artifacts: false + - job: "retrieve-tests-metadata" + artifacts: false + - job: "compile-test-assets" + artifacts: false + - job: "rspec-predictive:pipeline-generate" + artifacts: true + variables: + PARENT_PIPELINE_ID: $CI_PIPELINE_ID + trigger: + strategy: depend + forward: + yaml_variables: true + pipeline_variables: true + include: + - artifact: "${RSPEC_PREDICTIVE_PIPELINE_TEMPLATE_YML}.yml" + job: rspec-predictive:pipeline-generate + +rspec-ee:predictive:trigger: + extends: rspec:predictive:trigger + trigger: + include: + - artifact: "${RSPEC_PREDICTIVE_PIPELINE_TEMPLATE_YML}-ee.yml" + job: rspec-predictive:pipeline-generate + rspec migration pg13-as-if-foss: extends: - .rspec-base-pg13-as-if-foss @@ -409,12 +417,6 @@ rspec migration pg13-as-if-foss: - .rails:rules:as-if-foss-migration - .rspec-migration-parallel -rspec migration pg13-as-if-foss predictive: - extends: - - rspec migration pg13-as-if-foss - - .predictive-rspec-tests - - .rails:rules:as-if-foss-migration:predictive - rspec background_migration pg13-as-if-foss: extends: - .rspec-base-pg13-as-if-foss @@ -422,12 +424,6 @@ rspec background_migration pg13-as-if-foss: - .rails:rules:as-if-foss-background-migration - .rspec-background-migration-parallel -rspec background_migration pg13-as-if-foss predictive: - extends: - - rspec background_migration pg13-as-if-foss - - .predictive-rspec-tests - - .rails:rules:as-if-foss-background-migration:predictive - rspec migration pg13-as-if-foss single-db: extends: - rspec migration pg13-as-if-foss @@ -458,12 +454,6 @@ rspec unit pg13-as-if-foss: - .rails:rules:as-if-foss-unit - .rspec-unit-parallel -rspec unit pg13-as-if-foss predictive: - extends: - - rspec unit pg13-as-if-foss - - .predictive-rspec-tests - - .rails:rules:as-if-foss-unit:predictive - rspec unit pg13-as-if-foss single-db: extends: - rspec unit pg13-as-if-foss @@ -482,12 +472,6 @@ rspec integration pg13-as-if-foss: - .rails:rules:as-if-foss-integration - .rspec-integration-parallel -rspec integration pg13-as-if-foss predictive: - extends: - - rspec integration pg13-as-if-foss - - .predictive-rspec-tests - - .rails:rules:as-if-foss-integration:predictive - rspec integration pg13-as-if-foss single-db: extends: - rspec integration pg13-as-if-foss @@ -506,12 +490,6 @@ rspec system pg13-as-if-foss: - .rails:rules:as-if-foss-system - .rspec-system-parallel -rspec system pg13-as-if-foss predictive: - extends: - - rspec system pg13-as-if-foss - - .predictive-rspec-tests - - .rails:rules:as-if-foss-system:predictive - rspec system pg13-as-if-foss single-db: extends: - rspec system pg13-as-if-foss @@ -531,12 +509,6 @@ rspec-ee migration pg13: - .rails:rules:ee-only-migration - .rspec-ee-migration-parallel -rspec-ee migration pg13 predictive: - extends: - - rspec-ee migration pg13 - - .predictive-rspec-tests - - .rails:rules:ee-only-migration:predictive - rspec-ee background_migration pg13: extends: - .rspec-ee-base-pg13 @@ -544,12 +516,6 @@ rspec-ee background_migration pg13: - .rails:rules:ee-only-background-migration - .rspec-ee-background-migration-parallel -rspec-ee background_migration pg13 predictive: - extends: - - rspec-ee background_migration pg13 - - .predictive-rspec-tests - - .rails:rules:ee-only-background-migration:predictive - rspec-ee migration pg13 single-db: extends: - rspec-ee migration pg13 @@ -597,12 +563,6 @@ rspec-ee unit pg13 es8: - .rspec-ee-base-pg13-es8 - .rspec-ee-unit-parallel -rspec-ee unit pg13 predictive: - extends: - - rspec-ee unit pg13 - - .predictive-rspec-tests - - .rails:rules:ee-only-unit:predictive - rspec-ee unit pg13 single-db: extends: - rspec-ee unit pg13 @@ -626,12 +586,6 @@ rspec-ee integration pg13 es8: - .rspec-ee-base-pg13-es8 - .rspec-ee-integration-parallel -rspec-ee integration pg13 predictive: - extends: - - rspec-ee integration pg13 - - .predictive-rspec-tests - - .rails:rules:ee-only-integration:predictive - rspec-ee integration pg13 single-db: extends: - rspec-ee integration pg13 @@ -655,12 +609,6 @@ rspec-ee system pg13 es8: - .rspec-ee-base-pg13-es8 - .rspec-ee-system-parallel -rspec-ee system pg13 predictive: - extends: - - rspec-ee system pg13 - - .predictive-rspec-tests - - .rails:rules:ee-only-system:predictive - rspec-ee system pg13 single-db: extends: - rspec-ee system pg13 diff --git a/.gitlab/ci/rails/rspec-foss-impact.gitlab-ci.yml.erb b/.gitlab/ci/rails/rspec-foss-impact.gitlab-ci.yml.erb index 38d964af62acceef175a05fcc8841d4d58e1b4b2..e7a1ee6022fe55f34f6fe62474f0887b2042418c 100644 --- a/.gitlab/ci/rails/rspec-foss-impact.gitlab-ci.yml.erb +++ b/.gitlab/ci/rails/rspec-foss-impact.gitlab-ci.yml.erb @@ -1,4 +1,4 @@ -# RSpec FOSS impact pipeline loaded dynamically by script: scripts/generate-rspec-foss-impact-pipeline +# RSpec FOSS impact pipeline loaded dynamically by script: scripts/generate_rspec_pipeline.rb include: - local: .gitlab/ci/rails/shared.gitlab-ci.yml diff --git a/.gitlab/ci/rails/rspec-predictive.gitlab-ci.yml.erb b/.gitlab/ci/rails/rspec-predictive.gitlab-ci.yml.erb new file mode 100644 index 0000000000000000000000000000000000000000..fcd8754c76a1428e6b0716283d8f025df4c8fa34 --- /dev/null +++ b/.gitlab/ci/rails/rspec-predictive.gitlab-ci.yml.erb @@ -0,0 +1,153 @@ +# RSpec preditive pipeline loaded dynamically by script: scripts/generate_rspec_pipeline.rb + +include: + - local: .gitlab/ci/rails/shared.gitlab-ci.yml + +default: + image: $DEFAULT_CI_IMAGE + tags: + - gitlab-org + # Default job timeout set to 90m https://gitlab.com/gitlab-com/gl-infra/infrastructure/-/issues/10520 + timeout: 90m + interruptible: true + +stages: + - test + +dont-interrupt-me: + extends: .rules:dont-interrupt + stage: .pre + interruptible: false + script: + - echo "This jobs makes sure this pipeline won't be interrupted! See https://docs.gitlab.com/ee/ci/yaml/#interruptible." + +.base-predictive: + needs: + - pipeline: $PARENT_PIPELINE_ID + job: detect-tests + - pipeline: $PARENT_PIPELINE_ID + job: setup-test-env + - pipeline: $PARENT_PIPELINE_ID + job: retrieve-tests-metadata + - pipeline: $PARENT_PIPELINE_ID + job: compile-test-assets + rules: + - when: always + variables: + RSPEC_TESTS_MAPPING_ENABLED: "true" + +<% if test_suite_prefix.nil? %> +.base-rspec-predictive: + extends: + - .rspec-base-pg12 + - .base-predictive + variables: + # We're using the FOSS one here because we want to exclude EE-only ones + # For EE-only ones, we have EE-only jobs. + RSPEC_TESTS_FILTER_FILE: "${RSPEC_MATCHING_TESTS_FOSS_PATH}" + +<% if rspec_files_per_test_level.dig(:migration, :files).size > 0 %> +rspec migration predictive: + extends: + - .base-rspec-predictive + - .rspec-base-migration +<% if rspec_files_per_test_level.dig(:migration, :parallelization) > 1 %> + parallel: <%= rspec_files_per_test_level.dig(:migration, :parallelization) %> +<% end %> +<% end %> + +<% if rspec_files_per_test_level.dig(:background_migration, :files).size > 0 %> +rspec background_migration predictive: + extends: + - .base-rspec-predictive + - .rspec-base-migration +<% if rspec_files_per_test_level.dig(:background_migration, :parallelization) > 1 %> + parallel: <%= rspec_files_per_test_level.dig(:background_migration, :parallelization) %> +<% end %> +<% end %> + +<% if rspec_files_per_test_level.dig(:unit, :files).size > 0 %> +rspec unit predictive: + extends: + - .base-rspec-predictive +<% if rspec_files_per_test_level.dig(:unit, :parallelization) > 1 %> + parallel: <%= rspec_files_per_test_level.dig(:unit, :parallelization) %> +<% end %> +<% end %> + +<% if rspec_files_per_test_level.dig(:integration, :files).size > 0 %> +rspec integration predictive: + extends: + - .base-rspec-predictive +<% if rspec_files_per_test_level.dig(:integration, :parallelization) > 1 %> + parallel: <%= rspec_files_per_test_level.dig(:integration, :parallelization) %> +<% end %> +<% end %> + +<% if rspec_files_per_test_level.dig(:system, :files).size > 0 %> +rspec system predictive: + extends: + - .base-rspec-predictive +<% if rspec_files_per_test_level.dig(:system, :parallelization) > 1 %> + parallel: <%= rspec_files_per_test_level.dig(:system, :parallelization) %> +<% end %> +<% end %> + +<% end %> + +<% if test_suite_prefix == 'ee/' %> +.base-rspec-ee-predictive: + extends: + - .rspec-ee-base-pg12 + - .base-predictive + variables: + RSPEC_TESTS_FILTER_FILE: "${RSPEC_MATCHING_TESTS_EE_PATH}" + +<% if rspec_files_per_test_level.dig(:migration, :files).size > 0 %> +rspec-ee migration predictive: + extends: + - .base-rspec-ee-predictive + - .rspec-base-migration +<% if rspec_files_per_test_level.dig(:migration, :parallelization) > 1 %> + parallel: <%= rspec_files_per_test_level.dig(:migration, :parallelization) %> +<% end %> +<% end %> + +<% if rspec_files_per_test_level.dig(:background_migration, :files).size > 0 %> +rspec-ee background_migration predictive: + extends: + - .base-rspec-ee-predictive + - .rspec-base-migration +<% if rspec_files_per_test_level.dig(:background_migration, :parallelization) > 1 %> + parallel: <%= rspec_files_per_test_level.dig(:background_migration, :parallelization) %> +<% end %> +<% end %> + +<% if rspec_files_per_test_level.dig(:unit, :files).size > 0 %> +rspec-ee unit predictive: + extends: + - .base-rspec-ee-predictive +<% if rspec_files_per_test_level.dig(:unit, :parallelization) > 1 %> + parallel: <%= rspec_files_per_test_level.dig(:unit, :parallelization) %> +<% end %> +<% end %> + +<% if rspec_files_per_test_level.dig(:integration, :files).size > 0 %> +rspec-ee integration predictive: + extends: + - .base-rspec-ee-predictive +<% if rspec_files_per_test_level.dig(:integration, :parallelization) > 1 %> + parallel: <%= rspec_files_per_test_level.dig(:integration, :parallelization) %> +<% end %> +<% end %> + +<% if rspec_files_per_test_level.dig(:system, :files).size > 0 %> +rspec-ee system predictive: + extends: + - .base-rspec-ee-predictive +<% if rspec_files_per_test_level.dig(:system, :parallelization) > 1 %> + parallel: <%= rspec_files_per_test_level.dig(:system, :parallelization) %> +<% end %> +<% end %> + +<% end %> diff --git a/.gitlab/ci/rails/shared.gitlab-ci.yml b/.gitlab/ci/rails/shared.gitlab-ci.yml index 62e8547fa5af4ac4add77d76ecefa2835ae1ad05..adcfcd2010f360759fa2e315980ecbe590923a2c 100644 --- a/.gitlab/ci/rails/shared.gitlab-ci.yml +++ b/.gitlab/ci/rails/shared.gitlab-ci.yml @@ -28,10 +28,6 @@ include: - run_timed_command "scripts/gitaly-test-spawn" # Do not use 'bundle exec' here - echo -e "\e[0Ksection_end:`date +%s`:gitaly-test-spawn\r\e[0K" -.predictive-rspec-tests: - variables: - RSPEC_TESTS_MAPPING_ENABLED: "true" - .single-db: variables: DECOMPOSED_DB: "false" @@ -61,7 +57,6 @@ include: RUBY_GC_MALLOC_LIMIT_MAX: 134217728 RECORD_DEPRECATIONS: "true" GEO_SECONDARY_PROXY: 0 - RSPEC_TESTS_FILTER_FILE: "${RSPEC_MATCHING_TESTS_PATH}" SUCCESSFULLY_RETRIED_TEST_EXIT_CODE: 137 needs: - job: "setup-test-env" diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index c61819d0a4c99d686da5d6b4572ea4d51105d8b0..9930efd90b5798314a87071eb0eb5a7f3a17781d 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -593,7 +593,6 @@ ################## # Conditions set # ################## - .strict-ee-only-rules: rules: - <<: *if-not-ee @@ -610,15 +609,6 @@ - <<: *if-merge-request-labels-pipeline-expedite when: never -.rails:rules:predictive-default-rules: - rules: - - <<: *if-merge-request-approved - when: never - - <<: *if-automated-merge-request - when: never - - <<: *if-security-merge-request - when: never - .rails:rules:run-search-tests: rules: - !reference [".rails:rules:default-branch-schedule-nightly--code-backstage-ee-only", rules] @@ -639,6 +629,40 @@ - <<: *if-merge-request-not-approved when: never +.rails:rules:system-default-rules: + rules: + - <<: *if-merge-request-labels-run-all-rspec + - <<: *if-merge-request + changes: *core-backend-patterns + - <<: *if-merge-request + changes: *workhorse-patterns + - <<: *if-automated-merge-request + changes: *code-backstage-patterns + - <<: *if-security-merge-request + changes: *code-backstage-patterns + - <<: *if-merge-request-not-approved + when: never + +.rails:rules:previous-failed-tests-default-rules: + rules: + - <<: *if-security-merge-request + when: never + - <<: *if-merge-request-labels-run-all-rspec + - <<: *if-merge-request + changes: *code-backstage-patterns + +########################### +# Conditions set for JiHu # +########################### +.rails:rules:predictive-default-rules: + rules: + - <<: *if-merge-request-approved + when: never + - <<: *if-automated-merge-request + when: never + - <<: *if-security-merge-request + when: never + .rails:rules:as-if-foss-migration-unit-integration:predictive-default-rules: rules: - <<: *if-merge-request @@ -654,43 +678,115 @@ when: never - !reference [".rails:rules:as-if-foss-migration-unit-integration:predictive-default-rules", rules] -.rails:rules:system-default-rules: +.rails:rules:system:predictive-default-rules: rules: - <<: *if-merge-request-labels-run-all-rspec + when: never - <<: *if-merge-request changes: *core-backend-patterns + when: never - <<: *if-merge-request changes: *workhorse-patterns - - <<: *if-automated-merge-request - changes: *code-backstage-patterns - - <<: *if-security-merge-request - changes: *code-backstage-patterns - - <<: *if-merge-request-not-approved when: never + - <<: *if-merge-request + changes: *ci-patterns + when: never + - <<: *if-merge-request + changes: *code-backstage-patterns -.rails:rules:system:predictive-default-rules: +.rails:rules:ee-and-foss-migration:predictive: rules: - - <<: *if-merge-request-labels-run-all-rspec + - <<: *if-fork-merge-request + changes: *db-patterns + - !reference [".rails:rules:predictive-default-rules", rules] + - !reference [".rails:rules:unit-integration:predictive-default-rules", rules] + # When DB schema changes, many migrations spec may be affected. However, the test mapping from Crystalball does not map db change to a specific migration spec well. + # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68840. + - <<: *if-merge-request + changes: *db-patterns when: never + +.rails:rules:ee-and-foss-background-migration:predictive: + rules: + - !reference [".rails:rules:ee-and-foss-migration:predictive", rules] - <<: *if-merge-request - changes: *core-backend-patterns + changes: *backend-patterns + +.rails:rules:ee-and-foss-unit:predictive: + rules: + - <<: *if-fork-merge-request + changes: *backend-patterns + - !reference [".rails:rules:predictive-default-rules", rules] + - !reference [".rails:rules:unit-integration:predictive-default-rules", rules] + - <<: *if-merge-request + changes: *backend-patterns + - <<: *if-merge-request + changes: *backstage-patterns + +.rails:rules:ee-and-foss-integration:predictive: + rules: + - <<: *if-fork-merge-request + changes: *backend-patterns + - !reference [".rails:rules:predictive-default-rules", rules] + - !reference [".rails:rules:unit-integration:predictive-default-rules", rules] + - <<: *if-merge-request + changes: *backend-patterns + +.rails:rules:ee-and-foss-system:predictive: + rules: + - <<: *if-fork-merge-request + changes: *code-backstage-patterns + - !reference [".rails:rules:predictive-default-rules", rules] + - !reference [".rails:rules:system:predictive-default-rules", rules] + +.rails:rules:ee-only-migration:predictive: + rules: + - <<: *if-not-ee when: never + - !reference [".rails:rules:predictive-default-rules", rules] + - !reference [".rails:rules:unit-integration:predictive-default-rules", rules] + # When DB schema changes, many migrations spec may be affected. However, the test mapping from Crystalball does not map db change to a specific migration spec well. + # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68840. - <<: *if-merge-request - changes: *workhorse-patterns + changes: *db-patterns when: never + +.rails:rules:ee-only-background-migration:predictive: + rules: + - !reference [".rails:rules:ee-only-migration:predictive", rules] - <<: *if-merge-request - changes: *ci-patterns + changes: *backend-patterns + +.rails:rules:ee-only-unit:predictive: + rules: + - <<: *if-not-ee when: never + - <<: *if-fork-merge-request + changes: *backend-patterns + - !reference [".rails:rules:predictive-default-rules", rules] + - !reference [".rails:rules:unit-integration:predictive-default-rules", rules] - <<: *if-merge-request - changes: *code-backstage-patterns + changes: *backend-patterns -.rails:rules:previous-failed-tests-default-rules: +.rails:rules:ee-only-integration:predictive: rules: - - <<: *if-security-merge-request + - <<: *if-not-ee when: never - - <<: *if-merge-request-labels-run-all-rspec + - <<: *if-fork-merge-request + changes: *backend-patterns + - !reference [".rails:rules:predictive-default-rules", rules] + - !reference [".rails:rules:unit-integration:predictive-default-rules", rules] - <<: *if-merge-request + changes: *backend-patterns + +.rails:rules:ee-only-system:predictive: + rules: + - <<: *if-not-ee + when: never + - <<: *if-fork-merge-request changes: *code-backstage-patterns + - !reference [".rails:rules:predictive-default-rules", rules] + - !reference [".rails:rules:system:predictive-default-rules", rules] ################ # Shared rules # @@ -1335,17 +1431,18 @@ - <<: *if-default-refs changes: *db-patterns -.rails:rules:ee-and-foss-migration:predictive: +.rails:rules:rspec-predictive: rules: - - <<: *if-fork-merge-request - changes: *db-patterns - - !reference [".rails:rules:predictive-default-rules", rules] - - !reference [".rails:rules:unit-integration:predictive-default-rules", rules] - # When DB schema changes, many migrations spec may be affected. However, the test mapping from Crystalball does not map db change to a specific migration spec well. - # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68840. - - <<: *if-merge-request - changes: *db-patterns + - <<: *if-merge-request-approved + when: never + - <<: *if-automated-merge-request + when: never + - <<: *if-security-merge-request + when: never + - <<: *if-merge-request-labels-run-all-rspec when: never + - <<: *if-merge-request + changes: *code-backstage-patterns .rails:rules:ee-and-foss-background-migration: rules: @@ -1353,12 +1450,6 @@ - <<: *if-default-refs changes: *backend-patterns -.rails:rules:ee-and-foss-background-migration:predictive: - rules: - - !reference [".rails:rules:ee-and-foss-migration:predictive", rules] - - <<: *if-merge-request - changes: *backend-patterns - .rails:rules:ee-and-foss-mr-with-migration: rules: - <<: *if-merge-request @@ -1383,17 +1474,6 @@ - <<: *if-default-refs changes: *backstage-patterns -.rails:rules:ee-and-foss-unit:predictive: - rules: - - <<: *if-fork-merge-request - changes: *backend-patterns - - !reference [".rails:rules:predictive-default-rules", rules] - - !reference [".rails:rules:unit-integration:predictive-default-rules", rules] - - <<: *if-merge-request - changes: *backend-patterns - - <<: *if-merge-request - changes: *backstage-patterns - .rails:rules:ee-and-foss-integration: rules: - <<: *if-fork-merge-request @@ -1402,15 +1482,6 @@ - <<: *if-default-refs changes: *backend-patterns -.rails:rules:ee-and-foss-integration:predictive: - rules: - - <<: *if-fork-merge-request - changes: *backend-patterns - - !reference [".rails:rules:predictive-default-rules", rules] - - !reference [".rails:rules:unit-integration:predictive-default-rules", rules] - - <<: *if-merge-request - changes: *backend-patterns - .rails:rules:ee-and-foss-system: rules: - <<: *if-fork-merge-request @@ -1419,13 +1490,6 @@ - <<: *if-default-refs changes: *code-backstage-patterns -.rails:rules:ee-and-foss-system:predictive: - rules: - - <<: *if-fork-merge-request - changes: *code-backstage-patterns - - !reference [".rails:rules:predictive-default-rules", rules] - - !reference [".rails:rules:system:predictive-default-rules", rules] - .rails:rules:ee-and-foss-fast_spec_helper: rules: - <<: *if-merge-request-labels-run-all-rspec @@ -1460,30 +1524,12 @@ - <<: *if-default-refs changes: *db-patterns -.rails:rules:ee-only-migration:predictive: - rules: - - <<: *if-not-ee - when: never - - !reference [".rails:rules:predictive-default-rules", rules] - - !reference [".rails:rules:unit-integration:predictive-default-rules", rules] - # When DB schema changes, many migrations spec may be affected. However, the test mapping from Crystalball does not map db change to a specific migration spec well. - # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68840. - - <<: *if-merge-request - changes: *db-patterns - when: never - .rails:rules:ee-only-background-migration: rules: - !reference [".rails:rules:ee-only-migration", rules] - <<: *if-default-refs changes: *backend-patterns -.rails:rules:ee-only-background-migration:predictive: - rules: - - !reference [".rails:rules:ee-only-migration:predictive", rules] - - <<: *if-merge-request - changes: *backend-patterns - .rails:rules:ee-only-unit: rules: - <<: *if-not-ee @@ -1494,17 +1540,6 @@ - <<: *if-default-refs changes: *backend-patterns -.rails:rules:ee-only-unit:predictive: - rules: - - <<: *if-not-ee - when: never - - <<: *if-fork-merge-request - changes: *backend-patterns - - !reference [".rails:rules:predictive-default-rules", rules] - - !reference [".rails:rules:unit-integration:predictive-default-rules", rules] - - <<: *if-merge-request - changes: *backend-patterns - .rails:rules:ee-only-integration: rules: - <<: *if-not-ee @@ -1515,17 +1550,6 @@ - <<: *if-default-refs changes: *backend-patterns -.rails:rules:ee-only-integration:predictive: - rules: - - <<: *if-not-ee - when: never - - <<: *if-fork-merge-request - changes: *backend-patterns - - !reference [".rails:rules:predictive-default-rules", rules] - - !reference [".rails:rules:unit-integration:predictive-default-rules", rules] - - <<: *if-merge-request - changes: *backend-patterns - .rails:rules:ee-only-system: rules: - <<: *if-not-ee @@ -1536,15 +1560,6 @@ - <<: *if-default-refs changes: *code-backstage-patterns -.rails:rules:ee-only-system:predictive: - rules: - - <<: *if-not-ee - when: never - - <<: *if-fork-merge-request - changes: *code-backstage-patterns - - !reference [".rails:rules:predictive-default-rules", rules] - - !reference [".rails:rules:system:predictive-default-rules", rules] - .rails:rules:as-if-foss-migration: rules: - <<: *if-not-ee @@ -1563,30 +1578,12 @@ - <<: *if-merge-request-not-approved when: never -.rails:rules:as-if-foss-migration:predictive: - rules: - - <<: *if-not-ee - when: never - - !reference [".rails:rules:predictive-default-rules", rules] - - !reference [".rails:rules:as-if-foss-migration-unit-integration:predictive-default-rules", rules] - # When DB schema changes, many migrations spec may be affected. However, the test mapping from Crystalball does not map db change to a specific migration spec well. - # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68840. - - <<: *if-merge-request-labels-as-if-foss - changes: *db-patterns - when: never - .rails:rules:as-if-foss-background-migration: rules: - !reference [".rails:rules:as-if-foss-migration", rules] - <<: *if-merge-request-labels-as-if-foss changes: *backend-patterns -.rails:rules:as-if-foss-background-migration:predictive: - rules: - - !reference [".rails:rules:as-if-foss-migration:predictive", rules] - - <<: *if-merge-request-labels-as-if-foss - changes: *backend-patterns - .rails:rules:as-if-foss-unit: rules: - <<: *if-not-ee @@ -1597,17 +1594,6 @@ - <<: *if-merge-request-labels-as-if-foss changes: *backend-patterns -.rails:rules:as-if-foss-unit:predictive: - rules: - - <<: *if-not-ee - when: never - - <<: *if-fork-merge-request - when: never - - !reference [".rails:rules:predictive-default-rules", rules] - - !reference [".rails:rules:as-if-foss-migration-unit-integration:predictive-default-rules", rules] - - <<: *if-merge-request-labels-as-if-foss - changes: *backend-patterns - .rails:rules:as-if-foss-integration: rules: - <<: *if-not-ee @@ -1618,17 +1604,6 @@ - <<: *if-merge-request-labels-as-if-foss changes: *backend-patterns -.rails:rules:as-if-foss-integration:predictive: - rules: - - <<: *if-not-ee - when: never - - <<: *if-fork-merge-request - when: never - - !reference [".rails:rules:predictive-default-rules", rules] - - !reference [".rails:rules:as-if-foss-migration-unit-integration:predictive-default-rules", rules] - - <<: *if-merge-request-labels-as-if-foss - changes: *backend-patterns - .rails:rules:as-if-foss-system: rules: - <<: *if-not-ee @@ -1639,25 +1614,6 @@ - <<: *if-merge-request-labels-as-if-foss changes: *code-backstage-patterns -.rails:rules:as-if-foss-system:predictive: - rules: - - <<: *if-not-ee - when: never - - <<: *if-fork-merge-request - when: never - - !reference [".rails:rules:predictive-default-rules", rules] - - <<: *if-merge-request - changes: *core-backend-patterns - when: never - - <<: *if-merge-request - changes: *workhorse-patterns - when: never - - <<: *if-merge-request - changes: *ci-patterns - when: never - - <<: *if-merge-request-labels-as-if-foss - changes: *code-backstage-patterns - .rails:rules:ee-and-foss-db-library-code: rules: - <<: *if-default-refs @@ -1749,6 +1705,10 @@ when: never - <<: *if-merge-request-labels-skip-undercoverage when: never + # We cannot get the coverage data from child pipeline so we only run undercoverage on full pipelines for now + # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113410#note_1335422806 + - <<: *if-merge-request-not-approved + when: never - <<: *if-merge-request-labels-run-all-rspec - <<: *if-merge-request changes: *backend-patterns diff --git a/scripts/generate_rspec_pipeline.rb b/scripts/generate_rspec_pipeline.rb index e226acc0430f4657362366889ae0e92138219afe..292b3d85b207b346aead9ba94d2490992eaa206d 100755 --- a/scripts/generate_rspec_pipeline.rb +++ b/scripts/generate_rspec_pipeline.rb @@ -43,12 +43,20 @@ class GenerateRspecPipeline DEFAULT_AVERAGE_TEST_FILE_DURATION_IN_SECONDS = DURATION_OF_THE_TEST_SUITE_IN_SECONDS / NUMBER_OF_TESTS_IN_TOTAL_IN_THE_TEST_SUITE - # rspec_files_path: A file containing RSpec files to run, separated by a space # pipeline_template_path: A YAML pipeline configuration template to generate the final pipeline config from - def initialize(pipeline_template_path:, rspec_files_path: nil, knapsack_report_path: nil) + # rspec_files_path: A file containing RSpec files to run, separated by a space + # knapsack_report_path: A file containing a Knapsack report + # test_suite_prefix: An optional test suite folder prefix (e.g. `ee/` or `jh/`) + # generated_pipeline_path: An optional filename where to write the pipeline config (defaults to + # `"#{pipeline_template_path}.yml"`) + def initialize( + pipeline_template_path:, rspec_files_path: nil, knapsack_report_path: nil, test_suite_prefix: nil, + generated_pipeline_path: nil) @pipeline_template_path = pipeline_template_path.to_s @rspec_files_path = rspec_files_path.to_s @knapsack_report_path = knapsack_report_path.to_s + @test_suite_prefix = test_suite_prefix + @generated_pipeline_path = generated_pipeline_path || "#{pipeline_template_path}.yml" raise ArgumentError unless File.exist?(@pipeline_template_path) end @@ -56,11 +64,14 @@ def initialize(pipeline_template_path:, rspec_files_path: nil, knapsack_report_p def generate! if all_rspec_files.empty? info "Using #{SKIP_PIPELINE_YML_FILE} due to no RSpec files to run" - FileUtils.cp(SKIP_PIPELINE_YML_FILE, pipeline_filename) + FileUtils.cp(SKIP_PIPELINE_YML_FILE, generated_pipeline_path) return end - File.open(pipeline_filename, 'w') do |handle| + info "pipeline_template_path: #{pipeline_template_path}" + info "generated_pipeline_path: #{generated_pipeline_path}" + + File.open(generated_pipeline_path, 'w') do |handle| pipeline_yaml = ERB.new(File.read(pipeline_template_path)).result_with_hash(**erb_binding) handle.write(pipeline_yaml.squeeze("\n").strip) end @@ -68,7 +79,8 @@ def generate! private - attr_reader :pipeline_template_path, :rspec_files_path, :knapsack_report_path + attr_reader :pipeline_template_path, :rspec_files_path, :knapsack_report_path, :test_suite_prefix, + :generated_pipeline_path def info(text) $stdout.puts "[#{self.class.name}] #{text}" @@ -78,12 +90,11 @@ def all_rspec_files @all_rspec_files ||= File.exist?(rspec_files_path) ? File.read(rspec_files_path).split(' ') : [] end - def pipeline_filename - @pipeline_filename ||= "#{pipeline_template_path}.yml" - end - def erb_binding - { rspec_files_per_test_level: rspec_files_per_test_level } + { + rspec_files_per_test_level: rspec_files_per_test_level, + test_suite_prefix: test_suite_prefix + } end def rspec_files_per_test_level @@ -91,7 +102,7 @@ def rspec_files_per_test_level all_remaining_rspec_files = all_rspec_files.dup TEST_LEVELS.each_with_object(Hash.new { |h, k| h[k] = {} }) do |test_level, memo| # rubocop:disable Rails/IndexWith memo[test_level][:files] = all_remaining_rspec_files - .grep(Quality::TestLevel.new.regexp(test_level)) + .grep(test_level_service.regexp(test_level, true)) .tap { |files| files.each { |file| all_remaining_rspec_files.delete(file) } } memo[test_level][:parallelization] = optimal_nodes_count(test_level, memo[test_level][:files]) end @@ -125,10 +136,15 @@ def average_test_file_duration_in_seconds_per_test_level remaining_knapsack_report = knapsack_report.dup TEST_LEVELS.each_with_object({}) do |test_level, memo| matching_data_per_test_level = remaining_knapsack_report - .select { |test_file, _| test_file.match?(Quality::TestLevel.new.regexp(test_level)) } + .select { |test_file, _| test_file.match?(test_level_service.regexp(test_level, true)) } .tap { |test_data| test_data.each { |file, _| remaining_knapsack_report.delete(file) } } + memo[test_level] = - matching_data_per_test_level.values.sum / matching_data_per_test_level.keys.size + if matching_data_per_test_level.empty? + DEFAULT_AVERAGE_TEST_FILE_DURATION_IN_SECONDS + else + matching_data_per_test_level.values.sum / matching_data_per_test_level.keys.size + end end else TEST_LEVELS.each_with_object({}) do |test_level, memo| # rubocop:disable Rails/IndexWith @@ -146,6 +162,10 @@ def knapsack_report {} end end + + def test_level_service + @test_level_service ||= Quality::TestLevel.new(test_suite_prefix) + end end if $PROGRAM_NAME == __FILE__ @@ -166,6 +186,15 @@ def knapsack_report options[:knapsack_report_path] = value end + opts.on("-p", "--test-suite-prefix test_suite_prefix", String, "Test suite folder prefix") do |value| + options[:test_suite_prefix] = value + end + + opts.on("-o", "--generated-pipeline-path generated_pipeline_path", String, "Path where to write the pipeline " \ + "config") do |value| + options[:generated_pipeline_path] = value + end + opts.on("-h", "--help", "Prints this help") do puts opts exit diff --git a/spec/scripts/generate_rspec_pipeline_spec.rb b/spec/scripts/generate_rspec_pipeline_spec.rb index b3eaf9e9127224e903737f039cb5b76f4ea3fa1d..91b5739cf6386855a9a64145c8b0d2b677ef7d2f 100644 --- a/spec/scripts/generate_rspec_pipeline_spec.rb +++ b/spec/scripts/generate_rspec_pipeline_spec.rb @@ -13,42 +13,49 @@ "spec/lib/gitlab/background_migration/a_spec.rb spec/lib/gitlab/background_migration/b_spec.rb " \ "spec/models/a_spec.rb spec/models/b_spec.rb " \ "spec/controllers/a_spec.rb spec/controllers/b_spec.rb " \ - "spec/features/a_spec.rb spec/features/b_spec.rb" + "spec/features/a_spec.rb spec/features/b_spec.rb " \ + "ee/spec/features/a_spec.rb" end let(:pipeline_template) { Tempfile.new(['pipeline_template', '.yml.erb']) } let(:pipeline_template_content) do <<~YAML - <% if rspec_files_per_test_level[:migration][:files].size > 0 %> + <% if test_suite_prefix.nil? && rspec_files_per_test_level[:migration][:files].size > 0 %> rspec migration: <% if rspec_files_per_test_level[:migration][:parallelization] > 1 %> parallel: <%= rspec_files_per_test_level[:migration][:parallelization] %> <% end %> <% end %> - <% if rspec_files_per_test_level[:background_migration][:files].size > 0 %> + <% if test_suite_prefix.nil? && rspec_files_per_test_level[:background_migration][:files].size > 0 %> rspec background_migration: <% if rspec_files_per_test_level[:background_migration][:parallelization] > 1 %> parallel: <%= rspec_files_per_test_level[:background_migration][:parallelization] %> <% end %> <% end %> - <% if rspec_files_per_test_level[:unit][:files].size > 0 %> + <% if test_suite_prefix.nil? && rspec_files_per_test_level[:unit][:files].size > 0 %> rspec unit: <% if rspec_files_per_test_level[:unit][:parallelization] > 1 %> parallel: <%= rspec_files_per_test_level[:unit][:parallelization] %> <% end %> <% end %> - <% if rspec_files_per_test_level[:integration][:files].size > 0 %> + <% if test_suite_prefix.nil? && rspec_files_per_test_level[:integration][:files].size > 0 %> rspec integration: <% if rspec_files_per_test_level[:integration][:parallelization] > 1 %> parallel: <%= rspec_files_per_test_level[:integration][:parallelization] %> <% end %> <% end %> - <% if rspec_files_per_test_level[:system][:files].size > 0 %> + <% if test_suite_prefix.nil? && rspec_files_per_test_level[:system][:files].size > 0 %> rspec system: <% if rspec_files_per_test_level[:system][:parallelization] > 1 %> parallel: <%= rspec_files_per_test_level[:system][:parallelization] %> <% end %> <% end %> + <% if test_suite_prefix == 'ee/' && rspec_files_per_test_level[:system][:files].size > 0 %> + rspec-ee system: + <% if rspec_files_per_test_level[:system][:parallelization] > 1 %> + parallel: <%= rspec_files_per_test_level[:system][:parallelization] %> + <% end %> + <% end %> YAML end @@ -65,7 +72,8 @@ "spec/controllers/a_spec.rb": 60.2, "spec/controllers/ab_spec.rb": 180.4, "spec/features/a_spec.rb": 360.1, - "spec/features/b_spec.rb": 180.5 + "spec/features/b_spec.rb": 180.5, + "ee/spec/features/a_spec.rb": 180.5 } JSON end @@ -177,6 +185,53 @@ end end + context 'when test_suite_prefix is given' do + subject do + described_class.new( + rspec_files_path: rspec_files.path, + pipeline_template_path: pipeline_template.path, + knapsack_report_path: knapsack_report.path, + test_suite_prefix: 'ee/' + ) + end + + it 'generates the pipeline config based on the test_suite_prefix' do + subject.generate! + + expect(File.read("#{pipeline_template.path}.yml")) + .to eq("rspec-ee system:") + end + end + + context 'when generated_pipeline_path is given' do + let(:custom_pipeline_filename) { Tempfile.new(['custom_pipeline_filename', '.yml']) } + + around do |example| + example.run + ensure + custom_pipeline_filename.close + custom_pipeline_filename.unlink + end + + subject do + described_class.new( + rspec_files_path: rspec_files.path, + pipeline_template_path: pipeline_template.path, + generated_pipeline_path: custom_pipeline_filename.path + ) + end + + it 'writes the pipeline config in the given generated_pipeline_path' do + subject.generate! + + expect(File.read(custom_pipeline_filename.path)) + .to eq( + "rspec migration:\nrspec background_migration:\nrspec unit:\n" \ + "rspec integration:\nrspec system:" + ) + end + end + context 'when rspec_files does not exist' do subject { described_class.new(rspec_files_path: nil, pipeline_template_path: pipeline_template.path) } diff --git a/spec/tooling/quality/test_level_spec.rb b/spec/tooling/quality/test_level_spec.rb index aac7d19c0795b73baa4b2990e7ee7dbf9b80a891..a7e4e42206a25c8ec197c2e273e13501db5985e7 100644 --- a/spec/tooling/quality/test_level_spec.rb +++ b/spec/tooling/quality/test_level_spec.rb @@ -46,7 +46,7 @@ context 'when level is unit' do it 'returns a pattern' do expect(subject.pattern(:unit)) - .to eq("spec/{bin,channels,config,contracts,db,dependencies,elastic,elastic_integration,experiments,factories,finders,frontend,graphql,haml_lint,helpers,initializers,lib,metrics_server,models,policies,presenters,rack_servers,replicators,routing,rubocop,scripts,serializers,services,sidekiq,sidekiq_cluster,spam,support_specs,tasks,uploaders,validators,views,workers,tooling,components}{,/**/}*_spec.rb") + .to eq("spec/{bin,channels,components,config,contracts,db,dependencies,elastic,elastic_integration,experiments,factories,finders,frontend,graphql,haml_lint,helpers,initializers,lib,metrics_server,models,policies,presenters,rack_servers,replicators,routing,rubocop,scripts,serializers,services,sidekiq,sidekiq_cluster,spam,support_specs,tasks,uploaders,validators,views,workers,tooling}{,/**/}*_spec.rb") end end @@ -121,7 +121,7 @@ context 'when level is unit' do it 'returns a regexp' do expect(subject.regexp(:unit)) - .to eq(%r{spec/(bin|channels|config|contracts|db|dependencies|elastic|elastic_integration|experiments|factories|finders|frontend|graphql|haml_lint|helpers|initializers|lib|metrics_server|models|policies|presenters|rack_servers|replicators|routing|rubocop|scripts|serializers|services|sidekiq|sidekiq_cluster|spam|support_specs|tasks|uploaders|validators|views|workers|tooling|components)/}) + .to eq(%r{spec/(bin|channels|components|config|contracts|db|dependencies|elastic|elastic_integration|experiments|factories|finders|frontend|graphql|haml_lint|helpers|initializers|lib|metrics_server|models|policies|presenters|rack_servers|replicators|routing|rubocop|scripts|serializers|services|sidekiq|sidekiq_cluster|spam|support_specs|tasks|uploaders|validators|views|workers|tooling)/}) end end @@ -167,6 +167,13 @@ end end + context 'when start_with == true' do + it 'returns a regexp' do + expect(described_class.new(['ee/']).regexp(:system, true)) + .to eq(%r{^(ee/)spec/(features)/}) + end + end + describe 'performance' do it 'memoizes the regexp for a given level' do expect(subject.regexp(:system).object_id).to eq(subject.regexp(:system).object_id) diff --git a/tooling/quality/test_level.rb b/tooling/quality/test_level.rb index eeda135f3eec980972f3d2651aaf4dad95aa8ba1..20e00763f65438fb3a167c5abe2618ead8baa5fb 100644 --- a/tooling/quality/test_level.rb +++ b/tooling/quality/test_level.rb @@ -18,6 +18,7 @@ class TestLevel unit: %w[ bin channels + components config contracts db @@ -54,7 +55,6 @@ class TestLevel views workers tooling - components ], integration: %w[ commands @@ -77,8 +77,8 @@ def pattern(level) @patterns[level] ||= "#{prefixes_for_pattern}spec/#{folders_pattern(level)}{,/**/}*#{suffix(level)}".freeze # rubocop:disable Style/RedundantFreeze end - def regexp(level) - @regexps[level] ||= Regexp.new("#{prefixes_for_regex}spec/#{folders_regex(level)}").freeze + def regexp(level, start_with = false) + @regexps[level] ||= Regexp.new("#{'^' if start_with}#{prefixes_for_regex}spec/#{folders_regex(level)}").freeze end def level_for(file_path)