diff options
113 files changed, 645 insertions, 244 deletions
diff --git a/.rubocop_todo/layout/hash_alignment.yml b/.rubocop_todo/layout/hash_alignment.yml index a09458eecf9..9ecfe11d765 100644 --- a/.rubocop_todo/layout/hash_alignment.yml +++ b/.rubocop_todo/layout/hash_alignment.yml @@ -22,26 +22,6 @@ Layout/HashAlignment: - 'ee/spec/lib/gitlab/elastic/indexer_spec.rb' - 'ee/spec/lib/gitlab/insights/loader_spec.rb' - 'ee/spec/lib/gitlab/template/custom_templates_spec.rb' - - 'ee/spec/models/application_setting_spec.rb' - - 'ee/spec/models/approval_merge_request_rule_spec.rb' - - 'ee/spec/models/member_spec.rb' - - 'ee/spec/models/preloaders/environments/protected_environment_preloader_spec.rb' - - 'ee/spec/models/protected_environments/approval_summary_spec.rb' - - 'ee/spec/models/vulnerabilities/statistic_spec.rb' - - 'ee/spec/requests/api/deployments_spec.rb' - - 'ee/spec/requests/api/graphql/app_sec/fuzzing/coverage/corpus_type_spec.rb' - - 'ee/spec/requests/api/graphql/group/epics_spec.rb' - - 'ee/spec/requests/api/internal/base_spec.rb' - - 'ee/spec/requests/api/issues_spec.rb' - - 'ee/spec/requests/api/protected_environments_spec.rb' - - 'ee/spec/requests/api/templates_spec.rb' - - 'ee/spec/requests/ee/projects/environments_controller_spec.rb' - - 'ee/spec/requests/ee/projects/service_desk_controller_spec.rb' - - 'ee/spec/requests/projects/security/dast_site_profiles_controller_spec.rb' - - 'ee/spec/requests/rack_attack_global_spec.rb' - - 'ee/spec/serializers/integrations/zentao_serializers/issue_entity_spec.rb' - - 'ee/spec/services/app_sec/dast/profiles/create_associations_service_spec.rb' - - 'ee/spec/services/app_sec/dast/scan_configs/fetch_service_spec.rb' - 'ee/spec/services/arkose/blocked_users_report_service_spec.rb' - 'ee/spec/services/audit_events/protected_branch_audit_event_service_spec.rb' - 'ee/spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb' diff --git a/.rubocop_todo/layout/space_inside_block_braces.yml b/.rubocop_todo/layout/space_inside_block_braces.yml index 4fdc308eaa5..25fc6dba4d9 100644 --- a/.rubocop_todo/layout/space_inside_block_braces.yml +++ b/.rubocop_todo/layout/space_inside_block_braces.yml @@ -20,18 +20,6 @@ Layout/SpaceInsideBlockBraces: - 'spec/controllers/snippets/notes_controller_spec.rb' - 'spec/dependencies/omniauth_saml_spec.rb' - 'spec/experiments/application_experiment_spec.rb' - - 'spec/factories/ci/build_trace_chunks.rb' - - 'spec/factories/ci/job_artifacts.rb' - - 'spec/factories/ci/pipeline_artifacts.rb' - - 'spec/factories/commit_statuses.rb' - - 'spec/factories/emails.rb' - - 'spec/factories/external_pull_requests.rb' - - 'spec/factories/gitlab/database/postgres_index.rb' - - 'spec/factories/packages/dependencies.rb' - - 'spec/factories/packages/package_tags.rb' - - 'spec/factories/packages/packages.rb' - - 'spec/factories/prometheus_alert.rb' - - 'spec/factories/prometheus_metrics.rb' - 'spec/finders/ci/jobs_finder_spec.rb' - 'spec/finders/ci/runners_finder_spec.rb' - 'spec/finders/concerns/packages/finder_helper_spec.rb' @@ -41,26 +29,6 @@ Layout/SpaceInsideBlockBraces: - 'spec/finders/packages/group_packages_finder_spec.rb' - 'spec/finders/packages/npm/package_finder_spec.rb' - 'spec/finders/projects_finder_spec.rb' - - 'spec/frontend/fixtures/api_merge_requests.rb' - - 'spec/frontend/fixtures/api_projects.rb' - - 'spec/frontend/fixtures/application_settings.rb' - - 'spec/frontend/fixtures/blob.rb' - - 'spec/frontend/fixtures/branches.rb' - - 'spec/frontend/fixtures/clusters.rb' - - 'spec/frontend/fixtures/deploy_keys.rb' - - 'spec/frontend/fixtures/groups.rb' - - 'spec/frontend/fixtures/issues.rb' - - 'spec/frontend/fixtures/jobs.rb' - - 'spec/frontend/fixtures/labels.rb' - - 'spec/frontend/fixtures/merge_requests.rb' - - 'spec/frontend/fixtures/merge_requests_diffs.rb' - - 'spec/frontend/fixtures/metrics_dashboard.rb' - - 'spec/frontend/fixtures/pipeline_schedules.rb' - - 'spec/frontend/fixtures/pipelines.rb' - - 'spec/frontend/fixtures/projects.rb' - - 'spec/frontend/fixtures/raw.rb' - - 'spec/frontend/fixtures/snippet.rb' - - 'spec/frontend/fixtures/todos.rb' - 'spec/helpers/application_settings_helper_spec.rb' - 'spec/helpers/blob_helper_spec.rb' - 'spec/helpers/gitlab_script_tag_helper_spec.rb' @@ -91,34 +59,6 @@ Layout/SpaceInsideBlockBraces: - 'spec/rubocop/cop/migration/create_table_with_foreign_keys_spec.rb' - 'spec/serializers/cluster_entity_spec.rb' - 'spec/serializers/import/provider_repo_serializer_spec.rb' - - 'spec/support/helpers/cycle_analytics_helpers.rb' - - 'spec/support/redis/redis_shared_examples.rb' - - 'spec/support/shared_contexts/graphql/requests/packages_shared_context.rb' - - 'spec/support/shared_contexts/markdown_golden_master_shared_examples.rb' - - 'spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb' - - 'spec/support/shared_examples/controllers/error_tracking_shared_examples.rb' - - 'spec/support/shared_examples/controllers/snippets_sort_order_shared_examples.rb' - - 'spec/support/shared_examples/features/board_sidebar_labels_examples.rb' - - 'spec/support/shared_examples/features/snippets_shared_examples.rb' - - 'spec/support/shared_examples/features/wiki/user_views_asciidoc_page_with_includes_shared_examples.rb' - - 'spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb' - - 'spec/support/shared_examples/lib/gitlab/sql/set_operator_shared_examples.rb' - - 'spec/support/shared_examples/models/cluster_application_core_shared_examples.rb' - - 'spec/support/shared_examples/models/concerns/analytics/cycle_analytics/stage_event_model_examples.rb' - - 'spec/support/shared_examples/models/concerns/has_repository_shared_examples.rb' - - 'spec/support/shared_examples/models/concerns/incident_management/escalatable_shared_examples.rb' - - 'spec/support/shared_examples/models/label_note_shared_examples.rb' - - 'spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb' - - 'spec/support/shared_examples/models/project_latest_successful_build_for_shared_examples.rb' - - 'spec/support/shared_examples/requests/api/issues/merge_requests_count_shared_examples.rb' - - 'spec/support/shared_examples/requests/api/labels_api_shared_examples.rb' - - 'spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb' - - 'spec/support/shared_examples/requests/lfs_http_shared_examples.rb' - - 'spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb' - - 'spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb' - - 'spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb' - - 'spec/support/shared_examples/services/merge_request_shared_examples.rb' - - 'spec/support/shared_examples/uploaders/object_storage_shared_examples.rb' - 'spec/tasks/gitlab/snippets_rake_spec.rb' - 'spec/uploaders/packages/debian/distribution_release_file_uploader_spec.rb' - 'spec/validators/addressable_url_validator_spec.rb' diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 61d6cbe8694..52a7b5b79d7 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -bcabbb9feb2e80a3dd9930a6033997a364996368 +e64db386270cd47b44f88f04b139d34313d4780b @@ -29,7 +29,7 @@ gem 'marginalia', '~> 1.10.0' gem 'declarative_policy', '~> 1.1.0' # Authentication libraries -gem 'devise', '~> 4.7.2' +gem 'devise', '~> 4.8.1' gem 'devise-pbkdf2-encryptable', '~> 0.0.0', path: 'vendor/gems/devise-pbkdf2-encryptable' gem 'bcrypt', '~> 3.1', '>= 3.1.14' gem 'doorkeeper', '~> 5.5.0.rc2' diff --git a/Gemfile.lock b/Gemfile.lock index 4f1b5449336..febed30c061 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -306,7 +306,7 @@ GEM ruby-statistics (>= 2.1) thor (>= 0.19, < 2) device_detector (1.0.0) - devise (4.7.3) + devise (4.8.1) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 4.1.0) @@ -1445,8 +1445,8 @@ GEM concurrent-ruby (~> 1.0) method_source (~> 1.0) vmstat (2.3.0) - warden (1.2.8) - rack (>= 2.0.6) + warden (1.2.9) + rack (>= 2.0.9) warning (1.3.0) webauthn (2.3.0) android_key_attestation (~> 0.3.0) @@ -1536,7 +1536,7 @@ DEPENDENCIES deprecation_toolkit (~> 1.5.1) derailed_benchmarks device_detector - devise (~> 4.7.2) + devise (~> 4.8.1) devise-pbkdf2-encryptable (~> 0.0.0)! devise-two-factor (~> 4.0.2) diff_match_patch (~> 0.1.0) diff --git a/app/assets/javascripts/invite_members/components/invite_members_modal.vue b/app/assets/javascripts/invite_members/components/invite_members_modal.vue index 87f1ed31a7f..a334f5e4bf7 100644 --- a/app/assets/javascripts/invite_members/components/invite_members_modal.vue +++ b/app/assets/javascripts/invite_members/components/invite_members_modal.vue @@ -118,6 +118,7 @@ export default { selectedAccessLevel: undefined, errorsLimit: 2, isErrorsSectionExpanded: false, + emptyInvitesError: false, }; }, computed: { @@ -133,8 +134,8 @@ export default { labelIntroText() { return this.$options.labels[this.inviteTo][this.mode].introText; }, - inviteDisabled() { - return this.newUsersToInvite.length === 0; + isEmptyInvites() { + return Boolean(this.newUsersToInvite.length); }, hasInvalidMembers() { return !isEmpty(this.invalidMembers); @@ -219,6 +220,18 @@ export default { }); }, }, + watch: { + isEmptyInvites: { + handler(updatedValue) { + // nothing to do if the invites are **still** empty and the emptyInvites were never set from submit + if (!updatedValue && !this.emptyInvitesError) { + return; + } + + this.clearEmptyInviteError(); + }, + }, + }, mounted() { eventHub.$on('openModal', (options) => { this.openModal(options); @@ -260,10 +273,19 @@ export default { const tracking = new ExperimentTracking(experimentName); tracking.event(eventName); }, + showEmptyInvitesError() { + this.invalidFeedbackMessage = this.$options.labels.emptyInvitesErrorText; + this.emptyInvitesError = true; + }, sendInvite({ accessLevel, expiresAt }) { this.isLoading = true; this.clearValidation(); + if (!this.isEmptyInvites) { + this.showEmptyInvitesError(); + return; + } + const [usersToInviteByEmail, usersToAddById] = this.partitionNewUsersToInvite(); const apiAddByInvite = this.isProject @@ -338,6 +360,10 @@ export default { this.invalidFeedbackMessage = ''; this.invalidMembers = {}; }, + clearEmptyInviteError() { + this.invalidFeedbackMessage = ''; + this.emptyInvitesError = false; + }, removeToken(token) { delete this.invalidMembers[memberName(token)]; this.invalidMembers = { ...this.invalidMembers }; @@ -360,7 +386,6 @@ export default { :label-intro-text="labelIntroText" :label-search-field="$options.labels.searchField" :form-group-description="formGroupDescription" - :submit-disabled="inviteDisabled" :invalid-feedback-message="invalidFeedbackMessage" :is-loading="isLoading" :new-users-to-invite="newUsersToInvite" diff --git a/app/assets/javascripts/invite_members/constants.js b/app/assets/javascripts/invite_members/constants.js index 288c2bb9829..f502e1ea369 100644 --- a/app/assets/javascripts/invite_members/constants.js +++ b/app/assets/javascripts/invite_members/constants.js @@ -81,6 +81,9 @@ export const MEMBER_ERROR_LIST_TEXT = s__( ); export const COLLAPSED_ERRORS = s__('InviteMembersModal|Show more (%{count})'); export const EXPANDED_ERRORS = s__('InviteMembersModal|Show less'); +export const EMPTY_INVITES_ERROR_TEXT = s__( + 'InviteMembersModal|Please select members or type email addresses to invite', +); export const MEMBER_MODAL_LABELS = { modal: { @@ -119,6 +122,7 @@ export const MEMBER_MODAL_LABELS = { memberErrorListText: MEMBER_ERROR_LIST_TEXT, collapsedErrors: COLLAPSED_ERRORS, expandedErrors: EXPANDED_ERRORS, + emptyInvitesErrorText: EMPTY_INVITES_ERROR_TEXT, }; export const GROUP_MODAL_LABELS = { diff --git a/app/controllers/concerns/packages_access.rb b/app/controllers/concerns/packages_access.rb index 6df2e064bb2..a7d16a5bc88 100644 --- a/app/controllers/concerns/packages_access.rb +++ b/app/controllers/concerns/packages_access.rb @@ -15,6 +15,6 @@ module PackagesAccess end def verify_read_package! - authorize_read_package!(project) + access_denied! unless can?(current_user, :read_package, project&.packages_policy_subject) end end diff --git a/app/helpers/deploy_tokens_helper.rb b/app/helpers/deploy_tokens_helper.rb index 560d2fcd29f..d4da928edf1 100644 --- a/app/helpers/deploy_tokens_helper.rb +++ b/app/helpers/deploy_tokens_helper.rb @@ -14,7 +14,7 @@ module DeployTokensHelper def packages_registry_enabled?(group_or_project) Gitlab.config.packages.enabled && - can?(current_user, :read_package, group_or_project) + can?(current_user, :read_package, group_or_project&.packages_policy_subject) end def deploy_token_revoke_button_data(token:, group_or_project:) diff --git a/app/models/concerns/pg_full_text_searchable.rb b/app/models/concerns/pg_full_text_searchable.rb index 813827478da..335fcec2611 100644 --- a/app/models/concerns/pg_full_text_searchable.rb +++ b/app/models/concerns/pg_full_text_searchable.rb @@ -108,6 +108,7 @@ module PgFullTextSearchable # This fixes an inconsistency with how to_tsvector and websearch_to_tsquery process URLs # See https://gitlab.com/gitlab-org/gitlab/-/issues/354784#note_905431920 search_term = remove_url_scheme(search_term) + search_term = ActiveSupport::Inflector.transliterate(search_term) joins(:search_data).where( Arel::Nodes::InfixOperation.new( diff --git a/app/models/group.rb b/app/models/group.rb index 55455d85531..e0d10030b9f 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -890,6 +890,14 @@ class Group < Namespace end end + def packages_policy_subject + if Feature.enabled?(:read_package_policy_rule, self) + ::Packages::Policies::Group.new(self) + else + self + end + end + private def feature_flag_enabled_for_self_or_ancestor?(feature_flag) diff --git a/app/models/packages/policies/group.rb b/app/models/packages/policies/group.rb new file mode 100644 index 00000000000..66cd361f2ed --- /dev/null +++ b/app/models/packages/policies/group.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Packages + module Policies + class Group + attr_accessor :group + + delegate_missing_to :group + + def initialize(group) + @group = group + end + end + end +end diff --git a/app/models/packages/policies/project.rb b/app/models/packages/policies/project.rb new file mode 100644 index 00000000000..a5c6703be42 --- /dev/null +++ b/app/models/packages/policies/project.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Packages + module Policies + class Project + attr_accessor :project + + delegate_missing_to :project + + def initialize(project) + @project = project + end + end + end +end diff --git a/app/models/project.rb b/app/models/project.rb index 66ed6ea6ce9..175998efe93 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -3050,6 +3050,14 @@ class Project < ApplicationRecord licensed_feature_available?(:security_training) end + def packages_policy_subject + if Feature.enabled?(:read_package_policy_rule, group) + ::Packages::Policies::Project.new(self) + else + self + end + end + def destroy_deployment_by_id(deployment_id) deployments.where(id: deployment_id).fast_destroy_all end diff --git a/app/policies/packages/package_policy.rb b/app/policies/packages/package_policy.rb index 8eef280c640..829d62a6430 100644 --- a/app/policies/packages/package_policy.rb +++ b/app/policies/packages/package_policy.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true module Packages class PackagePolicy < BasePolicy - delegate { @subject.project } + delegate { @subject.project&.packages_policy_subject } end end diff --git a/app/policies/packages/policies/group_policy.rb b/app/policies/packages/policies/group_policy.rb new file mode 100644 index 00000000000..32dbcb1b65b --- /dev/null +++ b/app/policies/packages/policies/group_policy.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Packages + module Policies + class GroupPolicy < BasePolicy + delegate(:group) { @subject.group } + + overrides(:read_package) + + rule { group.public_group }.policy do + enable :read_package + end + + rule { group.reporter }.policy do + enable :read_package + end + + rule { group.read_package_registry_deploy_token }.policy do + enable :read_package + end + + rule { group.write_package_registry_deploy_token }.policy do + enable :read_package + end + end + end +end diff --git a/app/policies/packages/policies/project_policy.rb b/app/policies/packages/policies/project_policy.rb new file mode 100644 index 00000000000..8e05165c00d --- /dev/null +++ b/app/policies/packages/policies/project_policy.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Packages + module Policies + class ProjectPolicy < BasePolicy + delegate(:project) { @subject.project } + + overrides(:read_package) + + rule { anonymous & ~project.public_project }.prevent_all + + rule { ~project.public_project & ~project.internal_access & ~project.project_allowed_for_job_token }.prevent_all + + rule { project.packages_disabled }.policy do + prevent(:read_package) + end + + rule { can?(:reporter_access) }.policy do + enable :read_package + end + + rule { can?(:public_access) }.policy do + enable :read_package + end + + rule { project.read_package_registry_deploy_token }.policy do + enable :read_package + end + + rule { project.write_package_registry_deploy_token }.policy do + enable :read_package + end + end + end +end diff --git a/app/services/packages/conan/search_service.rb b/app/services/packages/conan/search_service.rb index 31ee9bea084..df22a895c00 100644 --- a/app/services/packages/conan/search_service.rb +++ b/app/services/packages/conan/search_service.rb @@ -44,7 +44,7 @@ module Packages name, version, username, _ = query.split(%r{[@/]}) full_path = Packages::Conan::Metadatum.full_path_from(package_username: username) project = Project.find_by_full_path(full_path) - return unless Ability.allowed?(current_user, :read_package, project) + return unless Ability.allowed?(current_user, :read_package, project&.packages_policy_subject) result = project.packages.with_name(name).with_version(version).order_created.last [result&.conan_recipe].compact diff --git a/app/services/packages/rubygems/dependency_resolver_service.rb b/app/services/packages/rubygems/dependency_resolver_service.rb index c44b26e2b92..839a7683632 100644 --- a/app/services/packages/rubygems/dependency_resolver_service.rb +++ b/app/services/packages/rubygems/dependency_resolver_service.rb @@ -8,7 +8,10 @@ module Packages DEFAULT_PLATFORM = 'ruby' def execute - return ServiceResponse.error(message: "forbidden", http_status: :forbidden) unless Ability.allowed?(current_user, :read_package, project) + unless Ability.allowed?(current_user, :read_package, project&.packages_policy_subject) + return ServiceResponse.error(message: "forbidden", http_status: :forbidden) + end + return ServiceResponse.error(message: "#{gem_name} not found", http_status: :not_found) if packages.empty? payload = packages.map do |package| diff --git a/config/feature_flags/development/read_package_policy_rule.yml b/config/feature_flags/development/read_package_policy_rule.yml new file mode 100644 index 00000000000..151c5a8c0b5 --- /dev/null +++ b/config/feature_flags/development/read_package_policy_rule.yml @@ -0,0 +1,8 @@ +--- +name: read_package_policy_rule +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90963 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/366711 +milestone: '15.4' +type: development +group: group::package +default_enabled: false diff --git a/doc/administration/auth/atlassian.md b/doc/administration/auth/atlassian.md index 6be53922a5a..1d20d87bdf4 100644 --- a/doc/administration/auth/atlassian.md +++ b/doc/administration/auth/atlassian.md @@ -71,7 +71,10 @@ To enable the Atlassian OmniAuth provider for passwordless authentication you mu 1. Change `YOUR_CLIENT_ID` and `YOUR_CLIENT_SECRET` to the Client credentials you received in [application registration](#atlassian-application-registration) steps. 1. Save the configuration file. -1. [Reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart GitLab](../restart_gitlab.md#installations-from-source) for the changes to take effect if you installed GitLab via Omnibus or from source respectively. + +1. For the changes to take effect: + - If you installed via Omnibus, [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure). + - If you installed from source, [restart GitLab](../restart_gitlab.md#installations-from-source). On the sign-in page there should now be an Atlassian icon below the regular sign in form. Select the icon to begin the authentication process. diff --git a/doc/administration/auth/jwt.md b/doc/administration/auth/jwt.md index 99cba3f220d..71ab084065a 100644 --- a/doc/administration/auth/jwt.md +++ b/doc/administration/auth/jwt.md @@ -70,8 +70,9 @@ JWT provides you with a secret key for you to use. 1. Change `YOUR_APP_SECRET` to the client secret and set `auth_url` to your redirect URL. 1. Save the configuration file. -1. [Reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart GitLab](../restart_gitlab.md#installations-from-source) for the changes to take effect if you - installed GitLab via Omnibus or from source respectively. +1. For the changes to take effect: + - If you installed via Omnibus, [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure). + - If you installed from source, [restart GitLab](../restart_gitlab.md#installations-from-source). On the sign in page there should now be a JWT icon below the regular sign in form. Select the icon to begin the authentication process. JWT asks the user to diff --git a/doc/administration/auth/ldap/index.md b/doc/administration/auth/ldap/index.md index 2f0a0db9d6f..19f656d2f14 100644 --- a/doc/administration/auth/ldap/index.md +++ b/doc/administration/auth/ldap/index.md @@ -344,7 +344,7 @@ The `user_filter` DN can contain special characters. For example: OU=GitLab\2C Inc,DC=gitlab,DC=com ``` -- Escape open and close brackets with `\28` and `\29`, respectively. For example: +- Escape open brackets with `\28` and close brackets with `\29`. For example: ```plaintext OU=Gitlab \28Inc\29,DC=gitlab,DC=com diff --git a/doc/administration/gitaly/configure_gitaly.md b/doc/administration/gitaly/configure_gitaly.md index 5b868c274cd..ac03c3ffc02 100644 --- a/doc/administration/gitaly/configure_gitaly.md +++ b/doc/administration/gitaly/configure_gitaly.md @@ -1089,9 +1089,10 @@ lots of CI fetch traffic. The pack-objects cache wraps `git pack-objects`, an internal part of Git that gets invoked indirectly via the PostUploadPack and -SSHUploadPack Gitaly RPCs. These are the RPCs that Gitaly runs when a -user does a Git fetch via HTTP or SSH, respectively. When the cache is -enabled, anything that uses PostUploadPack or SSHUploadPack can +SSHUploadPack Gitaly RPCs. Gitaly runs PostUploadPack when a +user does a Git fetch via HTTP, or SSHUploadPack when a +user does a Git fetch via SSH. +When the cache is enabled, anything that uses PostUploadPack or SSHUploadPack can benefit from it. It is orthogonal to: - The transport (HTTP or SSH). diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md index e545fb3cd1b..d5eb354c104 100644 --- a/doc/administration/pages/index.md +++ b/doc/administration/pages/index.md @@ -156,7 +156,7 @@ Watch the [video tutorial](https://youtu.be/dD8c7WNcc6s) for this configuration. **Requirements:** - [Wildcard DNS setup](#dns-configuration) -- Wildcard TLS certificate +- TLS certificate. Can be either Wildcard, or any other type meeting the [requirements](../../user/project/pages/custom_domains_ssl_tls_certification/index.md#manual-addition-of-ssltls-certificates). --- @@ -368,7 +368,7 @@ world. Custom domains and TLS are supported. If you don't have IPv6, you can omit the IPv6 address. -1. If you haven't named your certificate and key `example.io.crt` and `example.io.key` respectively, +1. If you haven't named your certificate `example.io.crt` and your key `example.io.key`, then you need to also add the full paths as shown below: ```ruby diff --git a/doc/administration/pages/source.md b/doc/administration/pages/source.md index 5ab508dbaca..5bee96081bb 100644 --- a/doc/administration/pages/source.md +++ b/doc/administration/pages/source.md @@ -364,9 +364,8 @@ world. Custom domains and TLS are supported. ``` 1. Edit `/etc/default/gitlab` and set `gitlab_pages_enabled` to `true` in - order to enable the pages daemon. In `gitlab_pages_options` the - `-pages-domain`, `-listen-http` and `-listen-https` must match the `host`, - `external_http` and `external_https` settings that you set above respectively. + order to enable the pages daemon. In `gitlab_pages_options`, you must match the + `-pages-domain` with `host`, `-listen-http` with `external_http`, and `-listen-https` with `external_https` settings. The `-root-cert` and `-root-key` settings are the wildcard TLS certificates of the `example.io` domain: diff --git a/doc/api/members.md b/doc/api/members.md index a9817918d0b..aa7d697c6c6 100644 --- a/doc/api/members.md +++ b/doc/api/members.md @@ -47,6 +47,7 @@ GET /projects/:id/members | `id` | integer/string | yes | The ID or [URL-encoded path of the project or group](index.md#namespaced-path-encoding) owned by the authenticated user | | `query` | string | no | A query string to search for members | | `user_ids` | array of integers | no | Filter the results on the given user IDs | +| `skip_users` | array of integers | no | Filter skipped users out of the results | ```shell curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/:id/members" diff --git a/doc/api/project_level_variables.md b/doc/api/project_level_variables.md index 81bb4a26614..39e7f441b42 100644 --- a/doc/api/project_level_variables.md +++ b/doc/api/project_level_variables.md @@ -5,7 +5,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w type: reference, api --- -# Project-level Variables API **(FREE)** +# Project-level CI/CD variables API **(FREE)** ## List project variables @@ -44,9 +44,10 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a ] ``` -## Show variable details +## Get a single variable -Get the details of a project's specific variable. +Get the details of a single variable. If there are multiple variables with the same key, +use `filter` to select the correct `environment_scope`. ```plaintext GET /projects/:id/variables/:key @@ -73,9 +74,11 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a } ``` -## Create variable +## Create a variable -Create a new variable. +Create a new variable. If a variable with the same `key` already exists, the new variable +must have a different `environment_scope`. Otherwise, GitLab returns a message similar to: +`VARIABLE_NAME has already been taken`. ```plaintext POST /projects/:id/variables @@ -107,9 +110,10 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \ } ``` -## Update variable +## Update a variable -Update a project's variable. +Update a project's variable. If there are multiple variables with the same key, +use `filter` to select the correct `environment_scope`. ```plaintext PUT /projects/:id/variables/:key @@ -142,9 +146,10 @@ curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" \ } ``` -## Remove variable +## Delete a variable -Remove a project's variable. +Delete a project's variable. If there are multiple variables with the same key, +use `filter` to select the correct `environment_scope`. ```plaintext DELETE /projects/:id/variables/:key @@ -165,10 +170,34 @@ curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://git > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34490) in GitLab 13.2. > - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/227052) in GitLab 13.4. -This parameter is used for filtering by attributes, such as `environment_scope`. +When multiple variables have the same `key`, [GET](#get-a-single-variable), [PUT](#update-a-variable), +or [DELETE](#delete-a-variable) requests might return: -Example usage: - -```shell -curl --globoff --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/variables/VARIABLE_1?filter[environment_scope]=production" +```plaintext +There are multiple variables with provided parameters. Please use 'filter[environment_scope]'. ``` + +Use `filter[environment_scope]` to select the variable with the matching `environment_scope` attribute. + +For example: + +- GET: + + ```shell + curl --globoff --header "PRIVATE-TOKEN: <your_access_token>" \ + "https://gitlab.example.com/api/v4/projects/1/variables/SCOPED_VARIABLE_1?filter[environment_scope]=production" + ``` + +- PUT: + + ```shell + curl --request PUT --globoff --header "PRIVATE-TOKEN: <your_access_token>" \ + "https://gitlab.example.com/api/v4/projects/1/variables/SCOPED_VARIABLE_1?value=scoped-variable-updated-value&environment_scope=production&filter[environment_scope]=production" + ``` + +- DELETE: + + ```shell + curl --request DELETE --globoff --header "PRIVATE-TOKEN: <your_access_token>" \ + "https://gitlab.example.com/api/v4/projects/1/variables/SCOPED_VARIABLE_1?filter[environment_scope]=production" + ``` diff --git a/doc/integration/cas.md b/doc/integration/cas.md index 38305967246..45c79cd9726 100644 --- a/doc/integration/cas.md +++ b/doc/integration/cas.md @@ -71,8 +71,8 @@ configure CAS for back-channel logout. 1. Save the configuration file. -1. [Reconfigure](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure) or - [restart GitLab](../administration/restart_gitlab.md#installations-from-source) for the changes to - take effect if you installed GitLab via Omnibus or from source respectively. +1. For the changes to take effect: + - If you installed via Omnibus, [reconfigure GitLab](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure). + - If you installed from source, [restart GitLab](../administration/restart_gitlab.md#installations-from-source). On the sign in page there should now be a CAS tab in the sign in form. diff --git a/doc/integration/ding_talk.md b/doc/integration/ding_talk.md index 437648b1adf..71dadd766b2 100644 --- a/doc/integration/ding_talk.md +++ b/doc/integration/ding_talk.md @@ -83,4 +83,6 @@ Sign in to DingTalk Open Platform and create an application on it. DingTalk gene 1. Save the configuration file. -1. [Reconfigure](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart GitLab](../administration/restart_gitlab.md#installations-from-source) for the changes to take effect if you installed GitLab via Omnibus or from source respectively. +1. For the changes to take effect: + - If you installed via Omnibus, [reconfigure GitLab](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure). + - If you installed from source, [restart GitLab](../administration/restart_gitlab.md#installations-from-source). diff --git a/doc/integration/facebook.md b/doc/integration/facebook.md index 99ebce32936..ea5a3cc6d38 100644 --- a/doc/integration/facebook.md +++ b/doc/integration/facebook.md @@ -104,8 +104,9 @@ Facebook. Facebook generates an app ID and secret key for you to use. 1. Save the configuration file. -1. [Reconfigure](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart GitLab](../administration/restart_gitlab.md#installations-from-source) for the changes to take effect if you - installed GitLab via Omnibus or from source respectively. +1. For the changes to take effect: + - If you installed via Omnibus, [reconfigure GitLab](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure). + - If you installed from source, [restart GitLab](../administration/restart_gitlab.md#installations-from-source). On the sign in page there should now be a Facebook icon below the regular sign in form. Select the icon to begin the authentication process. Facebook asks the diff --git a/doc/integration/google.md b/doc/integration/google.md index 4862b898ed5..80176fac41b 100644 --- a/doc/integration/google.md +++ b/doc/integration/google.md @@ -117,10 +117,9 @@ On your GitLab server: ``` 1. Save the configuration file. -1. [Reconfigure](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure) - or [restart GitLab](../administration/restart_gitlab.md#installations-from-source) for - the changes to take effect if you installed GitLab via Omnibus or from source - respectively. +1. For the changes to take effect: + - If you installed via Omnibus, [reconfigure GitLab](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure). + - If you installed from source, [restart GitLab](../administration/restart_gitlab.md#installations-from-source). On the sign in page there should now be a Google icon below the regular sign in form. Select the icon to begin the authentication process. Google asks the diff --git a/doc/integration/mattermost/index.md b/doc/integration/mattermost/index.md index 3293732b59b..8b5a374ca11 100644 --- a/doc/integration/mattermost/index.md +++ b/doc/integration/mattermost/index.md @@ -79,7 +79,7 @@ mattermost_nginx['ssl_certificate'] = "/etc/gitlab/ssl/mattermost-nginx.crt" mattermost_nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/mattermost-nginx.key" ``` -where `mattermost-nginx.crt` and `mattermost-nginx.key` are SSL cert and key, respectively. +where `mattermost-nginx.crt` is the SSL certificate and `mattermost-nginx.key` is the SSL key. Once the configuration is set, run `sudo gitlab-ctl reconfigure` to apply the changes. diff --git a/doc/integration/salesforce.md b/doc/integration/salesforce.md index 5300018e888..70d6e0aa0d8 100644 --- a/doc/integration/salesforce.md +++ b/doc/integration/salesforce.md @@ -81,9 +81,10 @@ To get the credentials (a pair of Client ID and Client Secret), you must [create ![Salesforce App Secret Details](img/salesforce_app_secret_details.png) 1. Save the configuration file. -1. [Reconfigure GitLab](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure) or - [restart GitLab](../administration/restart_gitlab.md#installations-from-source) for the changes - to take effect if you installed GitLab via Omnibus or from source respectively. + +1. For the changes to take effect: + - If you installed via Omnibus, [reconfigure GitLab](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure). + - If you installed from source, [restart GitLab](../administration/restart_gitlab.md#installations-from-source). On the sign in page, there should now be a Salesforce icon below the regular sign in form. Select the icon to begin the authentication process. Salesforce asks the user to sign in and authorize the GitLab application. diff --git a/doc/integration/saml.md b/doc/integration/saml.md index 0c517d07f41..f484f4cbc02 100644 --- a/doc/integration/saml.md +++ b/doc/integration/saml.md @@ -143,7 +143,9 @@ as described in the section on [Security](#security). Otherwise, your users are 1. Change the value of `issuer` to a unique name, which identifies the application to the IdP. -1. For the changes to take effect, you must [reconfigure](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure) GitLab if you installed via Omnibus or [restart GitLab](../administration/restart_gitlab.md#installations-from-source) if you installed from source. +1. For the changes to take effect: + - If you installed via Omnibus, [reconfigure GitLab](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure). + - If you installed from source, [restart GitLab](../administration/restart_gitlab.md#installations-from-source). 1. Register the GitLab SP in your SAML 2.0 IdP, using the application name specified in `issuer`. diff --git a/doc/integration/twitter.md b/doc/integration/twitter.md index 2218529a729..aa9014adc49 100644 --- a/doc/integration/twitter.md +++ b/doc/integration/twitter.md @@ -88,7 +88,8 @@ Twitter. Twitter generates a client ID and secret key for you to use. 1. Save the configuration file. -1. [Reconfigure](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart GitLab](../administration/restart_gitlab.md#installations-from-source) for the changes to take effect if you - installed GitLab via Omnibus or from source respectively. +1. For the changes to take effect: + - If you installed via Omnibus, [reconfigure GitLab](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure). + - If you installed from source, [restart GitLab](../administration/restart_gitlab.md#installations-from-source). On the sign in page there should now be a Twitter icon below the regular sign in form. Select the icon to begin the authentication process. Twitter asks the user to sign in and authorize the GitLab application. If everything goes well the user is returned to GitLab and signed in. diff --git a/doc/user/packages/container_registry/reduce_container_registry_storage.md b/doc/user/packages/container_registry/reduce_container_registry_storage.md index 788e57b6410..f889f4836d9 100644 --- a/doc/user/packages/container_registry/reduce_container_registry_storage.md +++ b/doc/user/packages/container_registry/reduce_container_registry_storage.md @@ -219,7 +219,7 @@ Examples: ```shell curl --request PUT --header 'Content-Type: application/json;charset=UTF-8' --header "PRIVATE-TOKEN: <your_access_token>" \ - --data-binary '{"container_expiration_policy_attributes":{"cadence":"1month","enabled":true,"keep_n":1,"older_than":"14d","name_regex":"","name_regex_delete":".*","name_regex_keep":".*-main"}}' \ + --data-binary '{"container_expiration_policy_attributes":{"cadence":"1month","enabled":true,"keep_n":1,"older_than":"14d","name_regex":".*","name_regex_keep":".*-main"}}' \ "https://gitlab.example.com/api/v4/projects/2" ``` diff --git a/lib/api/concerns/packages/conan_endpoints.rb b/lib/api/concerns/packages/conan_endpoints.rb index a90269b565c..d8c2eb4ff33 100644 --- a/lib/api/concerns/packages/conan_endpoints.rb +++ b/lib/api/concerns/packages/conan_endpoints.rb @@ -135,7 +135,7 @@ module API route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true get 'packages/:conan_package_reference', urgency: :low do - authorize!(:read_package, project) + authorize_read_package!(project) presenter = ::Packages::Conan::PackagePresenter.new( package, @@ -154,7 +154,7 @@ module API route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true get urgency: :low do - authorize!(:read_package, project) + authorize_read_package!(project) presenter = ::Packages::Conan::PackagePresenter.new(package, current_user, project) @@ -237,7 +237,7 @@ module API route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true post 'packages/:conan_package_reference/upload_urls', urgency: :low do - authorize!(:read_package, project) + authorize_read_package!(project) status 200 present package_upload_urls, with: ::API::Entities::ConanPackage::ConanUploadUrls @@ -250,7 +250,7 @@ module API route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true post 'upload_urls', urgency: :low do - authorize!(:read_package, project) + authorize_read_package!(project) status 200 present recipe_upload_urls, with: ::API::Entities::ConanPackage::ConanUploadUrls diff --git a/lib/api/helpers/packages/conan/api_helpers.rb b/lib/api/helpers/packages/conan/api_helpers.rb index 994d3c4c473..ba1c409317e 100644 --- a/lib/api/helpers/packages/conan/api_helpers.rb +++ b/lib/api/helpers/packages/conan/api_helpers.rb @@ -23,7 +23,7 @@ module API end def present_download_urls(entity) - authorize!(:read_package, project) + authorize_read_package!(project) presenter = ::Packages::Conan::PackagePresenter.new( package, @@ -161,7 +161,7 @@ module API end def download_package_file(file_type) - authorize!(:read_package, project) + authorize_read_package!(project) package_file = ::Packages::Conan::PackageFileFinder .new( diff --git a/lib/api/helpers/packages_helpers.rb b/lib/api/helpers/packages_helpers.rb index 2221eec0f82..66c4c661454 100644 --- a/lib/api/helpers/packages_helpers.rb +++ b/lib/api/helpers/packages_helpers.rb @@ -14,7 +14,7 @@ module API end def authorize_read_package!(subject = user_project) - authorize!(:read_package, subject) + authorize!(:read_package, subject.try(:packages_policy_subject) || subject) end def authorize_create_package!(subject = user_project) diff --git a/lib/api/members.rb b/lib/api/members.rb index d26fdd09ee7..f4e38207aca 100644 --- a/lib/api/members.rb +++ b/lib/api/members.rb @@ -24,6 +24,7 @@ module API params do optional :query, type: String, desc: 'A query string to search for members' optional :user_ids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Array of user ids to look up for membership' + optional :skip_users, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Array of user ids to be skipped for membership' optional :show_seat_info, type: Boolean, desc: 'Show seat information for members' use :optional_filter_params_ee use :pagination diff --git a/lib/api/rubygem_packages.rb b/lib/api/rubygem_packages.rb index 85bbd0879b7..85d6f0c5a9b 100644 --- a/lib/api/rubygem_packages.rb +++ b/lib/api/rubygem_packages.rb @@ -65,7 +65,7 @@ module API requires :file_name, type: String, desc: 'Package file name' end get "gems/:file_name", requirements: FILE_NAME_REQUIREMENTS do - authorize!(:read_package, user_project) + authorize_read_package!(user_project) package_files = ::Packages::PackageFile .for_rubygem_with_file_name(user_project, params[:file_name]) diff --git a/lib/gitlab/cache/import/caching.rb b/lib/gitlab/cache/import/caching.rb index 87988279c8d..4dbce0b05e1 100644 --- a/lib/gitlab/cache/import/caching.rb +++ b/lib/gitlab/cache/import/caching.rb @@ -153,11 +153,11 @@ module Gitlab # timeout - The time after which the cache key should expire. def self.write_multiple(mapping, key_prefix: nil, timeout: TIMEOUT) Redis::Cache.with do |redis| - redis.pipelined do |pipeline| + redis.pipelined do |multi| mapping.each do |raw_key, value| key = cache_key_for("#{key_prefix}#{raw_key}") - pipeline.set(key, value, ex: timeout) + multi.set(key, value, ex: timeout) end end end diff --git a/lib/sidebars/projects/menus/packages_registries_menu.rb b/lib/sidebars/projects/menus/packages_registries_menu.rb index 914368e6fec..e4d4441c687 100644 --- a/lib/sidebars/projects/menus/packages_registries_menu.rb +++ b/lib/sidebars/projects/menus/packages_registries_menu.rb @@ -77,7 +77,8 @@ module Sidebars end def packages_registry_disabled? - !::Gitlab.config.packages.enabled || !can?(context.current_user, :read_package, context.project) + !::Gitlab.config.packages.enabled || + !can?(context.current_user, :read_package, context.project&.packages_policy_subject) end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 637a56e8cb6..9bef7fa2c67 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -21572,6 +21572,9 @@ msgstr "" msgid "InviteMembersModal|Members were successfully added" msgstr "" +msgid "InviteMembersModal|Please select members or type email addresses to invite" +msgstr "" + msgid "InviteMembersModal|Review the invite errors and try again:" msgstr "" @@ -42020,6 +42023,9 @@ msgstr "" msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}" msgstr "" +msgid "UsageQuota|CI/CD minutes usage since %{usageSince}" +msgstr "" + msgid "UsageQuota|Code packages and container images." msgstr "" @@ -42212,9 +42218,6 @@ msgstr "" msgid "UsageQuota|Usage quotas help link" msgstr "" -msgid "UsageQuota|Usage since %{usageSince}" -msgstr "" - msgid "UsageQuota|When you purchase additional storage, we automatically unlock projects that were locked when you reached the %{actualRepositorySizeLimit} limit." msgstr "" diff --git a/spec/factories/ci/build_trace_chunks.rb b/spec/factories/ci/build_trace_chunks.rb index 115eb32111c..64a297932de 100644 --- a/spec/factories/ci/build_trace_chunks.rb +++ b/spec/factories/ci/build_trace_chunks.rb @@ -23,7 +23,7 @@ FactoryBot.define do end trait :database_with_data do - data_store { :database} + data_store { :database } transient do initial_data { 'test data' } @@ -55,7 +55,7 @@ FactoryBot.define do end trait :persisted do - data_store { :database} + data_store { :database } transient do initial_data { 'test data' } diff --git a/spec/factories/ci/job_artifacts.rb b/spec/factories/ci/job_artifacts.rb index 114ad3a5847..f9a3c176ef2 100644 --- a/spec/factories/ci/job_artifacts.rb +++ b/spec/factories/ci/job_artifacts.rb @@ -15,7 +15,7 @@ FactoryBot.define do end trait :remote_store do - file_store { JobArtifactUploader::Store::REMOTE} + file_store { JobArtifactUploader::Store::REMOTE } end after :build do |artifact| diff --git a/spec/factories/ci/pipeline_artifacts.rb b/spec/factories/ci/pipeline_artifacts.rb index 85277ce6fbf..d096f149c3a 100644 --- a/spec/factories/ci/pipeline_artifacts.rb +++ b/spec/factories/ci/pipeline_artifacts.rb @@ -30,7 +30,7 @@ FactoryBot.define do end trait :remote_store do - file_store { ::ObjectStorage::Store::REMOTE} + file_store { ::ObjectStorage::Store::REMOTE } end trait :with_coverage_report do diff --git a/spec/factories/commit_statuses.rb b/spec/factories/commit_statuses.rb index fa10b37cdbf..b41e30e214c 100644 --- a/spec/factories/commit_statuses.rb +++ b/spec/factories/commit_statuses.rb @@ -6,10 +6,10 @@ FactoryBot.define do stage { 'test' } stage_idx { 0 } status { 'success' } - description { 'commit status'} + description { 'commit status' } pipeline factory: :ci_pipeline - started_at { 'Tue, 26 Jan 2016 08:21:42 +0100'} - finished_at { 'Tue, 26 Jan 2016 08:23:42 +0100'} + started_at { 'Tue, 26 Jan 2016 08:21:42 +0100' } + finished_at { 'Tue, 26 Jan 2016 08:23:42 +0100' } trait :success do status { 'success' } diff --git a/spec/factories/emails.rb b/spec/factories/emails.rb index b30fa8a5896..e51c3358a9b 100644 --- a/spec/factories/emails.rb +++ b/spec/factories/emails.rb @@ -6,6 +6,6 @@ FactoryBot.define do email { generate(:email_alias) } trait(:confirmed) { confirmed_at { Time.now } } - trait(:skip_validate) { to_create {|instance| instance.save!(validate: false) } } + trait(:skip_validate) { to_create { |instance| instance.save!(validate: false) } } end end diff --git a/spec/factories/external_pull_requests.rb b/spec/factories/external_pull_requests.rb index 7a6e77f8572..470814f4360 100644 --- a/spec/factories/external_pull_requests.rb +++ b/spec/factories/external_pull_requests.rb @@ -12,6 +12,6 @@ FactoryBot.define do target_sha { 'a09386439ca39abe575675ffd4b89ae824fec22f' } status { :open } - trait(:closed) { status { 'closed'} } + trait(:closed) { status { 'closed' } } end end diff --git a/spec/factories/gitlab/database/postgres_index.rb b/spec/factories/gitlab/database/postgres_index.rb index 54bbb738512..8492b44c404 100644 --- a/spec/factories/gitlab/database/postgres_index.rb +++ b/spec/factories/gitlab/database/postgres_index.rb @@ -13,7 +13,7 @@ FactoryBot.define do exclusion { false } expression { false } partial { false } - definition { "CREATE INDEX #{identifier} ON #{tablename} (bar)"} + definition { "CREATE INDEX #{identifier} ON #{tablename} (bar)" } ondisk_size_bytes { 100.megabytes } end end diff --git a/spec/factories/packages/dependencies.rb b/spec/factories/packages/dependencies.rb index a62d48c2e73..c99f11d6db3 100644 --- a/spec/factories/packages/dependencies.rb +++ b/spec/factories/packages/dependencies.rb @@ -2,11 +2,11 @@ FactoryBot.define do factory :packages_dependency, class: 'Packages::Dependency' do - sequence(:name) { |n| "@test/package-#{n}"} + sequence(:name) { |n| "@test/package-#{n}" } sequence(:version_pattern) { |n| "~6.2.#{n}" } trait(:rubygems) do - sequence(:name) { |n| "gem-dependency-#{n}"} + sequence(:name) { |n| "gem-dependency-#{n}" } end end end diff --git a/spec/factories/packages/package_tags.rb b/spec/factories/packages/package_tags.rb index 3d2eea4a73b..1a40d5d600f 100644 --- a/spec/factories/packages/package_tags.rb +++ b/spec/factories/packages/package_tags.rb @@ -3,6 +3,6 @@ FactoryBot.define do factory :packages_tag, class: 'Packages::Tag' do package - sequence(:name) { |n| "tag-#{n}"} + sequence(:name) { |n| "tag-#{n}" } end end diff --git a/spec/factories/packages/packages.rb b/spec/factories/packages/packages.rb index 90c68074a3b..3d5bdc95722 100644 --- a/spec/factories/packages/packages.rb +++ b/spec/factories/packages/packages.rb @@ -115,7 +115,7 @@ FactoryBot.define do end factory :npm_package do - sequence(:name) { |n| "@#{project.root_namespace.path}/package-#{n}"} + sequence(:name) { |n| "@#{project.root_namespace.path}/package-#{n}" } sequence(:version) { |n| "1.0.#{n}" } package_type { :npm } @@ -153,7 +153,7 @@ FactoryBot.define do end factory :nuget_package do - sequence(:name) { |n| "NugetPackage#{n}"} + sequence(:name) { |n| "NugetPackage#{n}" } sequence(:version) { |n| "1.0.#{n}" } package_type { :nuget } @@ -175,7 +175,7 @@ FactoryBot.define do end factory :pypi_package do - sequence(:name) { |n| "pypi-package-#{n}"} + sequence(:name) { |n| "pypi-package-#{n}" } sequence(:version) { |n| "1.0.#{n}" } package_type { :pypi } @@ -193,7 +193,7 @@ FactoryBot.define do end factory :composer_package do - sequence(:name) { |n| "composer-package-#{n}"} + sequence(:name) { |n| "composer-package-#{n}" } sequence(:version) { |n| "1.0.#{n}" } package_type { :composer } @@ -210,7 +210,7 @@ FactoryBot.define do end factory :golang_package do - sequence(:name) { |n| "golang.org/x/pkg-#{n}"} + sequence(:name) { |n| "golang.org/x/pkg-#{n}" } sequence(:version) { |n| "v1.0.#{n}" } package_type { :golang } end diff --git a/spec/factories/prometheus_alert.rb b/spec/factories/prometheus_alert.rb index ad3868c38ed..14fdd993c7a 100644 --- a/spec/factories/prometheus_alert.rb +++ b/spec/factories/prometheus_alert.rb @@ -15,7 +15,7 @@ FactoryBot.define do end trait :with_runbook_url do - runbook_url { 'https://runbooks.gitlab.com/metric_gt_1'} + runbook_url { 'https://runbooks.gitlab.com/metric_gt_1' } end end end diff --git a/spec/factories/prometheus_metrics.rb b/spec/factories/prometheus_metrics.rb index 503d392a524..e785bf2ac9d 100644 --- a/spec/factories/prometheus_metrics.rb +++ b/spec/factories/prometheus_metrics.rb @@ -9,7 +9,7 @@ FactoryBot.define do group { :business } project legend { 'legend' } - dashboard_path { '.gitlab/dashboards/dashboard_path.yml'} + dashboard_path { '.gitlab/dashboards/dashboard_path.yml' } trait :common do common { true } diff --git a/spec/frontend/fixtures/api_merge_requests.rb b/spec/frontend/fixtures/api_merge_requests.rb index 75bc8c8df25..7d95c506e6c 100644 --- a/spec/frontend/fixtures/api_merge_requests.rb +++ b/spec/frontend/fixtures/api_merge_requests.rb @@ -7,7 +7,7 @@ RSpec.describe API::MergeRequests, '(JavaScript fixtures)', type: :request do include JavaScriptFixturesHelpers let_it_be(:admin) { create(:admin, name: 'root') } - let_it_be(:namespace) { create(:namespace, name: 'gitlab-test' )} + let_it_be(:namespace) { create(:namespace, name: 'gitlab-test' ) } let_it_be(:project) { create(:project, :repository, namespace: namespace, path: 'lorem-ipsum') } let_it_be(:early_mrs) do 4.times { |i| create(:merge_request, source_project: project, source_branch: "branch-#{i}") } diff --git a/spec/frontend/fixtures/api_projects.rb b/spec/frontend/fixtures/api_projects.rb index eada2f8e0f7..5acc1095d5c 100644 --- a/spec/frontend/fixtures/api_projects.rb +++ b/spec/frontend/fixtures/api_projects.rb @@ -7,7 +7,7 @@ RSpec.describe API::Projects, '(JavaScript fixtures)', type: :request do include JavaScriptFixturesHelpers let(:admin) { create(:admin, name: 'root') } - let(:namespace) { create(:namespace, name: 'gitlab-test' )} + let(:namespace) { create(:namespace, name: 'gitlab-test' ) } let(:project) { create(:project, :repository, namespace: namespace, path: 'lorem-ipsum') } let(:project_empty) { create(:project_empty_repo, namespace: namespace, path: 'lorem-ipsum-empty') } diff --git a/spec/frontend/fixtures/application_settings.rb b/spec/frontend/fixtures/application_settings.rb index a7a989f31ec..b3ce23c8cd7 100644 --- a/spec/frontend/fixtures/application_settings.rb +++ b/spec/frontend/fixtures/application_settings.rb @@ -8,7 +8,7 @@ RSpec.describe Admin::ApplicationSettingsController, '(JavaScript fixtures)', ty include AdminModeHelper let(:admin) { create(:admin) } - let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} + let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) } let(:project) { create(:project_empty_repo, namespace: namespace, path: 'application-settings') } before do diff --git a/spec/frontend/fixtures/blob.rb b/spec/frontend/fixtures/blob.rb index b2bbdd2749e..54c5b83da3e 100644 --- a/spec/frontend/fixtures/blob.rb +++ b/spec/frontend/fixtures/blob.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe Projects::BlobController, '(JavaScript fixtures)', type: :controller do include JavaScriptFixturesHelpers - let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} + let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) } let(:project) { create(:project, :repository, namespace: namespace, path: 'branches-project') } let(:user) { project.first_owner } diff --git a/spec/frontend/fixtures/branches.rb b/spec/frontend/fixtures/branches.rb index b3bb4b8873a..6cda2f0f665 100644 --- a/spec/frontend/fixtures/branches.rb +++ b/spec/frontend/fixtures/branches.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe 'Branches (JavaScript fixtures)' do include JavaScriptFixturesHelpers - let_it_be(:namespace) { create(:namespace, name: 'frontend-fixtures' )} + let_it_be(:namespace) { create(:namespace, name: 'frontend-fixtures' ) } let_it_be(:project) { create(:project, :repository, namespace: namespace, path: 'branches-project') } let_it_be(:user) { project.first_owner } diff --git a/spec/frontend/fixtures/clusters.rb b/spec/frontend/fixtures/clusters.rb index 49596d98774..426a76f29e0 100644 --- a/spec/frontend/fixtures/clusters.rb +++ b/spec/frontend/fixtures/clusters.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe Projects::ClustersController, '(JavaScript fixtures)', type: :controller do include JavaScriptFixturesHelpers - let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} + let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) } let(:project) { create(:project, :repository, namespace: namespace) } let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) } let(:user) { project.first_owner } diff --git a/spec/frontend/fixtures/deploy_keys.rb b/spec/frontend/fixtures/deploy_keys.rb index 154084e0181..24d602216d8 100644 --- a/spec/frontend/fixtures/deploy_keys.rb +++ b/spec/frontend/fixtures/deploy_keys.rb @@ -7,11 +7,11 @@ RSpec.describe Projects::DeployKeysController, '(JavaScript fixtures)', type: :c include AdminModeHelper let(:admin) { create(:admin) } - let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} + let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) } let(:project) { create(:project_empty_repo, namespace: namespace, path: 'todos-project') } - let(:project2) { create(:project, :internal)} - let(:project3) { create(:project, :internal)} - let(:project4) { create(:project, :internal)} + let(:project2) { create(:project, :internal) } + let(:project3) { create(:project, :internal) } + let(:project4) { create(:project, :internal) } before do # Using an admin for these fixtures because they are used for verifying a frontend diff --git a/spec/frontend/fixtures/groups.rb b/spec/frontend/fixtures/groups.rb index ddd436b98c6..9c22ff176ff 100644 --- a/spec/frontend/fixtures/groups.rb +++ b/spec/frontend/fixtures/groups.rb @@ -6,7 +6,7 @@ RSpec.describe 'Groups (JavaScript fixtures)', type: :controller do include JavaScriptFixturesHelpers let(:user) { create(:user) } - let(:group) { create(:group, name: 'frontend-fixtures-group', runners_token: 'runnerstoken:intabulasreferre')} + let(:group) { create(:group, name: 'frontend-fixtures-group', runners_token: 'runnerstoken:intabulasreferre') } before do group.add_owner(user) diff --git a/spec/frontend/fixtures/issues.rb b/spec/frontend/fixtures/issues.rb index cde796497d4..e3d88098841 100644 --- a/spec/frontend/fixtures/issues.rb +++ b/spec/frontend/fixtures/issues.rb @@ -6,7 +6,7 @@ RSpec.describe Projects::IssuesController, '(JavaScript fixtures)', type: :contr include JavaScriptFixturesHelpers let(:user) { create(:user, feed_token: 'feedtoken:coldfeed') } - let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} + let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) } let(:project) { create(:project_empty_repo, namespace: namespace, path: 'issues-project') } render_views diff --git a/spec/frontend/fixtures/jobs.rb b/spec/frontend/fixtures/jobs.rb index 2e15eefdce6..3657a5405a4 100644 --- a/spec/frontend/fixtures/jobs.rb +++ b/spec/frontend/fixtures/jobs.rb @@ -7,7 +7,7 @@ RSpec.describe 'Jobs (JavaScript fixtures)' do include JavaScriptFixturesHelpers include GraphqlHelpers - let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} + let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) } let(:project) { create(:project, :repository, namespace: namespace, path: 'builds-project') } let(:user) { project.first_owner } let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.id) } diff --git a/spec/frontend/fixtures/labels.rb b/spec/frontend/fixtures/labels.rb index 6736baed199..2445c9376e2 100644 --- a/spec/frontend/fixtures/labels.rb +++ b/spec/frontend/fixtures/labels.rb @@ -6,7 +6,7 @@ RSpec.describe 'Labels (JavaScript fixtures)' do include JavaScriptFixturesHelpers let(:user) { create(:user) } - let(:group) { create(:group, name: 'frontend-fixtures-group' )} + let(:group) { create(:group, name: 'frontend-fixtures-group' ) } let(:project) { create(:project_empty_repo, namespace: group, path: 'labels-project') } let!(:project_label_bug) { create(:label, project: project, title: 'bug', color: '#FF0000') } diff --git a/spec/frontend/fixtures/merge_requests.rb b/spec/frontend/fixtures/merge_requests.rb index cb4eb43b88d..cbf26a70e5f 100644 --- a/spec/frontend/fixtures/merge_requests.rb +++ b/spec/frontend/fixtures/merge_requests.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :controller do include JavaScriptFixturesHelpers - let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} + let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) } let(:project) { create(:project, :repository, namespace: namespace, path: 'merge-requests-project') } let(:user) { project.first_owner } diff --git a/spec/frontend/fixtures/merge_requests_diffs.rb b/spec/frontend/fixtures/merge_requests_diffs.rb index 7f0d650b710..ff4b27844a6 100644 --- a/spec/frontend/fixtures/merge_requests_diffs.rb +++ b/spec/frontend/fixtures/merge_requests_diffs.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe Projects::MergeRequests::DiffsController, '(JavaScript fixtures)', type: :controller do include JavaScriptFixturesHelpers - let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} + let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) } let(:project) { create(:project, :repository, namespace: namespace, path: 'merge-requests-project') } let(:user) { project.first_owner } let(:merge_request) { create(:merge_request, source_project: project, target_project: project, description: '- [ ] Task List Item') } diff --git a/spec/frontend/fixtures/metrics_dashboard.rb b/spec/frontend/fixtures/metrics_dashboard.rb index d59b01b04af..7f8b3d378d3 100644 --- a/spec/frontend/fixtures/metrics_dashboard.rb +++ b/spec/frontend/fixtures/metrics_dashboard.rb @@ -7,7 +7,7 @@ RSpec.describe MetricsDashboard, '(JavaScript fixtures)', type: :controller do include MetricsDashboardHelpers let_it_be(:user) { create(:user) } - let_it_be(:namespace) { create(:namespace, name: 'monitoring' )} + let_it_be(:namespace) { create(:namespace, name: 'monitoring' ) } let_it_be(:project) { project_with_dashboard_namespace('.gitlab/dashboards/test.yml', nil, namespace: namespace) } let_it_be(:environment) { create(:environment, id: 1, project: project) } let_it_be(:params) { { environment: environment } } diff --git a/spec/frontend/fixtures/pipeline_schedules.rb b/spec/frontend/fixtures/pipeline_schedules.rb index e155d27920d..5b7a445557e 100644 --- a/spec/frontend/fixtures/pipeline_schedules.rb +++ b/spec/frontend/fixtures/pipeline_schedules.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe Projects::PipelineSchedulesController, '(JavaScript fixtures)', type: :controller do include JavaScriptFixturesHelpers - let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} + let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) } let(:project) { create(:project, :public, :repository) } let(:user) { project.first_owner } let!(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: user) } diff --git a/spec/frontend/fixtures/pipelines.rb b/spec/frontend/fixtures/pipelines.rb index 709e14183df..114db26d6a9 100644 --- a/spec/frontend/fixtures/pipelines.rb +++ b/spec/frontend/fixtures/pipelines.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe Projects::PipelinesController, '(JavaScript fixtures)', type: :controller do include JavaScriptFixturesHelpers - let_it_be(:namespace) { create(:namespace, name: 'frontend-fixtures' )} + let_it_be(:namespace) { create(:namespace, name: 'frontend-fixtures' ) } let_it_be(:project) { create(:project, :repository, namespace: namespace, path: 'pipelines-project') } let_it_be(:commit_without_author) { RepoHelpers.another_sample_commit } diff --git a/spec/frontend/fixtures/projects.rb b/spec/frontend/fixtures/projects.rb index fa7d61df3e8..b9c427c7505 100644 --- a/spec/frontend/fixtures/projects.rb +++ b/spec/frontend/fixtures/projects.rb @@ -8,7 +8,7 @@ RSpec.describe 'Projects (JavaScript fixtures)', type: :controller do runners_token = 'runnerstoken:intabulasreferre' - let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} + let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) } let(:project) { create(:project, namespace: namespace, path: 'builds-project', runners_token: runners_token, avatar: fixture_file_upload('spec/fixtures/dk.png', 'image/png')) } let(:project_with_repo) { create(:project, :repository, description: 'Code and stuff', avatar: fixture_file_upload('spec/fixtures/dk.png', 'image/png')) } let(:project_variable_populated) { create(:project, namespace: namespace, path: 'builds-project2', runners_token: runners_token) } diff --git a/spec/frontend/fixtures/raw.rb b/spec/frontend/fixtures/raw.rb index b117cfea5fa..7bd5b8c5f6c 100644 --- a/spec/frontend/fixtures/raw.rb +++ b/spec/frontend/fixtures/raw.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe 'Raw files', '(JavaScript fixtures)' do include JavaScriptFixturesHelpers - let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} + let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) } let(:project) { create(:project, :repository, namespace: namespace, path: 'raw-project') } let(:response) { @response } diff --git a/spec/frontend/fixtures/snippet.rb b/spec/frontend/fixtures/snippet.rb index f05ff3ee269..58d4bc5c1f3 100644 --- a/spec/frontend/fixtures/snippet.rb +++ b/spec/frontend/fixtures/snippet.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe SnippetsController, '(JavaScript fixtures)', type: :controller do include JavaScriptFixturesHelpers - let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} + let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) } let(:project) { create(:project, :repository, namespace: namespace, path: 'branches-project') } let(:user) { project.first_owner } let(:snippet) { create(:personal_snippet, :public, title: 'snippet.md', content: '# snippet', file_name: 'snippet.md', author: user) } diff --git a/spec/frontend/fixtures/todos.rb b/spec/frontend/fixtures/todos.rb index 7dce09e8f49..d934396f803 100644 --- a/spec/frontend/fixtures/todos.rb +++ b/spec/frontend/fixtures/todos.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe 'Todos (JavaScript fixtures)' do include JavaScriptFixturesHelpers - let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} + let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) } let(:project) { create(:project_empty_repo, namespace: namespace, path: 'todos-project') } let(:user) { project.first_owner } let(:issue_1) { create(:issue, title: 'issue_1', project: project) } diff --git a/spec/frontend/invite_members/components/invite_members_modal_spec.js b/spec/frontend/invite_members/components/invite_members_modal_spec.js index 2058784b033..e9e1fbad07b 100644 --- a/spec/frontend/invite_members/components/invite_members_modal_spec.js +++ b/spec/frontend/invite_members/components/invite_members_modal_spec.js @@ -19,6 +19,7 @@ import { MEMBERS_TO_PROJECT_CELEBRATE_INTRO_TEXT, LEARN_GITLAB, EXPANDED_ERRORS, + EMPTY_INVITES_ERROR_TEXT, } from '~/invite_members/constants'; import eventHub from '~/invite_members/event_hub'; import ContentTransition from '~/vue_shared/components/content_transition.vue'; @@ -255,6 +256,8 @@ describe('InviteMembersModal', () => { it('tracks the submit for invite_members_for_task', async () => { await setupComponentWithTasks(); + await triggerMembersTokenSelect([user1]); + clickInviteButton(); expect(ExperimentTracking).toHaveBeenCalledWith(INVITE_MEMBERS_FOR_TASK.name, { @@ -265,6 +268,16 @@ describe('InviteMembersModal', () => { INVITE_MEMBERS_FOR_TASK.submit, ); }); + + it('does not track the submit for invite_members_for_task when invites have not been entered', async () => { + await setupComponentWithTasks(); + clickInviteButton(); + + expect(ExperimentTracking).not.toHaveBeenCalledWith( + INVITE_MEMBERS_FOR_TASK.name, + expect.any, + ); + }); }); }); @@ -380,6 +393,25 @@ describe('InviteMembersModal', () => { "The member's email address is not allowed for this project. Go to the Admin area > Sign-up restrictions, and check Allowed domains for sign-ups."; const expectedSyntaxError = 'email contains an invalid email address'; + describe('when no invites have been entered in the form and then some are entered', () => { + beforeEach(async () => { + createInviteMembersToGroupWrapper(); + }); + + it('displays an error', async () => { + clickInviteButton(); + + await waitForPromises(); + + expect(membersFormGroupInvalidFeedback()).toBe(EMPTY_INVITES_ERROR_TEXT); + expect(findMembersSelect().props('exceptionState')).toBe(false); + + await triggerMembersTokenSelect([user1]); + + expect(membersFormGroupInvalidFeedback()).toBe(''); + }); + }); + describe('when inviting an existing user to group by user ID', () => { const postData = { user_id: '1,2', diff --git a/spec/lib/api/helpers/packages_helpers_spec.rb b/spec/lib/api/helpers/packages_helpers_spec.rb index 0c51e25bad9..69303a12d43 100644 --- a/spec/lib/api/helpers/packages_helpers_spec.rb +++ b/spec/lib/api/helpers/packages_helpers_spec.rb @@ -5,6 +5,8 @@ require 'spec_helper' RSpec.describe API::Helpers::PackagesHelpers do let_it_be(:helper) { Class.new.include(described_class).new } let_it_be(:project) { create(:project) } + let_it_be(:group) { create(:group) } + let_it_be(:package) { create(:package) } describe 'authorize_packages_access!' do subject { helper.authorize_packages_access!(project) } @@ -17,7 +19,45 @@ RSpec.describe API::Helpers::PackagesHelpers do end end - %i[read_package create_package destroy_package].each do |action| + describe 'authorize_read_package!' do + using RSpec::Parameterized::TableSyntax + + where(:subject, :expected_class) do + ref(:project) | ::Packages::Policies::Project + ref(:group) | ::Packages::Policies::Group + ref(:package) | ::Packages::Package + end + + with_them do + it 'calls authorize! with correct subject' do + expect(helper).to receive(:authorize!).with(:read_package, have_attributes(id: subject.id, class: expected_class)) + + expect(helper.send('authorize_read_package!', subject)).to eq nil + end + end + + context 'with feature flag disabled' do + before do + stub_feature_flags(read_package_policy_rule: false) + end + + where(:subject, :expected_class) do + ref(:project) | ::Project + ref(:group) | ::Group + ref(:package) | ::Packages::Package + end + + with_them do + it 'calls authorize! with correct subject' do + expect(helper).to receive(:authorize!).with(:read_package, have_attributes(id: subject.id, class: expected_class)) + + expect(helper.send('authorize_read_package!', subject)).to eq nil + end + end + end + end + + %i[create_package destroy_package].each do |action| describe "authorize_#{action}!" do subject { helper.send("authorize_#{action}!", project) } diff --git a/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb b/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb index 9b78fc807bf..f24c40cfe89 100644 --- a/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb +++ b/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb @@ -65,7 +65,7 @@ RSpec.describe Sidebars::Projects::Menus::PackagesRegistriesMenu do describe 'Packages Registry' do let(:item_id) { :packages_registry } - context 'when user can read packages' do + shared_examples 'when user can read packages' do context 'when config package setting is disabled' do it 'the menu item is not added to list of menu items' do stub_config(packages: { enabled: false }) @@ -83,13 +83,25 @@ RSpec.describe Sidebars::Projects::Menus::PackagesRegistriesMenu do end end - context 'when user cannot read packages' do + shared_examples 'when user cannot read packages' do let(:user) { nil } it 'the menu item is not added to list of menu items' do is_expected.to be_nil end end + + it_behaves_like 'when user can read packages' + it_behaves_like 'when user cannot read packages' + + context 'with feature flag disabled' do + before do + stub_feature_flags(read_package_policy_rule: false) + end + + it_behaves_like 'when user can read packages' + it_behaves_like 'when user cannot read packages' + end end describe 'Container Registry' do diff --git a/spec/models/concerns/pg_full_text_searchable_spec.rb b/spec/models/concerns/pg_full_text_searchable_spec.rb index 55e3caf3c4c..3e42a3504ac 100644 --- a/spec/models/concerns/pg_full_text_searchable_spec.rb +++ b/spec/models/concerns/pg_full_text_searchable_spec.rb @@ -96,6 +96,7 @@ RSpec.describe PgFullTextSearchable do it 'ignores accents' do expect(model_class.pg_full_text_search('jurgen')).to contain_exactly(with_accent) + expect(model_class.pg_full_text_search('Jürgen')).to contain_exactly(with_accent) end it 'does not support searching by non-Latin characters' do diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 61662411ac8..a36b656a864 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -3465,6 +3465,23 @@ RSpec.describe Group do end end + describe '#packages_policy_subject' do + it 'returns wrapper' do + expect(group.packages_policy_subject).to be_a(Packages::Policies::Group) + expect(group.packages_policy_subject.group).to eq(group) + end + + context 'with feature flag disabled' do + before do + stub_feature_flags(read_package_policy_rule: false) + end + + it 'returns group' do + expect(group.packages_policy_subject).to eq(group) + end + end + end + describe '#gitlab_deploy_token' do subject(:gitlab_deploy_token) { group.gitlab_deploy_token } diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 49d6ff97894..f4628844eaf 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -8450,6 +8450,25 @@ RSpec.describe Project, factory_default: :keep do end end + describe '#packages_policy_subject' do + let_it_be(:project) { create(:project) } + + it 'returns wrapper' do + expect(project.packages_policy_subject).to be_a(Packages::Policies::Project) + expect(project.packages_policy_subject.project).to eq(project) + end + + context 'with feature flag disabled' do + before do + stub_feature_flags(read_package_policy_rule: false) + end + + it 'returns project' do + expect(project.packages_policy_subject).to eq(project) + end + end + end + describe '#destroy_deployment_by_id' do let(:project) { create(:project, :repository) } diff --git a/spec/policies/packages/policies/group_policy_spec.rb b/spec/policies/packages/policies/group_policy_spec.rb new file mode 100644 index 00000000000..d0d9a9a22f5 --- /dev/null +++ b/spec/policies/packages/policies/group_policy_spec.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Packages::Policies::GroupPolicy do + include_context 'GroupPolicy context' + + subject { described_class.new(current_user, group.packages_policy_subject) } + + describe 'read_package' do + context 'with admin' do + let(:current_user) { admin } + + context 'when admin mode is enabled', :enable_admin_mode do + it { is_expected.to be_allowed(:read_package) } + end + + context 'when admin mode is disabled' do + it { is_expected.to be_disallowed(:read_package) } + end + end + + context 'with owner' do + let(:current_user) { owner } + + it { is_expected.to be_allowed(:read_package) } + end + + context 'with maintainer' do + let(:current_user) { maintainer } + + it { is_expected.to be_allowed(:read_package) } + end + + context 'with reporter' do + let(:current_user) { reporter } + + it { is_expected.to be_allowed(:read_package) } + end + + context 'with guest' do + let(:current_user) { guest } + + it { is_expected.to be_disallowed(:read_package) } + end + + context 'with non member' do + let(:current_user) { create(:user) } + + it { is_expected.to be_disallowed(:read_package) } + end + + context 'with anonymous' do + let(:current_user) { nil } + + it { is_expected.to be_disallowed(:read_package) } + end + end + + describe 'deploy token access' do + let!(:group_deploy_token) do + create(:group_deploy_token, group: group, deploy_token: deploy_token) + end + + subject { described_class.new(deploy_token, group.packages_policy_subject) } + + context 'when a deploy token with read_package_registry scope' do + let(:deploy_token) { create(:deploy_token, :group, read_package_registry: true) } + + it { is_expected.to be_allowed(:read_package) } + end + + context 'when a deploy token with write_package_registry scope' do + let(:deploy_token) { create(:deploy_token, :group, write_package_registry: true) } + + it { is_expected.to be_allowed(:read_package) } + end + end +end diff --git a/spec/policies/packages/policies/project_policy_spec.rb b/spec/policies/packages/policies/project_policy_spec.rb new file mode 100644 index 00000000000..15c5942ea4d --- /dev/null +++ b/spec/policies/packages/policies/project_policy_spec.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Packages::Policies::ProjectPolicy do + include_context 'ProjectPolicy context' + + let(:project) { public_project } + + subject { described_class.new(current_user, project.packages_policy_subject) } + + describe 'deploy token access' do + let!(:project_deploy_token) do + create(:project_deploy_token, project: project, deploy_token: deploy_token) + end + + subject { described_class.new(deploy_token, project.packages_policy_subject) } + + context 'when a deploy token with read_package_registry scope' do + let(:deploy_token) { create(:deploy_token, read_package_registry: true) } + + it { is_expected.to be_allowed(:read_package) } + + it_behaves_like 'package access with repository disabled' + end + + context 'when a deploy token with write_package_registry scope' do + let(:deploy_token) { create(:deploy_token, write_package_registry: true) } + + it { is_expected.to be_allowed(:read_package) } + + it_behaves_like 'package access with repository disabled' + end + end + + describe 'read_package' do + context 'with admin' do + let(:current_user) { admin } + + it { is_expected.to be_allowed(:read_package) } + + it_behaves_like 'package access with repository disabled' + end + + context 'with owner' do + let(:current_user) { owner } + + it { is_expected.to be_allowed(:read_package) } + end + + context 'with maintainer' do + let(:current_user) { maintainer } + + it { is_expected.to be_allowed(:read_package) } + end + + context 'with developer' do + let(:current_user) { developer } + + it { is_expected.to be_allowed(:read_package) } + end + + context 'with reporter' do + let(:current_user) { reporter } + + it { is_expected.to be_allowed(:read_package) } + end + + context 'with guest' do + let(:current_user) { guest } + + it { is_expected.to be_allowed(:read_package) } + end + + context 'with non member' do + let(:current_user) { non_member } + + it { is_expected.to be_allowed(:read_package) } + end + + context 'with anonymous' do + let(:current_user) { anonymous } + + it { is_expected.to be_allowed(:read_package) } + end + end +end diff --git a/spec/support/helpers/cycle_analytics_helpers.rb b/spec/support/helpers/cycle_analytics_helpers.rb index 05e9a099a2b..6d41d7b7414 100644 --- a/spec/support/helpers/cycle_analytics_helpers.rb +++ b/spec/support/helpers/cycle_analytics_helpers.rb @@ -7,7 +7,7 @@ module CycleAnalyticsHelpers def path_nav_stage_names_without_median # Returns the path names with the median value stripped out - page.all('.gl-path-button').collect(&:text).map {|name_with_median| name_with_median.split("\n")[0] } + page.all('.gl-path-button').collect(&:text).map { |name_with_median| name_with_median.split("\n")[0] } end def fill_in_custom_stage_fields diff --git a/spec/support/redis/redis_shared_examples.rb b/spec/support/redis/redis_shared_examples.rb index d4c8682ec71..33945509675 100644 --- a/spec/support/redis/redis_shared_examples.rb +++ b/spec/support/redis/redis_shared_examples.rb @@ -3,19 +3,19 @@ RSpec.shared_examples "redis_shared_examples" do include StubENV - let(:test_redis_url) { "redis://redishost:#{redis_port}"} + let(:test_redis_url) { "redis://redishost:#{redis_port}" } let(:config_file_name) { instance_specific_config_file } let(:config_old_format_socket) { "spec/fixtures/config/redis_old_format_socket.yml" } let(:config_new_format_socket) { "spec/fixtures/config/redis_new_format_socket.yml" } - let(:old_socket_path) {"/path/to/old/redis.sock" } - let(:new_socket_path) {"/path/to/redis.sock" } + let(:old_socket_path) { "/path/to/old/redis.sock" } + let(:new_socket_path) { "/path/to/redis.sock" } let(:config_old_format_host) { "spec/fixtures/config/redis_old_format_host.yml" } let(:config_new_format_host) { "spec/fixtures/config/redis_new_format_host.yml" } let(:redis_port) { 6379 } let(:redis_database) { 99 } let(:sentinel_port) { 26379 } - let(:config_with_environment_variable_inside) { "spec/fixtures/config/redis_config_with_env.yml"} - let(:config_env_variable_url) {"TEST_GITLAB_REDIS_URL"} + let(:config_with_environment_variable_inside) { "spec/fixtures/config/redis_config_with_env.yml" } + let(:config_env_variable_url) { "TEST_GITLAB_REDIS_URL" } let(:rails_root) { Dir.mktmpdir('redis_shared_examples') } before do diff --git a/spec/support/shared_contexts/graphql/requests/packages_shared_context.rb b/spec/support/shared_contexts/graphql/requests/packages_shared_context.rb index b29a231f3a6..d7cfdc09732 100644 --- a/spec/support/shared_contexts/graphql/requests/packages_shared_context.rb +++ b/spec/support/shared_contexts/graphql/requests/packages_shared_context.rb @@ -16,8 +16,8 @@ RSpec.shared_context 'package details setup' do let(:metadata_response) { graphql_data_at(:package, :metadata) } let(:first_file) { package.package_files.find { |f| a_graphql_entity_for(f).matches?(first_file_response) } } let(:package_files_response) { graphql_data_at(:package, :package_files, :nodes) } - let(:first_file_response) { graphql_data_at(:package, :package_files, :nodes, 0)} - let(:first_file_response_metadata) { graphql_data_at(:package, :package_files, :nodes, 0, :file_metadata)} + let(:first_file_response) { graphql_data_at(:package, :package_files, :nodes, 0) } + let(:first_file_response_metadata) { graphql_data_at(:package, :package_files, :nodes, 0, :file_metadata) } let(:query) do graphql_query_for(:package, { id: package_global_id }, <<~FIELDS) diff --git a/spec/support/shared_contexts/markdown_golden_master_shared_examples.rb b/spec/support/shared_contexts/markdown_golden_master_shared_examples.rb index dea03af2248..168aef0f174 100644 --- a/spec/support/shared_contexts/markdown_golden_master_shared_examples.rb +++ b/spec/support/shared_contexts/markdown_golden_master_shared_examples.rb @@ -42,7 +42,7 @@ RSpec.shared_context 'API::Markdown Golden Master shared context' do |markdown_y if focused_markdown_examples_string = ENV['FOCUSED_MARKDOWN_EXAMPLES'] focused_markdown_examples = focused_markdown_examples_string.split(',').map(&:strip) || [] - markdown_examples.reject! {|markdown_example| !focused_markdown_examples.include?(markdown_example.fetch(:name)) } + markdown_examples.reject! { |markdown_example| !focused_markdown_examples.include?(markdown_example.fetch(:name)) } end markdown_examples.each do |markdown_example| diff --git a/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb b/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb index b90270356f8..3974338238a 100644 --- a/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb +++ b/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb @@ -64,5 +64,5 @@ RSpec.shared_context 'conan file upload endpoints' do let(:jwt) { build_jwt(personal_access_token) } let(:headers_with_token) { build_token_auth_header(jwt.encoded).merge(workhorse_headers) } - let(:recipe_path) { "foo/bar/#{project.full_path.tr('/', '+')}/baz"} + let(:recipe_path) { "foo/bar/#{project.full_path.tr('/', '+')}/baz" } end diff --git a/spec/support/shared_examples/controllers/error_tracking_shared_examples.rb b/spec/support/shared_examples/controllers/error_tracking_shared_examples.rb index 08e5efcf63c..1bf2f158504 100644 --- a/spec/support/shared_examples/controllers/error_tracking_shared_examples.rb +++ b/spec/support/shared_examples/controllers/error_tracking_shared_examples.rb @@ -3,5 +3,5 @@ RSpec.shared_examples 'sets the polling header' do subject { response.headers[Gitlab::PollingInterval::HEADER_NAME] } - it { is_expected.to eq '1000'} + it { is_expected.to eq '1000' } end diff --git a/spec/support/shared_examples/controllers/snippets_sort_order_shared_examples.rb b/spec/support/shared_examples/controllers/snippets_sort_order_shared_examples.rb index aa4d78b23f4..112b9cbb204 100644 --- a/spec/support/shared_examples/controllers/snippets_sort_order_shared_examples.rb +++ b/spec/support/shared_examples/controllers/snippets_sort_order_shared_examples.rb @@ -3,7 +3,7 @@ RSpec.shared_examples 'snippets sort order' do let(:params) { {} } let(:sort_argument) { {} } - let(:sort_params) { params.merge(sort_argument)} + let(:sort_params) { params.merge(sort_argument) } before do sign_in(user) diff --git a/spec/support/shared_examples/features/board_sidebar_labels_examples.rb b/spec/support/shared_examples/features/board_sidebar_labels_examples.rb index 520980c2615..4e5b371c18d 100644 --- a/spec/support/shared_examples/features/board_sidebar_labels_examples.rb +++ b/spec/support/shared_examples/features/board_sidebar_labels_examples.rb @@ -17,7 +17,7 @@ RSpec.shared_context 'labels from nested groups and projects' do let_it_be(:maintainer) { create(:user) } let(:labels_select) { find("[data-testid='sidebar-labels']") } - let(:labels_dropdown) { labels_select.find('[data-testid="dropdown-content"]')} + let(:labels_dropdown) { labels_select.find('[data-testid="dropdown-content"]') } before do group.add_maintainer(maintainer) diff --git a/spec/support/shared_examples/features/snippets_shared_examples.rb b/spec/support/shared_examples/features/snippets_shared_examples.rb index c402333107c..bf870b3ce66 100644 --- a/spec/support/shared_examples/features/snippets_shared_examples.rb +++ b/spec/support/shared_examples/features/snippets_shared_examples.rb @@ -194,7 +194,7 @@ end RSpec.shared_examples 'personal snippet with references' do let_it_be(:project) { create(:project, :repository) } let_it_be(:merge_request) { create(:merge_request, source_project: project) } - let_it_be(:project_snippet) { create(:project_snippet, :repository, project: project)} + let_it_be(:project_snippet) { create(:project_snippet, :repository, project: project) } let_it_be(:issue) { create(:issue, project: project) } let_it_be(:commit) { project.commit } diff --git a/spec/support/shared_examples/features/wiki/user_views_asciidoc_page_with_includes_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_views_asciidoc_page_with_includes_shared_examples.rb index 6fdc5ecae73..91bd7c6cb29 100644 --- a/spec/support/shared_examples/features/wiki/user_views_asciidoc_page_with_includes_shared_examples.rb +++ b/spec/support/shared_examples/features/wiki/user_views_asciidoc_page_with_includes_shared_examples.rb @@ -2,7 +2,7 @@ RSpec.shared_examples 'User views AsciiDoc page with includes' do let_it_be(:wiki_content_selector) { '[data-qa-selector=wiki_page_content]' } # rubocop:disable QA/SelectorUsage - let!(:included_wiki_page) { create_wiki_page('included_page', content: 'Content from the included page')} + let!(:included_wiki_page) { create_wiki_page('included_page', content: 'Content from the included page') } let!(:wiki_page) { create_wiki_page('home', content: "Content from the main page.\ninclude::included_page.asciidoc[]") } def create_wiki_page(title, content:) diff --git a/spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb index b786d7e5527..c388a3a2c7f 100644 --- a/spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb +++ b/spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb @@ -131,7 +131,7 @@ RSpec.shared_examples 'common trace features' do end context 'logs contains "section_start"' do - let(:log) { "section_start:1506417476:a_section\r\033[0Klooks like a section_start:invalid\nsection_end:1506417477:a_section\r\033[0K"} + let(:log) { "section_start:1506417476:a_section\r\033[0Klooks like a section_start:invalid\nsection_end:1506417477:a_section\r\033[0K" } it "returns only one section" do expect(sections).not_to be_empty @@ -144,7 +144,7 @@ RSpec.shared_examples 'common trace features' do end context 'missing section_end' do - let(:log) { "section_start:1506417476:a_section\r\033[0KSome logs\nNo section_end\n"} + let(:log) { "section_start:1506417476:a_section\r\033[0KSome logs\nNo section_end\n" } it "returns no sections" do expect(sections).to be_empty @@ -152,7 +152,7 @@ RSpec.shared_examples 'common trace features' do end context 'missing section_start' do - let(:log) { "Some logs\nNo section_start\nsection_end:1506417476:a_section\r\033[0K"} + let(:log) { "Some logs\nNo section_start\nsection_end:1506417476:a_section\r\033[0K" } it "returns no sections" do expect(sections).to be_empty @@ -160,7 +160,7 @@ RSpec.shared_examples 'common trace features' do end context 'inverted section_start section_end' do - let(:log) { "section_end:1506417476:a_section\r\033[0Klooks like a section_start:invalid\nsection_start:1506417477:a_section\r\033[0K"} + let(:log) { "section_end:1506417476:a_section\r\033[0Klooks like a section_start:invalid\nsection_start:1506417477:a_section\r\033[0K" } it "returns no sections" do expect(sections).to be_empty @@ -169,7 +169,7 @@ RSpec.shared_examples 'common trace features' do end describe '#write' do - subject { trace.send(:write, mode) { } } + subject { trace.send(:write, mode) {} } let(:mode) { 'wb' } diff --git a/spec/support/shared_examples/lib/gitlab/sql/set_operator_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/sql/set_operator_shared_examples.rb index ead8b174d46..ec7b2794703 100644 --- a/spec/support/shared_examples/lib/gitlab/sql/set_operator_shared_examples.rb +++ b/spec/support/shared_examples/lib/gitlab/sql/set_operator_shared_examples.rb @@ -25,7 +25,7 @@ RSpec.shared_examples 'SQL set operator' do |operator_keyword| empty_relation = User.none.select(:id) set_operator = described_class.new([empty_relation, relation_1, relation_2]) - expect {User.where("users.id IN (#{set_operator.to_sql})").to_a}.not_to raise_error + expect { User.where("users.id IN (#{set_operator.to_sql})").to_a }.not_to raise_error expect(set_operator.to_sql).to eq("(#{to_sql(relation_1)})\n#{operator_keyword}\n(#{to_sql(relation_2)})") end diff --git a/spec/support/shared_examples/models/cluster_application_core_shared_examples.rb b/spec/support/shared_examples/models/cluster_application_core_shared_examples.rb index 51071ae47c3..ca9122bf61f 100644 --- a/spec/support/shared_examples/models/cluster_application_core_shared_examples.rb +++ b/spec/support/shared_examples/models/cluster_application_core_shared_examples.rb @@ -37,7 +37,7 @@ RSpec.shared_examples 'cluster application core specs' do |application_name| with_them do subject { described_class.new(cluster: cluster).helm_command_module } - let(:cluster) { build(:cluster, helm_major_version: helm_major_version)} + let(:cluster) { build(:cluster, helm_major_version: helm_major_version) } it { is_expected.to eq(expected_helm_command_module) } end diff --git a/spec/support/shared_examples/models/concerns/analytics/cycle_analytics/stage_event_model_examples.rb b/spec/support/shared_examples/models/concerns/analytics/cycle_analytics/stage_event_model_examples.rb index 8ff30021d6e..6f104f400bc 100644 --- a/spec/support/shared_examples/models/concerns/analytics/cycle_analytics/stage_event_model_examples.rb +++ b/spec/support/shared_examples/models/concerns/analytics/cycle_analytics/stage_event_model_examples.rb @@ -89,7 +89,7 @@ RSpec.shared_examples 'StageEventModel' do let_it_be(:user) { create(:user) } let_it_be(:project) { create(:user) } let_it_be(:milestone) { create(:milestone) } - let_it_be(:issuable_with_assignee) { create(issuable_factory, assignees: [user])} + let_it_be(:issuable_with_assignee) { create(issuable_factory, assignees: [user]) } let_it_be(:record) { create(stage_event_factory, start_event_timestamp: 3.years.ago.to_date, end_event_timestamp: 2.years.ago.to_date) } let_it_be(:record_with_author) { create(stage_event_factory, author_id: user.id) } diff --git a/spec/support/shared_examples/models/concerns/has_repository_shared_examples.rb b/spec/support/shared_examples/models/concerns/has_repository_shared_examples.rb index a403a27adef..0a07c9d677b 100644 --- a/spec/support/shared_examples/models/concerns/has_repository_shared_examples.rb +++ b/spec/support/shared_examples/models/concerns/has_repository_shared_examples.rb @@ -125,17 +125,17 @@ RSpec.shared_examples 'model with repository' do end describe '#valid_repo?' do - it { expect(stubbed_container.valid_repo?).to be(false)} + it { expect(stubbed_container.valid_repo?).to be(false) } it { expect(container.valid_repo?).to be(true) } end describe '#repository_exists?' do - it { expect(stubbed_container.repository_exists?).to be(false)} + it { expect(stubbed_container.repository_exists?).to be(false) } it { expect(container.repository_exists?).to be(true) } end describe '#repo_exists?' do - it { expect(stubbed_container.repo_exists?).to be(false)} + it { expect(stubbed_container.repo_exists?).to be(false) } it { expect(container.repo_exists?).to be(true) } end diff --git a/spec/support/shared_examples/models/concerns/incident_management/escalatable_shared_examples.rb b/spec/support/shared_examples/models/concerns/incident_management/escalatable_shared_examples.rb index 8ee76efc896..a5970f134d9 100644 --- a/spec/support/shared_examples/models/concerns/incident_management/escalatable_shared_examples.rb +++ b/spec/support/shared_examples/models/concerns/incident_management/escalatable_shared_examples.rb @@ -77,7 +77,7 @@ RSpec.shared_examples 'a model including Escalatable' do end context 'scopes' do - let(:all_escalatables) { described_class.where(id: [triggered_escalatable, acknowledged_escalatable, ignored_escalatable, resolved_escalatable])} + let(:all_escalatables) { described_class.where(id: [triggered_escalatable, acknowledged_escalatable, ignored_escalatable, resolved_escalatable]) } describe '.order_status' do subject { all_escalatables.order_status(order) } diff --git a/spec/support/shared_examples/models/label_note_shared_examples.rb b/spec/support/shared_examples/models/label_note_shared_examples.rb index 73066fb631a..f61007f57fd 100644 --- a/spec/support/shared_examples/models/label_note_shared_examples.rb +++ b/spec/support/shared_examples/models/label_note_shared_examples.rb @@ -12,7 +12,7 @@ RSpec.shared_examples 'label note created from events' do def label_refs(events) labels = events.map(&:label).compact - labels.map { |l| l.to_reference}.sort.join(' ') + labels.map { |l| l.to_reference }.sort.join(' ') end let(:time) { Time.now } diff --git a/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb b/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb index 6b0ae589efb..3d7d97bbeae 100644 --- a/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb +++ b/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb @@ -202,17 +202,17 @@ RSpec.shared_examples 'Debian Distribution' do |factory, container, can_freeze| end else describe 'group distribution specifics' do - let_it_be(:public_project) { create(:project, :public, group: distribution_with_suite.container)} + let_it_be(:public_project) { create(:project, :public, group: distribution_with_suite.container) } let_it_be(:public_distribution_with_same_codename) { create(:debian_project_distribution, container: public_project, codename: distribution_with_suite.codename) } - let_it_be(:public_package_with_same_codename) { create(:debian_package, project: public_project, published_in: public_distribution_with_same_codename)} + let_it_be(:public_package_with_same_codename) { create(:debian_package, project: public_project, published_in: public_distribution_with_same_codename) } let_it_be(:public_distribution_with_same_suite) { create(:debian_project_distribution, container: public_project, suite: distribution_with_suite.suite) } - let_it_be(:public_package_with_same_suite) { create(:debian_package, project: public_project, published_in: public_distribution_with_same_suite)} + let_it_be(:public_package_with_same_suite) { create(:debian_package, project: public_project, published_in: public_distribution_with_same_suite) } - let_it_be(:private_project) { create(:project, :private, group: distribution_with_suite.container)} + let_it_be(:private_project) { create(:project, :private, group: distribution_with_suite.container) } let_it_be(:private_distribution_with_same_codename) { create(:debian_project_distribution, container: private_project, codename: distribution_with_suite.codename) } - let_it_be(:private_package_with_same_codename) { create(:debian_package, project: private_project, published_in: private_distribution_with_same_codename)} + let_it_be(:private_package_with_same_codename) { create(:debian_package, project: private_project, published_in: private_distribution_with_same_codename) } let_it_be(:private_distribution_with_same_suite) { create(:debian_project_distribution, container: private_project, suite: distribution_with_suite.suite) } - let_it_be(:private_package_with_same_suite) { create(:debian_package, project: private_project, published_in: private_distribution_with_same_codename)} + let_it_be(:private_package_with_same_suite) { create(:debian_package, project: private_project, published_in: private_distribution_with_same_codename) } describe '#packages' do subject { distribution_with_suite.packages } diff --git a/spec/support/shared_examples/models/project_latest_successful_build_for_shared_examples.rb b/spec/support/shared_examples/models/project_latest_successful_build_for_shared_examples.rb index 66cd8d1df12..9093b386a5d 100644 --- a/spec/support/shared_examples/models/project_latest_successful_build_for_shared_examples.rb +++ b/spec/support/shared_examples/models/project_latest_successful_build_for_shared_examples.rb @@ -64,7 +64,7 @@ RSpec.shared_examples 'latest successful build for sha or ref' do context 'with build belonging to a child pipeline' do let(:child_pipeline) { create_pipeline(project) } let(:parent_bridge) { create(:ci_bridge, pipeline: pipeline, project: pipeline.project) } - let!(:pipeline_source) { create(:ci_sources_pipeline, source_job: parent_bridge, pipeline: child_pipeline)} + let!(:pipeline_source) { create(:ci_sources_pipeline, source_job: parent_bridge, pipeline: child_pipeline) } let!(:child_build) { create_build(child_pipeline, 'child-build') } let(:build_name) { child_build.name } diff --git a/spec/support/shared_examples/requests/api/issues/merge_requests_count_shared_examples.rb b/spec/support/shared_examples/requests/api/issues/merge_requests_count_shared_examples.rb index 971b21b5b32..8c4ff120471 100644 --- a/spec/support/shared_examples/requests/api/issues/merge_requests_count_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/issues/merge_requests_count_shared_examples.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true def get_issue - json_response.is_a?(Array) ? json_response.detect {|issue| issue['id'] == target_issue.id} : json_response + json_response.is_a?(Array) ? json_response.detect { |issue| issue['id'] == target_issue.id } : json_response end RSpec.shared_examples 'accessible merge requests count' do diff --git a/spec/support/shared_examples/requests/api/labels_api_shared_examples.rb b/spec/support/shared_examples/requests/api/labels_api_shared_examples.rb index 02e50b789cc..41d21490343 100644 --- a/spec/support/shared_examples/requests/api/labels_api_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/labels_api_shared_examples.rb @@ -9,6 +9,6 @@ RSpec.shared_examples 'fetches labels' do expect(json_response).to be_an Array expect(json_response).to all(match_schema('public_api/v4/labels/label')) expect(json_response.size).to eq(expected_labels.size) - expect(json_response.map {|r| r['name'] }).to match_array(expected_labels) + expect(json_response.map { |r| r['name'] }).to match_array(expected_labels) end end diff --git a/spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb b/spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb index 3ca2b9fa6de..2d036cb2aa3 100644 --- a/spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb @@ -70,7 +70,7 @@ RSpec.shared_examples 'repository_storage_moves API' do |container_type| get_container_repository_storage_moves - json_ids = json_response.map {|storage_move| storage_move['id'] } + json_ids = json_response.map { |storage_move| storage_move['id'] } expect(json_ids).to eq([ storage_move.id, storage_move_middle.id, diff --git a/spec/support/shared_examples/requests/lfs_http_shared_examples.rb b/spec/support/shared_examples/requests/lfs_http_shared_examples.rb index 294ceffd77b..83be0cc1fe3 100644 --- a/spec/support/shared_examples/requests/lfs_http_shared_examples.rb +++ b/spec/support/shared_examples/requests/lfs_http_shared_examples.rb @@ -49,7 +49,7 @@ RSpec.shared_examples 'LFS http 404 response' do end RSpec.shared_examples 'LFS http expected response code and message' do - let(:response_code) { } + let(:response_code) {} let(:response_headers) { {} } let(:content_type) { LfsRequest::CONTENT_TYPE } let(:message) {} diff --git a/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb b/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb index 6cae7d8e00f..9546b6cbea4 100644 --- a/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb +++ b/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb @@ -23,7 +23,7 @@ RSpec.shared_examples 'creates an alert management alert or errors' do end context 'and fails to save' do - let(:errors) { double(messages: { hosts: ['hosts array is over 255 chars'] }, '[]': [] )} + let(:errors) { double(messages: { hosts: ['hosts array is over 255 chars'] }, '[]': [] ) } before do allow(service).to receive(:alert).and_call_original diff --git a/spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb b/spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb index a46c2f0ac5c..1638e2fa86a 100644 --- a/spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb +++ b/spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb @@ -3,8 +3,8 @@ RSpec.shared_examples 'issues move service' do |group| shared_examples 'updating timestamps' do it 'updates updated_at' do - expect {described_class.new(parent, user, params).execute(issue)} - .to change {issue.reload.updated_at} + expect { described_class.new(parent, user, params).execute(issue) } + .to change { issue.reload.updated_at } end end diff --git a/spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb b/spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb index 2aac7e328f0..366fa4763e1 100644 --- a/spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb +++ b/spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb @@ -33,7 +33,7 @@ RSpec.shared_examples 'gitlab projects import validations' do context 'when there is a project with the same path' do let(:existing_project) { create(:project, namespace: namespace) } - let(:path) { existing_project.path} + let(:path) { existing_project.path } it 'does not create the project' do project = subject.execute diff --git a/spec/support/shared_examples/services/merge_request_shared_examples.rb b/spec/support/shared_examples/services/merge_request_shared_examples.rb index d2595b92cbc..b3ba0a1be93 100644 --- a/spec/support/shared_examples/services/merge_request_shared_examples.rb +++ b/spec/support/shared_examples/services/merge_request_shared_examples.rb @@ -123,7 +123,7 @@ end RSpec.shared_examples 'with an existing branch that has a merge request open' do |count| let(:changes) { existing_branch_changes } - let!(:merge_request) { create(:merge_request, source_project: project, source_branch: source_branch)} + let!(:merge_request) { create(:merge_request, source_project: project, source_branch: source_branch) } it_behaves_like 'a service that does not create a merge request' it_behaves_like 'a service that can change assignees of a merge request', count diff --git a/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb b/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb index f8b00d1e4c0..3c977e62a10 100644 --- a/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb +++ b/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb @@ -56,8 +56,8 @@ RSpec.shared_examples "migrates" do |to_store:, from_store: nil| it 'can access to the original file during migration' do file = subject.file - allow(subject).to receive(:delete_migrated_file) { } # Remove as a callback of :migrate - allow(subject).to receive(:record_upload) { } # Remove as a callback of :store (:record_upload) + allow(subject).to receive(:delete_migrated_file) {} # Remove as a callback of :migrate + allow(subject).to receive(:record_upload) {} # Remove as a callback of :store (:record_upload) expect(file.exists?).to be_truthy expect { migrate(to) }.not_to change { file.exists? } |