diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-03-01 15:10:00 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-03-01 15:10:00 +0000 |
commit | 8b1036168b0d395c379cbbaf457e256860147405 (patch) | |
tree | cd9f06daf5ef1f0f24137540bb1382d3e26c0bb6 | |
parent | 52522f10237f685c2535e6511d632bacdc7f6a78 (diff) | |
download | gitlab-ce-8b1036168b0d395c379cbbaf457e256860147405.tar.gz |
Add latest changes from gitlab-org/gitlab@master
70 files changed, 606 insertions, 183 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3b22e2fc21e..29e51b25200 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -12,6 +12,7 @@ stages: - post-qa - pages - notify + - release-environments # always use `gitlab-org` runners, however # in cases where jobs require Docker-in-Docker, the job @@ -32,6 +33,8 @@ default: .ruby2-variables: &ruby2-variables RUBY_VERSION: "2.7" + OMNIBUS_GITLAB_RUBY2_BUILD: "true" + OMNIBUS_GITLAB_CACHE_EDITION: "GITLAB_RUBY2" .default-branch-incident-variables: &default-branch-incident-variables CREATE_INCIDENT_FOR_PIPELINE_FAILURE: "true" diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS index 67bd749d3e0..53933fe3ee0 100644 --- a/.gitlab/CODEOWNERS +++ b/.gitlab/CODEOWNERS @@ -798,7 +798,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab /doc/integration/jenkins.md @ashrafkhamis /doc/integration/jira/ @ashrafkhamis /doc/integration/mattermost/ @axil -/doc/integration/partner_marketplace.md @drcatherinepope +/doc/integration/partner_marketplace.md @fneill /doc/integration/recaptcha.md @phillipwells /doc/integration/security_partners/ @rdickenson /doc/integration/slash_commands.md @ashrafkhamis diff --git a/.gitlab/ci/package-and-test/main.gitlab-ci.yml b/.gitlab/ci/package-and-test/main.gitlab-ci.yml index 301a2ddb5f1..9a8acbb3af0 100644 --- a/.gitlab/ci/package-and-test/main.gitlab-ci.yml +++ b/.gitlab/ci/package-and-test/main.gitlab-ci.yml @@ -122,6 +122,7 @@ trigger-omnibus-env: echo "OMNIBUS_GITLAB_CACHE_UPDATE=${OMNIBUS_GITLAB_CACHE_UPDATE:-false}" >> $BUILD_ENV for version_file in *_VERSION; do echo "$version_file=$(cat $version_file)" >> $BUILD_ENV; done echo "OMNIBUS_GITLAB_RUBY3_BUILD=${OMNIBUS_GITLAB_RUBY3_BUILD:-false}" >> $BUILD_ENV + echo "OMNIBUS_GITLAB_RUBY2_BUILD=${OMNIBUS_GITLAB_RUBY2_BUILD:-false}" >> $BUILD_ENV echo "OMNIBUS_GITLAB_CACHE_EDITION=${OMNIBUS_GITLAB_CACHE_EDITION:-GITLAB}" >> $BUILD_ENV echo "GITLAB_ASSETS_TAG=$(assets_image_tag)" >> $BUILD_ENV echo "Built environment file for omnibus build:" @@ -152,6 +153,7 @@ trigger-omnibus: SECURITY_SOURCES: $SECURITY_SOURCES CACHE_UPDATE: $OMNIBUS_GITLAB_CACHE_UPDATE RUBY3_BUILD: $OMNIBUS_GITLAB_RUBY3_BUILD + RUBY2_BUILD: $OMNIBUS_GITLAB_RUBY2_BUILD CACHE_EDITION: $OMNIBUS_GITLAB_CACHE_EDITION SKIP_QA_DOCKER: "true" SKIP_QA_TEST: "true" diff --git a/.gitlab/ci/package-and-test/variables.gitlab-ci.yml b/.gitlab/ci/package-and-test/variables.gitlab-ci.yml index c45807e5a23..1ba046308a8 100644 --- a/.gitlab/ci/package-and-test/variables.gitlab-ci.yml +++ b/.gitlab/ci/package-and-test/variables.gitlab-ci.yml @@ -6,6 +6,7 @@ variables: SKIP_REPORT_IN_ISSUES: "true" OMNIBUS_GITLAB_CACHE_UPDATE: "false" OMNIBUS_GITLAB_RUBY3_BUILD: "false" + OMNIBUS_GITLAB_RUBY2_BUILD: "false" OMNIBUS_GITLAB_CACHE_EDITION: "GITLAB" QA_LOG_LEVEL: "info" QA_TESTS: "" diff --git a/.gitlab/ci/qa.gitlab-ci.yml b/.gitlab/ci/qa.gitlab-ci.yml index a72e6fc0137..aea85dfd084 100644 --- a/.gitlab/ci/qa.gitlab-ci.yml +++ b/.gitlab/ci/qa.gitlab-ci.yml @@ -89,6 +89,9 @@ e2e:package-and-test: - DOCKER_VERSION - REGISTRY_GROUP - REGISTRY_HOST + - OMNIBUS_GITLAB_CACHE_EDITION + - OMNIBUS_GITLAB_RUBY3_BUILD + - OMNIBUS_GITLAB_RUBY2_BUILD trigger: strategy: depend forward: diff --git a/.gitlab/ci/release-environments.gitlab-ci.yml b/.gitlab/ci/release-environments.gitlab-ci.yml new file mode 100644 index 00000000000..a9d9c938ee0 --- /dev/null +++ b/.gitlab/ci/release-environments.gitlab-ci.yml @@ -0,0 +1,22 @@ +--- +start-release-environments-pipeline: + allow_failure: true + extends: + - .release-environments:rules:start-release-environments-pipeline + stage: release-environments + # We do not want to have ALL global variables passed as trigger variables, + # as they cannot be overridden. See this issue for more context: + # + # https://gitlab.com/gitlab-org/gitlab/-/issues/387183 + inherit: + variables: false + + # These variables are set in the pipeline schedules. + # They need to be explicitly passed on to the child pipeline. + # https://docs.gitlab.com/ee/ci/pipelines/multi_project_pipelines.html#pass-cicd-variables-to-a-downstream-pipeline-by-using-the-variables-keyword + variables: + # This is needed by `release-environments-build-cng-env` (`.gitlab/ci/release-environments/main.gitlab-ci.yml`). + PARENT_PIPELINE_ID: $CI_PIPELINE_ID + trigger: + strategy: depend + include: .gitlab/ci/release-environments/main.gitlab-ci.yml diff --git a/.gitlab/ci/release-environments/main.gitlab-ci.yml b/.gitlab/ci/release-environments/main.gitlab-ci.yml new file mode 100644 index 00000000000..e2fed0a6dbd --- /dev/null +++ b/.gitlab/ci/release-environments/main.gitlab-ci.yml @@ -0,0 +1,62 @@ +--- +default: + interruptible: true + +stages: + - prepare + +include: + - local: .gitlab/ci/global.gitlab-ci.yml + +release-environments-build-cng-env: + allow_failure: true + image: ${GITLAB_DEPENDENCY_PROXY_ADDRESS}ruby:${RUBY_VERSION}-alpine3.16 + stage: prepare + needs: + # We need this job because we need its `cached-assets-hash.txt` artifact, so that we can pass the assets image tag to the downstream CNG pipeline. + - pipeline: $PARENT_PIPELINE_ID + job: build-assets-image + variables: + BUILD_ENV: build.env + before_script: + - source ./scripts/utils.sh + - install_gitlab_gem + script: + - 'ruby -r./scripts/trigger-build.rb -e "puts Trigger.variables_for_env_file(Trigger::CNG.new.variables)" > $BUILD_ENV' + - echo "GITLAB_ASSETS_TAG=$(assets_image_tag)" >> $BUILD_ENV + - ruby -e 'puts "FULL_RUBY_VERSION=#{RUBY_VERSION}"' >> build.env + - cat $BUILD_ENV + artifacts: + reports: + dotenv: $BUILD_ENV + paths: + - $BUILD_ENV + expire_in: 7 days + when: always + +release-environments-build-cng: + allow_failure: true + stage: prepare + needs: ["release-environments-build-cng-env"] + inherit: + variables: false + variables: + GITLAB_REF_SLUG: "${GITLAB_REF_SLUG}" + # CNG pipeline specific variables + GITLAB_VERSION: "${GITLAB_VERSION}" + GITLAB_TAG: "${GITLAB_TAG}" + GITLAB_ASSETS_TAG: "${GITLAB_ASSETS_TAG}" + FORCE_RAILS_IMAGE_BUILDS: "${FORCE_RAILS_IMAGE_BUILDS}" + CE_PIPELINE: "${CE_PIPELINE}" # Based on https://docs.gitlab.com/ee/ci/jobs/job_control.html#check-if-a-variable-exists, `if: '$CE_PIPELINE'` will evaluate to `false` when this variable is empty + EE_PIPELINE: "${EE_PIPELINE}" # Based on https://docs.gitlab.com/ee/ci/jobs/job_control.html#check-if-a-variable-exists, `if: '$EE_PIPELINE'` will evaluate to `false` when this variable is empty + GITLAB_ELASTICSEARCH_INDEXER_VERSION: "${GITLAB_ELASTICSEARCH_INDEXER_VERSION}" + GITLAB_KAS_VERSION: "${GITLAB_KAS_VERSION}" + GITLAB_METRICS_EXPORTER_VERSION: "${GITLAB_METRICS_EXPORTER_VERSION}" + GITLAB_PAGES_VERSION: "${GITLAB_PAGES_VERSION}" + GITLAB_SHELL_VERSION: "${GITLAB_SHELL_VERSION}" + GITALY_SERVER_VERSION: "${GITALY_SERVER_VERSION}" + RUBY_VERSION: "${FULL_RUBY_VERSION}" + trigger: + project: gitlab-org/build/CNG-mirror + branch: $TRIGGER_BRANCH + strategy: depend diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index 894dafca8c3..066654565b2 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -1905,6 +1905,13 @@ when: never - if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_PATH == "gitlab-org/gitlab" && $CI_COMMIT_REF_NAME =~ /^[\d-]+-stable-ee$/' +.releases:rules:canonical-dot-com-gitlab-stable-branch-only-setup-test-env-patterns: + rules: + - if: '$CI_COMMIT_MESSAGE =~ /\[merge-train skip\]/' + when: never + - if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_PATH == "gitlab-org/gitlab" && $CI_COMMIT_REF_NAME =~ /^[\d-]+-stable-ee$/' + changes: *setup-test-env-patterns + .releases:rules:canonical-dot-com-security-gitlab-stable-branch-only: rules: - if: '$CI_COMMIT_MESSAGE =~ /\[merge-train skip\]/' @@ -2299,3 +2306,14 @@ - <<: *if-dot-com-gitlab-org-merge-request changes: *feature-flag-development-config-patterns allow_failure: true # See https://gitlab.com/gitlab-org/gitlab/-/issues/351136 + +############################## +# release-environments rules # +############################## +.release-environments:rules:start-release-environments-pipeline: + rules: + - <<: *if-not-ee + when: never + - <<: *if-merge-request-labels-pipeline-expedite + when: never + - !reference [".releases:rules:canonical-dot-com-gitlab-stable-branch-only-setup-test-env-patterns", rules] diff --git a/app/assets/javascripts/flash.js b/app/assets/javascripts/alert.js index 483f1d2c7a0..006c4f50d09 100644 --- a/app/assets/javascripts/flash.js +++ b/app/assets/javascripts/alert.js @@ -15,7 +15,7 @@ export const VARIANT_TIP = 'tip'; * * @example * // Render a new alert - * import { createAlert, VARIANT_WARNING } from '~/flash'; + * import { createAlert, VARIANT_WARNING } from '~/alert'; * * createAlert({ message: 'My error message' }); * createAlert({ message: 'My warning message', variant: VARIANT_WARNING }); diff --git a/app/assets/javascripts/service_ping_consent.js b/app/assets/javascripts/service_ping_consent.js index 654263ba27b..1cb4e188e54 100644 --- a/app/assets/javascripts/service_ping_consent.js +++ b/app/assets/javascripts/service_ping_consent.js @@ -1,5 +1,5 @@ import $ from 'jquery'; -import { createAlert } from './flash'; +import { createAlert } from '~/flash'; import axios from './lib/utils/axios_utils'; import { parseBoolean } from './lib/utils/common_utils'; import { __ } from './locale'; diff --git a/app/assets/javascripts/super_sidebar/components/nav_item.vue b/app/assets/javascripts/super_sidebar/components/nav_item.vue index 100f1d18793..173e62cefbd 100644 --- a/app/assets/javascripts/super_sidebar/components/nav_item.vue +++ b/app/assets/javascripts/super_sidebar/components/nav_item.vue @@ -1,11 +1,12 @@ <script> -import { GlCollapse, GlIcon } from '@gitlab/ui'; +import { GlCollapse, GlIcon, GlBadge } from '@gitlab/ui'; export default { name: 'NavItem', components: { GlCollapse, GlIcon, + GlBadge, }, props: { item: { @@ -25,6 +26,15 @@ export default { isSection() { return Boolean(this.item?.items?.length); }, + pillData() { + return this.item.pill_count; + }, + hasPill() { + return ( + Number.isFinite(this.pillData) || + (typeof this.pillData === 'string' && this.pillData !== '') + ); + }, isActive() { if (this.isSection) { return !this.expanded && this.item.is_active; @@ -84,8 +94,11 @@ export default { {{ item.subtitle }} </div> </div> - <span v-if="isSection" class="gl-flex-grow-1 gl-text-right gl-mr-3"> - <gl-icon :name="collapseIcon" /> + <span v-if="isSection || hasPill" class="gl-flex-grow-1 gl-text-right gl-mr-3"> + <gl-badge v-if="hasPill" size="sm" variant="info"> + {{ pillData }} + </gl-badge> + <gl-icon v-else-if="isSection" :name="collapseIcon" /> </span> </a> <gl-collapse v-if="isSection" :id="item.title" v-model="expanded"> diff --git a/app/assets/javascripts/super_sidebar/components/user_bar.vue b/app/assets/javascripts/super_sidebar/components/user_bar.vue index a03d9a65e5f..82db6a2e351 100644 --- a/app/assets/javascripts/super_sidebar/components/user_bar.vue +++ b/app/assets/javascripts/super_sidebar/components/user_bar.vue @@ -1,5 +1,5 @@ <script> -import { GlButton, GlTooltipDirective } from '@gitlab/ui'; +import { GlBadge, GlButton, GlTooltipDirective } from '@gitlab/ui'; import { __ } from '~/locale'; import SafeHtml from '~/vue_shared/directives/safe_html'; import logo from '../../../../views/shared/_logo.svg'; @@ -10,10 +10,14 @@ import MergeRequestMenu from './merge_request_menu.vue'; import UserMenu from './user_menu.vue'; export default { + // "GitLab Next" is a proper noun, so don't translate "Next" + /* eslint-disable-next-line @gitlab/require-i18n-strings */ + NEXT_LABEL: 'Next', logo, components: { Counter, CreateMenu, + GlBadge, GlButton, MergeRequestMenu, UserMenu, @@ -48,9 +52,15 @@ export default { <template> <div class="user-bar"> <div class="gl-display-flex gl-align-items-center gl-px-3 gl-py-2 gl-gap-2"> - <div class="gl-flex-grow-1"> - <a v-safe-html="$options.logo" :href="rootPath"></a> - </div> + <a v-safe-html="$options.logo" :href="rootPath"></a> + <gl-badge + v-if="sidebarData.gitlab_com_and_canary" + variant="success" + :href="sidebarData.canary_toggle_com_url" + size="sm" + >{{ $options.NEXT_LABEL }}</gl-badge + > + <div class="gl-flex-grow-1"></div> <gl-button v-gl-tooltip:super-sidebar.hover.bottom="$options.i18n.collapseSidebar" :aria-label="$options.i18n.collapseSidebar" diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb index b4a36b7db22..a073f9b5db7 100644 --- a/app/controllers/concerns/authenticates_with_two_factor.rb +++ b/app/controllers/concerns/authenticates_with_two_factor.rb @@ -136,8 +136,6 @@ module AuthenticatesWithTwoFactor get_options = WebAuthn::Credential.options_for_get(allow: webauthn_registration_ids, user_verification: 'discouraged', extensions: { appid: WebAuthn.configuration.origin }) - - session[:credentialRequestOptions] = get_options session[:challenge] = get_options.challenge gon.push(webauthn: { options: Gitlab::Json.dump(get_options) }) end diff --git a/app/helpers/sidebars_helper.rb b/app/helpers/sidebars_helper.rb index b6901d011ab..4f5f01efcc4 100644 --- a/app/helpers/sidebars_helper.rb +++ b/app/helpers/sidebars_helper.rb @@ -77,6 +77,7 @@ module SidebarsHelper gitlab_version: Gitlab.version_info, gitlab_version_check: gitlab_version_check, gitlab_com_but_not_canary: Gitlab.com_but_not_canary?, + gitlab_com_and_canary: Gitlab.com_and_canary?, canary_toggle_com_url: Gitlab::Saas.canary_toggle_com_url } end diff --git a/config/feature_flags/development/ci_batch_project_includes_context.yml b/config/feature_flags/development/ci_batch_project_includes_context.yml new file mode 100644 index 00000000000..634ed19bf34 --- /dev/null +++ b/config/feature_flags/development/ci_batch_project_includes_context.yml @@ -0,0 +1,8 @@ +--- +name: ci_batch_project_includes_context +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/112570 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/392746 +milestone: '15.10' +type: development +group: group::pipeline authoring +default_enabled: false diff --git a/config/feature_flags/development/use_traversal_ids_for_ancestor_scopes.yml b/config/feature_flags/development/use_traversal_ids_for_ancestor_scopes.yml index e2619ef5231..d0f38c20ed0 100644 --- a/config/feature_flags/development/use_traversal_ids_for_ancestor_scopes.yml +++ b/config/feature_flags/development/use_traversal_ids_for_ancestor_scopes.yml @@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/340159 milestone: '14.3' type: development group: group::workspace -default_enabled: false +default_enabled: true diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index bcd051e8ace..b37fb7a877a 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -851,7 +851,7 @@ end # Settings['sidekiq'] ||= Settingslogic.new({}) Settings['sidekiq']['log_format'] ||= 'default' -Settings['sidekiq']['routing_rules'] ||= [] +Settings['sidekiq']['routing_rules'] = Settings.build_sidekiq_routing_rules(Settings['sidekiq']['routing_rules']) # # GitLab Shell diff --git a/config/settings.rb b/config/settings.rb index ae95af802c4..a76889f34ae 100644 --- a/config/settings.rb +++ b/config/settings.rb @@ -172,6 +172,13 @@ class Settings < Settingslogic cron_jobs['gitlab_service_ping_worker']['cron'] ||= cron_for_service_ping end + # Route jobs to queue based on worker name. + def build_sidekiq_routing_rules(rules) + return rules unless rules.nil? || rules&.empty? + + [[Gitlab::SidekiqConfig::WorkerMatcher::WILDCARD_MATCH, nil]] + end + private def base_url(config) diff --git a/config/webpack.config.js b/config/webpack.config.js index 8ae803ca738..550a3a62e24 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -180,6 +180,9 @@ const alias = { ROOT_PATH, 'app/assets/javascripts/sentry/sentry_browser_wrapper.js', ), + // temporary alias until we replace all `flash` imports for `alert` + // https://gitlab.com/gitlab-org/gitlab/-/merge_requests/109449 + '~/flash': path.join(ROOT_PATH, 'app/assets/javascripts/alert.js'), '~': path.join(ROOT_PATH, 'app/assets/javascripts'), emojis: path.join(ROOT_PATH, 'fixtures/emojis'), empty_states: path.join(ROOT_PATH, 'app/views/shared/empty_states'), diff --git a/db/post_migrate/20230227123949_validate_fk_on_ci_sources_pipelines_source_partition_id_and_source_job_id.rb b/db/post_migrate/20230227123949_validate_fk_on_ci_sources_pipelines_source_partition_id_and_source_job_id.rb new file mode 100644 index 00000000000..630483ee1f8 --- /dev/null +++ b/db/post_migrate/20230227123949_validate_fk_on_ci_sources_pipelines_source_partition_id_and_source_job_id.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class ValidateFkOnCiSourcesPipelinesSourcePartitionIdAndSourceJobId < Gitlab::Database::Migration[2.1] + TABLE_NAME = :ci_sources_pipelines + FK_NAME = :fk_be5624bf37_p + COLUMNS = [:source_partition_id, :source_job_id] + + def up + validate_foreign_key(TABLE_NAME, COLUMNS, name: FK_NAME) + end + + def down + # no-op + end +end diff --git a/db/post_migrate/20230227123950_remove_fk_to_ci_builds_ci_sources_pipelines_on_source_job_id.rb b/db/post_migrate/20230227123950_remove_fk_to_ci_builds_ci_sources_pipelines_on_source_job_id.rb new file mode 100644 index 00000000000..17ae2ad1325 --- /dev/null +++ b/db/post_migrate/20230227123950_remove_fk_to_ci_builds_ci_sources_pipelines_on_source_job_id.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +class RemoveFkToCiBuildsCiSourcesPipelinesOnSourceJobId < Gitlab::Database::Migration[2.1] + disable_ddl_transaction! + + SOURCE_TABLE_NAME = :ci_sources_pipelines + TARGET_TABLE_NAME = :ci_builds + COLUMN = :source_job_id + TARGET_COLUMN = :id + FK_NAME = :fk_be5624bf37 + + def up + with_lock_retries do + remove_foreign_key_if_exists( + SOURCE_TABLE_NAME, + TARGET_TABLE_NAME, + name: FK_NAME, + reverse_lock_order: true + ) + end + end + + def down + add_concurrent_foreign_key( + SOURCE_TABLE_NAME, + TARGET_TABLE_NAME, + column: COLUMN, + target_column: TARGET_COLUMN, + validate: true, + reverse_lock_order: true, + on_delete: :cascade, + name: FK_NAME + ) + end +end diff --git a/db/schema_migrations/20230227123949 b/db/schema_migrations/20230227123949 new file mode 100644 index 00000000000..ab216ad946c --- /dev/null +++ b/db/schema_migrations/20230227123949 @@ -0,0 +1 @@ +cf72b9c6cd86bf0fbb0599f16bfcfd360567a8cdf30275ba59c1aeaba8317f2a
\ No newline at end of file diff --git a/db/schema_migrations/20230227123950 b/db/schema_migrations/20230227123950 new file mode 100644 index 00000000000..e2b8672fb3f --- /dev/null +++ b/db/schema_migrations/20230227123950 @@ -0,0 +1 @@ +d7f195e2cb4ab9f7f4637ba7667605eea02e66ea417b4ae496a1acae9931be84
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 3521a31e04d..3407e31ed2b 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -34661,10 +34661,7 @@ ALTER TABLE ONLY snippets ADD CONSTRAINT fk_be41fd4bb7 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; ALTER TABLE ONLY ci_sources_pipelines - ADD CONSTRAINT fk_be5624bf37 FOREIGN KEY (source_job_id) REFERENCES ci_builds(id) ON DELETE CASCADE; - -ALTER TABLE ONLY ci_sources_pipelines - ADD CONSTRAINT fk_be5624bf37_p FOREIGN KEY (source_partition_id, source_job_id) REFERENCES ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE NOT VALID; + ADD CONSTRAINT fk_be5624bf37_p FOREIGN KEY (source_partition_id, source_job_id) REFERENCES ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE; ALTER TABLE ONLY packages_maven_metadata ADD CONSTRAINT fk_be88aed360 FOREIGN KEY (package_id) REFERENCES packages_packages(id) ON DELETE CASCADE; diff --git a/doc/user/project/repository/tags/index.md b/doc/user/project/repository/tags/index.md index 3d340789c2c..869012c64cc 100644 --- a/doc/user/project/repository/tags/index.md +++ b/doc/user/project/repository/tags/index.md @@ -57,6 +57,7 @@ git push origin --tags ## Related topics -- [Tagging](https://git-scm.com/book/en/v2/Git-Basics-Tagging) Git reference page -- [Protected tags](../../protected_tags.md) -- [Tags API](../../../../api/tags.md) +- [Tagging](https://git-scm.com/book/en/v2/Git-Basics-Tagging) Git reference page. +- [Protected tags](../../protected_tags.md). +- [Tags API](../../../../api/tags.md). +- [Use `if: $CI_COMMIT_TAG` to run CI/CD pipelines for tags](../../../../ci/jobs/job_control.md#common-if-clauses-for-rules). diff --git a/jest.config.base.js b/jest.config.base.js index 26e7c8e8d18..34134d6983b 100644 --- a/jest.config.base.js +++ b/jest.config.base.js @@ -44,6 +44,9 @@ module.exports = (path, options = {}) => { const TEST_FIXTURES_PATTERN = 'test_fixtures(/.*)$'; const moduleNameMapper = { + // temporary alias until we replace all `flash` imports for `alert` + // https://gitlab.com/gitlab-org/gitlab/-/merge_requests/109449 + '^~/flash$': '<rootDir>/app/assets/javascripts/alert', '^~(/.*)\\?(worker|raw)$': '<rootDir>/app/assets/javascripts$1', '^(.*)\\?(worker|raw)$': '$1', '^~(/.*)$': '<rootDir>/app/assets/javascripts$1', diff --git a/lib/gitlab/ci/config/external/file/base.rb b/lib/gitlab/ci/config/external/file/base.rb index 84f34f2584b..51ca3ea0683 100644 --- a/lib/gitlab/ci/config/external/file/base.rb +++ b/lib/gitlab/ci/config/external/file/base.rb @@ -73,6 +73,18 @@ module Gitlab validate_hash! end + # This method is overridden to load context into the memoized result + # or to lazily load context via BatchLoader + def preload_context + # no-op + end + + def preload_content + # calling the `content` method either loads content into the memoized result + # or lazily loads it via BatchLoader + content + end + def validate_location! if invalid_location_type? errors.push("Included file `#{masked_location}` needs to be a string") diff --git a/lib/gitlab/ci/config/external/file/project.rb b/lib/gitlab/ci/config/external/file/project.rb index f8d4cb27710..a53e6aa729f 100644 --- a/lib/gitlab/ci/config/external/file/project.rb +++ b/lib/gitlab/ci/config/external/file/project.rb @@ -15,7 +15,8 @@ module Gitlab # `Repository#blobs_at` does not support files with the `/` prefix. @location = Gitlab::Utils.remove_leading_slashes(params[:file]) - @project_name = get_project_name(params[:project]) + # We are using the same downcase in the `project` method. + @project_name = get_project_name(params[:project]).to_s.downcase @ref_name = params[:ref] || 'HEAD' super @@ -39,6 +40,15 @@ module Gitlab ) end + def preload_context + # + # calling these methods lazily loads them via BatchLoader + # + project + can_access_local_content? + sha + end + def validate_context! if !can_access_local_content? errors.push("Project `#{masked_project_name}` not found or access denied! Make sure any includes in the pipeline configuration are correctly defined.") @@ -58,21 +68,45 @@ module Gitlab private def project - strong_memoize(:project) do - ::Project.find_by_full_path(project_name) + return legacy_project if ::Feature.disabled?(:ci_batch_project_includes_context, context.project) + + BatchLoader.for(project_name).batch do |project_names, loader| + ::Project.where_full_path_in(project_names.uniq).each do |project| + # We are using the same downcase in the `initialize` method. + loader.call(project.full_path.downcase, project) + end end end def can_access_local_content? - strong_memoize(:can_access_local_content) do - context.logger.instrument(:config_file_project_validate_access) do - Ability.allowed?(context.user, :download_code, project) + if ::Feature.disabled?(:ci_batch_project_includes_context, context.project) + return legacy_can_access_local_content? + end + + context.logger.instrument(:config_file_project_validate_access) do + BatchLoader.for(project_name) + .batch(key: context.user) do |project_names, loader, args| + project_names.uniq.each do |project_name| + context.logger.instrument(:config_file_project_validate_access) do + loader.call(project_name, Ability.allowed?(args[:key], :download_code, project)) + end + end + end + end + end + + def sha + return legacy_sha if ::Feature.disabled?(:ci_batch_project_includes_context, context.project) + + BatchLoader.for([project_name, ref_name]).batch do |project_name_ref_pairs, loader| + project_name_ref_pairs.uniq.each do |project_name, ref_name| + loader.call([project_name, ref_name], project.commit(ref_name).try(:sha)) end end end def fetch_local_content - BatchLoader.for([sha, location]) + BatchLoader.for([sha.to_s, location]) .batch(key: project) do |locations, loader, args| context.logger.instrument(:config_file_fetch_project_content) do args[:key].repository.blobs_at(locations).each do |blob| @@ -84,8 +118,22 @@ module Gitlab end end - def sha - strong_memoize(:sha) do + def legacy_project + strong_memoize(:legacy_project) do + ::Project.find_by_full_path(project_name) + end + end + + def legacy_can_access_local_content? + strong_memoize(:legacy_can_access_local_content) do + context.logger.instrument(:config_file_project_validate_access) do + Ability.allowed?(context.user, :download_code, project) + end + end + end + + def legacy_sha + strong_memoize(:legacy_sha) do project.commit(ref_name).try(:sha) end end @@ -94,7 +142,7 @@ module Gitlab def expand_context_attrs { project: project, - sha: sha, + sha: sha.to_s, # we need to use `.to_s` to load the value from the BatchLoader user: context.user, parent_pipeline: context.parent_pipeline, variables: context.variables diff --git a/lib/gitlab/ci/config/external/mapper/verifier.rb b/lib/gitlab/ci/config/external/mapper/verifier.rb index 36cf091a532..6c2e52958da 100644 --- a/lib/gitlab/ci/config/external/mapper/verifier.rb +++ b/lib/gitlab/ci/config/external/mapper/verifier.rb @@ -10,6 +10,39 @@ module Gitlab private def process_without_instrumentation(files) + if ::Feature.disabled?(:ci_batch_project_includes_context, context.project) + return legacy_process_without_instrumentation(files) + end + + files.each do |file| + verify_execution_time! + + file.validate_location! + file.preload_context if file.valid? + end + + # We do not combine the loops because we need to load the context of all files via `BatchLoader`. + files.each do |file| # rubocop:disable Style/CombinableLoops + verify_execution_time! + + file.validate_context! if file.valid? + file.preload_content if file.valid? + end + + # We do not combine the loops because we need to load the content of all files via `BatchLoader`. + files.each do |file| # rubocop:disable Style/CombinableLoops + # Checking the max includes will be changed with https://gitlab.com/gitlab-org/gitlab/-/issues/367150 + verify_max_includes! + verify_execution_time! + + file.validate_content! if file.valid? + file.load_and_validate_expanded_hash! if file.valid? + + context.expandset << file + end + end + + def legacy_process_without_instrumentation(files) files.each do |file| verify_execution_time! diff --git a/lib/gitlab/ci/reports/security/vulnerability_reports_comparer.rb b/lib/gitlab/ci/reports/security/vulnerability_reports_comparer.rb index 4be4cf62e7b..adc666d9987 100644 --- a/lib/gitlab/ci/reports/security/vulnerability_reports_comparer.rb +++ b/lib/gitlab/ci/reports/security/vulnerability_reports_comparer.rb @@ -10,6 +10,7 @@ module Gitlab attr_reader :base_report, :head_report ACCEPTABLE_REPORT_AGE = 1.week + MAX_FINDINGS_COUNT = 25 def initialize(project, base_report, head_report) @base_report = base_report @@ -40,21 +41,13 @@ module Gitlab def added strong_memoize(:added) do - if @signatures_enabled - @added_findings - else - head_report.findings - base_report.findings - end + all_added_findings.take(MAX_FINDINGS_COUNT) # rubocop:disable CodeReuse/ActiveRecord (This is Array#take) end end def fixed strong_memoize(:fixed) do - if @signatures_enabled - @fixed_findings - else - base_report.findings - head_report.findings - end + all_fixed_findings.take(MAX_FINDINGS_COUNT) # rubocop:disable CodeReuse/ActiveRecord (This is Array#take) end end @@ -89,6 +82,22 @@ module Gitlab @added_findings = matcher.unmatched_head_findings.values end + + def all_added_findings + if @signatures_enabled + @added_findings + else + head_report.findings - base_report.findings + end + end + + def all_fixed_findings + if @signatures_enabled + @fixed_findings + else + base_report.findings - head_report.findings + end + end end class FindingMatcher diff --git a/lib/sidebars/menu.rb b/lib/sidebars/menu.rb index defc9848bf9..4af385030a8 100644 --- a/lib/sidebars/menu.rb +++ b/lib/sidebars/menu.rb @@ -78,6 +78,7 @@ module Sidebars icon: sprite_icon, link: link, is_active: is_active, + pill_count: has_pill? ? pill_count : nil, items: items } end diff --git a/lib/sidebars/menu_item.rb b/lib/sidebars/menu_item.rb index 0e50c704695..becff240034 100644 --- a/lib/sidebars/menu_item.rb +++ b/lib/sidebars/menu_item.rb @@ -37,7 +37,8 @@ module Sidebars title: title, icon: sprite_icon, link: link, - active_routes: active_routes + active_routes: active_routes, + pill_count: has_pill ? pill_count : nil # Check whether support is needed for the following properties, # in order to get feature parity with the HAML renderer # https://gitlab.com/gitlab-org/gitlab/-/issues/391864 @@ -47,9 +48,6 @@ module Sidebars # nav_link_html_options # # item_id - # - # has_pill - # pill_count } end diff --git a/spec/config/settings_spec.rb b/spec/config/settings_spec.rb index d344a0e28e6..b464a4eee8b 100644 --- a/spec/config/settings_spec.rb +++ b/spec/config/settings_spec.rb @@ -198,4 +198,18 @@ RSpec.describe Settings, feature_category: :system_access do end end end + + describe '.build_sidekiq_routing_rules' do + using RSpec::Parameterized::TableSyntax + + where(:input_rules, :result) do + nil | [['*', nil]] + [] | [['*', nil]] + [['name=foobar', 'foobar']] | [['name=foobar', 'foobar']] + end + + with_them do + it { expect(described_class.send(:build_sidekiq_routing_rules, input_rules)).to eq(result) } + end + end end diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb index d262c0a2fcf..bd9e9f1c4cf 100644 --- a/spec/db/schema_spec.rb +++ b/spec/db/schema_spec.rb @@ -49,7 +49,7 @@ RSpec.describe 'Database schema', feature_category: :database do ci_resources: %w[partition_id build_id], ci_runner_projects: %w[runner_id], ci_running_builds: %w[partition_id build_id], - ci_sources_pipelines: %w[partition_id source_partition_id], + ci_sources_pipelines: %w[partition_id source_partition_id source_job_id], ci_stages: %w[partition_id], ci_trigger_requests: %w[commit_id], ci_unit_test_failures: %w[partition_id build_id], diff --git a/spec/frontend/flash_spec.js b/spec/frontend/alert_spec.js index 17d6cea23df..1ae8373016b 100644 --- a/spec/frontend/flash_spec.js +++ b/spec/frontend/alert_spec.js @@ -1,6 +1,6 @@ import * as Sentry from '@sentry/browser'; import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; -import { createAlert, VARIANT_WARNING } from '~/flash'; +import { createAlert, VARIANT_WARNING } from '~/alert'; jest.mock('@sentry/browser'); diff --git a/spec/frontend/clusters_list/store/actions_spec.js b/spec/frontend/clusters_list/store/actions_spec.js index 360fd3b2842..a2e2db3dcc2 100644 --- a/spec/frontend/clusters_list/store/actions_spec.js +++ b/spec/frontend/clusters_list/store/actions_spec.js @@ -11,7 +11,7 @@ import { HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_OK } from '~/lib/utils/http_status import Poll from '~/lib/utils/poll'; import { apiData } from '../mock_data'; -jest.mock('~/flash.js'); +jest.mock('~/flash'); describe('Clusters store actions', () => { let captureException; diff --git a/spec/frontend/contributors/store/actions_spec.js b/spec/frontend/contributors/store/actions_spec.js index b2ebdf2f53c..72b22548aa2 100644 --- a/spec/frontend/contributors/store/actions_spec.js +++ b/spec/frontend/contributors/store/actions_spec.js @@ -6,7 +6,7 @@ import { createAlert } from '~/flash'; import axios from '~/lib/utils/axios_utils'; import { HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_OK } from '~/lib/utils/http_status'; -jest.mock('~/flash.js'); +jest.mock('~/flash'); describe('Contributors store actions', () => { describe('fetchChartData', () => { diff --git a/spec/frontend/deploy_freeze/store/actions_spec.js b/spec/frontend/deploy_freeze/store/actions_spec.js index 9b96ce5d252..cd823e1fc28 100644 --- a/spec/frontend/deploy_freeze/store/actions_spec.js +++ b/spec/frontend/deploy_freeze/store/actions_spec.js @@ -11,7 +11,7 @@ import { freezePeriodsFixture } from '../helpers'; import { timezoneDataFixture } from '../../vue_shared/components/timezone_dropdown/helpers'; jest.mock('~/api.js'); -jest.mock('~/flash.js'); +jest.mock('~/flash'); describe('deploy freeze store actions', () => { const freezePeriodFixture = freezePeriodsFixture[0]; diff --git a/spec/frontend/design_management/pages/index_spec.js b/spec/frontend/design_management/pages/index_spec.js index 76ece922ded..5f147220ef9 100644 --- a/spec/frontend/design_management/pages/index_spec.js +++ b/spec/frontend/design_management/pages/index_spec.js @@ -41,7 +41,7 @@ import { moveDesignMutationResponseWithErrors, } from '../mock_data/apollo_mock'; -jest.mock('~/flash.js'); +jest.mock('~/flash'); const mockPageEl = { classList: { remove: jest.fn(), diff --git a/spec/frontend/design_management/utils/cache_update_spec.js b/spec/frontend/design_management/utils/cache_update_spec.js index 42777adfd58..1c8075fac02 100644 --- a/spec/frontend/design_management/utils/cache_update_spec.js +++ b/spec/frontend/design_management/utils/cache_update_spec.js @@ -13,7 +13,7 @@ import { import { createAlert } from '~/flash'; import design from '../mock_data/design'; -jest.mock('~/flash.js'); +jest.mock('~/flash'); describe('Design Management cache update', () => { const mockErrors = ['code red!']; diff --git a/spec/frontend/error_tracking/store/actions_spec.js b/spec/frontend/error_tracking/store/actions_spec.js index 3ec43010d80..31bfda3ef8b 100644 --- a/spec/frontend/error_tracking/store/actions_spec.js +++ b/spec/frontend/error_tracking/store/actions_spec.js @@ -7,7 +7,7 @@ import axios from '~/lib/utils/axios_utils'; import { HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_OK } from '~/lib/utils/http_status'; import { visitUrl } from '~/lib/utils/url_utility'; -jest.mock('~/flash.js'); +jest.mock('~/flash'); jest.mock('~/lib/utils/url_utility'); let mock; diff --git a/spec/frontend/error_tracking/store/details/actions_spec.js b/spec/frontend/error_tracking/store/details/actions_spec.js index 383d8aaeb20..66c1eb4573b 100644 --- a/spec/frontend/error_tracking/store/details/actions_spec.js +++ b/spec/frontend/error_tracking/store/details/actions_spec.js @@ -14,7 +14,7 @@ import Poll from '~/lib/utils/poll'; let mockedAdapter; let mockedRestart; -jest.mock('~/flash.js'); +jest.mock('~/flash'); jest.mock('~/lib/utils/url_utility'); describe('Sentry error details store actions', () => { diff --git a/spec/frontend/error_tracking/store/list/actions_spec.js b/spec/frontend/error_tracking/store/list/actions_spec.js index 590983bd93d..e6c2d4a149d 100644 --- a/spec/frontend/error_tracking/store/list/actions_spec.js +++ b/spec/frontend/error_tracking/store/list/actions_spec.js @@ -6,7 +6,7 @@ import { createAlert } from '~/flash'; import axios from '~/lib/utils/axios_utils'; import { HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_OK } from '~/lib/utils/http_status'; -jest.mock('~/flash.js'); +jest.mock('~/flash'); describe('error tracking actions', () => { let mock; diff --git a/spec/frontend/merge_conflicts/store/actions_spec.js b/spec/frontend/merge_conflicts/store/actions_spec.js index 19ef4b7db25..a5519331347 100644 --- a/spec/frontend/merge_conflicts/store/actions_spec.js +++ b/spec/frontend/merge_conflicts/store/actions_spec.js @@ -10,7 +10,7 @@ import * as actions from '~/merge_conflicts/store/actions'; import * as types from '~/merge_conflicts/store/mutation_types'; import { restoreFileLinesState, markLine, decorateFiles } from '~/merge_conflicts/utils'; -jest.mock('~/flash.js'); +jest.mock('~/flash'); jest.mock('~/merge_conflicts/utils'); jest.mock('~/lib/utils/cookies'); diff --git a/spec/frontend/packages_and_registries/infrastructure_registry/components/details/store/actions_spec.js b/spec/frontend/packages_and_registries/infrastructure_registry/components/details/store/actions_spec.js index bb970336b94..ae5742fdf6c 100644 --- a/spec/frontend/packages_and_registries/infrastructure_registry/components/details/store/actions_spec.js +++ b/spec/frontend/packages_and_registries/infrastructure_registry/components/details/store/actions_spec.js @@ -15,7 +15,7 @@ import { } from '~/packages_and_registries/shared/constants'; import { npmPackage as packageEntity } from '../../mock_data'; -jest.mock('~/flash.js'); +jest.mock('~/flash'); jest.mock('~/api.js'); describe('Actions Package details store', () => { diff --git a/spec/frontend/packages_and_registries/infrastructure_registry/components/list/stores/actions_spec.js b/spec/frontend/packages_and_registries/infrastructure_registry/components/list/stores/actions_spec.js index 2c185e040f4..b5251aba40f 100644 --- a/spec/frontend/packages_and_registries/infrastructure_registry/components/list/stores/actions_spec.js +++ b/spec/frontend/packages_and_registries/infrastructure_registry/components/list/stores/actions_spec.js @@ -9,7 +9,7 @@ import * as actions from '~/packages_and_registries/infrastructure_registry/list import * as types from '~/packages_and_registries/infrastructure_registry/list/stores/mutation_types'; import { DELETE_PACKAGE_ERROR_MESSAGE } from '~/packages_and_registries/shared/constants'; -jest.mock('~/flash.js'); +jest.mock('~/flash'); jest.mock('~/api.js'); describe('Actions Package list store', () => { diff --git a/spec/frontend/pipelines/test_reports/stores/actions_spec.js b/spec/frontend/pipelines/test_reports/stores/actions_spec.js index f6287107ed0..e813a63a53f 100644 --- a/spec/frontend/pipelines/test_reports/stores/actions_spec.js +++ b/spec/frontend/pipelines/test_reports/stores/actions_spec.js @@ -8,7 +8,7 @@ import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; import * as actions from '~/pipelines/stores/test_reports/actions'; import * as types from '~/pipelines/stores/test_reports/mutation_types'; -jest.mock('~/flash.js'); +jest.mock('~/flash'); describe('Actions TestReports Store', () => { let mock; diff --git a/spec/frontend/pipelines/test_reports/stores/mutations_spec.js b/spec/frontend/pipelines/test_reports/stores/mutations_spec.js index ed0cc71eb97..82c70c6db58 100644 --- a/spec/frontend/pipelines/test_reports/stores/mutations_spec.js +++ b/spec/frontend/pipelines/test_reports/stores/mutations_spec.js @@ -3,7 +3,7 @@ import * as types from '~/pipelines/stores/test_reports/mutation_types'; import mutations from '~/pipelines/stores/test_reports/mutations'; import { createAlert } from '~/flash'; -jest.mock('~/flash.js'); +jest.mock('~/flash'); describe('Mutations TestReports Store', () => { let mockState; diff --git a/spec/frontend/projects/commit/store/actions_spec.js b/spec/frontend/projects/commit/store/actions_spec.js index 008710984b9..1502985cfc7 100644 --- a/spec/frontend/projects/commit/store/actions_spec.js +++ b/spec/frontend/projects/commit/store/actions_spec.js @@ -8,7 +8,7 @@ import * as types from '~/projects/commit/store/mutation_types'; import getInitialState from '~/projects/commit/store/state'; import mockData from '../mock_data'; -jest.mock('~/flash.js'); +jest.mock('~/flash'); describe('Commit form modal store actions', () => { let axiosMock; diff --git a/spec/frontend/super_sidebar/components/nav_item_spec.js b/spec/frontend/super_sidebar/components/nav_item_spec.js new file mode 100644 index 00000000000..deea4f13c55 --- /dev/null +++ b/spec/frontend/super_sidebar/components/nav_item_spec.js @@ -0,0 +1,34 @@ +import { GlBadge } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import NavItem from '~/super_sidebar/components/nav_item.vue'; + +describe('NavItem component', () => { + let wrapper; + + const findPill = () => wrapper.findComponent(GlBadge); + const createWrapper = (item, props = {}) => { + wrapper = shallowMountExtended(NavItem, { + propsData: { + item, + ...props, + }, + }); + }; + + describe('pills', () => { + it.each([0, 5, 3.4, 'foo', '10%'])('item with pill_data `%p` renders a pill', (pillCount) => { + createWrapper({ title: 'Foo', pill_count: pillCount }); + + expect(findPill().text()).toEqual(pillCount.toString()); + }); + + it.each([null, undefined, false, true, '', NaN, Number.POSITIVE_INFINITY])( + 'item with pill_data `%p` renders no pill', + (pillCount) => { + createWrapper({ title: 'Foo', pill_count: pillCount }); + + expect(findPill().exists()).toEqual(false); + }, + ); + }); +}); diff --git a/spec/frontend/super_sidebar/components/user_bar_spec.js b/spec/frontend/super_sidebar/components/user_bar_spec.js index eceb792c3db..a5faad967fc 100644 --- a/spec/frontend/super_sidebar/components/user_bar_spec.js +++ b/spec/frontend/super_sidebar/components/user_bar_spec.js @@ -1,3 +1,4 @@ +import { GlBadge } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { __ } from '~/locale'; import CreateMenu from '~/super_sidebar/components/create_menu.vue'; @@ -13,11 +14,10 @@ describe('UserBar component', () => { const findCounter = (at) => wrapper.findAllComponents(Counter).at(at); const findMergeRequestMenu = () => wrapper.findComponent(MergeRequestMenu); - const createWrapper = (props = {}) => { + const createWrapper = (extraSidebarData = {}) => { wrapper = shallowMountExtended(UserBar, { propsData: { - sidebarData, - ...props, + sidebarData: { ...sidebarData, ...extraSidebarData }, }, provide: { rootPath: '/', @@ -56,4 +56,23 @@ describe('UserBar component', () => { expect(findCounter(2).props('label')).toBe(__('To-Do list')); }); }); + + describe('GitLab Next badge', () => { + describe('when on canary', () => { + it('should render a badge to switch off GitLab Next', () => { + createWrapper({ gitlab_com_and_canary: true }); + const badge = wrapper.findComponent(GlBadge); + expect(badge.text()).toBe('Next'); + expect(badge.attributes('href')).toBe(sidebarData.canary_toggle_com_url); + }); + }); + + describe('when not on canary', () => { + it('should not render the GitLab Next badge', () => { + createWrapper({ gitlab_com_and_canary: false }); + const badge = wrapper.findComponent(GlBadge); + expect(badge.exists()).toBe(false); + }); + }); + }); }); diff --git a/spec/frontend/super_sidebar/mock_data.js b/spec/frontend/super_sidebar/mock_data.js index 52dad82aca6..d549018ca67 100644 --- a/spec/frontend/super_sidebar/mock_data.js +++ b/spec/frontend/super_sidebar/mock_data.js @@ -81,6 +81,8 @@ export const sidebarData = { show_version_check: false, gitlab_version: { major: 16, minor: 0 }, gitlab_version_check: { severity: 'success' }, + gitlab_com_and_canary: false, + canary_toggle_com_url: 'https://next.gitlab.com', }; export const userMenuMockStatus = { diff --git a/spec/helpers/sidebars_helper_spec.rb b/spec/helpers/sidebars_helper_spec.rb index f40e897925c..318fbea746a 100644 --- a/spec/helpers/sidebars_helper_spec.rb +++ b/spec/helpers/sidebars_helper_spec.rb @@ -123,6 +123,7 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do gitlab_version: Gitlab.version_info, gitlab_version_check: helper.gitlab_version_check, gitlab_com_but_not_canary: Gitlab.com_but_not_canary?, + gitlab_com_and_canary: Gitlab.com_and_canary?, canary_toggle_com_url: Gitlab::Saas.canary_toggle_com_url }) end diff --git a/spec/lib/gitlab/ci/config/external/file/project_spec.rb b/spec/lib/gitlab/ci/config/external/file/project_spec.rb index 9c6f2e1364f..0ef39a22932 100644 --- a/spec/lib/gitlab/ci/config/external/file/project_spec.rb +++ b/spec/lib/gitlab/ci/config/external/file/project_spec.rb @@ -97,6 +97,36 @@ RSpec.describe Gitlab::Ci::Config::External::File::Project, feature_category: :p end end + context 'when a valid path is used in uppercase' do + let(:params) do + { project: project.full_path.upcase, file: '/file.yml' } + end + + around do |example| + create_and_delete_files(project, { '/file.yml' => 'image: image:1.0' }) do + example.run + end + end + + it { is_expected.to be_truthy } + end + + context 'when a valid different case path is used' do + let_it_be(:project) { create(:project, :repository, path: 'mY-teSt-proJect', name: 'My Test Project') } + + let(:params) do + { project: "#{project.namespace.full_path}/my-test-projecT", file: '/file.yml' } + end + + around do |example| + create_and_delete_files(project, { '/file.yml' => 'image: image:1.0' }) do + example.run + end + end + + it { is_expected.to be_truthy } + end + context 'when a valid path with custom ref is used' do let(:params) do { project: project.full_path, ref: 'master', file: '/file.yml' } diff --git a/spec/lib/gitlab/ci/config/external/mapper/verifier_spec.rb b/spec/lib/gitlab/ci/config/external/mapper/verifier_spec.rb index b4576fb7a1d..478b26e97cd 100644 --- a/spec/lib/gitlab/ci/config/external/mapper/verifier_spec.rb +++ b/spec/lib/gitlab/ci/config/external/mapper/verifier_spec.rb @@ -84,42 +84,101 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper::Verifier, feature_category: end context 'when files are project files' do - let_it_be(:included_project) { create(:project, :repository, namespace: project.namespace, creator: user) } + let_it_be(:included_project1) { create(:project, :repository, namespace: project.namespace, creator: user) } + let_it_be(:included_project2) { create(:project, :repository, namespace: project.namespace, creator: user) } let(:files) do [ Gitlab::Ci::Config::External::File::Project.new( - { file: 'myfolder/file1.yml', project: included_project.full_path }, context + { file: 'myfolder/file1.yml', project: included_project1.full_path }, context ), Gitlab::Ci::Config::External::File::Project.new( - { file: 'myfolder/file2.yml', project: included_project.full_path }, context + { file: 'myfolder/file2.yml', project: included_project1.full_path }, context ), Gitlab::Ci::Config::External::File::Project.new( - { file: 'myfolder/file3.yml', project: included_project.full_path }, context + { file: 'myfolder/file3.yml', project: included_project1.full_path, ref: 'master' }, context + ), + Gitlab::Ci::Config::External::File::Project.new( + { file: 'myfolder/file1.yml', project: included_project2.full_path }, context + ), + Gitlab::Ci::Config::External::File::Project.new( + { file: 'myfolder/file2.yml', project: included_project2.full_path }, context ) ] end around(:all) do |example| - create_and_delete_files(included_project, project_files) do - example.run + create_and_delete_files(included_project1, project_files) do + create_and_delete_files(included_project2, project_files) do + example.run + end end end it 'returns an array of file objects' do expect(process.map(&:location)).to contain_exactly( - 'myfolder/file1.yml', 'myfolder/file2.yml', 'myfolder/file3.yml' + 'myfolder/file1.yml', 'myfolder/file2.yml', 'myfolder/file3.yml', 'myfolder/file1.yml', 'myfolder/file2.yml' ) end it 'adds files to the expandset' do - expect { process }.to change { context.expandset.count }.by(3) + expect { process }.to change { context.expandset.count }.by(5) end it 'calls Gitaly only once for all files', :request_store do - # 1 for project.commit.id, 3 for the sha check, 1 for the files + files # calling this to load project creations and the `project.commit.id` call + + # 3 for the sha check, 2 for the files in batch expect { process }.to change { Gitlab::GitalyClient.get_request_count }.by(5) end + + it 'queries with batch', :use_sql_query_cache do + files # calling this to load project creations and the `project.commit.id` call + + queries = ActiveRecord::QueryRecorder.new(skip_cached: false) { process } + projects_queries = queries.occurrences_starting_with('SELECT "projects"') + access_check_queries = queries.occurrences_starting_with('SELECT MAX("project_authorizations"."access_level")') + + expect(projects_queries.values.sum).to eq(1) + expect(access_check_queries.values.sum).to eq(2) + end + + context 'when the FF ci_batch_project_includes_context is disabled' do + before do + stub_feature_flags(ci_batch_project_includes_context: false) + end + + it 'returns an array of file objects' do + expect(process.map(&:location)).to contain_exactly( + 'myfolder/file1.yml', 'myfolder/file2.yml', 'myfolder/file3.yml', + 'myfolder/file1.yml', 'myfolder/file2.yml' + ) + end + + it 'adds files to the expandset' do + expect { process }.to change { context.expandset.count }.by(5) + end + + it 'calls Gitaly for all files', :request_store do + files # calling this to load project creations and the `project.commit.id` call + + # 5 for the sha check, 2 for the files in batch + expect { process }.to change { Gitlab::GitalyClient.get_request_count }.by(7) + end + + it 'queries without batch', :use_sql_query_cache do + files # calling this to load project creations and the `project.commit.id` call + + queries = ActiveRecord::QueryRecorder.new(skip_cached: false) { process } + projects_queries = queries.occurrences_starting_with('SELECT "projects"') + access_check_queries = queries.occurrences_starting_with( + 'SELECT MAX("project_authorizations"."access_level")' + ) + + expect(projects_queries.values.sum).to eq(5) + expect(access_check_queries.values.sum).to eq(5) + end + end end context 'when a file includes other files' do diff --git a/spec/lib/gitlab/ci/reports/security/vulnerability_reports_comparer_spec.rb b/spec/lib/gitlab/ci/reports/security/vulnerability_reports_comparer_spec.rb index 6f75e2c55e8..393d65ff102 100644 --- a/spec/lib/gitlab/ci/reports/security/vulnerability_reports_comparer_spec.rb +++ b/spec/lib/gitlab/ci/reports/security/vulnerability_reports_comparer_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer do +RSpec.describe Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer, feature_category: :vulnerability_management do let(:identifier) { build(:ci_reports_security_identifier) } let_it_be(:project) { create(:project, :repository) } @@ -90,6 +90,18 @@ RSpec.describe Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer do expect(subject.added).to eq([vuln, low_vuln]) end end + + describe 'number of findings' do + let(:head_report) { build(:ci_reports_security_aggregated_reports, findings: [head_vulnerability, vuln, low_vuln]) } + + before do + stub_const("#{described_class}::MAX_FINDINGS_COUNT", 1) + end + + it 'returns no more than `MAX_FINDINGS_COUNT`' do + expect(subject.added).to eq([vuln]) + end + end end describe '#fixed' do @@ -123,6 +135,18 @@ RSpec.describe Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer do expect(subject.fixed).to eq(vul_findings) end end + + describe 'number of findings' do + let(:base_report) { build(:ci_reports_security_aggregated_reports, findings: [vuln, medium_vuln, base_vulnerability]) } + + before do + stub_const("#{described_class}::MAX_FINDINGS_COUNT", 1) + end + + it 'returns no more than `MAX_FINDINGS_COUNT`' do + expect(subject.fixed).to eq([vuln]) + end + end end describe 'with empty vulnerabilities' do diff --git a/spec/lib/gitlab/sidekiq_queue_spec.rb b/spec/lib/gitlab/sidekiq_queue_spec.rb index 5e91282612e..93632848788 100644 --- a/spec/lib/gitlab/sidekiq_queue_spec.rb +++ b/spec/lib/gitlab/sidekiq_queue_spec.rb @@ -4,15 +4,15 @@ require 'spec_helper' RSpec.describe Gitlab::SidekiqQueue, :clean_gitlab_redis_queues do around do |example| - Sidekiq::Queue.new('default').clear + Sidekiq::Queue.new('foobar').clear Sidekiq::Testing.disable!(&example) - Sidekiq::Queue.new('default').clear + Sidekiq::Queue.new('foobar').clear end def add_job(args, user:, klass: 'AuthorizedProjectsWorker') Sidekiq::Client.push( 'class' => klass, - 'queue' => 'default', + 'queue' => 'foobar', 'args' => args, 'meta.user' => user.username ) @@ -20,7 +20,7 @@ RSpec.describe Gitlab::SidekiqQueue, :clean_gitlab_redis_queues do describe '#drop_jobs!' do shared_examples 'queue processing' do - let(:sidekiq_queue) { described_class.new('default') } + let(:sidekiq_queue) { described_class.new('foobar') } let_it_be(:sidekiq_queue_user) { create(:user) } before do @@ -80,7 +80,7 @@ RSpec.describe Gitlab::SidekiqQueue, :clean_gitlab_redis_queues do it 'raises NoMetadataError' do add_job([1], user: create(:user)) - expect { described_class.new('default').drop_jobs!({ username: 'sidekiq_queue_user' }, timeout: 1) } + expect { described_class.new('foobar').drop_jobs!({ username: 'sidekiq_queue_user' }, timeout: 1) } .to raise_error(described_class::NoMetadataError) end end diff --git a/spec/lib/sidebars/menu_spec.rb b/spec/lib/sidebars/menu_spec.rb index 7c4d74aecc8..641f1c6e7e6 100644 --- a/spec/lib/sidebars/menu_spec.rb +++ b/spec/lib/sidebars/menu_spec.rb @@ -23,14 +23,23 @@ RSpec.describe Sidebars::Menu, feature_category: :navigation do end describe '#serialize_for_super_sidebar' do + before do + allow(menu).to receive(:title).and_return('Title') + allow(menu).to receive(:active_routes).and_return({ path: 'foo' }) + end + it 'returns a tree-like structure of itself and all menu items' do menu.add_item(Sidebars::MenuItem.new(title: 'Is active', link: 'foo2', active_routes: { controller: 'fooc' })) - menu.add_item(Sidebars::MenuItem.new(title: 'Not active', link: 'foo3', active_routes: { controller: 'barc' })) + menu.add_item(Sidebars::MenuItem.new( + title: 'Not active', + link: 'foo3', + active_routes: { controller: 'barc' }, + has_pill: true, + pill_count: 10 + )) menu.add_item(nil_menu_item) allow(context).to receive(:route_is_active).and_return(->(x) { x[:controller] == 'fooc' }) - allow(menu).to receive(:title).and_return('Title') - allow(menu).to receive(:active_routes).and_return({ path: 'foo' }) expect(menu.serialize_for_super_sidebar).to eq( { @@ -38,22 +47,39 @@ RSpec.describe Sidebars::Menu, feature_category: :navigation do icon: nil, link: "foo2", is_active: true, + pill_count: nil, items: [ { title: "Is active", icon: nil, link: "foo2", - is_active: true + is_active: true, + pill_count: nil }, { title: "Not active", icon: nil, link: "foo3", - is_active: false + is_active: false, + pill_count: 10 } ] }) end + + it 'returns pill data if defined' do + allow(menu).to receive(:has_pill?).and_return(true) + allow(menu).to receive(:pill_count).and_return('foo') + expect(menu.serialize_for_super_sidebar).to eq( + { + title: "Title", + icon: nil, + link: nil, + is_active: false, + pill_count: 'foo', + items: [] + }) + end end describe '#serialize_as_menu_item_args' do diff --git a/spec/lib/sidebars/static_menu_spec.rb b/spec/lib/sidebars/static_menu_spec.rb index a42fed4b170..086eb332a15 100644 --- a/spec/lib/sidebars/static_menu_spec.rb +++ b/spec/lib/sidebars/static_menu_spec.rb @@ -21,13 +21,15 @@ RSpec.describe Sidebars::StaticMenu, feature_category: :navigation do title: "Is active", icon: nil, link: "foo2", - is_active: true + is_active: true, + pill_count: nil }, { title: "Not active", icon: nil, link: "foo3", - is_active: false + is_active: false, + pill_count: nil } ] ) diff --git a/spec/support/helpers/query_recorder.rb b/spec/support/helpers/query_recorder.rb index 5be9ba9ae1e..e8fa73a1b95 100644 --- a/spec/support/helpers/query_recorder.rb +++ b/spec/support/helpers/query_recorder.rb @@ -102,6 +102,10 @@ module ActiveRecord @occurrences ||= @log.group_by(&:to_s).transform_values(&:count) end + def occurrences_starting_with(str) + occurrences.select { |query, _count| query.starts_with?(str) } + end + def ignorable?(values) return true if skip_schema_queries && values[:name]&.include?("SCHEMA") return true if values[:name]&.match(/License Load/) diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml index 7b7b7da13e5..2e3014b2f51 100644 --- a/spec/support/rspec_order_todo.yml +++ b/spec/support/rspec_order_todo.yml @@ -3199,7 +3199,6 @@ - './ee/spec/workers/concerns/elastic/indexing_control_spec.rb' - './ee/spec/workers/concerns/elastic/migration_obsolete_spec.rb' - './ee/spec/workers/concerns/elastic/migration_options_spec.rb' -- './ee/spec/workers/concerns/geo_queue_spec.rb' - './ee/spec/workers/concerns/update_orchestration_policy_configuration_spec.rb' - './ee/spec/workers/create_github_webhook_worker_spec.rb' - './ee/spec/workers/deployments/auto_rollback_worker_spec.rb' @@ -10377,18 +10376,14 @@ - './spec/workers/clusters/integrations/check_prometheus_health_worker_spec.rb' - './spec/workers/concerns/application_worker_spec.rb' - './spec/workers/concerns/cluster_agent_queue_spec.rb' -- './spec/workers/concerns/cluster_queue_spec.rb' - './spec/workers/concerns/cronjob_queue_spec.rb' - './spec/workers/concerns/gitlab/github_import/object_importer_spec.rb' -- './spec/workers/concerns/gitlab/github_import/queue_spec.rb' - './spec/workers/concerns/gitlab/github_import/rescheduling_methods_spec.rb' - './spec/workers/concerns/gitlab/github_import/stage_methods_spec.rb' - './spec/workers/concerns/gitlab/notify_upon_death_spec.rb' - './spec/workers/concerns/limited_capacity/job_tracker_spec.rb' - './spec/workers/concerns/limited_capacity/worker_spec.rb' - './spec/workers/concerns/packages/cleanup_artifact_worker_spec.rb' -- './spec/workers/concerns/pipeline_background_queue_spec.rb' -- './spec/workers/concerns/pipeline_queue_spec.rb' - './spec/workers/concerns/project_import_options_spec.rb' - './spec/workers/concerns/reenqueuer_spec.rb' - './spec/workers/concerns/repository_check_queue_spec.rb' diff --git a/spec/workers/concerns/cluster_agent_queue_spec.rb b/spec/workers/concerns/cluster_agent_queue_spec.rb index b5189cbd8c8..4f67102a0be 100644 --- a/spec/workers/concerns/cluster_agent_queue_spec.rb +++ b/spec/workers/concerns/cluster_agent_queue_spec.rb @@ -14,6 +14,5 @@ RSpec.describe ClusterAgentQueue do end end - it { expect(worker.queue).to eq('cluster_agent:example') } it { expect(worker.get_feature_category).to eq(:kubernetes_management) } end diff --git a/spec/workers/concerns/cluster_queue_spec.rb b/spec/workers/concerns/cluster_queue_spec.rb deleted file mode 100644 index c03ca9cea48..00000000000 --- a/spec/workers/concerns/cluster_queue_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe ClusterQueue do - let(:worker) do - Class.new do - def self.name - 'DummyWorker' - end - - include ApplicationWorker - include ClusterQueue - end - end - - it 'sets a default pipelines queue automatically' do - expect(worker.sidekiq_options['queue']) - .to eq 'gcp_cluster:dummy' - end -end diff --git a/spec/workers/concerns/cronjob_queue_spec.rb b/spec/workers/concerns/cronjob_queue_spec.rb index 0244535051f..7dd016fc78a 100644 --- a/spec/workers/concerns/cronjob_queue_spec.rb +++ b/spec/workers/concerns/cronjob_queue_spec.rb @@ -40,10 +40,6 @@ RSpec.describe CronjobQueue do stub_const("AnotherWorker", another_worker) end - it 'sets the queue name of a worker' do - expect(worker.sidekiq_options['queue'].to_s).to eq('cronjob:dummy') - end - it 'disables retrying of failed jobs' do expect(worker.sidekiq_options['retry']).to eq(false) end diff --git a/spec/workers/concerns/gitlab/github_import/queue_spec.rb b/spec/workers/concerns/gitlab/github_import/queue_spec.rb deleted file mode 100644 index beca221b593..00000000000 --- a/spec/workers/concerns/gitlab/github_import/queue_spec.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Gitlab::GithubImport::Queue do - it 'sets the Sidekiq options for the worker' do - worker = Class.new do - def self.name - 'DummyWorker' - end - - include ApplicationWorker - include Gitlab::GithubImport::Queue - end - - expect(worker.sidekiq_options['queue']).to eq('github_importer:dummy') - end -end diff --git a/spec/workers/concerns/pipeline_background_queue_spec.rb b/spec/workers/concerns/pipeline_background_queue_spec.rb deleted file mode 100644 index 77c7e7440c5..00000000000 --- a/spec/workers/concerns/pipeline_background_queue_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe PipelineBackgroundQueue do - let(:worker) do - Class.new do - def self.name - 'DummyWorker' - end - - include ApplicationWorker - include PipelineBackgroundQueue - end - end - - it 'sets a default object storage queue automatically' do - expect(worker.sidekiq_options['queue']) - .to eq 'pipeline_background:dummy' - end -end diff --git a/spec/workers/concerns/pipeline_queue_spec.rb b/spec/workers/concerns/pipeline_queue_spec.rb deleted file mode 100644 index 6c1ac2052e4..00000000000 --- a/spec/workers/concerns/pipeline_queue_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe PipelineQueue do - let(:worker) do - Class.new do - def self.name - 'DummyWorker' - end - - include ApplicationWorker - include PipelineQueue - end - end - - it 'sets a default pipelines queue automatically' do - expect(worker.sidekiq_options['queue']) - .to eq 'pipeline_default:dummy' - end -end diff --git a/spec/workers/concerns/repository_check_queue_spec.rb b/spec/workers/concerns/repository_check_queue_spec.rb index ae377c09b37..08ac73aac7b 100644 --- a/spec/workers/concerns/repository_check_queue_spec.rb +++ b/spec/workers/concerns/repository_check_queue_spec.rb @@ -14,10 +14,6 @@ RSpec.describe RepositoryCheckQueue do end end - it 'sets the queue name of a worker' do - expect(worker.sidekiq_options['queue'].to_s).to eq('repository_check:dummy') - end - it 'disables retrying of failed jobs' do expect(worker.sidekiq_options['retry']).to eq(false) end diff --git a/spec/workers/ssh_keys/expired_notification_worker_spec.rb b/spec/workers/ssh_keys/expired_notification_worker_spec.rb index 26d9460d73e..f93d02e86c0 100644 --- a/spec/workers/ssh_keys/expired_notification_worker_spec.rb +++ b/spec/workers/ssh_keys/expired_notification_worker_spec.rb @@ -7,7 +7,6 @@ RSpec.describe SshKeys::ExpiredNotificationWorker, type: :worker do it 'uses a cronjob queue' do expect(worker.sidekiq_options_hash).to include( - 'queue' => 'cronjob:ssh_keys_expired_notification', 'queue_namespace' => :cronjob ) end diff --git a/spec/workers/ssh_keys/expiring_soon_notification_worker_spec.rb b/spec/workers/ssh_keys/expiring_soon_notification_worker_spec.rb index e907d035020..ed6701532a5 100644 --- a/spec/workers/ssh_keys/expiring_soon_notification_worker_spec.rb +++ b/spec/workers/ssh_keys/expiring_soon_notification_worker_spec.rb @@ -7,7 +7,6 @@ RSpec.describe SshKeys::ExpiringSoonNotificationWorker, type: :worker do it 'uses a cronjob queue' do expect(worker.sidekiq_options_hash).to include( - 'queue' => 'cronjob:ssh_keys_expiring_soon_notification', 'queue_namespace' => :cronjob ) end |