diff options
96 files changed, 793 insertions, 470 deletions
diff --git a/.gitlab/ci/review-apps/qa.gitlab-ci.yml b/.gitlab/ci/review-apps/qa.gitlab-ci.yml index 69ce028987a..d28819208b7 100644 --- a/.gitlab/ci/review-apps/qa.gitlab-ci.yml +++ b/.gitlab/ci/review-apps/qa.gitlab-ci.yml @@ -99,6 +99,7 @@ review-qa-non-blocking: variables: QA_SCENARIO: Test::Instance::ReviewNonBlocking QA_RUN_TYPE: review-qa-non-blocking + when: manual allow_failure: true review-qa-non-blocking-parallel: extends: diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index 1d7617e9df5..79da496d782 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -902,6 +902,7 @@ when: never - <<: *if-merge-request-labels-as-if-foss - <<: *if-merge-request-labels-run-all-rspec + - <<: *if-merge-request-labels-frontend-and-feature-flag - <<: *if-default-refs changes: *code-backstage-qa-patterns - <<: *if-default-refs @@ -932,6 +933,7 @@ - !reference [".strict-ee-only-rules", rules] - !reference [".frontend:rules:default-frontend-jobs-as-if-foss", rules] - <<: *if-merge-request-labels-run-all-jest + - <<: *if-merge-request-labels-frontend-and-feature-flag - <<: *if-merge-request changes: *frontend-patterns-for-as-if-foss diff --git a/.secretsignore b/.secretsignore deleted file mode 100644 index 071423bd3c1..00000000000 --- a/.secretsignore +++ /dev/null @@ -1,66 +0,0 @@ -# This file is for defining paths and secrets that will be ignored by ripsecret - -doc/* -spec/* -ee/spec/* -qa/* -*_spec.rb -config/gitlab.yml.example -workhorse/testdata/localhost.key -db/fixtures/**/*.rb - -[secrets] -AUTO_DEVOPS_DOMAIN -BACKWARD_DIRECTION -CI_BUILD_BEFORE_SHA -CI_BUILD_REF_NAME -CI_BUILD_REF_SLUG -CI_COMMIT_BRANCH -CI_COMMIT_REF_SLUG -CI_DEFAULT_BRANCH -CI_DEPLOY_FREEZE -CI_DEPLOY_PASSWORD -CI_ENVIRONMENT_SLUG -CI_ENVIRONMENT_URL -CI_GITLAB_FIPS_MODE -CI_JOB_NAME_SLUG -CI_JOB_STARTED_AT -CI_PAGES_DOMAIN -CI_PROJECT_NAME -CI_PROJECT_PATH -CI_PROJECT_PATH_SLUG -CI_PROJECT_VISIBILITY -CI_REGISTRY_IMAGE -CI_REGISTRY_PASSWORD -CI_REPOSITORY_URL -CROWDIN_API_KEY -DAST_API_PROFILE -DAST_PASSWORD_BASE64 -DAST_SUBMIT_FIELD -DAST_USERNAME_FIELD -DORA_METRICS_KEYS -ESCALATION_STATUS -FIFTY_PACKAGE_FILES -FORTY_PACKAGE_FILES -FORWARD_DIRECTION -GITLAB_FEATURES -GITLAB_USER_EMAIL -GITLAB_USER_LOGIN -GITLAB_USER_NAME -HARBOR_PASSWORD -HARBOR_USERNAME -KUBE_CA_PEM_FILE -KUBE_SERVICE_ACCOUNT -NAVSOURCE_VALUE -ONE_HUNDRED_TAGS -ONE_PACKAGE_FILE -STAGING_ENABLED -TEN_PACKAGE_FILES -THIRTY_PACKAGE_FILES -TRIGGER_PAYLOAD -TWENTY_FIVE_TAGS -TWENTY_PACKAGE_FILES -YOUR-ACCESSKEYID -YOUR-CLIENT-SECRET -YOUR_AUTH0_CLIENT_SECRET -sbdMsxcgW2Xs75Q2uHc9FhUCZSEV3fSg @@ -362,7 +362,7 @@ gem 'prometheus-client-mmap', '~> 0.16', require: 'prometheus/client' gem 'warning', '~> 1.3.0' group :development do - gem 'lefthook', '~> 1.2.1', require: false + gem 'lefthook', '~> 1.2.2', require: false gem 'rubocop' gem 'solargraph', '~> 0.47.2', require: false diff --git a/Gemfile.checksum b/Gemfile.checksum index ee9dcac0206..5f15974fe93 100644 --- a/Gemfile.checksum +++ b/Gemfile.checksum @@ -313,7 +313,7 @@ {"name":"kramdown-parser-gfm","version":"1.1.0","platform":"ruby","checksum":"fb39745516427d2988543bf01fc4cf0ab1149476382393e0e9c48592f6581729"}, {"name":"kubeclient","version":"4.9.3","platform":"ruby","checksum":"d5d38e719fbac44f396851aa57cd1b9f4f7dab4410ab680ccd21c9b741230046"}, {"name":"launchy","version":"2.5.0","platform":"ruby","checksum":"954243c4255920982ce682f89a42e76372dba94770bf09c23a523e204bdebef5"}, -{"name":"lefthook","version":"1.2.1","platform":"ruby","checksum":"857c7f99fe03252e6b1fd67267e08bd700cf94e3d178c7b7bf18d79f84686abf"}, +{"name":"lefthook","version":"1.2.2","platform":"ruby","checksum":"51359198401f1d6ccd4d21ce974eadad871d725f6de08fddb2f441512e34049f"}, {"name":"letter_opener","version":"1.7.0","platform":"ruby","checksum":"095bc0d58e006e5b43ea7d219e64ecf2de8d1f7d9dafc432040a845cf59b4725"}, {"name":"letter_opener_web","version":"2.0.0","platform":"ruby","checksum":"33860ad41e1785d75456500e8ca8bba8ed71ee6eaf08a98d06bbab67c5577b6f"}, {"name":"libyajl2","version":"1.2.0","platform":"ruby","checksum":"1117cd1e48db013b626e36269bbf1cef210538ca6d2e62d3fa3db9ded005b258"}, diff --git a/Gemfile.lock b/Gemfile.lock index 7eaaee07595..97b8c995cb1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -835,7 +835,7 @@ GEM rest-client (~> 2.0) launchy (2.5.0) addressable (~> 2.7) - lefthook (1.2.1) + lefthook (1.2.2) letter_opener (1.7.0) launchy (~> 2.2) letter_opener_web (2.0.0) @@ -1718,7 +1718,7 @@ DEPENDENCIES knapsack (~> 1.21.1) kramdown (~> 2.3.1) kubeclient (~> 4.9.3) - lefthook (~> 1.2.1) + lefthook (~> 1.2.2) letter_opener_web (~> 2.0.0) license_finder (~> 7.0) licensee (~> 9.15) @@ -1872,4 +1872,4 @@ DEPENDENCIES yajl-ruby (~> 1.4.3) BUNDLED WITH - 2.3.25 + 2.3.26 diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 7878e08e549..9b4323979b7 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -198,17 +198,17 @@ } } + $line-height: map-get($spacers, 4) + px-to-rem(2px); + &-icon { /** * 2px extra is to give a little more height than needed * to hide timeline line before/after the element starts/ends */ - height: map-get($spacers, 4) + px-to-rem(2px); + height: $line-height; z-index: 1; position: relative; - top: -3px; padding: $gl-padding-4 0; - background-color: $body-bg; &.opened { color: $green-500; @@ -220,7 +220,7 @@ } &-content { - line-height: initial; + line-height: $line-height; margin-left: $gl-padding-8; } } diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb index ee5774d4868..05addcf83d2 100644 --- a/app/models/concerns/taskable.rb +++ b/app/models/concerns/taskable.rb @@ -63,14 +63,15 @@ module Taskable def task_status(short: false) return '' if description.blank? - prep, completed = if short - ['/', ''] - else - [' of ', ' completed'] - end - sum = tasks.summary - "#{sum.complete_count}#{prep}#{sum.item_count} #{'checklist item'.pluralize(sum.item_count)}#{completed}" + checklist_item_noun = n_('checklist item', 'checklist items', sum.item_count) + if short + format(s_('Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}'), +checklist_item_noun: checklist_item_noun, complete_count: sum.complete_count, total_count: sum.item_count) + else + format(s_('Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed'), +checklist_item_noun: checklist_item_noun, complete_count: sum.complete_count, total_count: sum.item_count) + end end # Return a short string that describes the current state of this Taskable's diff --git a/app/models/member.rb b/app/models/member.rb index 80c5fd7e468..8deeebbeda9 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -61,6 +61,7 @@ class Member < ApplicationRecord validate :access_level_inclusion validate :validate_member_role_access_level validate :validate_access_level_locked_for_member_role, on: :update + validate :validate_member_role_belongs_to_same_root_namespace scope :with_invited_user_state, -> do joins('LEFT JOIN users as invited_user ON invited_user.email = members.invite_email') @@ -515,6 +516,15 @@ class Member < ApplicationRecord end end + def validate_member_role_belongs_to_same_root_namespace + return unless member_role_id + + return if member_namespace.id == member_role.namespace_id + return if member_namespace.root_ancestor.id == member_role.namespace_id + + errors.add(:member_namespace, _("must be in same hierarchy as custom role's namespace")) + end + def send_invite # override in subclass end diff --git a/app/models/members/member_role.rb b/app/models/members/member_role.rb index b4e3d6874ef..0661082dced 100644 --- a/app/models/members/member_role.rb +++ b/app/models/members/member_role.rb @@ -7,12 +7,21 @@ class MemberRole < ApplicationRecord # rubocop:disable Gitlab/NamespacedClass validates :namespace, presence: true validates :base_access_level, presence: true validate :belongs_to_top_level_namespace + validate :validate_namespace_locked, on: :update + + validates_associated :members private def belongs_to_top_level_namespace return if !namespace || namespace.root? - errors.add(:namespace, s_("must be top-level namespace")) + errors.add(:namespace, s_("MemberRole|must be top-level namespace")) + end + + def validate_namespace_locked + return unless namespace_id_changed? + + errors.add(:namespace, s_("MemberRole|can't be changed")) end end diff --git a/app/services/ci/append_build_trace_service.rb b/app/services/ci/append_build_trace_service.rb index 0eef0ff0e61..a5ef5b27100 100644 --- a/app/services/ci/append_build_trace_service.rb +++ b/app/services/ci/append_build_trace_service.rb @@ -20,6 +20,9 @@ module Ci # as we cannot use `content_length[1]` # Issue: https://gitlab.com/gitlab-org/gitlab-runner/issues/3275 + # Update the build metadata prior to appending trace content + update_build_metadata if debug_trace + content_range = stream_range.split('-') body_start = content_range[0].to_i body_end = body_start + body_data.bytesize @@ -49,6 +52,18 @@ module Ci params.fetch(:content_range) end + def debug_trace + params.fetch(:debug_trace, false) + end + + # rubocop: disable CodeReuse/ActiveRecord + def update_build_metadata + metadata = Ci::BuildMetadata.find_by(build_id: build) + + metadata.update!(debug_trace_enabled: debug_trace) + end + # rubocop: enable CodeReuse/ActiveRecord + def log_range_error(stream_size, body_end) extra = { build_id: build.id, diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb index 4106dfe0ecc..43a2f4e9a71 100644 --- a/app/services/ci/create_pipeline_service.rb +++ b/app/services/ci/create_pipeline_service.rb @@ -140,25 +140,24 @@ module Ci def build_logger Gitlab::Ci::Pipeline::Logger.new(project: project) do |l| l.log_when do |observations| - observations.any? do |name, values| - values.any? && + observations.any? do |name, observation| name.to_s.end_with?('duration_s') && - values.max >= LOG_MAX_DURATION_THRESHOLD + Array(observation).max >= LOG_MAX_DURATION_THRESHOLD end end l.log_when do |observations| - values = observations['pipeline_size_count'] - next false if values.empty? + count = observations['pipeline_size_count'] + next false unless count - values.max >= LOG_MAX_PIPELINE_SIZE + count >= LOG_MAX_PIPELINE_SIZE end l.log_when do |observations| - values = observations['pipeline_creation_duration_s'] - next false if values.empty? + duration = observations['pipeline_creation_duration_s'] + next false unless duration - values.max >= LOG_MAX_CREATION_THRESHOLD + duration >= LOG_MAX_CREATION_THRESHOLD end end end diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml index 203724fc1f1..41c2187f0b1 100644 --- a/app/views/projects/merge_requests/show.html.haml +++ b/app/views/projects/merge_requests/show.html.haml @@ -51,7 +51,7 @@ - if moved_mr_sidebar_enabled? - if !!@issuable_sidebar.dig(:current_user, :id) .js-sidebar-todo-widget-root{ data: { project_path: @issuable_sidebar[:project_full_path], iid: @issuable_sidebar[:iid], id: @issuable_sidebar[:id] } } - .gl-ml-auto.gl-align-items-center.gl-display-none.gl-md-display-flex.gl-ml-3.js-expand-sidebar{ class: "gl-lg-display-none!" } + .gl-ml-auto.gl-align-items-center.gl-display-none.gl-md-display-flex.gl-ml-3.js-expand-sidebar.gl-absolute.gl-right-5{ class: "gl-lg-display-none!" } = render Pajamas::ButtonComponent.new(icon: 'chevron-double-lg-left', button_options: { class: 'js-sidebar-toggle' }) do = _('Expand') diff --git a/config/feature_flags/development/ci_skip_auto_cancelation_on_child_pipelines.yml b/config/feature_flags/development/ci_skip_auto_cancelation_on_child_pipelines.yml deleted file mode 100644 index 71d5836bee1..00000000000 --- a/config/feature_flags/development/ci_skip_auto_cancelation_on_child_pipelines.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: ci_skip_auto_cancelation_on_child_pipelines -introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/100854" -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/377712 -milestone: '15.5' -type: development -group: group::pipeline execution -default_enabled: false diff --git a/db/migrate/20221114212908_add_debug_trace_to_ci_builds_metadata.rb b/db/migrate/20221114212908_add_debug_trace_to_ci_builds_metadata.rb new file mode 100644 index 00000000000..aee479dfcee --- /dev/null +++ b/db/migrate/20221114212908_add_debug_trace_to_ci_builds_metadata.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddDebugTraceToCiBuildsMetadata < Gitlab::Database::Migration[2.0] + enable_lock_retries! + + def change + add_column :p_ci_builds_metadata, :debug_trace_enabled, :boolean, null: false, default: false + end +end diff --git a/db/schema_migrations/20221114212908 b/db/schema_migrations/20221114212908 new file mode 100644 index 00000000000..cbd453b2cc9 --- /dev/null +++ b/db/schema_migrations/20221114212908 @@ -0,0 +1 @@ +0a939e4568d4edcdee322a9a4f69dac51e7604a30e79d2eced9e131a7e06937a
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 586a9aaacff..0e8904c919e 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -12727,7 +12727,8 @@ CREATE TABLE p_ci_builds_metadata ( id bigint NOT NULL, runtime_runner_features jsonb DEFAULT '{}'::jsonb NOT NULL, id_tokens jsonb DEFAULT '{}'::jsonb NOT NULL, - partition_id bigint DEFAULT 100 NOT NULL + partition_id bigint DEFAULT 100 NOT NULL, + debug_trace_enabled boolean DEFAULT false NOT NULL ) PARTITION BY LIST (partition_id); @@ -12755,7 +12756,8 @@ CREATE TABLE ci_builds_metadata ( id bigint DEFAULT nextval('ci_builds_metadata_id_seq'::regclass) NOT NULL, runtime_runner_features jsonb DEFAULT '{}'::jsonb NOT NULL, id_tokens jsonb DEFAULT '{}'::jsonb NOT NULL, - partition_id bigint DEFAULT 100 NOT NULL + partition_id bigint DEFAULT 100 NOT NULL, + debug_trace_enabled boolean DEFAULT false NOT NULL ); ALTER TABLE ONLY p_ci_builds_metadata ATTACH PARTITION ci_builds_metadata FOR VALUES IN ('100'); diff --git a/doc/administration/audit_events.md b/doc/administration/audit_events.md index 9f00a46dbb6..e8d19363242 100644 --- a/doc/administration/audit_events.md +++ b/doc/administration/audit_events.md @@ -227,6 +227,7 @@ The following user actions on a GitLab instance generate instance audit events: - Removed SSH key ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/220127) in GitLab 14.1) - Added or removed GPG key ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/220127) in GitLab 14.1) - A user's two-factor authentication was disabled ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/238177) in GitLab 15.1) +- Enabled Admin Mode ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/362101) in GitLab 15.7) Instance events can also be accessed via the [Instance Audit Events API](../api/audit_events.md#instance-audit-events). diff --git a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md index 6993d48b450..7c5feb24e15 100644 --- a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md +++ b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md @@ -1,11 +1,95 @@ --- -redirect_to: 'index.md' -remove_date: '2023-02-01' +stage: Systems +group: Distribution +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments --- -This document was moved to [another location](index.md). +# GitLab Rails Console Cheat Sheet **(FREE SELF)** -<!-- This redirect file can be deleted after 2023-02-01. --> -<!-- Redirects that point to other docs in the same project expire in three months. --> -<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. --> -<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> +This was the GitLab Support Team's collection of information regarding the GitLab Rails +console, for use while troubleshooting. It is listed here for posterity, +as most content has been moved to feature-specific troubleshooting pages and sections, +see epic [&8147](https://gitlab.com/groups/gitlab-org/-/epics/8147#tree). +Please update your bookmarks accordingly. + +If you are currently having an issue with GitLab, +it is highly recommended that you first check +our guide on [the Rails console](../operations/rails_console.md), +and your [support options](https://about.gitlab.com/support/), +before attempting the information pointed to from here. + +WARNING: +Some of these scripts could be damaging if not run correctly, +or under the right conditions. We highly recommend running them under the +guidance of a Support Engineer, or running them in a test environment with a +backup of the instance ready to be restored, just in case. + +WARNING: +As GitLab changes, changes to the code are inevitable, +and so some scripts may not work as they once used to. These are not kept +up-to-date as these scripts/commands were added as they were found/needed. As +mentioned above, we recommend running these scripts under the supervision of a +Support Engineer, who can also verify that they continue to work as they +should and, if needed, update the script for the latest version of GitLab. + +## Mirrors + +### Find mirrors with "bad decrypt" errors + +This content has been converted to a Rake task, see [verify database values can be decrypted using the current secrets](../raketasks/check.md#verify-database-values-can-be-decrypted-using-the-current-secrets). + +### Transfer mirror users and tokens to a single service account + +This content has been moved to [Troubleshooting Repository mirroring](../../user/project/repository/mirror/index.md#transfer-mirror-users-and-tokens-to-a-single-service-account-in-rails-console). + +## Merge requests + +## CI + +This content has been moved to [Troubleshooting CI/CD](../../ci/troubleshooting.md). + +## License + +This content has been moved to [Activate GitLab EE with a license file or key](../../user/admin_area/license_file.md). + +## Registry + +### Registry Disk Space Usage by Project + +Find this content in the [Container Registry troubleshooting documentation](../packages/container_registry.md#registry-disk-space-usage-by-project). + +### Run the Cleanup policy now + +Find this content in the [Container Registry troubleshooting documentation](../packages/container_registry.md#run-the-cleanup-policy-now). + +## Sidekiq + +This content has been moved to [Troubleshooting Sidekiq](../sidekiq/sidekiq_troubleshooting.md). + +## Geo + +### Reverify all uploads (or any SSF data type which is verified) + +Moved to [Geo replication troubleshooting](../geo/replication/troubleshooting.md#reverify-all-uploads-or-any-ssf-data-type-which-is-verified). + +### Artifacts + +Moved to [Geo replication troubleshooting](../geo/replication/troubleshooting.md#find-failed-artifacts). + +### Repository verification failures + +Moved to [Geo replication troubleshooting](../geo/replication/troubleshooting.md#find-repository-verification-failures). + +### Resync repositories + +Moved to [Geo replication troubleshooting - Resync repository types except for project or project wiki repositories](../geo/replication/troubleshooting.md#repository-types-except-for-project-or-project-wiki-repositories). + +Moved to [Geo replication troubleshooting - Resync project and project wiki repositories](../geo/replication/troubleshooting.md#resync-project-and-project-wiki-repositories). + +### Blob types + +Moved to [Geo replication troubleshooting](../geo/replication/troubleshooting.md#blob-types). + +## Generate Service Ping + +This content has been moved to [Service Ping Troubleshooting](../../development/service_ping/troubleshooting.md). diff --git a/doc/development/pipelines/index.md b/doc/development/pipelines/index.md index 01bb813e794..78d07bb804f 100644 --- a/doc/development/pipelines/index.md +++ b/doc/development/pipelines/index.md @@ -88,10 +88,12 @@ In addition, there are a few circumstances where we would always run the full Je - when the `pipeline:run-all-jest` label is set on the merge request - when the merge request is created by an automation (for example, Gitaly update or MR targeting a stable branch) - when the merge request is created in a security mirror -- when any CI configuration file is changed (for example, `.gitlab-ci.yml` or `.gitlab/ci/**/*`) -- when any frontend "core" file is changed (for example, `package.json`, `yarn.lock`, `babel.config.js`, `jest.config.*.js`, `config/helpers/**/*.js`) +- when relevant CI configuration file is changed (`.gitlab/ci/rules.gitlab-ci.yml`, `.gitlab/ci/frontend.gitlab-ci.yml`) +- when any frontend dependency file is changed (for example, `package.json`, `yarn.lock`, `config/webpack.config.js`, `config/helpers/**/*.js`) - when any vendored JavaScript file is changed (for example, `vendor/assets/javascripts/**/*`) -- when any backend file is changed ([see the patterns list for details](https://gitlab.com/gitlab-org/gitlab/-/blob/3616946936c1adbd9e754c1bd06f86ba670796d8/.gitlab/ci/rules.gitlab-ci.yml#L205-216)) + +The `rules` definitions for full Jest tests are defined at `.frontend:rules:jest` in +[`rules.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/42321b18b946c64d2f6f788c38844499a5ae9141/.gitlab/ci/rules.gitlab-ci.yml#L938-955). ### Fork pipelines diff --git a/doc/development/pipelines/internals.md b/doc/development/pipelines/internals.md index 2861e2f266b..133d11986d5 100644 --- a/doc/development/pipelines/internals.md +++ b/doc/development/pipelines/internals.md @@ -203,7 +203,7 @@ and included in `rules` definitions via [YAML anchors](../../ci/yaml/yaml_optimi | `ci-qa-patterns` | Only create job for CI configuration-related changes related to the `qa` stage. | | `yaml-lint-patterns` | Only create job for YAML-related changes. | | `docs-patterns` | Only create job for docs-related changes. | -| `frontend-dependency-patterns` | Only create job when frontend dependencies are updated (that is, `package.json`, and `yarn.lock`). changes. | +| `frontend-dependency-patterns` | Only create job when frontend dependencies are updated (for example, `package.json`, and `yarn.lock`) changes. | | `frontend-patterns-for-as-if-foss` | Only create job for frontend-related changes that have impact on FOSS. | | `backend-patterns` | Only create job for backend-related changes. | | `db-patterns` | Only create job for DB-related changes. | diff --git a/lefthook.yml b/lefthook.yml index cfe68b5c390..c44ea8cdeab 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -79,7 +79,10 @@ pre-push: files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD glob: 'data/removals/*.yml' run: echo "Changes to removals files detected. Checking removals..\n"; bundle exec rake gitlab:docs:check_removals +pre-commit: + parallel: true + commands: secrets-detection: tags: secrets - files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD - run: 'if command -v ripsecrets > /dev/null 2>&1; then ripsecrets --strict-ignore {files}; else echo "WARNING: ripsecrets is not installed. Please install it."; fi' + files: git diff --name-only --diff-filter=d --staged + run: 'if command -v gitleaks > /dev/null 2>&1; then gitleaks protect --no-banner --staged --redact --verbose; else echo "WARNING: gitleaks is not installed. Please install it. See https://github.com/zricethezav/gitleaks#installing."; fi' diff --git a/lib/api/ci/runner.rb b/lib/api/ci/runner.rb index e64db375421..b073eb49bf1 100644 --- a/lib/api/ci/runner.rb +++ b/lib/api/ci/runner.rb @@ -229,15 +229,17 @@ module API params do requires :id, type: Integer, desc: %q(Job's ID) optional :token, type: String, desc: %q(Job's authentication token) + optional :debug_trace, type: Boolean, desc: %q(Enable or Disable the debug trace) end patch '/:id/trace', urgency: :low, feature_category: :continuous_integration do job = authenticate_job!(heartbeat_runner: true) error!('400 Missing header Content-Range', 400) unless request.headers.key?('Content-Range') content_range = request.headers['Content-Range'] + debug_trace = Gitlab::Utils.to_boolean(params[:debug_trace]) result = ::Ci::AppendBuildTraceService - .new(job, content_range: content_range) + .new(job, content_range: content_range, debug_trace: debug_trace) .execute(request.body.read) if result.status == 403 diff --git a/lib/gitlab/auth/current_user_mode.rb b/lib/gitlab/auth/current_user_mode.rb index fc391543f4d..9bd4711c4bb 100644 --- a/lib/gitlab/auth/current_user_mode.rb +++ b/lib/gitlab/auth/current_user_mode.rb @@ -106,8 +106,8 @@ module Gitlab end def enable_admin_mode!(password: nil, skip_password_validation: false) - return unless user&.admin? - return unless skip_password_validation || user&.valid_password?(password) + return false unless user&.admin? + return false unless skip_password_validation || user&.valid_password?(password) raise NotRequestedError unless admin_mode_requested? @@ -115,6 +115,10 @@ module Gitlab current_session_data[ADMIN_MODE_REQUESTED_TIME_KEY] = nil current_session_data[ADMIN_MODE_START_TIME_KEY] = Time.now + + audit_user_enable_admin_mode + + true end def disable_admin_mode! @@ -175,6 +179,10 @@ module Gitlab def privileged_runtime? Gitlab::Runtime.rake? || Gitlab::Runtime.rails_runner? || Gitlab::Runtime.console? end + + def audit_user_enable_admin_mode; end end end end + +Gitlab::Auth::CurrentUserMode.prepend_mod_with('Gitlab::Auth::CurrentUserMode') diff --git a/lib/gitlab/ci/lint.rb b/lib/gitlab/ci/lint.rb index 51743a1f273..e0112a1b1c2 100644 --- a/lib/gitlab/ci/lint.rb +++ b/lib/gitlab/ci/lint.rb @@ -73,7 +73,7 @@ module Gitlab end def yaml_processor_result(content, logger) - logger.instrument(:yaml_process) do + logger.instrument(:yaml_process, once: true) do Gitlab::Ci::YamlProcessor.new(content, project: @project, user: @current_user, sha: @sha, @@ -119,7 +119,7 @@ module Gitlab environment: job[:environment], when: job[:when], allow_failure: job[:allow_failure], - needs: job.dig(:needs_attributes) + needs: job[:needs_attributes] } end end @@ -130,10 +130,10 @@ module Gitlab def build_logger Gitlab::Ci::Pipeline::Logger.new(project: @project) do |l| l.log_when do |observations| - values = observations['yaml_process_duration_s'] - next false if values.empty? + duration = observations['yaml_process_duration_s'] + next false unless duration - values.max >= LOG_MAX_DURATION_THRESHOLD + duration >= LOG_MAX_DURATION_THRESHOLD end end end diff --git a/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines.rb b/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines.rb index 07a3aff1862..53c8a7ac122 100644 --- a/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines.rb +++ b/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines.rb @@ -11,11 +11,10 @@ module Gitlab # rubocop: disable CodeReuse/ActiveRecord def perform! - ff_enabled = Feature.enabled?(:ci_skip_auto_cancelation_on_child_pipelines, project) - return if ff_enabled && pipeline.parent_pipeline? # skip if child pipeline + return if pipeline.parent_pipeline? # skip if child pipeline return unless project.auto_cancel_pending_pipelines? - Gitlab::OptimisticLocking.retry_lock(auto_cancelable_pipelines(ff_enabled), name: 'cancel_pending_pipelines') do |cancelables| + Gitlab::OptimisticLocking.retry_lock(auto_cancelable_pipelines, name: 'cancel_pending_pipelines') do |cancelables| cancelables.select(:id).each_batch(of: BATCH_SIZE) do |cancelables_batch| auto_cancel_interruptible_pipelines(cancelables_batch.ids) end @@ -29,19 +28,14 @@ module Gitlab private - def auto_cancelable_pipelines(ff_enabled) - relation = project.all_pipelines + def auto_cancelable_pipelines + project.all_pipelines .created_after(1.week.ago) .ci_and_parent_sources .for_ref(pipeline.ref) .where_not_sha(project.commit(pipeline.ref).try(:id)) .alive_or_scheduled - - if ff_enabled - relation.id_not_in(pipeline.id) - else - relation.id_not_in(pipeline.same_family_pipeline_ids) - end + .id_not_in(pipeline.id) end def auto_cancel_interruptible_pipelines(pipeline_ids) diff --git a/lib/gitlab/ci/pipeline/chain/command.rb b/lib/gitlab/ci/pipeline/chain/command.rb index 5ec04b4889e..837c09e8045 100644 --- a/lib/gitlab/ci/pipeline/chain/command.rb +++ b/lib/gitlab/ci/pipeline/chain/command.rb @@ -98,7 +98,7 @@ module Gitlab def observe_step_duration(step_class, duration) step = step_class.name.underscore.parameterize(separator: '_') - logger.observe("pipeline_step_#{step}_duration_s", duration) + logger.observe("pipeline_step_#{step}_duration_s", duration, once: true) if Feature.enabled?(:ci_pipeline_creation_step_duration_tracking, type: :ops) metrics.pipeline_creation_step_duration_histogram @@ -107,14 +107,14 @@ module Gitlab end def observe_creation_duration(duration) - logger.observe(:pipeline_creation_duration_s, duration) + logger.observe(:pipeline_creation_duration_s, duration, once: true) metrics.pipeline_creation_duration_histogram .observe({}, duration.seconds) end def observe_pipeline_size(pipeline) - logger.observe(:pipeline_size_count, pipeline.total_size) + logger.observe(:pipeline_size_count, pipeline.total_size, once: true) metrics.pipeline_size_histogram .observe({ source: pipeline.source.to_s, plan: project.actual_plan_name }, pipeline.total_size) diff --git a/lib/gitlab/ci/pipeline/chain/config/process.rb b/lib/gitlab/ci/pipeline/chain/config/process.rb index 5548fca320f..ad6b2fd3411 100644 --- a/lib/gitlab/ci/pipeline/chain/config/process.rb +++ b/lib/gitlab/ci/pipeline/chain/config/process.rb @@ -11,7 +11,7 @@ module Gitlab def perform! raise ArgumentError, 'missing config content' unless @command.config_content - result = logger.instrument(:pipeline_config_process) do + result = logger.instrument(:pipeline_config_process, once: true) do processor = ::Gitlab::Ci::YamlProcessor.new( @command.config_content, { project: project, diff --git a/lib/gitlab/ci/pipeline/chain/create.rb b/lib/gitlab/ci/pipeline/chain/create.rb index 207b4b5ff8b..d4c4f94c7d3 100644 --- a/lib/gitlab/ci/pipeline/chain/create.rb +++ b/lib/gitlab/ci/pipeline/chain/create.rb @@ -9,7 +9,7 @@ module Gitlab include Gitlab::Utils::StrongMemoize def perform! - logger.instrument_with_sql(:pipeline_save) do + logger.instrument_once_with_sql(:pipeline_save) do BulkInsertableAssociations.with_bulk_insert do ::Ci::BulkInsertableTags.with_bulk_insert_tags do pipeline.transaction do diff --git a/lib/gitlab/ci/pipeline/chain/seed.rb b/lib/gitlab/ci/pipeline/chain/seed.rb index feae123f216..3f5df5ce71c 100644 --- a/lib/gitlab/ci/pipeline/chain/seed.rb +++ b/lib/gitlab/ci/pipeline/chain/seed.rb @@ -13,7 +13,7 @@ module Gitlab raise ArgumentError, 'missing workflow rules result' unless @command.workflow_rules_result # Allocate next IID. This operation must be outside of transactions of pipeline creations. - logger.instrument(:pipeline_allocate_seed_attributes) do + logger.instrument(:pipeline_allocate_seed_attributes, once: true) do pipeline.ensure_project_iid! pipeline.ensure_ci_ref! end @@ -25,7 +25,7 @@ module Gitlab ## # Gather all runtime build/stage errors # - seed_errors = logger.instrument(:pipeline_seed_evaluation) do + seed_errors = logger.instrument(:pipeline_seed_evaluation, once: true) do pipeline_seed.errors end @@ -44,7 +44,7 @@ module Gitlab def pipeline_seed strong_memoize(:pipeline_seed) do - logger.instrument(:pipeline_seed_initialization) do + logger.instrument(:pipeline_seed_initialization, once: true) do stages_attributes = @command.yaml_processor_result.stages_attributes Gitlab::Ci::Pipeline::Seed::Pipeline.new(context, stages_attributes) @@ -61,11 +61,13 @@ module Gitlab end def root_variables - logger.instrument(:pipeline_seed_merge_variables) do - ::Gitlab::Ci::Variables::Helpers.merge_variables( - @command.yaml_processor_result.root_variables, - @command.workflow_rules_result.variables - ) + strong_memoize(:root_variables) do + logger.instrument(:pipeline_seed_merge_variables, once: true) do + ::Gitlab::Ci::Variables::Helpers.merge_variables( + @command.yaml_processor_result.root_variables, + @command.workflow_rules_result.variables + ) + end end end end diff --git a/lib/gitlab/ci/pipeline/logger.rb b/lib/gitlab/ci/pipeline/logger.rb index 5a1743dcfca..9659cec4889 100644 --- a/lib/gitlab/ci/pipeline/logger.rb +++ b/lib/gitlab/ci/pipeline/logger.rb @@ -23,7 +23,7 @@ module Gitlab log_conditions.push(block) end - def instrument(operation) + def instrument(operation, once: false) return yield unless enabled? raise ArgumentError, 'block not given' unless block_given? @@ -32,25 +32,29 @@ module Gitlab result = yield - observe("#{operation}_duration_s", current_monotonic_time - op_started_at) + observe("#{operation}_duration_s", current_monotonic_time - op_started_at, once: once) result end - def instrument_with_sql(operation, &block) + def instrument_once_with_sql(operation, &block) op_start_db_counters = current_db_counter_payload - result = instrument(operation, &block) + result = instrument(operation, once: true, &block) - observe_sql_counters(operation, op_start_db_counters, current_db_counter_payload) + observe_sql_counters(operation, op_start_db_counters, current_db_counter_payload, once: true) result end - def observe(operation, value) + def observe(operation, value, once: false) return unless enabled? - observations[operation.to_s].push(value) + if once + observations[operation.to_s] = value + else + observations[operation.to_s].push(value) + end end def commit(pipeline:, caller:) @@ -79,14 +83,18 @@ module Gitlab end def observations_hash - observations.transform_values do |values| - next if values.empty? - - { - 'count' => values.size, - 'max' => values.max, - 'sum' => values.sum - } + observations.transform_values do |observation| + next if observation.blank? + + if observation.is_a?(Array) + { + 'count' => observation.size, + 'max' => observation.max, + 'sum' => observation.sum + } + else + observation + end end.compact end @@ -117,12 +125,12 @@ module Gitlab @observations ||= Hash.new { |hash, key| hash[key] = [] } end - def observe_sql_counters(operation, start_db_counters, end_db_counters) + def observe_sql_counters(operation, start_db_counters, end_db_counters, once: false) end_db_counters.each do |key, value| result = value - start_db_counters.fetch(key, 0) next if result == 0 - observe("#{operation}_#{key}", result) + observe("#{operation}_#{key}", result, once: once) end end diff --git a/lib/gitlab/gfm/uploads_rewriter.rb b/lib/gitlab/gfm/uploads_rewriter.rb index 58b46a85aae..6b5f10ed78e 100644 --- a/lib/gitlab/gfm/uploads_rewriter.rb +++ b/lib/gitlab/gfm/uploads_rewriter.rb @@ -54,8 +54,7 @@ module Gitlab file = find_file(match[:secret], match[:file]) # No file will be returned for a path traversal - return '' if file.nil? - + return markdown if file.nil? return markdown unless file.try(:exists?) klass = @target_parent.is_a?(Namespace) ? NamespaceFileUploader : FileUploader diff --git a/lib/gitlab/git.rb b/lib/gitlab/git.rb index 4b877bf44da..8e1b51fcec5 100644 --- a/lib/gitlab/git.rb +++ b/lib/gitlab/git.rb @@ -16,6 +16,7 @@ module Gitlab CommitError = Class.new(BaseError) OSError = Class.new(BaseError) UnknownRef = Class.new(BaseError) + AmbiguousRef = Class.new(BaseError) CommandTimedOut = Class.new(CommandError) InvalidPageToken = Class.new(BaseError) InvalidRefFormatError = Class.new(BaseError) diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 2d13d3378c2..1bbbc16fe67 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -134,6 +134,10 @@ module Gitlab wrapped_gitaly_errors do gitaly_ref_client.find_branch(name) end + rescue Gitlab::Git::AmbiguousRef + # Gitaly returns "reference is ambiguous" error in case when users request + # branch "my-branch", when another branch "my-branch/branch" exists. + # We handle this error here and return nil for this case. end def find_tag(name) diff --git a/lib/gitlab/gitaly_client/ref_service.rb b/lib/gitlab/gitaly_client/ref_service.rb index de76ade76cb..3ab596da529 100644 --- a/lib/gitlab/gitaly_client/ref_service.rb +++ b/lib/gitlab/gitaly_client/ref_service.rb @@ -17,6 +17,8 @@ module Gitlab 'desc' => Gitaly::SortDirection::DESCENDING }.freeze + AMBIGUOUS_REFERENCE = 'reference is ambiguous' + # 'repository' is a Gitlab::Git::Repository def initialize(repository) @repository = repository @@ -109,6 +111,10 @@ module Gitlab target_commit = Gitlab::Git::Commit.decorate(@repository, branch.target_commit) Gitlab::Git::Branch.new(@repository, branch.name.dup, branch.target_commit.id, target_commit) + rescue GRPC::BadStatus => e + raise e unless e.message.include?(AMBIGUOUS_REFERENCE) + + raise Gitlab::Git::AmbiguousRef, "branch is ambiguous: #{branch_name}" end def find_tag(tag_name) diff --git a/lib/gitlab/memory/reporter.rb b/lib/gitlab/memory/reporter.rb new file mode 100644 index 00000000000..b4f3f950b95 --- /dev/null +++ b/lib/gitlab/memory/reporter.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +module Gitlab + module Memory + class Reporter + def initialize + @worker_uuid = SecureRandom.uuid + + init_prometheus_metrics + end + + def run_report(report) + start_monotonic_time = Gitlab::Metrics::System.monotonic_time + start_thread_cpu_time = Gitlab::Metrics::System.thread_cpu_time + + file_path = report.run(report_id) + + cpu_s = Gitlab::Metrics::System.thread_cpu_duration(start_thread_cpu_time) + duration_s = Gitlab::Metrics::System.monotonic_time - start_monotonic_time + + log_report(name: report.name, cpu_s: cpu_s, duration_s: duration_s, size: file_size(file_path)) + + @report_duration_counter.increment({ report: report.name }, duration_s) + end + + private + + def log_report(name:, duration_s:, cpu_s:, size:) + Gitlab::AppLogger.info( + message: 'finished', + pid: $$, + worker_id: worker_id, + perf_report: name, + duration_s: duration_s.round(2), + cpu_s: cpu_s.round(2), + perf_report_size_bytes: size, + perf_report_worker_uuid: @worker_uuid + ) + end + + def report_id + [worker_id, @worker_uuid].join(".") + end + + def worker_id + ::Prometheus::PidProvider.worker_id + end + + def file_size(file_path) + File.size(file_path.to_s) + rescue Errno::ENOENT + 0 + end + + def init_prometheus_metrics + default_labels = { pid: worker_id } + + @report_duration_counter = Gitlab::Metrics.counter( + :gitlab_diag_report_duration_seconds_total, + 'Total time elapsed for running diagnostic report', + default_labels + ) + end + end + end +end diff --git a/lib/gitlab/memory/reports/jemalloc_stats.rb b/lib/gitlab/memory/reports/jemalloc_stats.rb index 720f22ddbe4..2e5a053031e 100644 --- a/lib/gitlab/memory/reports/jemalloc_stats.rb +++ b/lib/gitlab/memory/reports/jemalloc_stats.rb @@ -16,9 +16,8 @@ module Gitlab # The cleanup logic will be redundant after we'll implement the uploads, which would perform the cleanup. DEFAULT_MAX_REPORTS_STORED = 250 - def initialize(reports_path:, filename_label:) + def initialize(reports_path:) @reports_path = reports_path - @filename_label = filename_label # Store report in tmp subdir while it is still streaming. # This will clearly separate finished reports from the files we are still writing to. @@ -26,11 +25,16 @@ module Gitlab FileUtils.mkdir_p(@tmp_dir) end - def run + def name + 'jemalloc_stats' + end + + def run(report_id) return unless active? - Gitlab::Memory::Jemalloc.dump_stats(path: reports_path, tmp_dir: @tmp_dir, - filename_label: filename_label).tap do + Gitlab::Memory::Jemalloc.dump_stats(path: reports_path, + tmp_dir: @tmp_dir, + filename_label: report_id).tap do cleanup end end @@ -41,7 +45,7 @@ module Gitlab private - attr_reader :reports_path, :filename_label + attr_reader :reports_path def cleanup reports_files_modified_order[0...-max_reports_stored].each do |f| diff --git a/lib/gitlab/memory/reports_daemon.rb b/lib/gitlab/memory/reports_daemon.rb index 7070c65c705..9a044dbd06c 100644 --- a/lib/gitlab/memory/reports_daemon.rb +++ b/lib/gitlab/memory/reports_daemon.rb @@ -9,7 +9,7 @@ module Gitlab DEFAULT_REPORTS_PATH = Dir.tmpdir - def initialize(**options) + def initialize(reporter: Reporter.new, reports: nil, **options) super @alive = true @@ -24,17 +24,10 @@ module Gitlab @reports_path = ENV["GITLAB_DIAGNOSTIC_REPORTS_PATH"] || DEFAULT_REPORTS_PATH - # Set unique uuid for every ReportsDaemon instance. - # Because we spawn a single instance of it per process, it will also uniquely identify the worker. - # Unlike `::Prometheus::PidProvider.worker_id`, this uuid will remain unique across all Puma clusters. - # This way, we can identify reports that were produced from the same worker process during its lifetime. - @worker_uuid = SecureRandom.uuid - - @reports = [ - Gitlab::Memory::Reports::JemallocStats.new(reports_path: reports_path, filename_label: filename_label) + @reporter = reporter + @reports = reports || [ + Gitlab::Memory::Reports::JemallocStats.new(reports_path: reports_path) ] - - init_prometheus_metrics end attr_reader :sleep_s, :sleep_max_delta_s, :sleep_between_reports_s, :reports_path @@ -44,16 +37,7 @@ module Gitlab sleep interval_with_jitter reports.select(&:active?).each do |report| - start_monotonic_time = Gitlab::Metrics::System.monotonic_time - start_thread_cpu_time = Gitlab::Metrics::System.thread_cpu_time - - file_path = report.run - - cpu_s = Gitlab::Metrics::System.thread_cpu_duration(start_thread_cpu_time) - duration_s = Gitlab::Metrics::System.monotonic_time - start_monotonic_time - - log_report(label: report_label(report), cpu_s: cpu_s, duration_s: duration_s, size: file_size(file_path)) - @report_duration_counter.increment({ report: report_label(report) }, duration_s) + @reporter.run_report(report) sleep sleep_between_reports_s end @@ -62,11 +46,7 @@ module Gitlab private - attr_reader :alive, :reports, :worker_uuid - - def filename_label - [worker_id, worker_uuid].join(".") - end + attr_reader :alive, :reports # Returns the sleep interval with a random adjustment. # The random adjustment is put in place to ensure continued availability. @@ -74,46 +54,9 @@ module Gitlab sleep_s + rand(sleep_max_delta_s) end - def log_report(label:, duration_s:, cpu_s:, size:) - Gitlab::AppLogger.info( - message: 'finished', - pid: $$, - worker_id: worker_id, - perf_report: label, - duration_s: duration_s.round(2), - cpu_s: cpu_s.round(2), - perf_report_size_bytes: size, - perf_report_worker_uuid: worker_uuid - ) - end - - def worker_id - ::Prometheus::PidProvider.worker_id - end - - def report_label(report) - report.class.to_s.demodulize.underscore - end - def stop_working @alive = false end - - def init_prometheus_metrics - default_labels = { pid: worker_id } - - @report_duration_counter = Gitlab::Metrics.counter( - :gitlab_diag_report_duration_seconds_total, - 'Total time elapsed for running diagnostic report', - default_labels - ) - end - - def file_size(file_path) - File.size(file_path.to_s) - rescue Errno::ENOENT - 0 - end end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index ceb57944c8e..82fd192bb7e 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -25410,6 +25410,12 @@ msgstr "" msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}" msgstr "" +msgid "MemberRole|can't be changed" +msgstr "" + +msgid "MemberRole|must be top-level namespace" +msgstr "" + msgid "Members" msgstr "" @@ -40271,6 +40277,12 @@ msgstr "" msgid "TasksToBeDone|Set up CI/CD pipelines to build, test, deploy, and monitor code" msgstr "" +msgid "Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed" +msgstr "" + +msgid "Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}" +msgstr "" + msgid "Team" msgstr "" @@ -47021,6 +47033,9 @@ msgstr "" msgid "You don't have permission to review this deployment. Contact the project or group owner for help." msgstr "" +msgid "You don't have permission to view this epic" +msgstr "" + msgid "You don't have permissions to create this project" msgstr "" @@ -47960,6 +47975,11 @@ msgid_plural "checks" msgstr[0] "" msgstr[1] "" +msgid "checklist item" +msgid_plural "checklist items" +msgstr[0] "" +msgstr[1] "" + msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}" msgstr "" @@ -49239,6 +49259,9 @@ msgstr "" msgid "must be greater than start date" msgstr "" +msgid "must be in same hierarchy as custom role's namespace" +msgstr "" + msgid "must be inside the fork network" msgstr "" @@ -49248,9 +49271,6 @@ msgstr "" msgid "must be set for a project namespace" msgstr "" -msgid "must be top-level namespace" -msgstr "" - msgid "must be unique by status and elapsed time within a policy" msgstr "" diff --git a/spec/features/admin/admin_abuse_reports_spec.rb b/spec/features/admin/admin_abuse_reports_spec.rb index 3a02ce89aa9..10f12d7116f 100644 --- a/spec/features/admin/admin_abuse_reports_spec.rb +++ b/spec/features/admin/admin_abuse_reports_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe "Admin::AbuseReports", :js do +RSpec.describe "Admin::AbuseReports", :js, feature_category: :not_owned do let(:user) { create(:user) } context 'as an admin' do diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb index b297d92b2fa..5fbe7039c1d 100644 --- a/spec/features/admin/admin_appearance_spec.rb +++ b/spec/features/admin/admin_appearance_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Admin Appearance' do +RSpec.describe 'Admin Appearance', feature_category: :not_owned do let!(:appearance) { create(:appearance) } let(:admin) { create(:admin) } diff --git a/spec/features/admin/admin_broadcast_messages_spec.rb b/spec/features/admin/admin_broadcast_messages_spec.rb index b5416f539f1..a6bbdd70fc3 100644 --- a/spec/features/admin/admin_broadcast_messages_spec.rb +++ b/spec/features/admin/admin_broadcast_messages_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Admin Broadcast Messages' do +RSpec.describe 'Admin Broadcast Messages', feature_category: :onboarding do before do admin = create(:admin) sign_in(admin) diff --git a/spec/features/admin/admin_browse_spam_logs_spec.rb b/spec/features/admin/admin_browse_spam_logs_spec.rb index 471a7e8f0ab..348b6db94fe 100644 --- a/spec/features/admin/admin_browse_spam_logs_spec.rb +++ b/spec/features/admin/admin_browse_spam_logs_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Admin browse spam logs' do +RSpec.describe 'Admin browse spam logs', feature_category: :not_owned do let!(:spam_log) { create(:spam_log, description: 'abcde ' * 20) } before do diff --git a/spec/features/admin/admin_deploy_keys_spec.rb b/spec/features/admin/admin_deploy_keys_spec.rb index 56b8c7fce14..e55e1cce6b9 100644 --- a/spec/features/admin/admin_deploy_keys_spec.rb +++ b/spec/features/admin/admin_deploy_keys_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'admin deploy keys', :js do +RSpec.describe 'admin deploy keys', :js, feature_category: :authentication_and_authorization do include Spec::Support::Helpers::ModalHelpers let_it_be(:admin) { create(:admin) } diff --git a/spec/features/admin/admin_dev_ops_reports_spec.rb b/spec/features/admin/admin_dev_ops_reports_spec.rb index f65862c568f..f290464b043 100644 --- a/spec/features/admin/admin_dev_ops_reports_spec.rb +++ b/spec/features/admin/admin_dev_ops_reports_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'DevOps Report page', :js do +RSpec.describe 'DevOps Report page', :js, feature_category: :devops_reports do before do admin = create(:admin) sign_in(admin) diff --git a/spec/features/admin/admin_disables_git_access_protocol_spec.rb b/spec/features/admin/admin_disables_git_access_protocol_spec.rb index b370b779afe..76620b93557 100644 --- a/spec/features/admin/admin_disables_git_access_protocol_spec.rb +++ b/spec/features/admin/admin_disables_git_access_protocol_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Admin disables Git access protocol', :js do +RSpec.describe 'Admin disables Git access protocol', :js, feature_category: :source_code_management do include StubENV include MobileHelpers diff --git a/spec/features/admin/admin_disables_two_factor_spec.rb b/spec/features/admin/admin_disables_two_factor_spec.rb index 4463dbb1eb0..eed20d449cd 100644 --- a/spec/features/admin/admin_disables_two_factor_spec.rb +++ b/spec/features/admin/admin_disables_two_factor_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Admin disables 2FA for a user' do +RSpec.describe 'Admin disables 2FA for a user', feature_category: :system_access do include Spec::Support::Helpers::ModalHelpers it 'successfully', :js do diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb index 657dd52228e..c36a742af6b 100644 --- a/spec/features/admin/admin_groups_spec.rb +++ b/spec/features/admin/admin_groups_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Admin Groups' do +RSpec.describe 'Admin Groups', feature_category: :subgroups do include Select2Helper include Spec::Support::Helpers::Features::MembersHelpers include Spec::Support::Helpers::Features::InviteMembersModalHelper diff --git a/spec/features/admin/admin_health_check_spec.rb b/spec/features/admin/admin_health_check_spec.rb index 0f6cba6c105..de71a48d2dc 100644 --- a/spec/features/admin/admin_health_check_spec.rb +++ b/spec/features/admin/admin_health_check_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe "Admin Health Check", :feature do +RSpec.describe "Admin Health Check", feature_category: :continuous_verification do include StubENV let_it_be(:admin) { create(:admin) } diff --git a/spec/features/admin/admin_hook_logs_spec.rb b/spec/features/admin/admin_hook_logs_spec.rb index a2ee6343886..d6507e68692 100644 --- a/spec/features/admin/admin_hook_logs_spec.rb +++ b/spec/features/admin/admin_hook_logs_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Admin::HookLogs' do +RSpec.describe 'Admin::HookLogs', feature_category: :continuous_verification do let_it_be(:system_hook) { create(:system_hook) } let_it_be(:hook_log) { create(:web_hook_log, web_hook: system_hook, internal_error_message: 'some error') } let_it_be(:admin) { create(:admin) } diff --git a/spec/features/admin/admin_hooks_spec.rb b/spec/features/admin/admin_hooks_spec.rb index dc5b0ae009e..c4cd88817bc 100644 --- a/spec/features/admin/admin_hooks_spec.rb +++ b/spec/features/admin/admin_hooks_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Admin::Hooks' do +RSpec.describe 'Admin::Hooks', feature_category: :integrations do include Spec::Support::Helpers::ModalHelpers let_it_be(:user) { create(:admin) } diff --git a/spec/features/admin/admin_jobs_spec.rb b/spec/features/admin/admin_jobs_spec.rb index 36822f89c12..f0eaa83f05e 100644 --- a/spec/features/admin/admin_jobs_spec.rb +++ b/spec/features/admin/admin_jobs_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Admin Jobs' do +RSpec.describe 'Admin Jobs', feature_category: :continuous_integration do before do admin = create(:admin) sign_in(admin) diff --git a/spec/features/admin/admin_labels_spec.rb b/spec/features/admin/admin_labels_spec.rb index fa5c94aa66e..8d2813d26f7 100644 --- a/spec/features/admin/admin_labels_spec.rb +++ b/spec/features/admin/admin_labels_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'admin issues labels' do +RSpec.describe 'admin issues labels', feature_category: :team_planning do include Spec::Support::Helpers::ModalHelpers let!(:bug_label) { Label.create!(title: 'bug', template: true) } diff --git a/spec/features/admin/admin_manage_applications_spec.rb b/spec/features/admin/admin_manage_applications_spec.rb index 4cf290293bd..b4c77e802a8 100644 --- a/spec/features/admin/admin_manage_applications_spec.rb +++ b/spec/features/admin/admin_manage_applications_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'admin manage applications' do +RSpec.describe 'admin manage applications', feature_category: :system_access do let_it_be(:new_application_path) { new_admin_application_path } let_it_be(:applications_path) { admin_applications_path } let_it_be(:index_path) { admin_applications_path } diff --git a/spec/features/admin/admin_mode/login_spec.rb b/spec/features/admin/admin_mode/login_spec.rb index 6b4c9adb096..393721fe451 100644 --- a/spec/features/admin/admin_mode/login_spec.rb +++ b/spec/features/admin/admin_mode/login_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Admin Mode Login' do +RSpec.describe 'Admin Mode Login', feature_category: :authentication_and_authorization do include TermsHelper include UserLoginHelper include LdapHelpers diff --git a/spec/features/admin/admin_mode/logout_spec.rb b/spec/features/admin/admin_mode/logout_spec.rb index 3ca66ef0d6a..f4e8941d25a 100644 --- a/spec/features/admin/admin_mode/logout_spec.rb +++ b/spec/features/admin/admin_mode/logout_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Admin Mode Logout', :js do +RSpec.describe 'Admin Mode Logout', :js, feature_category: :authentication_and_authorization do include TermsHelper include UserLoginHelper include Spec::Support::Helpers::Features::TopNavSpecHelpers diff --git a/spec/features/admin/admin_mode/workers_spec.rb b/spec/features/admin/admin_mode/workers_spec.rb index 8405e9132b6..f3639fd0800 100644 --- a/spec/features/admin/admin_mode/workers_spec.rb +++ b/spec/features/admin/admin_mode/workers_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' # Test an operation that triggers background jobs requiring administrative rights -RSpec.describe 'Admin mode for workers', :request_store do +RSpec.describe 'Admin mode for workers', :request_store, feature_category: :authentication_and_authorization do include Spec::Support::Helpers::Features::AdminUsersHelpers let(:user) { create(:user) } diff --git a/spec/features/admin/admin_mode_spec.rb b/spec/features/admin/admin_mode_spec.rb index 33cf0e8c4f8..769ff75b5a2 100644 --- a/spec/features/admin/admin_mode_spec.rb +++ b/spec/features/admin/admin_mode_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Admin mode', :js do +RSpec.describe 'Admin mode', :js, feature_category: :not_owned do include MobileHelpers include Spec::Support::Helpers::Features::TopNavSpecHelpers include StubENV diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb index 6b147b01991..0cb813c40f4 100644 --- a/spec/features/admin/admin_projects_spec.rb +++ b/spec/features/admin/admin_projects_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe "Admin::Projects" do +RSpec.describe "Admin::Projects", feature_category: :projects do include Spec::Support::Helpers::Features::MembersHelpers include Spec::Support::Helpers::Features::InviteMembersModalHelper include Spec::Support::Helpers::ModalHelpers diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb index 92a3b388994..342c4d55dd6 100644 --- a/spec/features/admin/admin_runners_spec.rb +++ b/spec/features/admin/admin_runners_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe "Admin Runners" do +RSpec.describe "Admin Runners", feature_category: :runner do include Spec::Support::Helpers::Features::RunnersHelpers include Spec::Support::Helpers::ModalHelpers diff --git a/spec/features/admin/admin_search_settings_spec.rb b/spec/features/admin/admin_search_settings_spec.rb index 989cb7cc787..3254bf75738 100644 --- a/spec/features/admin/admin_search_settings_spec.rb +++ b/spec/features/admin/admin_search_settings_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Admin searches application settings', :js do +RSpec.describe 'Admin searches application settings', :js, feature_category: :global_search do let_it_be(:admin) { create(:admin) } let_it_be(:application_settings) { create(:application_setting) } diff --git a/spec/features/admin/admin_sees_background_migrations_spec.rb b/spec/features/admin/admin_sees_background_migrations_spec.rb index d72259d91b3..e1746dad196 100644 --- a/spec/features/admin/admin_sees_background_migrations_spec.rb +++ b/spec/features/admin/admin_sees_background_migrations_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe "Admin > Admin sees background migrations" do +RSpec.describe "Admin > Admin sees background migrations", feature_category: :database do let_it_be(:admin) { create(:admin) } let(:job_class) { Gitlab::BackgroundMigration::CopyColumnUsingBackgroundMigrationJob } diff --git a/spec/features/admin/admin_sees_project_statistics_spec.rb b/spec/features/admin/admin_sees_project_statistics_spec.rb index 9d9217c4574..d3d0625ac43 100644 --- a/spec/features/admin/admin_sees_project_statistics_spec.rb +++ b/spec/features/admin/admin_sees_project_statistics_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe "Admin > Admin sees project statistics" do +RSpec.describe "Admin > Admin sees project statistics", feature_category: :projects do let(:current_user) { create(:admin) } before do diff --git a/spec/features/admin/admin_sees_projects_statistics_spec.rb b/spec/features/admin/admin_sees_projects_statistics_spec.rb index d340eb47f34..82361a985ae 100644 --- a/spec/features/admin/admin_sees_projects_statistics_spec.rb +++ b/spec/features/admin/admin_sees_projects_statistics_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe "Admin > Admin sees projects statistics" do +RSpec.describe "Admin > Admin sees projects statistics", feature_category: :projects do let(:current_user) { create(:admin) } before do diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index 72c9053ba49..3fdcf4151ed 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Admin updates settings' do +RSpec.describe 'Admin updates settings', feature_category: :not_owned do include StubENV include TermsHelper include UsageDataHelpers diff --git a/spec/features/admin/admin_system_info_spec.rb b/spec/features/admin/admin_system_info_spec.rb index 8ff31dfded7..6c4a316ae77 100644 --- a/spec/features/admin/admin_system_info_spec.rb +++ b/spec/features/admin/admin_system_info_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Admin System Info' do +RSpec.describe 'Admin System Info', feature_category: :not_owned do before do admin = create(:admin) sign_in(admin) diff --git a/spec/features/admin/admin_users_impersonation_tokens_spec.rb b/spec/features/admin/admin_users_impersonation_tokens_spec.rb index d93dac4834e..5e6cc206883 100644 --- a/spec/features/admin/admin_users_impersonation_tokens_spec.rb +++ b/spec/features/admin/admin_users_impersonation_tokens_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Admin > Users > Impersonation Tokens', :js do +RSpec.describe 'Admin > Users > Impersonation Tokens', :js, feature_category: :authentication_and_authorization do include Spec::Support::Helpers::ModalHelpers include Spec::Support::Helpers::AccessTokenHelpers diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb index f4b7fa45e4f..1f40f1f1bce 100644 --- a/spec/features/admin/admin_users_spec.rb +++ b/spec/features/admin/admin_users_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe "Admin::Users" do +RSpec.describe "Admin::Users", feature_category: :user_management do let(:current_user) { create(:admin) } before do diff --git a/spec/features/admin/admin_uses_repository_checks_spec.rb b/spec/features/admin/admin_uses_repository_checks_spec.rb index 2dffef93600..318572a7664 100644 --- a/spec/features/admin/admin_uses_repository_checks_spec.rb +++ b/spec/features/admin/admin_uses_repository_checks_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Admin uses repository checks', :request_store do +RSpec.describe 'Admin uses repository checks', :request_store, feature_category: :user_management do include StubENV include Spec::Support::Helpers::ModalHelpers diff --git a/spec/features/admin/dashboard_spec.rb b/spec/features/admin/dashboard_spec.rb index e7ff8c23a8c..baca60134b9 100644 --- a/spec/features/admin/dashboard_spec.rb +++ b/spec/features/admin/dashboard_spec.rb @@ -11,7 +11,7 @@ RSpec.describe 'admin visits dashboard' do gitlab_enable_admin_mode_sign_in(admin) end - context 'counting forks', :js do + context 'counting forks', :js, feature_category: :source_code_management do it 'correctly counts 2 forks of a project' do project = create(:project) project_fork = fork_project(project) @@ -28,7 +28,7 @@ RSpec.describe 'admin visits dashboard' do end end - describe 'Users statistic' do + describe 'Users statistic', feature_category: :user_management do let_it_be(:users_statistics) { create(:users_statistics) } it 'shows correct amounts of users', :aggregate_failures do @@ -54,7 +54,7 @@ RSpec.describe 'admin visits dashboard' do end end - describe 'Version check', :js do + describe 'Version check', :js, feature_category: :deployment_management do it 'shows badge on CE' do visit admin_root_path diff --git a/spec/features/admin/integrations/instance_integrations_spec.rb b/spec/features/admin/integrations/instance_integrations_spec.rb index 7b326ec161c..3b2ed1d9810 100644 --- a/spec/features/admin/integrations/instance_integrations_spec.rb +++ b/spec/features/admin/integrations/instance_integrations_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Instance integrations', :js do +RSpec.describe 'Instance integrations', :js, feature_category: :integrations do include_context 'instance integration activation' it_behaves_like 'integration settings form' do diff --git a/spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb b/spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb index 22a27b33671..d0ca5d76cc7 100644 --- a/spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb +++ b/spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb @@ -2,7 +2,8 @@ require 'spec_helper' -RSpec.describe 'User activates the instance-level Mattermost Slash Command integration', :js do +RSpec.describe 'User activates the instance-level Mattermost Slash Command integration', :js, +feature_category: :integrations do include_context 'instance integration activation' before do diff --git a/spec/features/admin/users/user_spec.rb b/spec/features/admin/users/user_spec.rb index 35b5c755b66..1552d4e6187 100644 --- a/spec/features/admin/users/user_spec.rb +++ b/spec/features/admin/users/user_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Admin::Users::User' do +RSpec.describe 'Admin::Users::User', feature_category: :user_management do include Spec::Support::Helpers::Features::AdminUsersHelpers include Spec::Support::Helpers::ModalHelpers diff --git a/spec/features/admin/users/users_spec.rb b/spec/features/admin/users/users_spec.rb index 9c59f0226e0..53d3aec9cff 100644 --- a/spec/features/admin/users/users_spec.rb +++ b/spec/features/admin/users/users_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Admin::Users' do +RSpec.describe 'Admin::Users', feature_category: :user_management do include Spec::Support::Helpers::Features::AdminUsersHelpers include Spec::Support::Helpers::ModalHelpers diff --git a/spec/features/alert_management/alert_details_spec.rb b/spec/features/alert_management/alert_details_spec.rb index 579b8221041..26233fbda64 100644 --- a/spec/features/alert_management/alert_details_spec.rb +++ b/spec/features/alert_management/alert_details_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Alert details', :js do +RSpec.describe 'Alert details', :js, feature_category: :incident_management do let_it_be(:project) { create(:project) } let_it_be(:developer) { create(:user) } let_it_be(:alert) { create(:alert_management_alert, project: project, status: 'triggered', title: 'Alert') } diff --git a/spec/features/alert_management/alert_management_list_spec.rb b/spec/features/alert_management/alert_management_list_spec.rb index 2fbce27033e..6ed3bdec5f5 100644 --- a/spec/features/alert_management/alert_management_list_spec.rb +++ b/spec/features/alert_management/alert_management_list_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Alert Management index', :js do +RSpec.describe 'Alert Management index', :js, feature_category: :incident_management do let_it_be(:project) { create(:project) } let_it_be(:developer) { create(:user) } diff --git a/spec/features/alert_management/user_filters_alerts_by_status_spec.rb b/spec/features/alert_management/user_filters_alerts_by_status_spec.rb index bebbbcbf5f7..c3dab05550e 100644 --- a/spec/features/alert_management/user_filters_alerts_by_status_spec.rb +++ b/spec/features/alert_management/user_filters_alerts_by_status_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'User filters Alert Management table by status', :js do +RSpec.describe 'User filters Alert Management table by status', :js, feature_category: :incident_management do let_it_be(:project) { create(:project) } let_it_be(:developer) { create(:user) } let_it_be(:alert1, reload: true) { create(:alert_management_alert, :triggered, project: project) } diff --git a/spec/features/alert_management/user_searches_alerts_spec.rb b/spec/features/alert_management/user_searches_alerts_spec.rb index 3bb1b260f36..d1e400f4145 100644 --- a/spec/features/alert_management/user_searches_alerts_spec.rb +++ b/spec/features/alert_management/user_searches_alerts_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'User searches Alert Management alerts', :js do +RSpec.describe 'User searches Alert Management alerts', :js, feature_category: :incident_management do let_it_be(:project) { create(:project) } let_it_be(:developer) { create(:user) } let_it_be(:alert) { create(:alert_management_alert, project: project, status: 'triggered') } diff --git a/spec/features/alert_management/user_updates_alert_status_spec.rb b/spec/features/alert_management/user_updates_alert_status_spec.rb index 2d7be3a0022..98fd7449c4f 100644 --- a/spec/features/alert_management/user_updates_alert_status_spec.rb +++ b/spec/features/alert_management/user_updates_alert_status_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'User updates Alert Management status', :js do +RSpec.describe 'User updates Alert Management status', :js, feature_category: :incident_management do let_it_be(:project) { create(:project) } let_it_be(:developer) { create(:user) } let_it_be(:alert) { create(:alert_management_alert, project: project, status: 'triggered') } diff --git a/spec/features/alert_management_spec.rb b/spec/features/alert_management_spec.rb index 3322c9c574f..de6b385b4cd 100644 --- a/spec/features/alert_management_spec.rb +++ b/spec/features/alert_management_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Alert management', :js do +RSpec.describe 'Alert management', :js, feature_category: :incident_management do let_it_be(:project) { create(:project) } let_it_be(:developer) { create(:user) } diff --git a/spec/features/alerts_settings/user_views_alerts_settings_spec.rb b/spec/features/alerts_settings/user_views_alerts_settings_spec.rb index 60f2f776595..70223b2c0d4 100644 --- a/spec/features/alerts_settings/user_views_alerts_settings_spec.rb +++ b/spec/features/alerts_settings/user_views_alerts_settings_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Alert integrations settings form', :js do +RSpec.describe 'Alert integrations settings form', :js, feature_category: :incident_management do let_it_be(:project) { create(:project) } let_it_be(:maintainer) { create(:user) } let_it_be(:developer) { create(:user) } diff --git a/spec/lib/gitlab/auth/current_user_mode_spec.rb b/spec/lib/gitlab/auth/current_user_mode_spec.rb index a21f0931b78..0a68a4a0ae2 100644 --- a/spec/lib/gitlab/auth/current_user_mode_spec.rb +++ b/spec/lib/gitlab/auth/current_user_mode_spec.rb @@ -194,10 +194,41 @@ RSpec.describe Gitlab::Auth::CurrentUserMode, :request_store do it 'creates a timestamp in the session' do subject.request_admin_mode! + subject.enable_admin_mode!(password: user.password) expect(session).to include(expected_session_entry(be_within(1.second).of(Time.now))) end + + it 'returns true after successful enable' do + subject.request_admin_mode! + + expect(subject.enable_admin_mode!(password: user.password)).to eq(true) + end + + it 'returns false after unsuccessful enable' do + subject.request_admin_mode! + + expect(subject.enable_admin_mode!(password: 'wrong password')).to eq(false) + end + + context 'when user is not an admin' do + let(:user) { build_stubbed(:user) } + + it 'returns false' do + subject.request_admin_mode! + + expect(subject.enable_admin_mode!(password: user.password)).to eq(false) + end + end + + context 'when admin mode is not requested' do + it 'raises error' do + expect do + subject.enable_admin_mode!(password: user.password) + end.to raise_error(Gitlab::Auth::CurrentUserMode::NotRequestedError) + end + end end describe '#disable_admin_mode!' do diff --git a/spec/lib/gitlab/ci/lint_spec.rb b/spec/lib/gitlab/ci/lint_spec.rb index cd3d4ee07bb..32afb893545 100644 --- a/spec/lib/gitlab/ci/lint_spec.rb +++ b/spec/lib/gitlab/ci/lint_spec.rb @@ -337,7 +337,7 @@ RSpec.describe Gitlab::Ci::Lint do end end - context 'pipeline logger' do + describe 'pipeline logger' do let(:counters) do { 'count' => a_kind_of(Numeric), @@ -346,7 +346,7 @@ RSpec.describe Gitlab::Ci::Lint do } end - let(:loggable_data) do + let(:expected_data) do { 'class' => 'Gitlab::Ci::Pipeline::Logger', 'config_build_context_duration_s' => counters, @@ -363,7 +363,7 @@ RSpec.describe Gitlab::Ci::Lint do 'pipeline_persisted' => false, 'pipeline_source' => 'unknown', 'project_id' => project&.id, - 'yaml_process_duration_s' => counters + 'yaml_process_duration_s' => a_kind_of(Numeric) } end @@ -401,7 +401,7 @@ RSpec.describe Gitlab::Ci::Lint do end it 'creates a log entry' do - expect(Gitlab::AppJsonLogger).to receive(:info).with(loggable_data) + expect(Gitlab::AppJsonLogger).to receive(:info).with(expected_data) validate end @@ -422,7 +422,7 @@ RSpec.describe Gitlab::Ci::Lint do let(:project) { nil } let(:project_nil_loggable_data) do - loggable_data.except('project_id') + expected_data.except('project_id') end it 'creates a log entry without project_id' do diff --git a/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb index fc3de2a14cd..16deeb6916f 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb @@ -173,21 +173,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::CancelPendingPipelines do expect(build_statuses(prev_pipeline)).to contain_exactly('running', 'success', 'created') expect(build_statuses(parent_pipeline)).to contain_exactly('running', 'running') end - - context 'when feature flag ci_skip_auto_cancelation_on_child_pipelines is disabled' do - before do - stub_feature_flags(ci_skip_auto_cancelation_on_child_pipelines: false) - end - - it 'does not cancel the parent pipeline' do - expect(build_statuses(parent_pipeline)).to contain_exactly('running', 'running') - - perform - - expect(build_statuses(prev_pipeline)).to contain_exactly('success', 'canceled', 'canceled') - expect(build_statuses(parent_pipeline)).to contain_exactly('running', 'running') - end - end end context 'when the previous pipeline source is webide' do diff --git a/spec/lib/gitlab/ci/pipeline/logger_spec.rb b/spec/lib/gitlab/ci/pipeline/logger_spec.rb index 049a9a416a0..4492ca716ae 100644 --- a/spec/lib/gitlab/ci/pipeline/logger_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/logger_spec.rb @@ -22,55 +22,54 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do end it 'records durations of instrumented operations' do - loggable_data = { + logger.instrument(:expensive_operation) { 123 } + + expected_data = { 'expensive_operation_duration_s' => { 'count' => 1, 'max' => a_kind_of(Numeric), 'sum' => a_kind_of(Numeric) } } - - logger.instrument(:expensive_operation) { 123 } - expect(logger.observations_hash).to match(a_hash_including(loggable_data)) + expect(logger.observations_hash).to match(a_hash_including(expected_data)) end it 'raises an error when block is not provided' do expect { logger.instrument(:expensive_operation) } .to raise_error(ArgumentError, 'block not given') end + + context 'when once: true' do + it 'logs only one observation' do + logger.instrument(:expensive_operation, once: true) { 123 } + logger.instrument(:expensive_operation, once: true) { 123 } + + expected_data = { + 'expensive_operation_duration_s' => a_kind_of(Numeric) + } + expect(logger.observations_hash).to match(a_hash_including(expected_data)) + end + end end - describe '#instrument_with_sql', :request_store do - subject(:instrument_with_sql) do - logger.instrument_with_sql(:expensive_operation, &operation) + describe '#instrument_once_with_sql', :request_store do + subject(:instrument_once_with_sql) do + logger.instrument_once_with_sql(:expensive_operation, &operation) end - def loggable_data(count:, db_count: nil) + def expected_data(count:, db_count: nil) database_name = Ci::ApplicationRecord.connection.pool.db_config.name - keys = %W[ - expensive_operation_duration_s - expensive_operation_db_count - expensive_operation_db_primary_count - expensive_operation_db_primary_duration_s - expensive_operation_db_#{database_name}_count - expensive_operation_db_#{database_name}_duration_s - ] - - data = keys.each.with_object({}) do |key, accumulator| - accumulator[key] = { - 'count' => count, - 'max' => a_kind_of(Numeric), - 'sum' => a_kind_of(Numeric) - } - end + total_db_count = count * db_count if db_count - if db_count - data['expensive_operation_db_count']['max'] = db_count - data['expensive_operation_db_count']['sum'] = count * db_count - end - - data + { + "expensive_operation_duration_s" => a_kind_of(Numeric), + "expensive_operation_db_count" => total_db_count || a_kind_of(Numeric), + "expensive_operation_db_primary_count" => a_kind_of(Numeric), + "expensive_operation_db_primary_duration_s" => a_kind_of(Numeric), + "expensive_operation_db_#{database_name}_count" => a_kind_of(Numeric), + "expensive_operation_db_#{database_name}_duration_s" => a_kind_of(Numeric) + } end context 'with a single query' do @@ -79,10 +78,10 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do it { is_expected.to eq(operation.call) } it 'includes SQL metrics' do - instrument_with_sql + instrument_once_with_sql expect(logger.observations_hash) - .to match(a_hash_including(loggable_data(count: 1, db_count: 1))) + .to match(a_hash_including(expected_data(count: 1, db_count: 1))) end end @@ -92,21 +91,10 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do it { is_expected.to eq(operation.call) } it 'includes SQL metrics' do - instrument_with_sql - - expect(logger.observations_hash) - .to match(a_hash_including(loggable_data(count: 1, db_count: 2))) - end - end - - context 'with multiple observations' do - let(:operation) { -> { Ci::Build.count + Ci::Bridge.count } } - - it 'includes SQL metrics' do - 2.times { logger.instrument_with_sql(:expensive_operation, &operation) } + instrument_once_with_sql expect(logger.observations_hash) - .to match(a_hash_including(loggable_data(count: 2, db_count: 2))) + .to match(a_hash_including(expected_data(count: 1, db_count: 2))) end end @@ -116,7 +104,7 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do it { is_expected.to eq(operation.call) } it 'does not include SQL metrics' do - instrument_with_sql + instrument_once_with_sql expect(logger.observations_hash.keys) .to match_array(['expensive_operation_duration_s']) @@ -126,14 +114,40 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do describe '#observe' do it 'records durations of observed operations' do - loggable_data = { + expect(logger.observe(:pipeline_creation_duration_s, 30)).to be_truthy + + expected_data = { 'pipeline_creation_duration_s' => { 'sum' => 30, 'count' => 1, 'max' => 30 } } + expect(logger.observations_hash).to match(a_hash_including(expected_data)) + end - expect(logger.observe(:pipeline_creation_duration_s, 30)).to be_truthy - expect(logger.observations_hash).to match(a_hash_including(loggable_data)) + context 'when once: true' do + it 'records the latest observation' do + expect(logger.observe(:pipeline_creation_duration_s, 20, once: true)).to be_truthy + expect(logger.observe(:pipeline_creation_duration_s, 30, once: true)).to be_truthy + + expected_data = { + 'pipeline_creation_duration_s' => 30 + } + expect(logger.observations_hash).to match(a_hash_including(expected_data)) + end + + it 'logs data as expected' do + expect(logger.observe(:pipeline_creation_duration_s, 30, once: true)).to be_truthy + expect(logger.observe(:pipeline_operation_x_duration_s, 20)).to be_truthy + expect(logger.observe(:pipeline_operation_x_duration_s, 20)).to be_truthy + + expected_data = { + 'pipeline_creation_duration_s' => 30, + 'pipeline_operation_x_duration_s' => { + 'sum' => 40, 'count' => 2, 'max' => 20 + } + } + expect(logger.observations_hash).to match(a_hash_including(expected_data)) + end end end @@ -152,7 +166,7 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do context 'when the feature flag is enabled' do let(:flag) { true } - let(:loggable_data) do + let(:expected_data) do { 'class' => described_class.name.to_s, 'pipeline_id' => pipeline.id, @@ -173,7 +187,7 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do it 'logs to application.json' do expect(Gitlab::AppJsonLogger) .to receive(:info) - .with(a_hash_including(loggable_data)) + .with(a_hash_including(expected_data)) .and_call_original expect(commit).to be_truthy @@ -194,7 +208,7 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do expect(Gitlab::AppJsonLogger) .to receive(:info) - .with(a_hash_including(loggable_data)) + .with(a_hash_including(expected_data)) .and_call_original expect(commit).to be_truthy @@ -205,7 +219,7 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do let(:project) {} let(:pipeline) { build(:ci_pipeline) } - let(:loggable_data) do + let(:expected_data) do { 'class' => described_class.name.to_s, 'pipeline_persisted' => false, @@ -223,7 +237,7 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do it 'logs to application.json' do expect(Gitlab::AppJsonLogger) .to receive(:info) - .with(a_hash_including(loggable_data)) + .with(a_hash_including(expected_data)) .and_call_original expect(commit).to be_truthy diff --git a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb index b1bff242f33..e1c0da69317 100644 --- a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb +++ b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb @@ -31,7 +31,7 @@ RSpec.describe Gitlab::Gfm::UploadsRewriter do referenced_files.compact.select(&:exists?) end - shared_examples "files are accessible" do + shared_examples 'files are accessible' do describe '#rewrite' do subject(:rewrite) { new_text } @@ -82,6 +82,18 @@ RSpec.describe Gitlab::Gfm::UploadsRewriter do rewrite expect(new_files).to be_empty + expect(new_text).to eq(text) + end + + it 'skips non-existant files' do + allow_next_instance_of(FileUploader) do |file| + allow(file).to receive(:exists?).and_return(false) + end + + rewrite + + expect(new_files).to be_empty + expect(new_text).to eq(text) end end end @@ -107,11 +119,11 @@ RSpec.describe Gitlab::Gfm::UploadsRewriter do end end - context "file are stored locally" do - include_examples "files are accessible" + context 'file are stored locally' do + include_examples 'files are accessible' end - context "files are stored remotely" do + context 'files are stored remotely' do before do stub_uploads_object_storage(FileUploader) @@ -120,7 +132,7 @@ RSpec.describe Gitlab::Gfm::UploadsRewriter do end end - include_examples "files are accessible" + include_examples 'files are accessible' end describe '#needs_rewrite?' do diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 1984c1157fe..a00df3a7dda 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -1377,6 +1377,24 @@ RSpec.describe Gitlab::Git::Repository do expect(branch).to eq(nil) end + + context 'when branch is ambiguous' do + let(:ambiguous_branch) { 'prefix' } + let(:branch_with_prefix) { 'prefix/branch' } + + before do + repository.create_branch(branch_with_prefix) + end + + after do + repository.delete_branch(branch_with_prefix) + end + + it 'returns nil for ambiguous branch' do + expect(repository.find_branch(branch_with_prefix)).to be_a_kind_of(Gitlab::Git::Branch) + expect(repository.find_branch(ambiguous_branch)).to eq(nil) + end + end end describe '#branches' do diff --git a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb index bd96e9baf1d..b413c47e033 100644 --- a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb @@ -102,6 +102,16 @@ RSpec.describe Gitlab::GitalyClient::RefService do client.find_branch('name') end + + context 'when Gitaly returns a ambiguios reference error' do + it 'raises an UnknownRef error' do + expect_any_instance_of(Gitaly::RefService::Stub) + .to receive(:find_branch) + .and_raise(GRPC::BadStatus.new(2, 'reference is ambiguous')) + + expect { client.find_branch('name') }.to raise_error(Gitlab::Git::AmbiguousRef, 'branch is ambiguous: name') + end + end end describe '#find_tag' do diff --git a/spec/lib/gitlab/memory/reporter_spec.rb b/spec/lib/gitlab/memory/reporter_spec.rb new file mode 100644 index 00000000000..7924b37cb4b --- /dev/null +++ b/spec/lib/gitlab/memory/reporter_spec.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Memory::Reporter, :aggregate_failures do + subject(:reporter) { described_class.new } + + let(:fake_report) do + Class.new do + attr_reader :did_run + + def name + 'fake_report' + end + + def run(report_id) + @did_run = true + '/path/to/report' + end + end + end + + let(:report) { fake_report.new } + + describe '#run_report' do + let(:report_duration_counter) { instance_double(::Prometheus::Client::Counter) } + let(:file_size) { 1_000_000 } + + before do + allow(Gitlab::Metrics).to receive(:counter).and_return(report_duration_counter) + allow(report_duration_counter).to receive(:increment) + + allow(::Prometheus::PidProvider).to receive(:worker_id).and_return('worker_1') + allow(File).to receive(:size).with('/path/to/report').and_return(file_size) + + allow(SecureRandom).to receive(:uuid).and_return('abc123') + end + + it 'runs the given report' do + expect { reporter.run_report(report) }.to change { report.did_run }.from(nil).to(true) + end + + it 'logs duration and other metrics' do + expect(Gitlab::AppLogger).to receive(:info).with( + hash_including( + :duration_s, + :cpu_s, + perf_report_size_bytes: file_size, + message: 'finished', + pid: Process.pid, + worker_id: 'worker_1', + perf_report_worker_uuid: 'abc123', + perf_report: 'fake_report' + )) + + reporter.run_report(report) + end + + it 'increments Prometheus duration counter' do + expect(report_duration_counter).to receive(:increment).with({ report: 'fake_report' }, an_instance_of(Float)) + + reporter.run_report(report) + end + + context 'when the report returns invalid file path' do + before do + allow(File).to receive(:size).with('/path/to/report').and_raise(Errno::ENOENT) + end + + it 'logs `0` as `perf_report_size_bytes`' do + expect(Gitlab::AppLogger).to receive(:info).with(hash_including(perf_report_size_bytes: 0)) + + reporter.run_report(report) + end + end + end +end diff --git a/spec/lib/gitlab/memory/reports/jemalloc_stats_spec.rb b/spec/lib/gitlab/memory/reports/jemalloc_stats_spec.rb index de27c8352f9..ee4c6004c52 100644 --- a/spec/lib/gitlab/memory/reports/jemalloc_stats_spec.rb +++ b/spec/lib/gitlab/memory/reports/jemalloc_stats_spec.rb @@ -5,8 +5,7 @@ require 'spec_helper' RSpec.describe Gitlab::Memory::Reports::JemallocStats do let_it_be(:outdir) { Dir.mktmpdir } - let(:filename_label) { SecureRandom.uuid } - let(:jemalloc_stats) { described_class.new(reports_path: outdir, filename_label: filename_label) } + let(:jemalloc_stats) { described_class.new(reports_path: outdir) } after do FileUtils.rm_f(outdir) @@ -27,14 +26,14 @@ RSpec.describe Gitlab::Memory::Reports::JemallocStats do .to receive(:dump_stats) .with(path: outdir, tmp_dir: File.join(outdir, '/tmp'), - filename_label: filename_label) + filename_label: 'test_report') .and_return(report_path) - expect(jemalloc_stats.run).to eq(report_path) + expect(jemalloc_stats.run('test_report')).to eq(report_path) end describe 'reports cleanup' do - let(:jemalloc_stats) { described_class.new(reports_path: outdir, filename_label: filename_label) } + let(:jemalloc_stats) { described_class.new(reports_path: outdir) } before do stub_env('GITLAB_DIAGNOSTIC_REPORTS_JEMALLOC_MAX_REPORTS_STORED', 3) @@ -62,7 +61,7 @@ RSpec.describe Gitlab::Memory::Reports::JemallocStats do end it 'keeps only `max_reports_stored` total newest files' do - expect { jemalloc_stats.run } + expect { jemalloc_stats.run('test_report') } .to change { Dir.entries(outdir).count { |e| e.match(/jemalloc_stats.*/) } } .from(5).to(3) @@ -90,7 +89,7 @@ RSpec.describe Gitlab::Memory::Reports::JemallocStats do end it 'does not remove any reports' do - expect { jemalloc_stats.run } + expect { jemalloc_stats.run('test_report') } .not_to change { Dir.entries(outdir).count { |e| e.match(/jemalloc_stats.*/) } } end end @@ -105,7 +104,7 @@ RSpec.describe Gitlab::Memory::Reports::JemallocStats do it 'does not run the report and returns nil' do expect(Gitlab::Memory::Jemalloc).not_to receive(:dump_stats) - expect(jemalloc_stats.run).to be_nil + expect(jemalloc_stats.run('test_report')).to be_nil end end end diff --git a/spec/lib/gitlab/memory/reports_daemon_spec.rb b/spec/lib/gitlab/memory/reports_daemon_spec.rb index ab616e92b00..b6be4eac919 100644 --- a/spec/lib/gitlab/memory/reports_daemon_spec.rb +++ b/spec/lib/gitlab/memory/reports_daemon_spec.rb @@ -3,85 +3,56 @@ require 'spec_helper' RSpec.describe Gitlab::Memory::ReportsDaemon, :aggregate_failures do - let(:daemon) { described_class.new } + let(:reporter) { instance_double(Gitlab::Memory::Reporter) } + let(:reports) { nil } let_it_be(:tmp_dir) { Dir.mktmpdir } + subject(:daemon) { described_class.new(reporter: reporter, reports: reports) } + after(:all) do FileUtils.remove_entry(tmp_dir) end describe '#run_thread' do - let(:report_duration_counter) { instance_double(::Prometheus::Client::Counter) } - let(:file_size) { 1_000_000 } - before do - allow(Gitlab::Metrics).to receive(:counter).and_return(report_duration_counter) - allow(report_duration_counter).to receive(:increment) - # make sleep no-op allow(daemon).to receive(:sleep) {} # let alive return 3 times: true, true, false allow(daemon).to receive(:alive).and_return(true, true, false) - - allow(File).to receive(:size).with(/#{daemon.reports_path}.*\.json/).and_return(file_size) - end - - it 'runs reports, logs and sets gauge' do - expect(daemon.send(:reports)) - .to all(receive(:run).twice { Tempfile.new("report.json", tmp_dir).path }) - - expect(::Prometheus::PidProvider).to receive(:worker_id).at_least(:once).and_return('worker_1') - - expect(Gitlab::AppLogger).to receive(:info).with( - hash_including( - :duration_s, - :cpu_s, - :perf_report_worker_uuid, - perf_report_size_bytes: file_size, - message: 'finished', - pid: Process.pid, - worker_id: 'worker_1', - perf_report: 'jemalloc_stats' - )).twice - - expect(report_duration_counter).to receive(:increment).with({ report: 'jemalloc_stats' }, an_instance_of(Float)) - - daemon.send(:run_thread) end - context 'when the report object returns invalid file path' do - before do - allow(File).to receive(:size).with(/#{daemon.reports_path}.*\.json/).and_raise(Errno::ENOENT) - end - - it 'logs `0` as `perf_report_size_bytes`' do - expect(daemon.send(:reports)) - .to all(receive(:run).twice { Tempfile.new("report.json", tmp_dir).path }) - - expect(Gitlab::AppLogger).to receive(:info).with(hash_including(perf_report_size_bytes: 0)).twice + context 'with default reports' do + it 'runs them using the given reporter' do + expect(reporter).to receive(:run_report).twice.with(an_instance_of(Gitlab::Memory::Reports::JemallocStats)) daemon.send(:run_thread) end end - it 'allows configure and run multiple reports' do + context 'with inactive reports' do # rubocop: disable RSpec/VerifiedDoubles # We test how ReportsDaemon could be extended in the future # We configure it with new reports classes which are not yet defined so we cannot make this an instance_double. - active_report_1 = double("Active Report 1", active?: true) - active_report_2 = double("Active Report 2", active?: true) - inactive_report = double("Inactive Report", active?: false) + let(:active_report_1) { double("Active Report 1", active?: true) } + let(:active_report_2) { double("Active Report 2", active?: true) } + let(:inactive_report) { double("Inactive Report", active?: false) } # rubocop: enable RSpec/VerifiedDoubles - allow(daemon).to receive(:reports).and_return([active_report_1, inactive_report, active_report_2]) + let(:reports) do + [active_report_1, active_report_2, inactive_report] + end - expect(active_report_1).to receive(:run).and_return(File.join(tmp_dir, 'report_1.json')).twice - expect(active_report_2).to receive(:run).and_return(File.join(tmp_dir, 'report_2.json')).twice - expect(inactive_report).not_to receive(:run) + it 'runs only active reports' do + expect(reporter).to receive(:run_report).ordered.with(active_report_1) + expect(reporter).to receive(:run_report).ordered.with(active_report_2) + expect(reporter).to receive(:run_report).ordered.with(active_report_1) + expect(reporter).to receive(:run_report).ordered.with(active_report_2) + expect(reporter).not_to receive(:run_report).with(inactive_report) - daemon.send(:run_thread) + daemon.send(:run_thread) + end end context 'sleep timers logic' do @@ -90,9 +61,6 @@ RSpec.describe Gitlab::Memory::ReportsDaemon, :aggregate_failures do daemon = described_class.new allow(daemon).to receive(:alive).and_return(true, true, false) - expect(daemon.send(:reports)) - .to all(receive(:run).twice { Tempfile.new("report.json", tmp_dir).path }) - expect(daemon).to receive(:sleep).with(described_class::DEFAULT_SLEEP_S).ordered expect(daemon).to receive(:sleep).with(described_class::DEFAULT_SLEEP_BETWEEN_REPORTS_S).ordered expect(daemon).to receive(:sleep).with(described_class::DEFAULT_SLEEP_S).ordered diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index 2ecd10cccc6..9b7a63bffdf 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -175,26 +175,31 @@ RSpec.describe Member do end context 'member role access level' do - let_it_be(:member) { create(:group_member, access_level: Gitlab::Access::DEVELOPER) } + let_it_be_with_reload(:member) { create(:group_member, access_level: Gitlab::Access::DEVELOPER) } - context 'no member role is associated' do + context 'when no member role is associated' do it 'is valid' do expect(member).to be_valid end end - context 'member role is associated' do + context 'when member role is associated' do let!(:member_role) do - create(:member_role, members: [member], base_access_level: Gitlab::Access::DEVELOPER) + create( + :member_role, + members: [member], + base_access_level: Gitlab::Access::DEVELOPER, + namespace: member.member_namespace + ) end - context 'member role matches access level' do + context 'when member role matches access level' do it 'is valid' do expect(member).to be_valid end end - context 'member role does not match access level' do + context 'when member role does not match access level' do it 'is invalid' do member_role.base_access_level = Gitlab::Access::MAINTAINER @@ -202,13 +207,57 @@ RSpec.describe Member do end end - context 'access_level cannot be changed' do + context 'when access_level is changed' do it 'is invalid' do member.access_level = Gitlab::Access::MAINTAINER expect(member).not_to be_valid - expect(member.errors.full_messages).to include( - "Access level cannot be changed since member is associated with a custom role" + expect(member.errors[:access_level]).to include( + _("cannot be changed since member is associated with a custom role") + ) + end + end + end + end + + context 'member role namespace' do + let_it_be_with_reload(:member) { create(:group_member) } + + context 'when no member role is associated' do + it 'is valid' do + expect(member).to be_valid + end + end + + context 'when member role is associated' do + let_it_be(:member_role) do + create(:member_role, members: [member], namespace: member.group, base_access_level: member.access_level) + end + + context 'when member#member_namespace is a group within hierarchy of member_role#namespace' do + it 'is valid' do + member.member_namespace = create(:group, parent: member_role.namespace) + + expect(member).to be_valid + end + end + + context 'when member#member_namespace is a project within hierarchy of member_role#namespace' do + it 'is valid' do + project = create(:project, group: member_role.namespace) + member.member_namespace = Namespace.find(project.parent_id) + + expect(member).to be_valid + end + end + + context 'when member#member_namespace is outside hierarchy of member_role#namespace' do + it 'is invalid' do + member.member_namespace = create(:group) + + expect(member).not_to be_valid + expect(member.errors[:member_namespace]).to include( + _("must be in same hierarchy as custom role's namespace") ) end end @@ -248,7 +297,7 @@ RSpec.describe Member do accepted_invite_user = build(:user, state: :active) @accepted_invite_member = create(:project_member, :invited, :developer, project: project) - .tap { |u| u.accept_invite!(accepted_invite_user) } + .tap { |u| u.accept_invite!(accepted_invite_user) } requested_user = create(:user).tap { |u| project.request_access(u) } @requested_member = project.requesters.find_by(user_id: requested_user.id) @@ -612,14 +661,14 @@ RSpec.describe Member do subject { described_class.authorizable.to_a } it 'includes the member who has an associated user record,'\ - 'but also having an invite_token' do - member = create(:project_member, - :developer, - :invited, - user: create(:user)) + 'but also having an invite_token' do + member = create(:project_member, + :developer, + :invited, + user: create(:user)) - expect(subject).to include(member) - end + expect(subject).to include(member) + end it { is_expected.to include @owner } it { is_expected.to include @maintainer } diff --git a/spec/models/members/member_role_spec.rb b/spec/models/members/member_role_spec.rb index e2691e2e78c..f9d6757bb90 100644 --- a/spec/models/members/member_role_spec.rb +++ b/spec/models/members/member_role_spec.rb @@ -9,39 +9,50 @@ RSpec.describe MemberRole do end describe 'validation' do - subject { described_class.new } + subject(:member_role) { build(:member_role) } it { is_expected.to validate_presence_of(:namespace) } it { is_expected.to validate_presence_of(:base_access_level) } - context 'for namespace' do - subject { build(:member_role) } - + context 'when for namespace' do let_it_be(:root_group) { create(:group) } context 'when namespace is a subgroup' do it 'is invalid' do subgroup = create(:group, parent: root_group) - subject.namespace = subgroup + member_role.namespace = subgroup - expect(subject).to be_invalid + expect(member_role).to be_invalid + expect(member_role.errors[:namespace]).to include( + s_("MemberRole|must be top-level namespace") + ) end end context 'when namespace is a root group' do it 'is valid' do - subject.namespace = root_group + member_role.namespace = root_group - expect(subject).to be_valid + expect(member_role).to be_valid end end context 'when namespace is not present' do it 'is invalid with a different error message' do - subject.namespace = nil + member_role.namespace = nil + + expect(member_role).to be_invalid + expect(member_role.errors[:namespace]).to include(_("can't be blank")) + end + end + + context 'when namespace is outside hierarchy of member' do + it 'creates a validation error' do + member_role.save! + member_role.namespace = create(:group) - expect(subject).to be_invalid - expect(subject.errors.full_messages).to eq(["Namespace can't be blank"]) + expect(member_role).not_to be_valid + expect(member_role.errors[:namespace]).to include(s_("MemberRole|can't be changed")) end end end diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index 750b9a39e15..186afaa1b79 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -344,6 +344,18 @@ RSpec.describe API::Branches do end end + context 'when branch is ambiguous' do + let(:branch_name) { 'prefix' } + + before do + project.repository.create_branch('prefix/branch') + end + + it_behaves_like '404 response' do + let(:request) { get api(route, current_user) } + end + end + context 'when repository does not exist' do it_behaves_like '404 response' do let(:project) { create(:project, creator: user) } diff --git a/spec/services/ci/append_build_trace_service_spec.rb b/spec/services/ci/append_build_trace_service_spec.rb index 487dbacbe90..36061bc3596 100644 --- a/spec/services/ci/append_build_trace_service_spec.rb +++ b/spec/services/ci/append_build_trace_service_spec.rb @@ -76,4 +76,20 @@ RSpec.describe Ci::AppendBuildTraceService do expect(build.failure_reason).to eq 'trace_size_exceeded' end end + + context 'when debug_trace param is provided' do + let(:metadata) { Ci::BuildMetadata.find_by(build_id: build) } + + it 'changes the build metadata debug_trace value' do + stream_size = 192.kilobytes + body_data = 'x' * stream_size + content_range = "0-#{stream_size}" + + described_class + .new(build, content_range: content_range, debug_trace: true) + .execute(body_data) + + expect(metadata.debug_trace_enabled).to be(true) + end + end end diff --git a/spec/services/ci/create_pipeline_service/logger_spec.rb b/spec/services/ci/create_pipeline_service/logger_spec.rb index 9cd5d48a439..76155690ced 100644 --- a/spec/services/ci/create_pipeline_service/logger_spec.rb +++ b/spec/services/ci/create_pipeline_service/logger_spec.rb @@ -2,8 +2,8 @@ require 'spec_helper' -RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do - context 'pipeline logger' do +RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do # rubocop: disable RSpec/FilePath + describe 'pipeline logger' do let_it_be(:project) { create(:project, :repository) } let_it_be(:user) { project.first_owner } @@ -12,10 +12,6 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes let(:pipeline) { service.execute(:push).payload } let(:file_location) { 'spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml' } - before do - stub_ci_pipeline_yaml_file(gitlab_ci_yaml) - end - let(:counters) do { 'count' => a_kind_of(Numeric), @@ -32,15 +28,19 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes 'pipeline_persisted' => true, 'project_id' => project.id, 'pipeline_creation_service_duration_s' => a_kind_of(Numeric), - 'pipeline_creation_duration_s' => counters, - 'pipeline_size_count' => counters, - 'pipeline_step_gitlab_ci_pipeline_chain_seed_duration_s' => counters, + 'pipeline_creation_duration_s' => a_kind_of(Numeric), + 'pipeline_size_count' => a_kind_of(Numeric), + 'pipeline_step_gitlab_ci_pipeline_chain_seed_duration_s' => a_kind_of(Numeric), 'pipeline_seed_build_inclusion_duration_s' => counters, 'pipeline_builds_tags_count' => a_kind_of(Numeric), 'pipeline_builds_distinct_tags_count' => a_kind_of(Numeric) } end + before do + stub_ci_pipeline_yaml_file(gitlab_ci_yaml) + end + context 'when the duration is under the threshold' do it 'does not create a log entry but it collects the data' do expect(Gitlab::AppJsonLogger).not_to receive(:info) @@ -49,9 +49,9 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes expect(service.logger.observations_hash) .to match( a_hash_including( - 'pipeline_creation_duration_s' => counters, - 'pipeline_size_count' => counters, - 'pipeline_step_gitlab_ci_pipeline_chain_seed_duration_s' => counters + 'pipeline_creation_duration_s' => a_kind_of(Numeric), + 'pipeline_size_count' => a_kind_of(Numeric), + 'pipeline_step_gitlab_ci_pipeline_chain_seed_duration_s' => a_kind_of(Numeric) ) ) end @@ -60,7 +60,7 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes context 'when the durations exceeds the threshold' do let(:timer) do proc do - @timer = @timer.to_i + 30 + @timer = @timer.to_i + 30 # rubocop: disable RSpec/InstanceVariable end end @@ -86,17 +86,15 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes 'pipeline_persisted' => false, 'project_id' => project.id, 'pipeline_creation_service_duration_s' => a_kind_of(Numeric), - 'pipeline_step_gitlab_ci_pipeline_chain_seed_duration_s' => counters + 'pipeline_step_gitlab_ci_pipeline_chain_seed_duration_s' => a_kind_of(Numeric) } end - before do + it 'creates a log entry' do allow_next_instance_of(Ci::Pipeline) do |pipeline| expect(pipeline).to receive(:save!).and_raise { RuntimeError } end - end - it 'creates a log entry' do expect(Gitlab::AppJsonLogger) .to receive(:info) .with(a_hash_including(loggable_data)) @@ -123,7 +121,7 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes context 'when the size exceeds the threshold' do before do allow_next_instance_of(Ci::Pipeline) do |pipeline| - allow(pipeline).to receive(:total_size) { 5000 } + allow(pipeline).to receive(:total_size).and_return(5000) end end |