From 883700c26674e88da4d2053ef28131eff069489c Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Tue, 7 May 2024 00:18:19 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .rubocop_todo/lint/symbol_conversion.yml | 16 -- Gemfile | 2 +- Gemfile.checksum | 2 +- Gemfile.lock | 4 +- .../get_container_repositories.query.graphql | 1 - .../container_repository_details_type.rb | 4 - .../types/container_repository_tag_type.rb | 11 - .../types/container_repository_type.rb | 11 - .../types/packages/package_base_type.rb | 11 - app/services/projects/create_service.rb | 3 +- .../projects/update_statistics_service.rb | 12 +- ...y_operation_in_terraform_module_upload.yml | 9 - ...roject-pipeline-securityReportFindings.yml | 14 -- ...70527_remove_foreign_keys_geo_event_log.rb | 4 +- .../operations/rails_console.md | 7 + doc/api/graphql/reference/index.md | 6 - doc/ci/environments/deployment_safety.md | 2 +- .../review_apps/img/enable_review_app_v16.png | Bin 33945 -> 0 bytes doc/ci/review_apps/index.md | 8 +- doc/update/deprecations.md | 14 -- .../secret_detection/automatic_response.md | 2 + doc/user/packages/go_proxy/index.md | 2 +- doc/user/project/merge_requests/changes.md | 12 ++ .../terraform/modules/v1/project_packages.rb | 11 +- .../sidekiq_shard_awareness_validation.rb | 18 +- .../pagination/keyset/simple_order_builder.rb | 90 +++++--- .../schemas/graphql/container_repository.json | 4 - .../graphql/container_repository_details.json | 4 - .../graphql/packages/package_details.json | 6 - .../container_repository_details_type_spec.rb | 4 +- .../container_repository_tag_type_spec.rb | 2 +- .../types/container_repository_type_spec.rb | 2 +- .../types/packages/package_base_type_spec.rb | 2 +- .../types/packages/package_type_spec.rb | 3 +- ...revent_cross_database_modification_spec.rb | 4 +- .../reindexing/grafana_notifier_spec.rb | 2 +- .../diff/formatters/text_formatter_spec.rb | 4 +- .../gitlab/fake_application_settings_spec.rb | 2 +- .../formatters/lograge_with_timestamp_spec.rb | 6 +- .../loggers/queue_duration_logger_spec.rb | 2 +- .../loggers/token_logger_spec.rb | 4 +- .../pagination/keyset/connection_spec.rb | 6 + spec/lib/gitlab/harbor/client_spec.rb | 88 ++++---- spec/lib/gitlab/harbor/query_spec.rb | 100 ++++----- .../project/relation_factory_spec.rb | 20 +- spec/lib/gitlab/lfs/client_spec.rb | 2 +- .../metrics/subscribers/active_record_spec.rb | 12 +- ...sidekiq_shard_awareness_validation_spec.rb | 27 ++- .../keyset/simple_order_builder_spec.rb | 196 ++++++++++++++---- spec/lib/gitlab/prometheus_client_spec.rb | 16 +- ...ive_coercion_from_string_validator_spec.rb | 4 +- spec/lib/gitlab/slug/path_spec.rb | 2 +- .../container_repository_details_spec.rb | 8 +- .../group/container_repositories_spec.rb | 10 +- .../project/container_repositories_spec.rb | 4 +- .../modules/v1/project_packages_spec.rb | 14 -- .../update_statistics_service_spec.rb | 18 +- 57 files changed, 486 insertions(+), 368 deletions(-) delete mode 100644 config/feature_flags/gitlab_com_derisk/skip_copy_operation_in_terraform_module_upload.yml delete mode 100644 data/deprecations/15-1-project-pipeline-securityReportFindings.yml delete mode 100644 doc/ci/review_apps/img/enable_review_app_v16.png diff --git a/.rubocop_todo/lint/symbol_conversion.yml b/.rubocop_todo/lint/symbol_conversion.yml index 34304ed65bc..fef77e03433 100644 --- a/.rubocop_todo/lint/symbol_conversion.yml +++ b/.rubocop_todo/lint/symbol_conversion.yml @@ -77,22 +77,6 @@ Lint/SymbolConversion: - 'spec/lib/gitlab/ci/reports/accessibility_reports_spec.rb' - 'spec/lib/gitlab/ci/reports/codequality_reports_spec.rb' - 'spec/lib/gitlab/ci/yaml_processor_spec.rb' - - 'spec/lib/gitlab/database/query_analyzers/prevent_cross_database_modification_spec.rb' - - 'spec/lib/gitlab/database/reindexing/grafana_notifier_spec.rb' - - 'spec/lib/gitlab/diff/formatters/text_formatter_spec.rb' - - 'spec/lib/gitlab/fake_application_settings_spec.rb' - - 'spec/lib/gitlab/grape_logging/formatters/lograge_with_timestamp_spec.rb' - - 'spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb' - - 'spec/lib/gitlab/grape_logging/loggers/token_logger_spec.rb' - - 'spec/lib/gitlab/harbor/client_spec.rb' - - 'spec/lib/gitlab/harbor/query_spec.rb' - - 'spec/lib/gitlab/import_export/project/relation_factory_spec.rb' - - 'spec/lib/gitlab/lfs/client_spec.rb' - - 'spec/lib/gitlab/metrics/subscribers/active_record_spec.rb' - - 'spec/lib/gitlab/prometheus_client_spec.rb' - - 'spec/lib/gitlab/search/abuse_validators/no_abusive_coercion_from_string_validator_spec.rb' - - 'spec/lib/gitlab/slug/path_spec.rb' - - 'spec/lib/gitlab/tracking_spec.rb' - 'spec/lib/google_api/cloud_platform/client_spec.rb' - 'spec/lib/service_ping/devops_report_spec.rb' - 'spec/models/integrations/prometheus_spec.rb' diff --git a/Gemfile b/Gemfile index c8ffdb35435..9488680f723 100644 --- a/Gemfile +++ b/Gemfile @@ -208,7 +208,7 @@ gem 'elasticsearch-rails', '~> 7.2', require: 'elasticsearch/rails/instrumentati gem 'elasticsearch-api', '7.13.3' # rubocop:todo Gemfile/MissingFeatureCategory gem 'aws-sdk-core', '~> 3.194.1' # rubocop:todo Gemfile/MissingFeatureCategory gem 'aws-sdk-cloudformation', '~> 1' # rubocop:todo Gemfile/MissingFeatureCategory -gem 'aws-sdk-s3', '~> 1.149.0' # rubocop:todo Gemfile/MissingFeatureCategory +gem 'aws-sdk-s3', '~> 1.149.1' # rubocop:todo Gemfile/MissingFeatureCategory gem 'faraday_middleware-aws-sigv4', '~>0.3.0' # rubocop:todo Gemfile/MissingFeatureCategory gem 'typhoeus', '~> 1.4.0' # Used with Elasticsearch to support http keep-alive connections # rubocop:todo Gemfile/MissingFeatureCategory diff --git a/Gemfile.checksum b/Gemfile.checksum index 221b5e66349..90485822d71 100644 --- a/Gemfile.checksum +++ b/Gemfile.checksum @@ -38,7 +38,7 @@ {"name":"aws-sdk-cloudformation","version":"1.41.0","platform":"ruby","checksum":"31e47539719734413671edf9b1a31f8673fbf9688549f50c41affabbcb1c6b26"}, {"name":"aws-sdk-core","version":"3.194.1","platform":"ruby","checksum":"3ac7a3d6b872ae69ded6ee0394e3b64645dde1f7c78e123b7dd84700d392607a"}, {"name":"aws-sdk-kms","version":"1.76.0","platform":"ruby","checksum":"e7f75013cba9ba357144f66bbc600631c192e2cda9dd572794be239654e2cf49"}, -{"name":"aws-sdk-s3","version":"1.149.0","platform":"ruby","checksum":"17c0f00bad32f2c6b281e827252746b64812bdcabdc44cda59e6170c1d53b410"}, +{"name":"aws-sdk-s3","version":"1.149.1","platform":"ruby","checksum":"664e608190d42b486dc79b5dc65e7c2240923902a9833063327a9d831226a46a"}, {"name":"aws-sigv4","version":"1.8.0","platform":"ruby","checksum":"84dd99768b91b93b63d1d8e53ee837cfd06ab402812772a7899a78f9f9117cbc"}, {"name":"axe-core-api","version":"4.8.0","platform":"ruby","checksum":"88cf44fdbd5d501ae429f9ca6b37c4a46ba27ac673d478ab688eea3e353da62f"}, {"name":"axe-core-rspec","version":"4.9.0","platform":"ruby","checksum":"e5f81fa55af0c421254c98476511c4511e193c5659996f184541f74a1359df3a"}, diff --git a/Gemfile.lock b/Gemfile.lock index 04125abe56e..a990e3709a3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -311,7 +311,7 @@ GEM aws-sdk-kms (1.76.0) aws-sdk-core (~> 3, >= 3.188.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.149.0) + aws-sdk-s3 (1.149.1) aws-sdk-core (~> 3, >= 3.194.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.8) @@ -1932,7 +1932,7 @@ DEPENDENCIES awesome_print aws-sdk-cloudformation (~> 1) aws-sdk-core (~> 3.194.1) - aws-sdk-s3 (~> 1.149.0) + aws-sdk-s3 (~> 1.149.1) axe-core-rspec (~> 4.9.0) babosa (~> 2.0) base32 (~> 0.3.0) diff --git a/app/graphql/queries/container_registry/get_container_repositories.query.graphql b/app/graphql/queries/container_registry/get_container_repositories.query.graphql index 17fddd4d6be..c3aa9cbd2bc 100644 --- a/app/graphql/queries/container_registry/get_container_repositories.query.graphql +++ b/app/graphql/queries/container_registry/get_container_repositories.query.graphql @@ -69,7 +69,6 @@ query getProjectContainerRepositories( path status location - canDelete createdAt expirationPolicyStartedAt expirationPolicyCleanupStatus diff --git a/app/graphql/types/container_repository_details_type.rb b/app/graphql/types/container_repository_details_type.rb index 3498f28b6ed..6db5f31e610 100644 --- a/app/graphql/types/container_repository_details_type.rb +++ b/app/graphql/types/container_repository_details_type.rb @@ -29,10 +29,6 @@ module Types null: true, description: 'Deduplicated size of the image repository in bytes. This is only available on GitLab.com for repositories created after `2021-11-04`.' - def can_delete - Ability.allowed?(current_user, :destroy_container_image, object) - end - def size handling_errors { object.size } end diff --git a/app/graphql/types/container_repository_tag_type.rb b/app/graphql/types/container_repository_tag_type.rb index 7691844645a..3fa08ed09b2 100644 --- a/app/graphql/types/container_repository_tag_type.rb +++ b/app/graphql/types/container_repository_tag_type.rb @@ -10,13 +10,6 @@ module Types expose_permissions Types::PermissionTypes::ContainerRepositoryTag - field :can_delete, GraphQL::Types::Boolean, - null: false, - deprecated: { - reason: 'Use `userPermissions` field. See `ContainerRepositoryTagPermissions` type', - milestone: '16.7' - }, - description: 'Can the current user delete this tag.' field :created_at, Types::TimeType, null: true, description: 'Timestamp when the tag was created.' field :digest, GraphQL::Types::String, null: true, description: 'Digest of the tag.' field :location, GraphQL::Types::String, null: false, description: 'URL of the tag.' @@ -27,9 +20,5 @@ module Types field :revision, GraphQL::Types::String, null: true, description: 'Revision of the tag.' field :short_revision, GraphQL::Types::String, null: true, description: 'Short revision of the tag.' field :total_size, GraphQL::Types::BigInt, null: true, description: 'Size of the tag.' - - def can_delete - Ability.allowed?(current_user, :destroy_container_image, object) - end end end diff --git a/app/graphql/types/container_repository_type.rb b/app/graphql/types/container_repository_type.rb index 411a9a90ce9..6798c4d0624 100644 --- a/app/graphql/types/container_repository_type.rb +++ b/app/graphql/types/container_repository_type.rb @@ -10,13 +10,6 @@ module Types expose_permissions Types::PermissionTypes::ContainerRepository - field :can_delete, GraphQL::Types::Boolean, - null: false, - deprecated: { - reason: 'Use `userPermissions` field. See `ContainerRepositoryPermissions` type', - milestone: '16.7' - }, - description: 'Can the current user delete the container repository.' field :created_at, Types::TimeType, null: false, description: 'Timestamp when the container repository was created.' field :expiration_policy_cleanup_status, Types::ContainerRepositoryCleanupStatusEnum, null: true, description: 'Tags cleanup status for the container repository.' field :expiration_policy_started_at, Types::TimeType, null: true, description: 'Timestamp when the cleanup done by the expiration policy was started on the container repository.' @@ -37,10 +30,6 @@ module Types field :tags_count, GraphQL::Types::Int, null: false, description: 'Number of tags associated with this image.' field :updated_at, Types::TimeType, null: false, description: 'Timestamp when the container repository was updated.' - def can_delete - Ability.allowed?(current_user, :update_container_image, object) - end - def project Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, object.project_id).find end diff --git a/app/graphql/types/packages/package_base_type.rb b/app/graphql/types/packages/package_base_type.rb index 278119c78e2..1ab04223f77 100644 --- a/app/graphql/types/packages/package_base_type.rb +++ b/app/graphql/types/packages/package_base_type.rb @@ -16,13 +16,6 @@ module Types field :_links, Types::Packages::PackageLinksType, null: false, method: :itself, description: 'Map of links to perform actions on the package.' - field :can_destroy, GraphQL::Types::Boolean, - null: false, - deprecated: { - reason: 'Superseded by `user_permissions` field. See `Types::PermissionTypes::Package` type', - milestone: '16.6' - }, - description: 'Whether the user can destroy the package.' field :created_at, Types::TimeType, null: false, description: 'Date of creation.' field :metadata, Types::Packages::MetadataType, null: true, @@ -46,10 +39,6 @@ module Types Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, object.project_id).find end - def can_destroy - Ability.allowed?(current_user, :destroy_package, object) - end - def package_protection_rule_exists return false if Feature.disabled?(:packages_protected_packages, object.project) diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index 50f0db3728a..37588800439 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -6,6 +6,7 @@ module Projects ImportSourceDisabledError = Class.new(StandardError) INTERNAL_IMPORT_SOURCES = %w[gitlab_custom_project_template gitlab_project_migration].freeze + README_FILE = 'README.md' def initialize(user, params) @current_user = user @@ -197,7 +198,7 @@ module Projects commit_attrs = { branch_name: default_branch, commit_message: 'Initial commit', - file_path: 'README.md', + file_path: README_FILE, file_content: readme_content } diff --git a/app/services/projects/update_statistics_service.rb b/app/services/projects/update_statistics_service.rb index 0d51de4d26e..ce775a74bd5 100644 --- a/app/services/projects/update_statistics_service.rb +++ b/app/services/projects/update_statistics_service.rb @@ -50,9 +50,19 @@ module Projects end def record_onboarding_progress - return unless repository.commit_count > 1 || repository.branch_count > 1 + return unless repository.commit_count > 1 || + repository.branch_count > 1 || + !initialized_repository_with_no_or_only_readme_file? Onboarding::ProgressService.new(project.namespace).execute(action: :code_added) end + + def initialized_repository_with_no_or_only_readme_file? + return true if repository.empty? + + !repository.ls_files(project.default_branch).reject do |file| + file == ::Projects::CreateService::README_FILE + end.any? + end end end diff --git a/config/feature_flags/gitlab_com_derisk/skip_copy_operation_in_terraform_module_upload.yml b/config/feature_flags/gitlab_com_derisk/skip_copy_operation_in_terraform_module_upload.yml deleted file mode 100644 index f62952e3e47..00000000000 --- a/config/feature_flags/gitlab_com_derisk/skip_copy_operation_in_terraform_module_upload.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: skip_copy_operation_in_terraform_module_upload -feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/439811 -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/150713 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/457927 -milestone: '17.0' -group: group::package registry -type: gitlab_com_derisk -default_enabled: false diff --git a/data/deprecations/15-1-project-pipeline-securityReportFindings.yml b/data/deprecations/15-1-project-pipeline-securityReportFindings.yml deleted file mode 100644 index 40fe3bf5bff..00000000000 --- a/data/deprecations/15-1-project-pipeline-securityReportFindings.yml +++ /dev/null @@ -1,14 +0,0 @@ -- title: "project.pipeline.securityReportFindings GraphQL query" # (required) The name of the feature to be deprecated - announcement_milestone: "15.1" # (required) The milestone when this feature was first announced as deprecated. - removal_milestone: "17.0" # (required) The milestone when this feature is planned to be removed - breaking_change: true # (required) If this deprecation is a breaking change, set this value to true - reporter: matt_wilson # (required) GitLab username of the person reporting the deprecation - stage: Secure # (required) String value of the stage that the feature was created in. e.g., Growth - issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/343475 # (required) Link to the deprecation issue in GitLab - body: | # (required) Do not modify this line, instead modify the lines below. - Previous work helped [align the vulnerabilities calls for pipeline security tabs](https://gitlab.com/gitlab-org/gitlab/-/issues/343469) to match the vulnerabilities calls for project-level and group-level vulnerability reports. This helped the frontend have a more consistent interface. The old `project.pipeline.securityReportFindings` query was formatted differently than other vulnerability data calls. Now that it has been replaced with the new `project.pipeline.vulnerabilities` field, the old `project.pipeline.securityReportFindings` is being deprecated and will be removed in GitLab 17.0. -# The following items are not published on the docs page, but may be used in the future. - tiers: Ultimate # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate] - documentation_url: # (optional) This is a link to the current documentation page - image_url: # (optional) This is a link to a thumbnail image depicting the feature - video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg diff --git a/db/post_migrate/20240425170527_remove_foreign_keys_geo_event_log.rb b/db/post_migrate/20240425170527_remove_foreign_keys_geo_event_log.rb index c9542eb3b2f..65619a080bb 100644 --- a/db/post_migrate/20240425170527_remove_foreign_keys_geo_event_log.rb +++ b/db/post_migrate/20240425170527_remove_foreign_keys_geo_event_log.rb @@ -19,7 +19,7 @@ class RemoveForeignKeysGeoEventLog < Gitlab::Database::Migration[2.2] { to_table: :geo_repository_updated_events, options: { - name: :fk_78a6492f68, + name: :fk_rails_78a6492f68, column: [:repository_updated_event_id], on_delete: :cascade } @@ -64,7 +64,7 @@ class RemoveForeignKeysGeoEventLog < Gitlab::Database::Migration[2.2] remove_foreign_key( FROM_TABLE, fk[:to_table], - name: fk[:options][:name], + column: fk[:options][:column].first.to_s, if_exists: true ) end diff --git a/doc/administration/operations/rails_console.md b/doc/administration/operations/rails_console.md index 668ce75a012..7358fa4b1a3 100644 --- a/doc/administration/operations/rails_console.md +++ b/doc/administration/operations/rails_console.md @@ -59,6 +59,13 @@ The console is in the toolbox pod. Refer to our [Kubernetes cheat sheet](https:/ To exit the console, type: `quit`. +### Disable autocompletion + +Ruby autocompletion can slow down the terminal. If you want to: + +- Disable autocompletion, run `Reline.autocompletion = IRB.conf[:USE_AUTOCOMPLETE] = false`. +- Re-enable autocompletion, run `Reline.autocompletion = IRB.conf[:USE_AUTOCOMPLETE] = true`. + ## Enable Active Record logging You can enable output of Active Record debug logging in the Rails console diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 0273b6bbfd2..16fa504c62f 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -18171,7 +18171,6 @@ A container repository. | Name | Type | Description | | ---- | ---- | ----------- | -| `canDelete` **{warning-solid}** | [`Boolean!`](#boolean) | **Deprecated** in GitLab 16.7. Use `userPermissions` field. See `ContainerRepositoryPermissions` type. | | `createdAt` | [`Time!`](#time) | Timestamp when the container repository was created. | | `expirationPolicyCleanupStatus` | [`ContainerRepositoryCleanupStatus`](#containerrepositorycleanupstatus) | Tags cleanup status for the container repository. | | `expirationPolicyStartedAt` | [`Time`](#time) | Timestamp when the cleanup done by the expiration policy was started on the container repository. | @@ -18195,7 +18194,6 @@ Details of a container repository. | Name | Type | Description | | ---- | ---- | ----------- | -| `canDelete` **{warning-solid}** | [`Boolean!`](#boolean) | **Deprecated** in GitLab 16.7. Use `userPermissions` field. See `ContainerRepositoryPermissions` type. | | `createdAt` | [`Time!`](#time) | Timestamp when the container repository was created. | | `expirationPolicyCleanupStatus` | [`ContainerRepositoryCleanupStatus`](#containerrepositorycleanupstatus) | Tags cleanup status for the container repository. | | `expirationPolicyStartedAt` | [`Time`](#time) | Timestamp when the cleanup done by the expiration policy was started on the container repository. | @@ -18297,7 +18295,6 @@ A tag from a container repository. | Name | Type | Description | | ---- | ---- | ----------- | -| `canDelete` **{warning-solid}** | [`Boolean!`](#boolean) | **Deprecated** in GitLab 16.7. Use `userPermissions` field. See `ContainerRepositoryTagPermissions` type. | | `createdAt` | [`Time`](#time) | Timestamp when the tag was created. | | `digest` | [`String`](#string) | Digest of the tag. | | `location` | [`String!`](#string) | URL of the tag. | @@ -25687,7 +25684,6 @@ Represents a package with pipelines in the Package Registry. | Name | Type | Description | | ---- | ---- | ----------- | | `_links` | [`PackageLinks!`](#packagelinks) | Map of links to perform actions on the package. | -| `canDestroy` **{warning-solid}** | [`Boolean!`](#boolean) | **Deprecated** in GitLab 16.6. Superseded by `user_permissions` field. See `Types::PermissionTypes::Package` type. | | `createdAt` | [`Time!`](#time) | Date of creation. | | `id` | [`PackagesPackageID!`](#packagespackageid) | ID of the package. | | `metadata` | [`PackageMetadata`](#packagemetadata) | Package metadata. | @@ -25712,7 +25708,6 @@ Represents a package in the Package Registry. | Name | Type | Description | | ---- | ---- | ----------- | | `_links` | [`PackageLinks!`](#packagelinks) | Map of links to perform actions on the package. | -| `canDestroy` **{warning-solid}** | [`Boolean!`](#boolean) | **Deprecated** in GitLab 16.6. Superseded by `user_permissions` field. See `Types::PermissionTypes::Package` type. | | `createdAt` | [`Time!`](#time) | Date of creation. | | `id` | [`PackagesPackageID!`](#packagespackageid) | ID of the package. | | `metadata` | [`PackageMetadata`](#packagemetadata) | Package metadata. | @@ -25774,7 +25769,6 @@ Represents a package details in the Package Registry. | Name | Type | Description | | ---- | ---- | ----------- | | `_links` | [`PackageLinks!`](#packagelinks) | Map of links to perform actions on the package. | -| `canDestroy` **{warning-solid}** | [`Boolean!`](#boolean) | **Deprecated** in GitLab 16.6. Superseded by `user_permissions` field. See `Types::PermissionTypes::Package` type. | | `composerConfigRepositoryUrl` | [`String`](#string) | Url of the Composer setup endpoint. | | `composerUrl` | [`String`](#string) | Url of the Composer endpoint. | | `conanUrl` | [`String`](#string) | Url of the Conan project endpoint. | diff --git a/doc/ci/environments/deployment_safety.md b/doc/ci/environments/deployment_safety.md index a4668581085..e42cabbc515 100644 --- a/doc/ci/environments/deployment_safety.md +++ b/doc/ci/environments/deployment_safety.md @@ -73,7 +73,7 @@ For more information, see [Resource Group documentation](../resource_groups/inde ## Prevent outdated deployment jobs -> - In GitLab 15.5, the behavior was [changed](https://gitlab.com/gitlab-org/gitlab/-/issues/363328) to prevent outdated job runs. +> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/363328) in GitLab 15.5 to prevent outdated job runs. The effective execution order of pipeline jobs can vary from run to run, which could cause undesired behavior. For example, a [deployment job](../jobs/index.md#deployment-jobs) diff --git a/doc/ci/review_apps/img/enable_review_app_v16.png b/doc/ci/review_apps/img/enable_review_app_v16.png deleted file mode 100644 index f783632059af412ca213b911eea16568baa6e299..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33945 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 gYbXH{kGC-D Environments**. 1. Select **Enable review apps**. -1. Copy the provided code snippet and paste it into your - `.gitlab-ci.yml` file: - - ![enable review apps modal](img/enable_review_app_v16.png) - -You can edit this template as needed. +1. Follow the instructions in the dialog. + You can edit the provided `.gitlab-ci.yml` template as needed. ## Review apps auto-stop diff --git a/doc/update/deprecations.md b/doc/update/deprecations.md index b18e6a06327..7be0d6afbeb 100644 --- a/doc/update/deprecations.md +++ b/doc/update/deprecations.md @@ -2313,20 +2313,6 @@ From 17.0, npm and Yarn packages will be uploaded asynchronously. This is a brea As a workaround, you should use the [packages API](https://docs.gitlab.com/ee/api/packages.html) to check for packages. - - -
- -### project.pipeline.securityReportFindings GraphQL query - -
-- Announced in GitLab 15.1 -- Removal in GitLab 17.0 ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change)) -- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/343475). -
- -Previous work helped [align the vulnerabilities calls for pipeline security tabs](https://gitlab.com/gitlab-org/gitlab/-/issues/343469) to match the vulnerabilities calls for project-level and group-level vulnerability reports. This helped the frontend have a more consistent interface. The old `project.pipeline.securityReportFindings` query was formatted differently than other vulnerability data calls. Now that it has been replaced with the new `project.pipeline.vulnerabilities` field, the old `project.pipeline.securityReportFindings` is being deprecated and will be removed in GitLab 17.0. -
diff --git a/doc/user/application_security/secret_detection/automatic_response.md b/doc/user/application_security/secret_detection/automatic_response.md index 1855a2ee965..728fba696d0 100644 --- a/doc/user/application_security/secret_detection/automatic_response.md +++ b/doc/user/application_security/secret_detection/automatic_response.md @@ -46,6 +46,8 @@ Credentials are only post-processed when Secret Detection finds them: This diagram describes how a post-processing hook revokes a secret in the GitLab application: ```mermaid +%%{init: { "fontFamily": "GitLab Sans" }}%% + sequenceDiagram autonumber GitLab Rails-->+GitLab Rails: gl-secret-detection-report.json diff --git a/doc/user/packages/go_proxy/index.md b/doc/user/packages/go_proxy/index.md index 362946427ef..bc5036c744a 100644 --- a/doc/user/packages/go_proxy/index.md +++ b/doc/user/packages/go_proxy/index.md @@ -11,7 +11,7 @@ DETAILS: **Offering:** GitLab.com, Self-managed, GitLab Dedicated **Status:** Experiment -> - Introduced [with a flag](../../../administration/feature_flags.md) named `go_proxy`. Disabled by default. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/27376) in GitLab 13.1 [with a flag](../../../administration/feature_flags.md) named `go_proxy`. Disabled by default. FLAG: The availability of this feature is controlled by a feature flag. diff --git a/doc/user/project/merge_requests/changes.md b/doc/user/project/merge_requests/changes.md index 3ee4494d443..a48135090ab 100644 --- a/doc/user/project/merge_requests/changes.md +++ b/doc/user/project/merge_requests/changes.md @@ -47,6 +47,18 @@ The diff also includes navigation and comment aids to the left of the file, in t a line number, then select **Copy link address**. - To highlight a line, select the line number. +## Show a list of changed files + +Use the **file browser** to view a list of files changed in a merge request: + +1. On the left sidebar, select **Search or go to** and find your project. +1. Select **Code > Merge requests** and find your merge request. +1. Below the merge request title, select **Changes**. +1. Select **Show file browser** (**{file-tree}**) or press F to show + the file tree. + - For a tree view that shows nesting, select **Tree view** (**{file-tree}**). + - For a file list without nesting, select **List view** (**{list-bulleted}**). + ## Show all changes in a merge request To view the diff of changes included in a merge request: diff --git a/lib/api/terraform/modules/v1/project_packages.rb b/lib/api/terraform/modules/v1/project_packages.rb index e624ffe7a8c..934b6995bbc 100644 --- a/lib/api/terraform/modules/v1/project_packages.rb +++ b/lib/api/terraform/modules/v1/project_packages.rb @@ -53,16 +53,11 @@ module API end def authorize_workhorse_params - params = { + { subject: authorized_user_project, - maximum_size: authorized_user_project.actual_limits.terraform_module_max_file_size + maximum_size: authorized_user_project.actual_limits.terraform_module_max_file_size, + use_final_store_path: true } - - if ::Feature.enabled?(:skip_copy_operation_in_terraform_module_upload, authorized_user_project) - params[:use_final_store_path] = true - end - - params end end diff --git a/lib/gitlab/middleware/sidekiq_shard_awareness_validation.rb b/lib/gitlab/middleware/sidekiq_shard_awareness_validation.rb index 0ab5670ac3e..59f2f83f08f 100644 --- a/lib/gitlab/middleware/sidekiq_shard_awareness_validation.rb +++ b/lib/gitlab/middleware/sidekiq_shard_awareness_validation.rb @@ -3,15 +3,27 @@ module Gitlab module Middleware class SidekiqShardAwarenessValidation + include Gitlab::Utils::StrongMemoize + + SIDEKIQ_WEB_UI_PATH = %r{^/admin/sidekiq} + def initialize(app) @app = app end def call(env) - ::Gitlab::SidekiqSharding::Validator.enabled do - @app.call(env) - end + path = env['PATH_INFO'].delete_prefix(relative_url) + match_data = path.match(SIDEKIQ_WEB_UI_PATH) + + return @app.call(env) if match_data + + ::Gitlab::SidekiqSharding::Validator.enabled { @app.call(env) } end + + def relative_url + File.join('', Gitlab.config.gitlab.relative_url_root).chomp('/') + end + strong_memoize_attr :relative_url end end end diff --git a/lib/gitlab/pagination/keyset/simple_order_builder.rb b/lib/gitlab/pagination/keyset/simple_order_builder.rb index 1112f3985bd..fac83efb4f2 100644 --- a/lib/gitlab/pagination/keyset/simple_order_builder.rb +++ b/lib/gitlab/pagination/keyset/simple_order_builder.rb @@ -9,7 +9,7 @@ module Gitlab # # Return values: # [transformed_scope, true] # true indicates that the new scope was successfully built - # [orginal_scope, false] # false indicates that the order values are not supported in this class + # [original_scope, false] # false indicates that the order values are not supported in this class class SimpleOrderBuilder def self.build(scope) new(scope: scope).build @@ -22,7 +22,13 @@ module Gitlab @order_values = scope.order_values.compact @model_class = scope.model @arel_table = @model_class.arel_table - @primary_key = @model_class.primary_key + # Support cases where a single logical primary key has been specified, but also cases where there is a true + # composite primary key + @primary_keys = if @model_class.primary_key.nil? + @model_class.connection.primary_keys(@model_class.table_name) + else + [@model_class.primary_key] + end end def build @@ -33,6 +39,8 @@ module Gitlab # Ordered by a primary key. Ex. 'ORDER BY id'. elsif ordered_by_primary_key? primary_key_order + elsif ordered_by_primary_key_prefix? + primary_key_order_from_prefix # Ordered by one non-primary table column. Ex. 'ORDER BY created_at'. elsif ordered_by_other_columns? columns_with_tie_breaker_order(order_values) @@ -53,14 +61,15 @@ module Gitlab private - attr_reader :scope, :order_values, :model_class, :arel_table, :primary_key + attr_reader :scope, :order_values, :model_class, :arel_table, :primary_keys def table_column?(name) model_class.column_names.include?(name.to_s) end - def primary_key?(attribute) - arel_table[primary_key].to_s == attribute.to_s + def primary_keys?(attributes) + attrs_as_strings = attributes.map(&:to_s) + primary_keys.all? { |pk| attrs_as_strings.include?(arel_table[pk].to_s) } end def lower_named_function?(attribute) @@ -101,23 +110,43 @@ module Gitlab end def primary_key_descending_order - Gitlab::Pagination::Keyset::Order.build( - [ - Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( - attribute_name: model_class.primary_key, - order_expression: arel_table[primary_key].desc - ) - ]) + column_definitions = primary_keys.map do |pk| + Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( + attribute_name: pk, + order_expression: arel_table[pk].desc + ) + end + Gitlab::Pagination::Keyset::Order.build(column_definitions) end def primary_key_order + # This code path is only called if ordered_by_primary_key? returns true + # in .build(), thus we know that the order values are already the primary keys, and + # we can simply associate them with their columns here. + Gitlab::Pagination::Keyset::Order.build( - [ + order_values.map { |ov| column(ov) } + ) + end + + def primary_key_order_from_prefix + suffix_order = order_values.last.is_a?(Arel::Nodes::Ascending) ? :asc : :desc + Gitlab::Pagination::Keyset::Order.build( + primary_keys.zip(order_values).map do |pk, order_value| + expr = if order_value + order_value + elsif suffix_order == :asc + arel_table[pk].asc + else # suffix_order == :desc + arel_table[pk].desc + end + Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( - attribute_name: model_class.primary_key, - order_expression: order_values.first + attribute_name: pk, + order_expression: expr ) - ]) + end + ) end def columns_with_tie_breaker_order(order_values, tie_breaker_column_order = default_tie_breaker_column_order) @@ -126,7 +155,7 @@ module Gitlab Gitlab::Pagination::Keyset::Order.build( [ *other_columns, - tie_breaker_column_order + *tie_breaker_column_order ]) end @@ -168,10 +197,16 @@ module Gitlab end def ordered_by_primary_key? - return unless order_values.one? + return unless order_values.count == primary_keys.count - attribute = order_values.first.try(:expr) - attribute && primary_key?(attribute) + order_value_names = order_values.map { |ov| ov.try(:expr) } + primary_keys?(order_value_names) + end + + def ordered_by_primary_key_prefix? + order_values.zip(primary_keys).all? do |(ov, pk)| + ov.try(:expr) == arel_table[pk] + end end def ordered_by_other_columns? @@ -187,8 +222,9 @@ module Gitlab end def has_tie_breaker? - tie_breaker_attribute = order_values.last.try(:expr) - tie_breaker_attribute && primary_key?(tie_breaker_attribute) + tie_breaker_attributes = order_values.map { |ov| ov.try(:expr) }.last(primary_keys.count) + + primary_keys?(tie_breaker_attributes) end def supported_columns?(order_values) @@ -196,10 +232,12 @@ module Gitlab end def default_tie_breaker_column_order - Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( - attribute_name: model_class.primary_key, - order_expression: arel_table[primary_key].desc - ) + primary_keys.map do |pk| + Gitlab::Pagination::Keyset::ColumnOrderDefinition.new( + attribute_name: pk, + order_expression: arel_table[pk].desc + ) + end end end end diff --git a/spec/fixtures/api/schemas/graphql/container_repository.json b/spec/fixtures/api/schemas/graphql/container_repository.json index 95cc5e28b34..1fa7f17ab00 100644 --- a/spec/fixtures/api/schemas/graphql/container_repository.json +++ b/spec/fixtures/api/schemas/graphql/container_repository.json @@ -8,7 +8,6 @@ "createdAt", "updatedAt", "tagsCount", - "canDelete", "expirationPolicyCleanupStatus", "project", "lastCleanupDeletedTagsCount", @@ -48,9 +47,6 @@ "tagsCount": { "type": "integer" }, - "canDelete": { - "type": "boolean" - }, "expirationPolicyCleanupStatus": { "type": "string", "enum": [ diff --git a/spec/fixtures/api/schemas/graphql/container_repository_details.json b/spec/fixtures/api/schemas/graphql/container_repository_details.json index dccdd85274d..1b9c4493693 100644 --- a/spec/fixtures/api/schemas/graphql/container_repository_details.json +++ b/spec/fixtures/api/schemas/graphql/container_repository_details.json @@ -23,7 +23,6 @@ "name", "path", "location", - "canDelete", "userPermissions" ], "properties": { @@ -51,9 +50,6 @@ "createdAt": { "type": "string" }, - "canDelete": { - "type": "boolean" - }, "userPermissions": { "type": "object", "additionalProperties": false, diff --git a/spec/fixtures/api/schemas/graphql/packages/package_details.json b/spec/fixtures/api/schemas/graphql/packages/package_details.json index d01d75fcd46..5ab84cb2b35 100644 --- a/spec/fixtures/api/schemas/graphql/packages/package_details.json +++ b/spec/fixtures/api/schemas/graphql/packages/package_details.json @@ -15,7 +15,6 @@ "versions", "status", "statusMessage", - "canDestroy", "lastDownloadedAt", "_links", "userPermissions" @@ -39,11 +38,6 @@ "null" ] }, - "canDestroy": { - "type": [ - "boolean" - ] - }, "packageType": { "type": [ "string" diff --git a/spec/graphql/types/container_repository_details_type_spec.rb b/spec/graphql/types/container_repository_details_type_spec.rb index 75c2078a092..61663f65b0e 100644 --- a/spec/graphql/types/container_repository_details_type_spec.rb +++ b/spec/graphql/types/container_repository_details_type_spec.rb @@ -2,9 +2,9 @@ require 'spec_helper' -RSpec.describe GitlabSchema.types['ContainerRepositoryDetails'] do +RSpec.describe GitlabSchema.types['ContainerRepositoryDetails'], feature_category: :container_registry do fields = %i[id name path location created_at updated_at expiration_policy_started_at - status tags_count can_delete expiration_policy_cleanup_status tags size manifest + status tags_count expiration_policy_cleanup_status tags size manifest project migration_state last_cleanup_deleted_tags_count userPermissions] it { expect(described_class.graphql_name).to eq('ContainerRepositoryDetails') } diff --git a/spec/graphql/types/container_repository_tag_type_spec.rb b/spec/graphql/types/container_repository_tag_type_spec.rb index 4c91c39fcd6..df623af6e51 100644 --- a/spec/graphql/types/container_repository_tag_type_spec.rb +++ b/spec/graphql/types/container_repository_tag_type_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.describe GitlabSchema.types['ContainerRepositoryTag'], feature_category: :container_registry do fields = %i[name path location digest revision short_revision - total_size created_at can_delete user_permissions referrers published_at] + total_size created_at user_permissions referrers published_at] it { expect(described_class.graphql_name).to eq('ContainerRepositoryTag') } diff --git a/spec/graphql/types/container_repository_type_spec.rb b/spec/graphql/types/container_repository_type_spec.rb index 10f5f8bf7a2..3a23fd151a4 100644 --- a/spec/graphql/types/container_repository_type_spec.rb +++ b/spec/graphql/types/container_repository_type_spec.rb @@ -6,7 +6,7 @@ RSpec.describe GitlabSchema.types['ContainerRepository'], feature_category: :con include GraphqlHelpers fields = %i[id name path location created_at updated_at expiration_policy_started_at - status tags_count can_delete expiration_policy_cleanup_status project + status tags_count expiration_policy_cleanup_status project migration_state last_cleanup_deleted_tags_count user_permissions] it { expect(described_class.graphql_name).to eq('ContainerRepository') } diff --git a/spec/graphql/types/packages/package_base_type_spec.rb b/spec/graphql/types/packages/package_base_type_spec.rb index 6b568f4ae7f..c3319539639 100644 --- a/spec/graphql/types/packages/package_base_type_spec.rb +++ b/spec/graphql/types/packages/package_base_type_spec.rb @@ -13,7 +13,7 @@ RSpec.describe GitlabSchema.types['PackageBase'], feature_category: :package_reg created_at updated_at project tags metadata - status status_message can_destroy + status status_message user_permissions ] diff --git a/spec/graphql/types/packages/package_type_spec.rb b/spec/graphql/types/packages/package_type_spec.rb index dc1cc6a8bad..934e827cdbe 100644 --- a/spec/graphql/types/packages/package_type_spec.rb +++ b/spec/graphql/types/packages/package_type_spec.rb @@ -13,8 +13,7 @@ RSpec.describe GitlabSchema.types['Package'], feature_category: :package_registr created_at updated_at project tags pipelines metadata - status can_destroy - user_permissions + status user_permissions ] expect(described_class).to include_graphql_fields(*expected_fields) diff --git a/spec/lib/gitlab/database/query_analyzers/prevent_cross_database_modification_spec.rb b/spec/lib/gitlab/database/query_analyzers/prevent_cross_database_modification_spec.rb index d5584342dd5..b827a339b94 100644 --- a/spec/lib/gitlab/database/query_analyzers/prevent_cross_database_modification_spec.rb +++ b/spec/lib/gitlab/database/query_analyzers/prevent_cross_database_modification_spec.rb @@ -19,14 +19,14 @@ RSpec.describe Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModificatio describe '.context_key' do it 'contains class name' do expect(described_class.context_key) - .to eq 'analyzer_prevent_cross_database_modification_context'.to_sym + .to eq :analyzer_prevent_cross_database_modification_context end end describe '.suppress_key' do it 'contains class name' do expect(described_class.suppress_key) - .to eq 'analyzer_prevent_cross_database_modification_suppressed'.to_sym + .to eq :analyzer_prevent_cross_database_modification_suppressed end end end diff --git a/spec/lib/gitlab/database/reindexing/grafana_notifier_spec.rb b/spec/lib/gitlab/database/reindexing/grafana_notifier_spec.rb index e67c97cbf9c..b0b35eae9fa 100644 --- a/spec/lib/gitlab/database/reindexing/grafana_notifier_spec.rb +++ b/spec/lib/gitlab/database/reindexing/grafana_notifier_spec.rb @@ -18,7 +18,7 @@ RSpec.describe Gitlab::Database::Reindexing::GrafanaNotifier do let(:headers) do { 'Content-Type': 'application/json', - 'Authorization': "Bearer #{api_key}" + Authorization: "Bearer #{api_key}" } end diff --git a/spec/lib/gitlab/diff/formatters/text_formatter_spec.rb b/spec/lib/gitlab/diff/formatters/text_formatter_spec.rb index 5270c1777bc..5722b952388 100644 --- a/spec/lib/gitlab/diff/formatters/text_formatter_spec.rb +++ b/spec/lib/gitlab/diff/formatters/text_formatter_spec.rb @@ -47,8 +47,8 @@ RSpec.describe Gitlab::Diff::Formatters::TextFormatter do describe "#==" do it "is false when the line_range changes" do - formatter_1 = described_class.new(base.merge(line_range: { "start": { "line_code" => "foo" }, "end": { "line_code" => "bar" } })) - formatter_2 = described_class.new(base.merge(line_range: { "start": { "line_code" => "foo" }, "end": { "line_code" => "baz" } })) + formatter_1 = described_class.new(base.merge(line_range: { start: { "line_code" => "foo" }, end: { "line_code" => "bar" } })) + formatter_2 = described_class.new(base.merge(line_range: { start: { "line_code" => "foo" }, end: { "line_code" => "baz" } })) expect(formatter_1).not_to eq(formatter_2) end diff --git a/spec/lib/gitlab/fake_application_settings_spec.rb b/spec/lib/gitlab/fake_application_settings_spec.rb index b300498e898..0f5a9bf8aba 100644 --- a/spec/lib/gitlab/fake_application_settings_spec.rb +++ b/spec/lib/gitlab/fake_application_settings_spec.rb @@ -6,7 +6,7 @@ RSpec.describe Gitlab::FakeApplicationSettings do let(:defaults) do described_class.defaults.merge( foobar: 'asdf', - 'test?'.to_sym => 123, + test?: 123, # these two settings have no default in ApplicationSettingImplementation, # so we need to set one here domain_denylist: [], diff --git a/spec/lib/gitlab/grape_logging/formatters/lograge_with_timestamp_spec.rb b/spec/lib/gitlab/grape_logging/formatters/lograge_with_timestamp_spec.rb index f14f0098a1f..6c707d9a44d 100644 --- a/spec/lib/gitlab/grape_logging/formatters/lograge_with_timestamp_spec.rb +++ b/spec/lib/gitlab/grape_logging/formatters/lograge_with_timestamp_spec.rb @@ -14,9 +14,9 @@ RSpec.describe Gitlab::GrapeLogging::Formatters::LogrageWithTimestamp do method: 'PUT', path: '/api/v4/projects/1', params: { - 'description': '[FILTERED]', - 'name': 'gitlab test', - 'int': 42 + description: '[FILTERED]', + name: 'gitlab test', + int: 42 }, host: 'localhost', remote_ip: '127.0.0.1', diff --git a/spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb b/spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb index 1924cd687e4..a9f9b731981 100644 --- a/spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb +++ b/spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb @@ -27,7 +27,7 @@ RSpec.describe Gitlab::GrapeLogging::Loggers::QueueDurationLogger do it 'adds the duration to log parameters' do travel_to(start_time) do - expect(subject.parameters(mock_request, nil)).to eq( { 'queue_duration_s': 2.seconds.to_f }) + expect(subject.parameters(mock_request, nil)).to eq( { queue_duration_s: 2.seconds.to_f }) end end end diff --git a/spec/lib/gitlab/grape_logging/loggers/token_logger_spec.rb b/spec/lib/gitlab/grape_logging/loggers/token_logger_spec.rb index d2022a28a90..1c3a62ec789 100644 --- a/spec/lib/gitlab/grape_logging/loggers/token_logger_spec.rb +++ b/spec/lib/gitlab/grape_logging/loggers/token_logger_spec.rb @@ -21,13 +21,13 @@ RSpec.describe Gitlab::GrapeLogging::Loggers::TokenLogger do let(:mock_request) do instance_double(ActionDispatch::Request, 'env', env: { - 'gitlab.api.token' => { 'token_id': token_id, 'token_type': token_type } + 'gitlab.api.token' => { token_id: token_id, token_type: token_type } } ) end it 'adds the token information to log parameters' do - expect(subject.parameters(mock_request, nil)).to eq( { 'token_id': 1, 'token_type': "PersonalAccessToken" }) + expect(subject.parameters(mock_request, nil)).to eq( { token_id: 1, token_type: "PersonalAccessToken" }) end end end diff --git a/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb b/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb index e052ba5707f..d50e6aa1f9d 100644 --- a/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb +++ b/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb @@ -341,6 +341,12 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::Connection do self.table_name = 'no_primary_key' self.primary_key = nil end + + # Gitlab::Pagination::Keyset::SimpleOrderBuilder checks for multiple primary keys + # directly, without this mock it fails because the table doesn't actually exist. + allow(NoPrimaryKey.connection).to receive(:primary_keys) + .with('no_primary_key') + .and_return([]) end let(:nodes) { NoPrimaryKey.all } diff --git a/spec/lib/gitlab/harbor/client_spec.rb b/spec/lib/gitlab/harbor/client_spec.rb index 745e22191bd..5f2310968ae 100644 --- a/spec/lib/gitlab/harbor/client_spec.rb +++ b/spec/lib/gitlab/harbor/client_spec.rb @@ -28,13 +28,13 @@ RSpec.describe Gitlab::Harbor::Client do let(:mock_response) do [ { - "artifact_count": 1, - "creation_time": "2022-03-13T09:36:43.240Z", - "id": 1, - "name": "jihuprivate/busybox", - "project_id": 4, - "pull_count": 0, - "update_time": "2022-03-13T09:36:43.240Z" + artifact_count: 1, + creation_time: "2022-03-13T09:36:43.240Z", + id: 1, + name: "jihuprivate/busybox", + project_id: 4, + pull_count: 0, + update_time: "2022-03-13T09:36:43.240Z" } ] end @@ -50,7 +50,7 @@ RSpec.describe Gitlab::Harbor::Client do stub_request(:get, "https://demo.goharbor.io/api/v2.0/projects/testproject/repositories") .with( headers: { - 'Authorization': 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=', + Authorization: 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=', 'Content-Type': 'application/json' }) .to_return(status: 200, body: mock_response.to_json, headers: { "x-total-count": 2 }) @@ -66,7 +66,7 @@ RSpec.describe Gitlab::Harbor::Client do stub_request(:get, "https://demo.goharbor.io/api/v2.0/projects/testproject/repositories") .with( headers: { - 'Authorization': 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=', + Authorization: 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=', 'Content-Type': 'application/json' }) .to_return(status: 404, body: {}.to_json) @@ -84,7 +84,7 @@ RSpec.describe Gitlab::Harbor::Client do stub_request(:get, "https://demo.goharbor.io/api/v2.0/projects/testproject/repositories") .with( headers: { - 'Authorization': 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=', + Authorization: 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=', 'Content-Type': 'application/json' }) .to_return(status: 200, body: '[not json}') @@ -103,27 +103,27 @@ RSpec.describe Gitlab::Harbor::Client do let(:mock_response) do [ { - "digest": "sha256:661e8e44e5d7290fbd42d0495ab4ff6fdf1ad251a9f358969b3264a22107c14d", - "icon": "sha256:0048162a053eef4d4ce3fe7518615bef084403614f8bca43b40ae2e762e11e06", - "id": 1, - "project_id": 1, - "pull_time": "0001-01-01T00:00:00.000Z", - "push_time": "2022-04-23T08:04:08.901Z", - "repository_id": 1, - "size": 126745886, - "tags": [ + digest: "sha256:661e8e44e5d7290fbd42d0495ab4ff6fdf1ad251a9f358969b3264a22107c14d", + icon: "sha256:0048162a053eef4d4ce3fe7518615bef084403614f8bca43b40ae2e762e11e06", + id: 1, + project_id: 1, + pull_time: "0001-01-01T00:00:00.000Z", + push_time: "2022-04-23T08:04:08.901Z", + repository_id: 1, + size: 126745886, + tags: [ { - "artifact_id": 1, - "id": 1, - "immutable": false, - "name": "2", - "pull_time": "0001-01-01T00:00:00.000Z", - "push_time": "2022-04-23T08:04:08.920Z", - "repository_id": 1, - "signed": false + artifact_id: 1, + id: 1, + immutable: false, + name: "2", + pull_time: "0001-01-01T00:00:00.000Z", + push_time: "2022-04-23T08:04:08.920Z", + repository_id: 1, + signed: false } ], - "type": "IMAGE" + type: "IMAGE" } ] end @@ -139,7 +139,7 @@ RSpec.describe Gitlab::Harbor::Client do stub_request(:get, "https://demo.goharbor.io/api/v2.0/projects/testproject/repositories/test/artifacts") .with( headers: { - 'Authorization': 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=', + Authorization: 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=', 'Content-Type': 'application/json' }) .to_return(status: 200, body: mock_response.to_json, headers: { "x-total-count": 1 }) @@ -156,7 +156,7 @@ RSpec.describe Gitlab::Harbor::Client do stub_request(:get, "https://demo.goharbor.io/api/v2.0/projects/testproject/repositories/test/artifacts") .with( headers: { - 'Authorization': 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=', + Authorization: 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=', 'Content-Type': 'application/json' }) .to_return(status: 404, body: {}.to_json) @@ -174,7 +174,7 @@ RSpec.describe Gitlab::Harbor::Client do stub_request(:get, "https://demo.goharbor.io/api/v2.0/projects/testproject/repositories/test/artifacts") .with( headers: { - 'Authorization': 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=', + Authorization: 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=', 'Content-Type': 'application/json' }) .to_return(status: 200, body: '[not json}') @@ -193,14 +193,14 @@ RSpec.describe Gitlab::Harbor::Client do let(:mock_response) do [ { - "artifact_id": 1, - "id": 1, - "immutable": false, - "name": "2", - "pull_time": "0001-01-01T00:00:00.000Z", - "push_time": "2022-04-23T08:04:08.920Z", - "repository_id": 1, - "signed": false + artifact_id: 1, + id: 1, + immutable: false, + name: "2", + pull_time: "0001-01-01T00:00:00.000Z", + push_time: "2022-04-23T08:04:08.920Z", + repository_id: 1, + signed: false } ] end @@ -216,7 +216,7 @@ RSpec.describe Gitlab::Harbor::Client do stub_request(:get, "https://demo.goharbor.io/api/v2.0/projects/testproject/repositories/test/artifacts/1/tags") .with( headers: { - 'Authorization': 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=', + Authorization: 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=', 'Content-Type': 'application/json' }) .to_return(status: 200, body: mock_response.to_json, headers: { "x-total-count": 1 }) @@ -233,7 +233,7 @@ RSpec.describe Gitlab::Harbor::Client do stub_request(:get, "https://demo.goharbor.io/api/v2.0/projects/testproject/repositories/test/artifacts/1/tags") .with( headers: { - 'Authorization': 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=', + Authorization: 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=', 'Content-Type': 'application/json' }) .to_return(status: 404, body: {}.to_json) @@ -251,7 +251,7 @@ RSpec.describe Gitlab::Harbor::Client do stub_request(:get, "https://demo.goharbor.io/api/v2.0/projects/testproject/repositories/test/artifacts/1/tags") .with( headers: { - 'Authorization': 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=', + Authorization: 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=', 'Content-Type': 'application/json' }) .to_return(status: 200, body: '[not json}') @@ -270,8 +270,8 @@ RSpec.describe Gitlab::Harbor::Client do stub_request(:head, "https://demo.goharbor.io/api/v2.0/projects?project_name=testproject") .with( headers: { - 'Accept': 'application/json', - 'Authorization': 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=', + Accept: 'application/json', + Authorization: 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=', 'Content-Type': 'application/json' }) .to_return(status: 200, body: '', headers: {}) diff --git a/spec/lib/gitlab/harbor/query_spec.rb b/spec/lib/gitlab/harbor/query_spec.rb index fe05643e04a..8c9c9ec0674 100644 --- a/spec/lib/gitlab/harbor/query_spec.rb +++ b/spec/lib/gitlab/harbor/query_spec.rb @@ -174,13 +174,13 @@ RSpec.describe Gitlab::Harbor::Query do body: [ { - "id": 3, - "name": "testproject/thirdbusybox", - "artifact_count": 1, - "creation_time": "2022-03-15T07:12:14.479Z", - "update_time": "2022-03-15T07:12:14.479Z", - "project_id": 3, - "pull_count": 0 + id: 3, + name: "testproject/thirdbusybox", + artifact_count: 1, + creation_time: "2022-03-15T07:12:14.479Z", + update_time: "2022-03-15T07:12:14.479Z", + project_id: 3, + pull_count: 0 }.with_indifferent_access ] } @@ -194,8 +194,8 @@ RSpec.describe Gitlab::Harbor::Query do end expect(query.repositories.first).to include( - "name": "testproject/thirdbusybox", - "artifact_count": 1 + name: "testproject/thirdbusybox", + artifact_count: 1 ) end end @@ -245,34 +245,34 @@ RSpec.describe Gitlab::Harbor::Query do body: [ { - "digest": "sha256:14d4f50961544fdb669075c442509f194bdc4c0e344bde06e35dbd55af842a38", - "icon": "sha256:0048162a053eef4d4ce3fe7518615bef084403614f8bca43b40ae2e762e11e06", - "id": 5, - "project_id": 14, - "push_time": "2022-03-22T09:04:56.170Z", - "repository_id": 5, - "size": 774790, - "tags": [ + digest: "sha256:14d4f50961544fdb669075c442509f194bdc4c0e344bde06e35dbd55af842a38", + icon: "sha256:0048162a053eef4d4ce3fe7518615bef084403614f8bca43b40ae2e762e11e06", + id: 5, + project_id: 14, + push_time: "2022-03-22T09:04:56.170Z", + repository_id: 5, + size: 774790, + tags: [ { - "artifact_id": 5, - "id": 7, - "immutable": false, - "name": "2", - "pull_time": "0001-01-01T00:00:00.000Z", - "push_time": "2022-03-22T09:05:04.844Z", - "repository_id": 5 + artifact_id: 5, + id: 7, + immutable: false, + name: "2", + pull_time: "0001-01-01T00:00:00.000Z", + push_time: "2022-03-22T09:05:04.844Z", + repository_id: 5 }, { - "artifact_id": 5, - "id": 6, - "immutable": false, - "name": "1", - "pull_time": "0001-01-01T00:00:00.000Z", - "push_time": "2022-03-22T09:04:56.186Z", - "repository_id": 5 + artifact_id: 5, + id: 6, + immutable: false, + name: "1", + pull_time: "0001-01-01T00:00:00.000Z", + push_time: "2022-03-22T09:04:56.186Z", + repository_id: 5 } ], - "type": "IMAGE" + type: "IMAGE" }.with_indifferent_access ] } @@ -288,8 +288,8 @@ RSpec.describe Gitlab::Harbor::Query do artifact = query.artifacts.first expect(artifact).to include( - "digest": "sha256:14d4f50961544fdb669075c442509f194bdc4c0e344bde06e35dbd55af842a38", - "push_time": "2022-03-22T09:04:56.170Z" + digest: "sha256:14d4f50961544fdb669075c442509f194bdc4c0e344bde06e35dbd55af842a38", + push_time: "2022-03-22T09:04:56.170Z" ) expect(artifact["tags"].size).to eq(2) end @@ -338,22 +338,22 @@ RSpec.describe Gitlab::Harbor::Query do body: [ { - "artifact_id": 5, - "id": 7, - "immutable": false, - "name": "2", - "pull_time": "0001-01-01T00:00:00.000Z", - "push_time": "2022-03-22T09:05:04.844Z", - "repository_id": 5 + artifact_id: 5, + id: 7, + immutable: false, + name: "2", + pull_time: "0001-01-01T00:00:00.000Z", + push_time: "2022-03-22T09:05:04.844Z", + repository_id: 5 }, { - "artifact_id": 5, - "id": 6, - "immutable": false, - "name": "1", - "pull_time": "0001-01-01T00:00:00.000Z", - "push_time": "2022-03-22T09:04:56.186Z", - "repository_id": 5 + artifact_id: 5, + id: 6, + immutable: false, + name: "1", + pull_time: "0001-01-01T00:00:00.000Z", + push_time: "2022-03-22T09:04:56.186Z", + repository_id: 5 }.with_indifferent_access ] } @@ -369,8 +369,8 @@ RSpec.describe Gitlab::Harbor::Query do tag = query.tags.first expect(tag).to include( - "immutable": false, - "push_time": "2022-03-22T09:05:04.844Z" + immutable: false, + push_time: "2022-03-22T09:05:04.844Z" ) end end diff --git a/spec/lib/gitlab/import_export/project/relation_factory_spec.rb b/spec/lib/gitlab/import_export/project/relation_factory_spec.rb index 5d7fa5bb934..406d8f8a5d8 100644 --- a/spec/lib/gitlab/import_export/project/relation_factory_spec.rb +++ b/spec/lib/gitlab/import_export/project/relation_factory_spec.rb @@ -280,16 +280,16 @@ RSpec.describe Gitlab::ImportExport::Project::RelationFactory, :use_clean_rails_ let(:relation_sym) { :labels } let(:relation_hash) do { - "id": 3, - "title": "test3", - "color": "#428bca", - "group_id": project.group.id, - "created_at": "2016-07-22T08:55:44.161Z", - "updated_at": "2016-07-22T08:55:44.161Z", - "template": false, - "description": "", - "project_id": project.id, - "type": "GroupLabel" + id: 3, + title: "test3", + color: "#428bca", + group_id: project.group.id, + created_at: "2016-07-22T08:55:44.161Z", + updated_at: "2016-07-22T08:55:44.161Z", + template: false, + description: "", + project_id: project.id, + type: "GroupLabel" } end diff --git a/spec/lib/gitlab/lfs/client_spec.rb b/spec/lib/gitlab/lfs/client_spec.rb index db450c79dfa..dc2c1a01e14 100644 --- a/spec/lib/gitlab/lfs/client_spec.rb +++ b/spec/lib/gitlab/lfs/client_spec.rb @@ -91,7 +91,7 @@ RSpec.describe Gitlab::Lfs::Client do def stub_batch(objects:, headers:, operation: 'upload', transfer: 'basic') objects = objects.as_json(only: [:oid, :size]) - body = { operation: operation, 'transfers': [transfer], objects: objects }.to_json + body = { operation: operation, transfers: [transfer], objects: objects }.to_json headers = { 'Accept' => git_lfs_content_type, diff --git a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb index 58f90a3f1fb..0b603ef52f1 100644 --- a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb +++ b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb @@ -410,14 +410,14 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do end it 'does not record DB role metrics' do - expect(transaction).not_to receive(:increment).with("gitlab_transaction_db_primary_count_total".to_sym, any_args) - expect(transaction).not_to receive(:increment).with("gitlab_transaction_db_replica_count_total".to_sym, any_args) + expect(transaction).not_to receive(:increment).with(:gitlab_transaction_db_primary_count_total, any_args) + expect(transaction).not_to receive(:increment).with(:gitlab_transaction_db_replica_count_total, any_args) - expect(transaction).not_to receive(:increment).with("gitlab_transaction_db_primary_cached_count_total".to_sym, any_args) - expect(transaction).not_to receive(:increment).with("gitlab_transaction_db_replica_cached_count_total".to_sym, any_args) + expect(transaction).not_to receive(:increment).with(:gitlab_transaction_db_primary_cached_count_total, any_args) + expect(transaction).not_to receive(:increment).with(:gitlab_transaction_db_replica_cached_count_total, any_args) - expect(transaction).not_to receive(:observe).with("gitlab_sql_primary_duration_seconds".to_sym, any_args) - expect(transaction).not_to receive(:observe).with("gitlab_sql_replica_duration_seconds".to_sym, any_args) + expect(transaction).not_to receive(:observe).with(:gitlab_sql_primary_duration_seconds, any_args) + expect(transaction).not_to receive(:observe).with(:gitlab_sql_replica_duration_seconds, any_args) subscriber.sql(event) end diff --git a/spec/lib/gitlab/middleware/sidekiq_shard_awareness_validation_spec.rb b/spec/lib/gitlab/middleware/sidekiq_shard_awareness_validation_spec.rb index 6adaf465609..08c1bc2befb 100644 --- a/spec/lib/gitlab/middleware/sidekiq_shard_awareness_validation_spec.rb +++ b/spec/lib/gitlab/middleware/sidekiq_shard_awareness_validation_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.describe Gitlab::Middleware::SidekiqShardAwarenessValidation, feature_category: :scalability do let(:app) { ->(_env) { Sidekiq.redis(&:ping) } } - let(:env) { {} } + let(:env) { { 'PATH_INFO' => path } } around do |example| original_state = Thread.current[:validate_sidekiq_shard_awareness] @@ -16,11 +16,36 @@ RSpec.describe Gitlab::Middleware::SidekiqShardAwarenessValidation, feature_cate end describe '#call' do + let(:path) { 'api/v4/projects/1' } + subject(:app_call) { described_class.new(app).call(env) } it 'enables shard-awareness check within the context of a request' do expect { Sidekiq.redis(&:ping) }.not_to raise_error expect { app_call }.to raise_error(Gitlab::SidekiqSharding::Validator::UnroutedSidekiqApiError) end + + shared_examples 'no errors for sidekiq UI' do + it 'does not enable validation' do + expect { Sidekiq.redis(&:ping) }.not_to raise_error + expect { app_call }.not_to raise_error + end + end + + context 'when using sidekiq UI path' do + let(:path) { '/admin/sidekiq/queues' } + + it_behaves_like 'no errors for sidekiq UI' + + context 'with relative path' do + let(:relative_url_root) { '/gitlab' } + + before do + stub_config_setting(relative_url_root: relative_url_root) + end + + it_behaves_like 'no errors for sidekiq UI' + end + end end end diff --git a/spec/lib/gitlab/pagination/keyset/simple_order_builder_spec.rb b/spec/lib/gitlab/pagination/keyset/simple_order_builder_spec.rb index 9a59a515d35..cddb7d54bbf 100644 --- a/spec/lib/gitlab/pagination/keyset/simple_order_builder_spec.rb +++ b/spec/lib/gitlab/pagination/keyset/simple_order_builder_spec.rb @@ -2,67 +2,191 @@ require 'spec_helper' -RSpec.describe Gitlab::Pagination::Keyset::SimpleOrderBuilder do - let(:ordered_scope) { described_class.build(scope).first } +RSpec.describe Gitlab::Pagination::Keyset::SimpleOrderBuilder, + :unlimited_max_formatted_output_length, + feature_category: :database do + let(:extraction_successful) { described_class.build(scope).last } + let(:ordered_scope) do + ordered_scope, success = described_class.build(scope) + raise "Failed to extract order" unless success + + ordered_scope + end + let(:order_object) { Gitlab::Pagination::Keyset::Order.extract_keyset_order_object(ordered_scope) } - let(:column_definition) { order_object.column_definitions.first } + let(:column_definitions) { order_object.column_definitions } + let(:column_definition) { column_definitions.first } subject(:sql_with_order) { ordered_scope.to_sql } context 'when no order present' do - let(:scope) { Project.where(id: [1, 2, 3]) } - - it 'orders by primary key' do - expect(sql_with_order).to end_with('ORDER BY "projects"."id" DESC') - end - - it 'sets the column definition to not nullable' do - expect(column_definition).to be_not_nullable - end - - context "when the order scope's model uses default_scope" do - let(:scope) do - model = Class.new(ApplicationRecord) do - self.table_name = 'events' - - default_scope { reorder(nil) } # rubocop:disable Cop/DefaultScope - end - - model.reorder(nil) - end + context 'with a single-column primary key' do + let(:scope) { Project.where(id: [1, 2, 3]) } it 'orders by primary key' do - expect(sql_with_order).to end_with('ORDER BY "events"."id" DESC') + expect(sql_with_order).to end_with('ORDER BY "projects"."id" DESC') + end + + it 'sets the column definition to not nullable' do + expect(column_definition).to be_not_nullable + end + + context "when the order scope's model uses default_scope" do + let(:scope) do + model = Class.new(ApplicationRecord) do + self.table_name = 'events' + + default_scope { reorder(nil) } # rubocop:disable Cop/DefaultScope -- this is testing default scope behavior + end + + model.reorder(nil) + end + + it 'orders by primary key' do + expect(sql_with_order).to end_with('ORDER BY "events"."id" DESC') + end + end + end + + context 'with a multicolumn primary key' do + let(:scope) { MergeRequestDiffCommit.where(merge_request_diff_id: [1, 2, 3]) } + + it 'orders by primary keys' do + expected = <<~SQL.strip.tr("\n", ' ') + ORDER BY "merge_request_diff_commits"."merge_request_diff_id" DESC, + "merge_request_diff_commits"."relative_order" DESC + SQL + expect(sql_with_order).to end_with(expected) + end + + it 'sets the column definitions not nullable' do + expect(column_definitions).to all be_not_nullable end end end context 'when primary key order present' do - let(:scope) { Project.where(id: [1, 2, 3]).order(id: :asc) } + context 'with a single column primary key' do + let(:scope) { Project.where(id: [1, 2, 3]).order(id: :asc) } - it 'orders by primary key without altering the direction' do - expect(sql_with_order).to end_with('ORDER BY "projects"."id" ASC') + it 'orders by primary key without altering the direction' do + expect(sql_with_order).to end_with('ORDER BY "projects"."id" ASC') + end + end + + context 'with a multicolumn primary key specifying all columns' do + let(:scope) do + MergeRequestDiffCommit.where(merge_request_diff_id: [1, 2, 3]) + .order(merge_request_diff_id: :asc, relative_order: :asc) + end + + it 'orders by the full primary key' do + expected = <<~SQL.strip.tr("\n", ' ') + ORDER BY "merge_request_diff_commits"."merge_request_diff_id" ASC, + "merge_request_diff_commits"."relative_order" ASC + SQL + expect(sql_with_order).to end_with(expected) + end + end + + context 'with a multicolumn primary key specifying all columns in a different order' do + let(:scope) do + MergeRequestDiffCommit.where(merge_request_diff_id: [1, 2, 3]) + .order(relative_order: :asc, merge_request_diff_id: :asc) + end + + it 'orders by the full primary key' do + expected = <<~SQL.strip.tr("\n", ' ') + ORDER BY "merge_request_diff_commits"."relative_order" ASC, + "merge_request_diff_commits"."merge_request_diff_id" ASC + SQL + expect(sql_with_order).to end_with(expected) + end + end + end + + context 'when ordered by a prefix of a composite primary key' do + context 'in ascending order' do + let(:scope) { MergeRequestDiffCommit.order(:merge_request_diff_id) } + + it 'orders by the full primary key in ascending order' do + expected = <<~SQL.strip.tr("\n", ' ') + ORDER BY "merge_request_diff_commits"."merge_request_diff_id" ASC, + "merge_request_diff_commits"."relative_order" ASC + SQL + expect(sql_with_order).to end_with(expected) + end + end + + context 'in descending order' do + let(:scope) { MergeRequestDiffCommit.order(merge_request_diff_id: :desc) } + + it 'orders by the full primary key in descending order' do + expected = <<~SQL.strip.tr("\n", ' ') + ORDER BY "merge_request_diff_commits"."merge_request_diff_id" DESC, + "merge_request_diff_commits"."relative_order" DESC + SQL + expect(sql_with_order).to end_with(expected) + end end end context 'when ordered by other column' do - let(:scope) { Project.where(id: [1, 2, 3]).order(created_at: :asc) } + context 'with a single-column primary key' do + let(:scope) { Project.where(id: [1, 2, 3]).order(created_at: :asc) } - it 'adds extra primary key order as tie-breaker' do - expect(sql_with_order).to end_with('ORDER BY "projects"."created_at" ASC, "projects"."id" DESC') + it 'adds extra primary key order as tie-breaker' do + expect(sql_with_order).to end_with('ORDER BY "projects"."created_at" ASC, "projects"."id" DESC') + end + + it 'sets the column definition for created_at' do + expect(column_definition.attribute_name).to eq('created_at') + expect(column_definition.nullable?).to eq(true) # be_nullable calls non_null? method for some reason + end end - it 'sets the column definition for created_at' do - expect(column_definition.attribute_name).to eq('created_at') - expect(column_definition.nullable?).to eq(true) # be_nullable calls non_null? method for some reason + context 'with a multi-column primary key' do + let(:scope) { MergeRequestDiffCommit.where(merge_request_diff_id: [1, 2, 3]).order(:authored_date) } + + it 'adds extra primary keys as tie-breaker' do + expect(sql_with_order).to end_with(<<~SQL.strip.tr("\n", ' ')) + ORDER BY "merge_request_diff_commits"."authored_date" ASC, + "merge_request_diff_commits"."merge_request_diff_id" DESC, + "merge_request_diff_commits"."relative_order" DESC + SQL + end + + it 'sets the column definition for authored_date nullable' do + expect(column_definition.attribute_name).to eq('authored_date') + expect(column_definition.nullable?).to eq(true) # be_nullable calls non_null? method for some reason + end end end context 'when ordered by two columns where the last one is the tie breaker' do - let(:scope) { Project.where(id: [1, 2, 3]).order(created_at: :asc, id: :asc) } + context 'with a single column primary key' do + let(:scope) { Project.where(id: [1, 2, 3]).order(created_at: :asc, id: :asc) } - it 'preserves the order' do - expect(sql_with_order).to end_with('ORDER BY "projects"."created_at" ASC, "projects"."id" ASC') + it 'preserves the order' do + expect(sql_with_order).to end_with('ORDER BY "projects"."created_at" ASC, "projects"."id" ASC') + end + end + + context 'with a multi-column primary key' do + context 'when the full multi-column primary key is given as a tie breaker' do + let(:scope) do + MergeRequestDiffCommit.where(merge_request_diff_id: [1, 2, 3]) + .order(authored_date: :asc, merge_request_diff_id: :asc, relative_order: :asc) + end + + it 'preserves the order' do + expect(sql_with_order).to end_with(<<~SQL.strip.tr("\n", ' ')) + ORDER BY "merge_request_diff_commits"."authored_date" ASC, + "merge_request_diff_commits"."merge_request_diff_id" ASC, + "merge_request_diff_commits"."relative_order" ASC + SQL + end + end end end diff --git a/spec/lib/gitlab/prometheus_client_spec.rb b/spec/lib/gitlab/prometheus_client_spec.rb index d0bfc6e5610..8f39a15eb45 100644 --- a/spec/lib/gitlab/prometheus_client_spec.rb +++ b/spec/lib/gitlab/prometheus_client_spec.rb @@ -175,17 +175,17 @@ RSpec.describe Gitlab::PrometheusClient do let(:query) { 'avg (metric) by (job)' } let(:prometheus_response) do { - "status": "success", - "data": { - "resultType": "vector", - "result": [ + status: "success", + data: { + resultType: "vector", + result: [ { - "metric": { "job" => "gitlab-rails" }, - "value": [1488758662.506, "1"] + metric: { "job" => "gitlab-rails" }, + value: [1488758662.506, "1"] }, { - "metric": { "job" => "gitlab-sidekiq" }, - "value": [1488758662.506, "2"] + metric: { "job" => "gitlab-sidekiq" }, + value: [1488758662.506, "2"] } ] } diff --git a/spec/lib/gitlab/search/abuse_validators/no_abusive_coercion_from_string_validator_spec.rb b/spec/lib/gitlab/search/abuse_validators/no_abusive_coercion_from_string_validator_spec.rb index 76280e65867..08ad5ddff4a 100644 --- a/spec/lib/gitlab/search/abuse_validators/no_abusive_coercion_from_string_validator_spec.rb +++ b/spec/lib/gitlab/search/abuse_validators/no_abusive_coercion_from_string_validator_spec.rb @@ -15,8 +15,8 @@ RSpec.describe Gitlab::Search::AbuseValidators::NoAbusiveCoercionFromStringValid using ::RSpec::Parameterized::TableSyntax where(:attribute_value, :valid?) do - ['this is an arry'] | false - { 'this': 'is a hash' } | false + ['this is an arry'] | false + { this: 'is a hash' } | false 123 | false 456.78 | false 'now this is a string' | true diff --git a/spec/lib/gitlab/slug/path_spec.rb b/spec/lib/gitlab/slug/path_spec.rb index bbc2a05713d..86f36e1c32c 100644 --- a/spec/lib/gitlab/slug/path_spec.rb +++ b/spec/lib/gitlab/slug/path_spec.rb @@ -5,7 +5,7 @@ require 'fast_spec_helper' RSpec.describe Gitlab::Slug::Path, feature_category: :shared do describe '#generate' do { - 'name': 'name', + name: 'name', 'james.atom@bond.com': 'james', '--foobar--': 'foobar--', '--foo_bar--': 'foo_bar--', diff --git a/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb b/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb index 581e791b4b6..6ce0951e298 100644 --- a/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb +++ b/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb @@ -55,7 +55,9 @@ RSpec.describe 'container repository details', feature_category: :container_regi <<~GQL query($id: ContainerRepositoryID!, $n: ContainerRepositoryTagSort) { containerRepository(id: $id) { - canDelete + userPermissions { + destroyContainerRepository + } tags(sort: $n) { edges { node { @@ -68,7 +70,7 @@ RSpec.describe 'container repository details', feature_category: :container_regi GQL end - where(:project_visibility, :role, :access_granted, :can_delete) do + where(:project_visibility, :role, :access_granted, :destroy_container_repository) do :private | :maintainer | true | true :private | :developer | true | true :private | :reporter | true | false @@ -92,7 +94,7 @@ RSpec.describe 'container repository details', feature_category: :container_regi if access_granted expect(tags_response.size).to eq(repository_tags.size) - expect(container_repository_details_response.dig('canDelete')).to eq(can_delete) + expect(container_repository_details_response.dig('userPermissions', 'destroyContainerRepository')).to eq(destroy_container_repository) else expect(container_repository_details_response).to eq(nil) end diff --git a/spec/requests/api/graphql/group/container_repositories_spec.rb b/spec/requests/api/graphql/group/container_repositories_spec.rb index 9206ead1534..5d9c5050add 100644 --- a/spec/requests/api/graphql/group/container_repositories_spec.rb +++ b/spec/requests/api/graphql/group/container_repositories_spec.rb @@ -14,12 +14,12 @@ RSpec.describe 'getting container repositories in a group', feature_category: :s let_it_be(:container_repositories) { [container_repository, container_repositories_delete_scheduled, container_repositories_delete_failed].flatten } let_it_be(:container_expiration_policy) { project.container_expiration_policy } - let(:excluded_fields) { [] } + let(:excluded_fields) { %w[pipeline jobs productAnalyticsState mlModels] } let(:container_repositories_fields) do <<~GQL edges { node { - #{all_graphql_fields_for('container_repositories'.classify, max_depth: 1, excluded: excluded_fields)} + #{all_graphql_fields_for('container_repositories'.classify, excluded: excluded_fields)} } } GQL @@ -64,7 +64,7 @@ RSpec.describe 'getting container repositories in a group', feature_category: :s context 'with different permissions' do let_it_be(:user) { create(:user) } - where(:group_visibility, :role, :access_granted, :can_delete) do + where(:group_visibility, :role, :access_granted, :destroy_container_repository) do :private | :maintainer | true | true :private | :developer | true | true :private | :reporter | true | false @@ -91,7 +91,7 @@ RSpec.describe 'getting container repositories in a group', feature_category: :s if access_granted expect(container_repositories_response.size).to eq(container_repositories.size) container_repositories_response.each do |repository_response| - expect(repository_response.dig('node', 'canDelete')).to eq(can_delete) + expect(repository_response.dig('node', 'userPermissions', 'destroyContainerRepository')).to eq(destroy_container_repository) end else expect(container_repositories_response).to eq(nil) @@ -156,7 +156,7 @@ RSpec.describe 'getting container repositories in a group', feature_category: :s it_behaves_like 'handling graphql network errors with the container registry' it_behaves_like 'not hitting graphql network errors with the container registry' do - let(:excluded_fields) { %w[tags tagsCount] } + let(:excluded_fields) { %w[pipeline jobs tags tagsCount productAnalyticsState mlModels] } end it 'returns the total count of container repositories' do diff --git a/spec/requests/api/graphql/project/container_repositories_spec.rb b/spec/requests/api/graphql/project/container_repositories_spec.rb index 2307409c383..9a2d1c2317b 100644 --- a/spec/requests/api/graphql/project/container_repositories_spec.rb +++ b/spec/requests/api/graphql/project/container_repositories_spec.rb @@ -65,7 +65,7 @@ RSpec.describe 'getting container repositories in a project', feature_category: context 'with different permissions' do let_it_be(:user) { create(:user) } - where(:project_visibility, :role, :access_granted, :can_delete) do + where(:project_visibility, :role, :access_granted, :destroy_container_repository) do :private | :maintainer | true | true :private | :developer | true | true :private | :reporter | true | false @@ -90,7 +90,7 @@ RSpec.describe 'getting container repositories in a project', feature_category: if access_granted expect(container_repositories_response.size).to eq(container_repositories.size) container_repositories_response.each do |repository_response| - expect(repository_response.dig('node', 'canDelete')).to eq(can_delete) + expect(repository_response.dig('node', 'userPermissions', 'destroyContainerRepository')).to eq(destroy_container_repository) end else expect(container_repositories_response).to eq(nil) diff --git a/spec/requests/api/terraform/modules/v1/project_packages_spec.rb b/spec/requests/api/terraform/modules/v1/project_packages_spec.rb index b21a83b727d..eea4bd9d158 100644 --- a/spec/requests/api/terraform/modules/v1/project_packages_spec.rb +++ b/spec/requests/api/terraform/modules/v1/project_packages_spec.rb @@ -94,20 +94,6 @@ RSpec.describe API::Terraform::Modules::V1::ProjectPackages, feature_category: : api_request end - - context 'when feature flag is disabled' do - before do - stub_feature_flags(skip_copy_operation_in_terraform_module_upload: false) - end - - it 'sends use_final_store_path with false' do - expect(::Packages::PackageFileUploader).to receive(:workhorse_authorize).with( - hash_including(use_final_store_path: false) - ).and_call_original - - api_request - end - end end end diff --git a/spec/services/projects/update_statistics_service_spec.rb b/spec/services/projects/update_statistics_service_spec.rb index c90da48af8b..a097105fac6 100644 --- a/spec/services/projects/update_statistics_service_spec.rb +++ b/spec/services/projects/update_statistics_service_spec.rb @@ -72,15 +72,17 @@ RSpec.describe Projects::UpdateStatisticsService, feature_category: :groups_and_ end context 'with an existing project with project repository' do - let_it_be(:project) { create(:project) } - subject { service.execute } context 'when the repository is empty' do + let_it_be(:project) { create(:project) } + it_behaves_like 'does not record an onboarding progress action' end context 'when the repository has more than one commit or more than one branch' do + let_it_be(:project) { create(:project, :readme) } + where(:commit_count, :branch_count) do 2 | 1 1 | 2 @@ -96,6 +98,18 @@ RSpec.describe Projects::UpdateStatisticsService, feature_category: :groups_and_ let(:namespace) { project.namespace } end end + + context 'when project is the initial project created from registration, which only has a readme file' do + it_behaves_like 'does not record an onboarding progress action' + end + end + + context 'with project created from templates or imported where commit and branch count are no more than 1' do + let_it_be(:project) { create(:project, :custom_repo, files: { 'test.txt' => 'test', 'README.md' => 'test' }) } + + it_behaves_like 'records an onboarding progress action', :code_added do + let(:namespace) { project.namespace } + end end end end