diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-09 21:09:48 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-09 21:09:48 +0300 |
commit | 9b09561f47159655d05171b4bee980c669859864 (patch) | |
tree | 06a273b8ee38d644ab6a45c2c78856cb38c756e8 | |
parent | e91cb68359c900aa51ffdb1863502168742e94f0 (diff) |
Add latest changes from gitlab-org/gitlab@master
121 files changed, 1180 insertions, 276 deletions
diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml index 2103f352768..8cbdb0b0181 100644 --- a/.rubocop_manual_todo.yml +++ b/.rubocop_manual_todo.yml @@ -39,9 +39,6 @@ Graphql/ResolverType: - 'app/graphql/resolvers/ci/pipeline_stages_resolver.rb' - 'app/graphql/resolvers/merge_requests_resolver.rb' - 'app/graphql/resolvers/users/group_count_resolver.rb' - - 'ee/app/graphql/resolvers/geo/merge_request_diff_registries_resolver.rb' - - 'ee/app/graphql/resolvers/geo/package_file_registries_resolver.rb' - - 'ee/app/graphql/resolvers/geo/terraform_state_version_registries_resolver.rb' - 'ee/app/graphql/resolvers/vulnerabilities_base_resolver.rb' Rails/SaveBang: @@ -372,7 +372,7 @@ group :development, :test do gem 'spring', '~> 2.1.0' gem 'spring-commands-rspec', '~> 1.0.4' - gem 'gitlab-styles', '~> 5.1.0', require: false + gem 'gitlab-styles', '~> 5.2.0', require: false gem 'scss_lint', '~> 0.59.0', require: false gem 'haml_lint', '~> 0.36.0', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 3ec97fc4d5f..22ebab2492c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -454,7 +454,7 @@ GEM gitlab-puma (>= 2.7, < 5) gitlab-sidekiq-fetcher (0.5.2) sidekiq (~> 5) - gitlab-styles (5.1.0) + gitlab-styles (5.2.0) rubocop (~> 0.89.1) rubocop-gitlab-security (~> 0.1.0) rubocop-performance (~> 1.8.1) @@ -1363,7 +1363,7 @@ DEPENDENCIES gitlab-puma (~> 4.3.3.gitlab.2) gitlab-puma_worker_killer (~> 0.1.1.gitlab.1) gitlab-sidekiq-fetcher (= 0.5.2) - gitlab-styles (~> 5.1.0) + gitlab-styles (~> 5.2.0) gitlab_chronic_duration (~> 0.10.6.2) gitlab_omniauth-ldap (~> 2.1.1) gon (~> 6.2) diff --git a/app/graphql/resolvers/board_lists_resolver.rb b/app/graphql/resolvers/board_lists_resolver.rb index 0a43d6f3a16..35d938c50be 100644 --- a/app/graphql/resolvers/board_lists_resolver.rb +++ b/app/graphql/resolvers/board_lists_resolver.rb @@ -3,9 +3,13 @@ module Resolvers class BoardListsResolver < BaseResolver include BoardIssueFilterable + prepend ManualAuthorization include Gitlab::Graphql::Authorize::AuthorizeResource type Types::BoardListType, null: true + extras [:lookahead] + + authorize :read_list argument :id, Types::GlobalIDType[List], required: false, @@ -42,10 +46,6 @@ module Resolvers service.execute(board, create_default_lists: false) end - def authorized_resource?(board) - Ability.allowed?(context[:current_user], :read_list, board) - end - def load_preferences?(lookahead) lookahead&.selection(:edges)&.selection(:node)&.selects?(:collapsed) end diff --git a/app/graphql/resolvers/concerns/manual_authorization.rb b/app/graphql/resolvers/concerns/manual_authorization.rb new file mode 100644 index 00000000000..182110b9594 --- /dev/null +++ b/app/graphql/resolvers/concerns/manual_authorization.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +# TODO: remove this entirely when framework authorization is released +# See: https://gitlab.com/gitlab-org/gitlab/-/issues/290216 +module ManualAuthorization + def resolve(**args) + super + rescue ::Gitlab::Graphql::Errors::ResourceNotAvailable + nil + end +end diff --git a/app/graphql/resolvers/merge_request_pipelines_resolver.rb b/app/graphql/resolvers/merge_request_pipelines_resolver.rb index 6590dfdc78c..f84eedb4c3b 100644 --- a/app/graphql/resolvers/merge_request_pipelines_resolver.rb +++ b/app/graphql/resolvers/merge_request_pipelines_resolver.rb @@ -5,14 +5,32 @@ module Resolvers class MergeRequestPipelinesResolver < BaseResolver # The GraphQL type here gets defined in this include include ::ResolvesPipelines + include ::CachingArrayResolver alias_method :merge_request, :object + # Return at most 500 pipelines for each MR. + # Merge requests generally have many fewer pipelines than this. + def self.field_options + super.merge(max_page_size: 500) + end + def resolve(**args) return unless project - resolve_pipelines(project, args) - .merge(merge_request.all_pipelines) + super + end + + def query_for(args) + resolve_pipelines(project, args).merge(merge_request.all_pipelines) + end + + def model_class + ::Ci::Pipeline + end + + def query_input(**args) + args end def project diff --git a/app/graphql/resolvers/projects/jira_imports_resolver.rb b/app/graphql/resolvers/projects/jira_imports_resolver.rb index efd45c2c465..9ef924f2bee 100644 --- a/app/graphql/resolvers/projects/jira_imports_resolver.rb +++ b/app/graphql/resolvers/projects/jira_imports_resolver.rb @@ -3,10 +3,11 @@ module Resolvers module Projects class JiraImportsResolver < BaseResolver - type Types::JiraImportType.connection_type, null: true - + prepend ::ManualAuthorization include Gitlab::Graphql::Authorize::AuthorizeResource + type Types::JiraImportType.connection_type, null: true + alias_method :project, :object def resolve(**args) diff --git a/app/graphql/resolvers/projects/services_resolver.rb b/app/graphql/resolvers/projects/services_resolver.rb index 17d81e21c28..4f5a6cddbb3 100644 --- a/app/graphql/resolvers/projects/services_resolver.rb +++ b/app/graphql/resolvers/projects/services_resolver.rb @@ -3,9 +3,11 @@ module Resolvers module Projects class ServicesResolver < BaseResolver + prepend ManualAuthorization include Gitlab::Graphql::Authorize::AuthorizeResource type Types::Projects::ServiceType.connection_type, null: true + authorize :admin_project argument :active, GraphQL::BOOLEAN_TYPE, @@ -24,10 +26,6 @@ module Resolvers services(args[:active], args[:type]) end - def authorized_resource?(project) - Ability.allowed?(context[:current_user], :admin_project, project) - end - private def services(active, type) diff --git a/app/graphql/resolvers/snippets/blobs_resolver.rb b/app/graphql/resolvers/snippets/blobs_resolver.rb index 3a0dcb50faf..cfb1711aed4 100644 --- a/app/graphql/resolvers/snippets/blobs_resolver.rb +++ b/app/graphql/resolvers/snippets/blobs_resolver.rb @@ -3,9 +3,11 @@ module Resolvers module Snippets class BlobsResolver < BaseResolver + prepend ManualAuthorization include Gitlab::Graphql::Authorize::AuthorizeResource type Types::Snippets::BlobType.connection_type, null: true + authorize :read_snippet alias_method :snippet, :object @@ -27,10 +29,6 @@ module Resolvers end end - def authorized_resource?(snippet) - Ability.allowed?(context[:current_user], :read_snippet, snippet) - end - private def transformed_blob_paths(paths) diff --git a/app/graphql/types/ci/pipeline_type.rb b/app/graphql/types/ci/pipeline_type.rb index 0469c4c21f6..4709d5e8dd6 100644 --- a/app/graphql/types/ci/pipeline_type.rb +++ b/app/graphql/types/ci/pipeline_type.rb @@ -94,6 +94,9 @@ module Types field :project, Types::ProjectType, null: true, description: 'Project the pipeline belongs to' + field :active, GraphQL::BOOLEAN_TYPE, null: false, method: :active?, + description: 'Indicates if the pipeline is active' + def detailed_status object.detailed_status(context[:current_user]) end diff --git a/app/graphql/types/commit_type.rb b/app/graphql/types/commit_type.rb index fab54d044fb..37d19b4148b 100644 --- a/app/graphql/types/commit_type.rb +++ b/app/graphql/types/commit_type.rb @@ -12,6 +12,8 @@ module Types description: 'ID (global ID) of the commit' field :sha, type: GraphQL::STRING_TYPE, null: false, description: 'SHA1 ID of the commit' + field :short_id, type: GraphQL::STRING_TYPE, null: false, + description: 'Short SHA1 ID of the commit' field :title, type: GraphQL::STRING_TYPE, null: true, calls_gitaly: true, description: 'Title of the commit message' markdown_field :title_html, null: true diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb index bc8871cfa39..4c18ebba232 100644 --- a/app/graphql/types/merge_request_type.rb +++ b/app/graphql/types/merge_request_type.rb @@ -92,6 +92,8 @@ module Types description: 'Indicates if there is a rebase currently in progress for the merge request' field :default_merge_commit_message, GraphQL::STRING_TYPE, null: true, description: 'Default merge commit message of the merge request' + field :default_merge_commit_message_with_description, GraphQL::STRING_TYPE, null: true, + description: 'Default merge commit message of the merge request with description' field :merge_ongoing, GraphQL::BOOLEAN_TYPE, method: :merge_ongoing?, null: false, description: 'Indicates if a merge is currently occurring' field :source_branch_exists, GraphQL::BOOLEAN_TYPE, @@ -115,7 +117,7 @@ module Types description: 'The pipeline running on the branch HEAD of the merge request' field :pipelines, null: true, - description: 'Pipelines for the merge request', + description: 'Pipelines for the merge request. Note: for performance reasons, no more than the most recent 500 pipelines will be returned.', resolver: Resolvers::MergeRequestPipelinesResolver field :milestone, Types::MilestoneType, null: true, @@ -153,6 +155,16 @@ module Types field :approved_by, Types::UserType.connection_type, null: true, description: 'Users who approved the merge request' + field :squash_on_merge, GraphQL::BOOLEAN_TYPE, null: false, method: :squash_on_merge?, + description: 'Indicates if squash on merge is enabled' + field :available_auto_merge_strategies, [GraphQL::STRING_TYPE], null: true, calls_gitaly: true, + description: 'Array of available auto merge strategies' + field :has_ci, GraphQL::BOOLEAN_TYPE, null: false, method: :has_ci?, + description: 'Indicates if the merge request has CI' + field :mergeable, GraphQL::BOOLEAN_TYPE, null: false, method: :mergeable?, calls_gitaly: true, + description: 'Indicates if the merge request is mergeable' + field :commits_without_merge_commits, Types::CommitType.connection_type, null: true, + calls_gitaly: true, description: 'Merge request commits excluding merge commits' def approved_by object.approved_by_users @@ -203,6 +215,18 @@ module Types def discussion_locked !!object.discussion_locked end + + def default_merge_commit_message_with_description + object.default_merge_commit_message(include_description: true) + end + + def available_auto_merge_strategies + AutoMergeService.new(object.project, current_user).available_strategies(object) + end + + def commits_without_merge_commits + object.recent_commits.without_merge_commits + end end end diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb index 3d0dd8a1ffd..2061f91e89a 100644 --- a/app/graphql/types/project_type.rb +++ b/app/graphql/types/project_type.rb @@ -107,6 +107,8 @@ module Types description: 'Indicates if issues referenced by merge requests and commits within the default branch are closed automatically' field :suggestion_commit_message, GraphQL::STRING_TYPE, null: true, description: 'The commit message used to apply merge request suggestions' + field :squash_read_only, GraphQL::BOOLEAN_TYPE, null: false, method: :squash_readonly?, + description: 'Indicates if squash readonly is enabled' field :namespace, Types::NamespaceType, null: true, description: 'Namespace of the project' diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index f40755b9439..e6603237676 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -256,7 +256,7 @@ module EventsHelper end else content_tag :div, class: 'system-note-image user-avatar' do - author_avatar(event, size: 40) + author_avatar(event, size: 32) end end end diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb index 23b04febe25..6133a2b0aa7 100644 --- a/app/helpers/icons_helper.rb +++ b/app/helpers/icons_helper.rb @@ -8,22 +8,6 @@ module IconsHelper DEFAULT_ICON_SIZE = 16 - # Creates an icon tag given icon name(s) and possible icon modifiers. - # - # Right now this method simply delegates directly to `fa_icon` from the - # font-awesome-rails gem, but should we ever use a different icon pack in the - # future we won't have to change hundreds of method calls. - # @deprecated use sprite_icon to render a SVG icon - def icon(names, options = {}) - if (options.keys & %w[aria-hidden aria-label data-hidden]).empty? - # Add 'aria-hidden' and 'data-hidden' if they are not set in options. - options['aria-hidden'] = true - options['data-hidden'] = true - end - - options.include?(:base) ? fa_stacked_icon(names, options) : fa_icon(names, options) - end - def custom_icon(icon_name, size: DEFAULT_ICON_SIZE) memoized_icon("#{icon_name}_#{size}") do # We can't simply do the below, because there are some .erb SVGs. diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index fb5b49711d9..ef3326cfdae 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -5,9 +5,6 @@ class ApplicationSetting < ApplicationRecord include CacheMarkdownField include TokenAuthenticatable include ChronicDurationAttribute - include IgnorableColumns - - ignore_column :namespace_storage_size_limit, remove_with: '13.5', remove_after: '2020-09-22' INSTANCE_REVIEW_MIN_USERS = 50 GRAFANA_URL_ERROR_MESSAGE = 'Please check your Grafana URL setting in ' \ diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 3a5e745ed2f..b59eb22fd4c 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -853,15 +853,9 @@ module Ci end def same_family_pipeline_ids - if Feature.enabled?(:ci_root_ancestor_for_pipeline_family, project, default_enabled: false) - ::Gitlab::Ci::PipelineObjectHierarchy.new( - self.class.where(id: root_ancestor), options: { same_project: true } - ).base_and_descendants.select(:id) - else - ::Gitlab::Ci::PipelineObjectHierarchy.new( - base_and_ancestors(same_project: true), options: { same_project: true } - ).base_and_descendants.select(:id) - end + ::Gitlab::Ci::PipelineObjectHierarchy.new( + self.class.where(id: root_ancestor), options: { same_project: true } + ).base_and_descendants.select(:id) end def build_with_artifacts_in_self_and_descendants(name) diff --git a/app/services/issues/export_csv_service.rb b/app/services/issues/export_csv_service.rb index 1dcdfb9faea..8f513632929 100644 --- a/app/services/issues/export_csv_service.rb +++ b/app/services/issues/export_csv_service.rb @@ -34,7 +34,7 @@ module Issues private def associations_to_preload - %i(author assignees timelogs) + %i(author assignees timelogs milestone) end def header_to_value_hash diff --git a/changelogs/unreleased/ajk-quality-triage-ops-532.yml b/changelogs/unreleased/ajk-quality-triage-ops-532.yml new file mode 100644 index 00000000000..895d5162c0e --- /dev/null +++ b/changelogs/unreleased/ajk-quality-triage-ops-532.yml @@ -0,0 +1,5 @@ +--- +title: Eliminate N+1 performance issues in MergeRequest.pipelines in GraphQL API +merge_request: 47784 +author: +type: fixed diff --git a/changelogs/unreleased/fix-profile-avatar-size.yml b/changelogs/unreleased/fix-profile-avatar-size.yml new file mode 100644 index 00000000000..b0b3ec6bcf7 --- /dev/null +++ b/changelogs/unreleased/fix-profile-avatar-size.yml @@ -0,0 +1,5 @@ +--- +title: Fix avatar size in profile activity +merge_request: 49047 +author: +type: fixed diff --git a/changelogs/unreleased/remove-ff-ci-root-ancestor-for-pipeline-family.yml b/changelogs/unreleased/remove-ff-ci-root-ancestor-for-pipeline-family.yml new file mode 100644 index 00000000000..072e2b9e5a1 --- /dev/null +++ b/changelogs/unreleased/remove-ff-ci-root-ancestor-for-pipeline-family.yml @@ -0,0 +1,5 @@ +--- +title: Improve query that finds all pipelines in the same family +merge_request: 49240 +author: +type: performance diff --git a/changelogs/unreleased/sh-export-csv-service-nplus-one.yml b/changelogs/unreleased/sh-export-csv-service-nplus-one.yml new file mode 100644 index 00000000000..b5bb1f9b243 --- /dev/null +++ b/changelogs/unreleased/sh-export-csv-service-nplus-one.yml @@ -0,0 +1,5 @@ +--- +title: Fix N+1 queries loading milestones when exporting CSVs +merge_request: 49429 +author: +type: performance diff --git a/config/feature_flags/development/ci_root_ancestor_for_pipeline_family.yml b/config/feature_flags/development/ci_root_ancestor_for_pipeline_family.yml deleted file mode 100644 index 5ddc9d92a1c..00000000000 --- a/config/feature_flags/development/ci_root_ancestor_for_pipeline_family.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: ci_root_ancestor_for_pipeline_family -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46575 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/287812 -milestone: '13.7' -type: development -group: group::continuous integration -default_enabled: false diff --git a/doc/.vale/gitlab/Substitutions.yml b/doc/.vale/gitlab/Substitutions.yml index 4a2c181d271..0a56524e3f5 100644 --- a/doc/.vale/gitlab/Substitutions.yml +++ b/doc/.vale/gitlab/Substitutions.yml @@ -14,6 +14,7 @@ swap: Customer [Pp]ortal: Customers Portal frontmatter: front matter GitLabber: GitLab team member + GitLabbers: GitLab team members GitLab-shell: GitLab Shell gitlab omnibus: Omnibus GitLab param: parameter diff --git a/doc/.vale/gitlab/spelling-exceptions.txt b/doc/.vale/gitlab/spelling-exceptions.txt index cb2264324c6..25a552de172 100644 --- a/doc/.vale/gitlab/spelling-exceptions.txt +++ b/doc/.vale/gitlab/spelling-exceptions.txt @@ -172,6 +172,8 @@ globals Gmail Gollum Google +goroutine +goroutines Gosec Gradle Grafana @@ -212,6 +214,7 @@ JavaScript Jenkins Jenkinsfile Jira +jq jQuery jsdom JupyterHub diff --git a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md index 21513c58aeb..3d0f062d30f 100644 --- a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md +++ b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md @@ -464,8 +464,9 @@ User.billable.count ::HistoricalData.max_historical_user_count ``` +Using cURL and jq (up to a max 100, see the [pagination docs](../../api/README.md#pagination)): + ```shell -# Using curl and jq (up to a max 100, see pagination docs https://docs.gitlab.com/ee/api/#pagination curl --silent --header "Private-Token: ********************" "https://gitlab.example.com/api/v4/users?per_page=100&active" | jq --compact-output '.[] | [.id,.name,.username]' ``` diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql index e9924bd52d7..0b6bbbe9ba7 100644 --- a/doc/api/graphql/reference/gitlab_schema.graphql +++ b/doc/api/graphql/reference/gitlab_schema.graphql @@ -3076,6 +3076,11 @@ type Commit { sha: String! """ + Short SHA1 ID of the commit + """ + shortId: String! + + """ Rendered HTML of the commit signature """ signatureHtml: String @@ -3169,6 +3174,26 @@ enum CommitActionMode { } """ +The connection type for Commit. +""" +type CommitConnection { + """ + A list of edges. + """ + edges: [CommitEdge] + + """ + A list of nodes. + """ + nodes: [Commit] + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" Autogenerated input type of CommitCreate """ input CommitCreateInput { @@ -3223,6 +3248,21 @@ type CommitCreatePayload { errors: [String!]! } +""" +An edge in a connection. +""" +type CommitEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of the edge. + """ + node: Commit +} + enum CommitEncoding { """ Base64 encoding @@ -5443,6 +5483,11 @@ type DastSiteValidation { id: DastSiteValidationID! """ + Normalized URL of the target to be validated + """ + normalizedTargetUrl: String + + """ Status of the site validation """ status: DastSiteProfileValidationStatusEnum! @@ -12928,11 +12973,41 @@ type MergeRequest implements CurrentUserTodos & Noteable { autoMergeEnabled: Boolean! """ + Array of available auto merge strategies + """ + availableAutoMergeStrategies: [String!] + + """ Number of commits in the merge request """ commitCount: Int """ + Merge request commits excluding merge commits + """ + commitsWithoutMergeCommits( + """ + Returns the elements in the list that come after the specified cursor. + """ + after: String + + """ + Returns the elements in the list that come before the specified cursor. + """ + before: String + + """ + Returns the first _n_ elements from the list. + """ + first: Int + + """ + Returns the last _n_ elements from the list. + """ + last: Int + ): CommitConnection + + """ Indicates if the merge request has conflicts """ conflicts: Boolean! @@ -12978,6 +13053,11 @@ type MergeRequest implements CurrentUserTodos & Noteable { defaultMergeCommitMessage: String """ + Default merge commit message of the merge request with description + """ + defaultMergeCommitMessageWithDescription: String + + """ Description of the merge request (Markdown rendered as HTML for caching) """ description: String @@ -13053,6 +13133,11 @@ type MergeRequest implements CurrentUserTodos & Noteable { forceRemoveSourceBranch: Boolean """ + Indicates if the merge request has CI + """ + hasCi: Boolean! + + """ The pipeline running on the branch HEAD of the merge request """ headPipeline: Pipeline @@ -13118,11 +13203,20 @@ type MergeRequest implements CurrentUserTodos & Noteable { mergeStatus: String """ + """ + mergeTrainsCount: Int + + """ Indicates if the merge has been set to be merged when its pipeline succeeds (MWPS) """ mergeWhenPipelineSucceeds: Boolean """ + Indicates if the merge request is mergeable + """ + mergeable: Boolean! + + """ Indicates if all discussions in the merge request have been resolved, allowing the merge request to be merged """ mergeableDiscussionsState: Boolean @@ -13188,7 +13282,8 @@ type MergeRequest implements CurrentUserTodos & Noteable { ): UserConnection """ - Pipelines for the merge request + Pipelines for the merge request. Note: for performance reasons, no more than + the most recent 500 pipelines will be returned. """ pipelines( """ @@ -13293,6 +13388,11 @@ type MergeRequest implements CurrentUserTodos & Noteable { sourceProjectId: Int """ + Indicates if squash on merge is enabled + """ + squashOnMerge: Boolean! + + """ State of the merge request """ state: MergeRequestState! @@ -15435,6 +15535,11 @@ type PageInfo { type Pipeline { """ + Indicates if the pipeline is active + """ + active: Boolean! + + """ Base SHA of the source branch """ beforeSha: String @@ -17491,6 +17596,11 @@ type Project { snippetsEnabled: Boolean """ + Indicates if squash readonly is enabled + """ + squashReadOnly: Boolean! + + """ URL to connect to the project via SSH """ sshUrlToRepo: String diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json index 3782960aece..de392e3f2c0 100644 --- a/doc/api/graphql/reference/gitlab_schema.json +++ b/doc/api/graphql/reference/gitlab_schema.json @@ -8458,6 +8458,24 @@ "deprecationReason": null }, { + "name": "shortId", + "description": "Short SHA1 ID of the commit", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "signatureHtml", "description": "Rendered HTML of the commit signature", "args": [ @@ -8674,6 +8692,73 @@ "possibleTypes": null }, { + "kind": "OBJECT", + "name": "CommitConnection", + "description": "The connection type for Commit.", + "fields": [ + { + "name": "edges", + "description": "A list of edges.", + "args": [ + + ], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "CommitEdge", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "nodes", + "description": "A list of nodes.", + "args": [ + + ], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "Commit", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "pageInfo", + "description": "Information to aid in pagination.", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "PageInfo", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + + ], + "enumValues": null, + "possibleTypes": null + }, + { "kind": "INPUT_OBJECT", "name": "CommitCreateInput", "description": "Autogenerated input type of CommitCreate", @@ -8836,6 +8921,51 @@ "possibleTypes": null }, { + "kind": "OBJECT", + "name": "CommitEdge", + "description": "An edge in a connection.", + "fields": [ + { + "name": "cursor", + "description": "A cursor for use in pagination.", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "node", + "description": "The item at the end of the edge.", + "args": [ + + ], + "type": { + "kind": "OBJECT", + "name": "Commit", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + + ], + "enumValues": null, + "possibleTypes": null + }, + { "kind": "ENUM", "name": "CommitEncoding", "description": null, @@ -14978,6 +15108,20 @@ "deprecationReason": null }, { + "name": "normalizedTargetUrl", + "description": "Normalized URL of the target to be validated", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "status", "description": "Status of the site validation", "args": [ @@ -35709,6 +35853,28 @@ "deprecationReason": null }, { + "name": "availableAutoMergeStrategies", + "description": "Array of available auto merge strategies", + "args": [ + + ], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "commitCount", "description": "Number of commits in the merge request", "args": [ @@ -35723,6 +35889,59 @@ "deprecationReason": null }, { + "name": "commitsWithoutMergeCommits", + "description": "Merge request commits excluding merge commits", + "args": [ + { + "name": "after", + "description": "Returns the elements in the list that come after the specified cursor.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "before", + "description": "Returns the elements in the list that come before the specified cursor.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "first", + "description": "Returns the first _n_ elements from the list.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "last", + "description": "Returns the last _n_ elements from the list.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "CommitConnection", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "conflicts", "description": "Indicates if the merge request has conflicts", "args": [ @@ -35840,6 +36059,20 @@ "deprecationReason": null }, { + "name": "defaultMergeCommitMessageWithDescription", + "description": "Default merge commit message of the merge request with description", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "description", "description": "Description of the merge request (Markdown rendered as HTML for caching)", "args": [ @@ -36048,6 +36281,24 @@ "deprecationReason": null }, { + "name": "hasCi", + "description": "Indicates if the merge request has CI", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "headPipeline", "description": "The pipeline running on the branch HEAD of the merge request", "args": [ @@ -36225,6 +36476,20 @@ "deprecationReason": null }, { + "name": "mergeTrainsCount", + "description": "", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "mergeWhenPipelineSucceeds", "description": "Indicates if the merge has been set to be merged when its pipeline succeeds (MWPS)", "args": [ @@ -36239,6 +36504,24 @@ "deprecationReason": null }, { + "name": "mergeable", + "description": "Indicates if the merge request is mergeable", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "mergeableDiscussionsState", "description": "Indicates if all discussions in the merge request have been resolved, allowing the merge request to be merged", "args": [ @@ -36392,7 +36675,7 @@ }, { "name": "pipelines", - "description": "Pipelines for the merge request", + "description": "Pipelines for the merge request. Note: for performance reasons, no more than the most recent 500 pipelines will be returned.", "args": [ { "name": "status", @@ -36683,6 +36966,24 @@ "deprecationReason": null }, { + "name": "squashOnMerge", + "description": "Indicates if squash on merge is enabled", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "state", "description": "State of the merge request", "args": [ @@ -45918,6 +46219,24 @@ "description": null, "fields": [ { + "name": "active", + "description": "Indicates if the pipeline is active", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "beforeSha", "description": "Base SHA of the source branch", "args": [ @@ -51173,6 +51492,24 @@ "deprecationReason": null }, { + "name": "squashReadOnly", + "description": "Indicates if squash readonly is enabled", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "sshUrlToRepo", "description": "URL to connect to the project via SSH", "args": [ diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index ee08ae47a8c..0204b00301d 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -530,6 +530,7 @@ Represents the code coverage summary for a project. | `message` | String | Raw commit message | | `pipelines` | PipelineConnection | Pipelines of the commit ordered latest first | | `sha` | String! | SHA1 ID of the commit | +| `shortId` | String! | Short SHA1 ID of the commit | | `signatureHtml` | String | Rendered HTML of the commit signature | | `title` | String | Title of the commit message | | `titleHtml` | String | The GitLab Flavored Markdown rendering of `title` | @@ -939,6 +940,7 @@ Represents a DAST Site Validation. | Field | Type | Description | | ----- | ---- | ----------- | | `id` | DastSiteValidationID! | Global ID of the site validation | +| `normalizedTargetUrl` | String | Normalized URL of the target to be validated | | `status` | DastSiteProfileValidationStatusEnum! | Status of the site validation | ### DastSiteValidationCreatePayload @@ -2019,11 +2021,14 @@ Autogenerated return type of MarkAsSpamSnippet. | `assignees` | UserConnection | Assignees of the merge request | | `author` | User | User who created this merge request | | `autoMergeEnabled` | Boolean! | Indicates if auto merge is enabled for the merge request | +| `availableAutoMergeStrategies` | String! => Array | Array of available auto merge strategies | | `commitCount` | Int | Number of commits in the merge request | +| `commitsWithoutMergeCommits` | CommitConnection | Merge request commits excluding merge commits | | `conflicts` | Boolean! | Indicates if the merge request has conflicts | | `createdAt` | Time! | Timestamp of when the merge request was created | | `currentUserTodos` | TodoConnection! | Todos for the current user | | `defaultMergeCommitMessage` | String | Default merge commit message of the merge request | +| `defaultMergeCommitMessageWithDescription` | String | Default merge commit message of the merge request with description | | `description` | String | Description of the merge request (Markdown rendered as HTML for caching) | | `descriptionHtml` | String | The GitLab Flavored Markdown rendering of `description` | | `diffHeadSha` | String | Diff head SHA of the merge request | @@ -2034,6 +2039,7 @@ Autogenerated return type of MarkAsSpamSnippet. | `discussions` | DiscussionConnection! | All discussions on this noteable | | `downvotes` | Int! | Number of downvotes for the merge request | | `forceRemoveSourceBranch` | Boolean | Indicates if the project settings will lead to source branch deletion after merge | +| `hasCi` | Boolean! | Indicates if the merge request has CI | | `headPipeline` | Pipeline | The pipeline running on the branch HEAD of the merge request | | `id` | ID! | ID of the merge request | | `iid` | String! | Internal ID of the merge request | @@ -2043,13 +2049,15 @@ Autogenerated return type of MarkAsSpamSnippet. | `mergeError` | String | Error message due to a merge error | | `mergeOngoing` | Boolean! | Indicates if a merge is currently occurring | | `mergeStatus` | String | Status of the merge request | +| `mergeTrainsCount` | Int | | | `mergeWhenPipelineSucceeds` | Boolean | Indicates if the merge has been set to be merged when its pipeline succeeds (MWPS) | +| `mergeable` | Boolean! | Indicates if the merge request is mergeable | | `mergeableDiscussionsState` | Boolean | Indicates if all discussions in the merge request have been resolved, allowing the merge request to be merged | | `mergedAt` | Time | Timestamp of when the merge request was merged, null if not merged | | `milestone` | Milestone | The milestone of the merge request | | `notes` | NoteConnection! | All notes on this noteable | | `participants` | UserConnection | Participants in the merge request | -| `pipelines` | PipelineConnection | Pipelines for the merge request | +| `pipelines` | PipelineConnection | Pipelines for the merge request. Note: for performance reasons, no more than the most recent 500 pipelines will be returned. | | `project` | Project! | Alias for target_project | | `projectId` | Int! | ID of the merge request project | | `rebaseCommitSha` | String | Rebase commit SHA of the merge request | @@ -2062,6 +2070,7 @@ Autogenerated return type of MarkAsSpamSnippet. | `sourceBranchProtected` | Boolean! | Indicates if the source branch is protected | | `sourceProject` | Project | Source project of the merge request | | `sourceProjectId` | Int | ID of the merge request source project | +| `squashOnMerge` | Boolean! | Indicates if squash on merge is enabled | | `state` | MergeRequestState! | State of the merge request | | `subscribed` | Boolean! | Indicates if the currently logged in user is subscribed to this merge request | | `targetBranch` | String! | Target branch of the merge request | @@ -2402,6 +2411,7 @@ Information about pagination in a connection.. | Field | Type | Description | | ----- | ---- | ----------- | +| `active` | Boolean! | Indicates if the pipeline is active | | `beforeSha` | String | Base SHA of the source branch | | `cancelable` | Boolean! | Specifies if a pipeline can be canceled | | `committedAt` | Time | Timestamp of the pipeline's commit | @@ -2573,6 +2583,7 @@ Autogenerated return type of PipelineRetry. | `sharedRunnersEnabled` | Boolean | Indicates if shared runners are enabled for the project | | `snippets` | SnippetConnection | Snippets of the project | | `snippetsEnabled` | Boolean | Indicates if Snippets are enabled for the current user | +| `squashReadOnly` | Boolean! | Indicates if squash readonly is enabled | | `sshUrlToRepo` | String | URL to connect to the project via SSH | | `starCount` | Int! | Number of times the project has been starred | | `statistics` | ProjectStatistics | Statistics of the project | diff --git a/doc/ci/README.md b/doc/ci/README.md index 3675e8c1386..e18e540ee4f 100644 --- a/doc/ci/README.md +++ b/doc/ci/README.md @@ -142,7 +142,7 @@ Its feature set is listed on the table below according to DevOps stages. | [Auto Deploy](../topics/autodevops/stages.md#auto-deploy) | Deploy your application to a production environment in a Kubernetes cluster. | | [Building Docker images](docker/using_docker_build.md) | Maintain Docker-based projects using GitLab CI/CD. | | [Canary Deployments](../user/project/canary_deployments.md) **(PREMIUM)** | Ship features to only a portion of your pods and let a percentage of your user base to visit the temporarily deployed feature. | -| [Deploy Boards](../user/project/deploy_boards.md) **(PREMIUM)** | Check the current health and status of each CI/CD environment running on Kubernetes. | +| [Deploy Boards](../user/project/deploy_boards.md) | Check the current health and status of each CI/CD environment running on Kubernetes. | | [Feature Flags](../operations/feature_flags.md) **(PREMIUM)** | Deploy your features behind Feature Flags. | | [GitLab Pages](../user/project/pages/index.md) | Deploy static websites. | | [GitLab Releases](../user/project/releases/index.md) | Add release notes to Git tags. | diff --git a/doc/ci/environments/index.md b/doc/ci/environments/index.md index bff088a8322..77f2e1f653b 100644 --- a/doc/ci/environments/index.md +++ b/doc/ci/environments/index.md @@ -1066,7 +1066,7 @@ Below are some links you may find interesting: - [The `.gitlab-ci.yml` definition of environments](../yaml/README.md#environment) - [A blog post on Deployments & Environments](https://about.gitlab.com/blog/2016/08/26/ci-deployment-and-environments/) - [Review Apps - Use dynamic environments to deploy your code for every branch](../review_apps/index.md) -- [Deploy Boards for your applications running on Kubernetes](../../user/project/deploy_boards.md) **(PREMIUM)** +- [Deploy Boards for your applications running on Kubernetes](../../user/project/deploy_boards.md) <!-- ## Troubleshooting diff --git a/doc/ci/introduction/index.md b/doc/ci/introduction/index.md index ed34b0d6612..b69103ee8f0 100644 --- a/doc/ci/introduction/index.md +++ b/doc/ci/introduction/index.md @@ -150,7 +150,7 @@ according to each stage (Verify, Package, Release). - Ship features to only a portion of your pods and let a percentage of your user base to visit the temporarily deployed feature with [Canary Deployments](../../user/project/canary_deployments.md). **(PREMIUM)** - Deploy your features behind [Feature Flags](../../operations/feature_flags.md). - Add release notes to any Git tag with [GitLab Releases](../../user/project/releases/index.md). - - View of the current health and status of each CI environment running on Kubernetes with [Deploy Boards](../../user/project/deploy_boards.md). **(PREMIUM)** + - View of the current health and status of each CI environment running on Kubernetes with [Deploy Boards](../../user/project/deploy_boards.md). - Deploy your application to a production environment in a Kubernetes cluster with [Auto Deploy](../../topics/autodevops/stages.md#auto-deploy). With GitLab CI/CD you can also: diff --git a/doc/ci/triggers/README.md b/doc/ci/triggers/README.md index 47f3a59759d..49e1ebdc46b 100644 --- a/doc/ci/triggers/README.md +++ b/doc/ci/triggers/README.md @@ -164,7 +164,7 @@ need to add in project A's `.gitlab-ci.yml`: build_docs: stage: deploy script: - - "curl --request POST --form token=TOKEN --form ref=master https://gitlab.example.com/api/v4/projects/9/trigger/pipeline" + - 'curl --request POST --form token=TOKEN --form ref=master "https://gitlab.example.com/api/v4/projects/9/trigger/pipeline"' only: - tags ``` diff --git a/doc/development/agent/gitops.md b/doc/development/agent/gitops.md new file mode 100644 index 00000000000..8c8586326fa --- /dev/null +++ b/doc/development/agent/gitops.md @@ -0,0 +1,148 @@ +--- +stage: Configure +group: Configure +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +--- + +# GitOps with the Kubernetes Agent **(PREMIUM ONLY)** + +The [GitLab Kubernetes Agent](../../user/clusters/agent/index.md) supports the +[pull-based version](https://www.gitops.tech/#pull-based-deployments) of +[GitOps](https://www.gitops.tech/). To be useful, the feature must be able to perform these tasks: + +- Connect one or more Kubernetes clusters to a GitLab project or group. +- Synchronize cluster-wide state from a Git repository. +- Synchronize namespace-scoped state from a Git repository. +- Control the following settings: + + - The kinds of objects an agent can manage. + - Enabling the namespaced mode of operation for managing objects only in a specific namespace. + - Enabling the non-namespaced mode of operation for managing objects in any namespace, and + managing non-namespaced objects. + +- Synchronize state from one or more Git repositories into a cluster. +- Configure multiple agents running in different clusters to synchronize state + from the same repository. + +## GitOps architecture + +In this architecture, the Kubernetes cluster (`agentk`) periodically fetches +configuration from (`kas`), spawning a goroutine for each configured GitOps +repository. Each goroutine makes a streaming `GetObjectsToSynchronize()` gRPC call. +`kas` accepts these requests, then checks if this agent is authorized to access +this GitLab repository. If authorized, `kas` polls Gitaly for repository updates +and sends the latest manifests to the agent. + +Before each poll, `kas` verifies with GitLab that the agent's token is still valid. +When `agentk` receives an updated manifest, it performs a synchronization using +[`gitops-engine`](https://github.com/argoproj/gitops-engine). + +If a repository is removed from the list, `agentk` stops the `GetObjectsToSynchronize()` +calls to that repository. + +```mermaid +graph TB + agentk -- fetch configuration --> kas + agentk -- fetch GitOps manifests --> kas + + subgraph "GitLab" + kas[kas] + GitLabRoR[GitLab RoR] + Gitaly[Gitaly] + kas -- poll GitOps repositories --> Gitaly + kas -- authZ for agentk --> GitLabRoR + kas -- fetch configuration --> Gitaly + end + + subgraph "Kubernetes cluster" + agentk[agentk] + end +``` + +## Architecture considered but not implemented + +As part of the implementation process, this architecture was considered, but ultimately +not implemented. + +In this architecture, `agentk` periodically fetches configuration from `kas`. For each +configured GitOps repository, it spawns a goroutine. Each goroutine then spawns a +copy of [`git-sync`](https://github.com/kubernetes/git-sync). It polls a particular +repository and invokes a corresponding webhook on `agentk` when it changes. When that +happens, `agentk` performs a synchronization using +[`gitops-engine`](https://github.com/argoproj/gitops-engine). + +For repositories no longer in the list, `agentk` stops corresponding goroutines +and `git-sync` copies, also deleting their cloned repositories from disk: + +```mermaid +graph TB + agentk -- fetch configuration --> kas + git-sync -- poll GitOps repositories --> GitLabRoR + + subgraph "GitLab" + kas[kas] + GitLabRoR[GitLab RoR] + kas -- authZ for agentk --> GitLabRoR + kas -- fetch configuration --> Gitaly[Gitaly] + end + + subgraph "Kubernetes cluster" + agentk[agentk] + git-sync[git-sync] + agentk -- control --> git-sync + git-sync -- notify about changes --> agentk + end +``` + +## Comparing implemented and non-implemented architectures + +Both architectures attempt to answer the same question: how to grant an agent +access to a non-public repository? + +In the **implemented** architecture: + +- Favorable: Fewer moving parts, as `git-sync` and `git` are not used, making this + design more reliable. +- Favorable: Uses existing connectivity and authentication mechanisms are used (gRPC + `agentk` token). +- Favorable: No polling through external infrastructure. Saves traffic and avoids + noise in access logs. + +In the **unimplemented** architecture: + +- Favorable: `agentk` uses `git-sync` to access repositories with standard protocols + (either HTTPS, or SSH and Git) with accepted authentication and authorization methods. + + - Unfavorable: The user must put credentials into a `secret`. GitLab doesn't have + a mechanism for per-repository tokens for robots. + - Unfavorable: Rotating all credentials is more work than rotating a single `agentk` token. + +- Unfavorable: A dependency on an external component (`git-sync`) that can be avoided. +- Unfavorable: More network traffic and connections than the implemented design + +### Ideas considered for the unimplemented design + +As part of the design process, these ideas were considered, and discarded: + +- Running `git-sync` and `gitops-engine` as part of `kas`. + + - Favorable: More code and infrastructure under our control for GitLab.com + - Unfavorable: Running an arbitrary number of `git-sync` processes would require + an unbounded amount of RAM and disk space. + - Unfavorable: Unclear which `kas` replica is responsible for which agent and + repository synchronization. If done as part of `agentk`, leader election can be + done using [client-go](https://pkg.go.dev/k8s.io/client-go/tools/leaderelection?tab=doc). + +- Running `git-sync` and a "`gitops-engine` driver" helper program as a separate + Kubernetes `Deployment`. + + - Favorable: Better isolation and higher resiliency. For example, if the node + with `agentk` dies, not all synchronization stops. + - Favorable: Each deployment has its own memory and disk limits. + - Favorable: Per-repository synchronization identity (distinct `ServiceAccount`) + can be implemented. + - Unfavorable: Time consuming to implement properly: + + - Each `Deployment` needs CRUD (create, update, and delete) permissions. + - Users may want to customize a `Deployment`, or add and remove satellite objects + like `PodDisruptionBudget`, `HorizontalPodAutoscaler`, and `PodSecurityPolicy`. + - Metrics, monitoring, logs for the `Deployment`. diff --git a/doc/development/fe_guide/icons.md b/doc/development/fe_guide/icons.md index 9c68f4c88f9..cdfb9c2040f 100644 --- a/doc/development/fe_guide/icons.md +++ b/doc/development/fe_guide/icons.md @@ -48,8 +48,6 @@ sprite_icon(icon_name, size: nil, css_class: '') </svg> ``` -**Please note:** The `icon(icon_name, options: {})` helper function is deprecated and should not be used anymore. - ### Usage in Vue [GitLab UI](https://gitlab-org.gitlab.io/gitlab-ui/), our components library, provides a component to display sprite icons. diff --git a/doc/development/fe_guide/performance.md b/doc/development/fe_guide/performance.md index a28aae5c0c9..b5237e34f29 100644 --- a/doc/development/fe_guide/performance.md +++ b/doc/development/fe_guide/performance.md @@ -6,6 +6,200 @@ info: To determine the technical writer assigned to the Stage/Group associated w # Performance +Performance is an essential part and one of the main areas of concern for any modern application. + +## User Timing API + +[User Timing API](https://developer.mozilla.org/en-US/docs/Web/API/User_Timing_API) is a web API +[available in all modern browsers](https://caniuse.com/?search=User%20timing). It allows measuring +custom times and durations in your applications by placing special marks in your +code. You can use the User Timing API in GitLab to measure any timing, regardless of the framework, +including Rails, Vue, or vanilla JavaScript environments. For consistency and +convenience of adoption, GitLab offers several ways to enable custom user timing metrics in +your code. + +User Timing API introduces two important paradigms: `mark` and `measure`. + +**Mark** is the timestamp on the performance timeline. For example, +`performance.mark('my-component-start');` makes a browser note the time this code +is met. Then, you can obtain information about this mark by querying the global +performance object again. For example, in your DevTools console: + +```javascript +performance.getEntriesByName('my-component-start') +``` + +**Measure** is the duration between either: + +- Two marks +- The start of navigation and a mark +- The start of navigation and the moment the measurement is taken + +It takes several arguments of which the measurement’s name is the only one required. Examples: + +- Duration between the start and end marks: + + ```javascript + performance.measure('My component', 'my-component-start', 'my-component-end') + ``` + +- Duration between a mark and the moment the measurement is taken. The end mark is omitted in + this case. + + ```javascript + performance.measure('My component', 'my-component-start') + ``` + +- Duration between [the navigation start](https://developer.mozilla.org/en-US/docs/Web/API/Performance/timeOrigin) + and the moment the actual measurement is taken. + + ```javascript + performance.measure('My component') + ``` + +- Duration between [the navigation start](https://developer.mozilla.org/en-US/docs/Web/API/Performance/timeOrigin) + and a mark. You cannot omit the start mark in this case but you can set it to `undefined`. + + ```javascript + performance.measure('My component', undefined, 'my-component-end') + ``` + +To query a particular `measure`, You can use the same API, as for `mark`: + +```javascript +performance.getEntriesByName('My component') +``` + +You can also query for all captured marks and measurements: + +```javascript +performance.getEntriesByType('mark'); +performance.getEntriesByType('measure'); +``` + +Using `getEntriesByName()` or `getEntriesByType()` returns an Array of [the PerformanceMeasure +objects](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceMeasure) which contain +information about the measurement's start time and duration. + +### User Timing API utility + +You can use the `performanceMarkAndMeasure` utility anywhere in GitLab, as it's not tied to any +particular environment. + +`performanceMarkAndMeasure` takes an object as an argument, where: + +| Attribute | Type | Required | Description | +|:------------|:---------|:---------|:----------------------| +| `mark` | `String` | no | The name for the mark to set. Used for retrieving the mark later. If not specified, the mark is not set. | +| `measures` | `Array` | no | The list of the measurements to take at this point. | + +In return, the entries in the `measures` array are objects with the following API: + +| Attribute | Type | Required | Description | +|:------------|:---------|:---------|:----------------------| +| `name` | `String` | yes | The name for the measurement. Used for retrieving the mark later. Must be specified for every measure object, otherwise JavaScript fails. | +| `start` | `String` | no | The name of a mark **from** which the measurement should be taken. | +| `end` | `String` | no | The name of a mark **to** which the measurement should be taken. | + +Example: + +```javascript +import { performanceMarkAndMeasure } from '~/performance/utils'; +... +performanceMarkAndMeasure({ + mark: MR_DIFFS_MARK_DIFF_FILES_END, + measures: [ + { + name: MR_DIFFS_MEASURE_DIFF_FILES_DONE, + start: MR_DIFFS_MARK_DIFF_FILES_START, + end: MR_DIFFS_MARK_DIFF_FILES_END, + }, + ], +}); +``` + +### Vue performance plugin + +The plugin captures and measures the performance of the specified Vue components automatically +leveraging the Vue lifecycle and the User Timing API. + +To use the Vue performance plugin: + +1. Import the plugin: + + ```javascript + import PerformancePlugin from '~/performance/vue_performance_plugin'; + ``` + +1. Use it before initializing your Vue application: + + ```javascript + Vue.use(PerformancePlugin, { + components: [ + 'IdeTreeList', + 'FileTree', + 'RepoEditor', + ] + }); + ``` + +The plugin accepts the list of components, performance of which should be measured. The components +should be specified by their `name` option. + +You might need to explicitly set this option on the needed components, as +most components in the codebase don't have this option set: + +```javascript +export default { + name: 'IdeTreeList', + components: { + ... + ... +} +``` + +The plugin captures and stores the following: + +- The start **mark** for when the component has been initialized (in `beforeCreate()` hook) +- The end **mark** of the component when it has been rendered (next animation frame after `nextTick` + in `mounted()` hook). In most cases, this event does not wait for all sub-components to be + bootstrapped. To measure the sub-components, you should include those into the + plugin options. +- **Measure** duration between the two marks above. + +### Access stored measurements + +To access stored measurements, you can use either: + +- **Performance bar**. If you have it enabled (`P` + `B` key-combo), you can see the metrics + output in your DevTools console. +- **"Performance" tab** of the DevTools. You can get the measurements (not the marks, though) in + this tab when profiling performance. +- **DevTools console**. As mentioned above, you can query for the entries: + + ```javascript + performance.getEntriesByType('mark'); + performance.getEntriesByType('measure'); + ``` + +## Naming convention + +All the marks and measures should be instantiated with the constants from +`app/assets/javascripts/performance/constants.js`. When you’re ready to add a new mark’s or +measurement’s label, you can follow the pattern. + +NOTE: **Note:** +This pattern is a recommendation and not a hard rule. + +```javascript +app-*-start // for a start ‘mark’ +app-*-end // for an end ‘mark’ +app-* // for ‘measure’ +``` + +For example, `'webide-init-editor-start`, `mr-diffs-mark-file-tree-end`, and so on. We do it to +help identify marks and measures coming from the different apps on the same page. + ## Best Practices ### Realtime Components @@ -49,7 +243,7 @@ Only animate `opacity` & `transform` properties. Other properties (such as `top` Layout to be recalculated, which is much more expensive. For details on this, see "Styles that Affect Layout" in [High Performance Animations](https://www.html5rocks.com/en/tutorials/speed/high-performance-animations/). -If you _do_ need to change layout (e.g. a sidebar that pushes main content over), prefer [FLIP](https://aerotwist.com/blog/flip-your-animations/) to change expensive +If you _do_ need to change layout (for example, a sidebar that pushes main content over), prefer [FLIP](https://aerotwist.com/blog/flip-your-animations/) to change expensive properties once, and handle the actual animation with transforms. ## Reducing Asset Footprint @@ -198,7 +392,7 @@ data is used for users with capped data plans. General tips: - Don't add new fonts. -- Prefer font formats with better compression, e.g. WOFF2 is better than WOFF, which is better than TTF. +- Prefer font formats with better compression, for example, WOFF2 is better than WOFF, which is better than TTF. - Compress and minify assets wherever possible (For CSS/JS, Sprockets and webpack do this for us). - If some functionality can reasonably be achieved without adding extra libraries, avoid them. - Use page-specific JavaScript as described above to load libraries that are only needed on certain pages. diff --git a/doc/ssh/README.md b/doc/ssh/README.md index 7b0c5f5f16e..b26026f2dc2 100644 --- a/doc/ssh/README.md +++ b/doc/ssh/README.md @@ -215,7 +215,7 @@ Now you can copy the SSH key you created to your GitLab account. To do so, follo If you're using an RSA key, substitute accordingly. -1. Navigate to `https://gitlab.com` and sign in. +1. Navigate to `https://gitlab.com` or your local GitLab instance URL and sign in. 1. Select your avatar in the upper right corner, and click **Settings** 1. Click **SSH Keys**. 1. Paste the public key that you copied into the **Key** text box. diff --git a/doc/user/profile/personal_access_tokens.md b/doc/user/profile/personal_access_tokens.md index 5840e4a85c4..cfc70c5a6f0 100644 --- a/doc/user/profile/personal_access_tokens.md +++ b/doc/user/profile/personal_access_tokens.md @@ -14,7 +14,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w If you're unable to use [OAuth2](../../api/oauth2.md), you can use a personal access token to authenticate with the [GitLab API](../../api/README.md#personalproject-access-tokens). -You can also use personal access tokens with Git to authenticate over HTTP or SSH. Personal access tokens are required when [Two-Factor Authentication (2FA)](account/two_factor_authentication.md) is enabled. In both cases, you can authenticate with a token in place of your password. +You can also use personal access tokens with Git to authenticate over HTTP. Personal access tokens are required when [Two-Factor Authentication (2FA)](account/two_factor_authentication.md) is enabled. In both cases, you can authenticate with a token in place of your password. Personal access tokens expire on the date you define, at midnight UTC. diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md index c0c7eaf6ca1..0ba81692bf0 100644 --- a/doc/user/project/clusters/index.md +++ b/doc/user/project/clusters/index.md @@ -20,7 +20,7 @@ Using the GitLab project Kubernetes integration, you can: - Detect and [monitor Kubernetes](#monitoring-your-kubernetes-cluster). - Use it with [Auto DevOps](#auto-devops). - Use [Web terminals](#web-terminals). -- Use [Deploy Boards](#deploy-boards). **(PREMIUM)** +- Use [Deploy Boards](#deploy-boards). - Use [Canary Deployments](#canary-deployments). **(PREMIUM)** - Use [deployment variables](#deployment-variables). - Use [role-based or attribute-based access controls](add_remove_clusters.md#access-controls). @@ -316,7 +316,7 @@ the need to leave GitLab. [Read more about Canary Deployments](../canary_deployments.md) -#### Deploy Boards **(PREMIUM)** +#### Deploy Boards GitLab's Deploy Boards offer a consolidated view of the current health and status of each CI [environment](../../../ci/environments/index.md) running on Kubernetes, diff --git a/doc/user/project/deploy_boards.md b/doc/user/project/deploy_boards.md index 90bad9529d8..6673a746aa6 100644 --- a/doc/user/project/deploy_boards.md +++ b/doc/user/project/deploy_boards.md @@ -5,9 +5,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w type: howto, reference --- -# Deploy Boards **(PREMIUM)** +# Deploy Boards **(CORE)** -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/1589) in [GitLab Premium](https://about.gitlab.com/pricing/) 9.0. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/1589) in [GitLab Premium](https://about.gitlab.com/pricing/) 9.0. +> - [Moved](<https://gitlab.com/gitlab-org/gitlab/-/issues/212320>) to GitLab Core in 13.7. GitLab's Deploy Boards offer a consolidated view of the current health and status of each CI [environment](../../ci/environments/index.md) running on [Kubernetes](https://kubernetes.io), displaying the status diff --git a/doc/user/project/time_tracking.md b/doc/user/project/time_tracking.md index 33eccfda2c6..c94795578ef 100644 --- a/doc/user/project/time_tracking.md +++ b/doc/user/project/time_tracking.md @@ -49,7 +49,7 @@ you need to enter an estimate of 3 days, 5 hours and 10 minutes, you would write `/estimate 3d 5h 10m`. Time units that we support are listed at the bottom of this help page. -Every time you enter a new time estimate, any previous time estimates will be +Every time you enter a new time estimate, any previous time estimates are overridden by this new value. There should only be one valid estimate in an issue or a merge request. @@ -59,12 +59,12 @@ To remove an estimation entirely, use `/remove_estimate`. To enter a time spent, use `/spend 3d 5h 10m`. -Every new time spent entry will be added to the current total time spent for the +Every new time spent entry is added to the current total time spent for the issue or the merge request. -You can remove time by entering a negative amount: `/spend -3d` will remove 3 +You can remove time by entering a negative amount: for example, `/spend -3d` removes three days from the total time spent. You can't go below 0 minutes of time spent, -so GitLab will automatically reset the time spent if you remove a larger amount +so GitLab automatically resets the time spent if you remove a larger amount of time compared to the time that was entered already. To remove all the time spent at once, use `/remove_time_spent`. diff --git a/lib/gitlab/checks/push_check.rb b/lib/gitlab/checks/push_check.rb index 4ee53eea25a..0f0d8bb82ec 100644 --- a/lib/gitlab/checks/push_check.rb +++ b/lib/gitlab/checks/push_check.rb @@ -14,9 +14,17 @@ module Gitlab private def can_push? - user_access.can_push_to_branch?(ref) || + user_access_can_push? || project.branch_allows_collaboration?(user_access.user, branch_name) end + + def user_access_can_push? + if Feature.enabled?(:deploy_keys_on_protected_branches, project) + user_access.can_push_to_branch?(ref) + else + user_access.can_do_action?(:push_code) + end + end end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index cd34fa3beb3..27c5e2ef8a2 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -4609,9 +4609,6 @@ msgstr "" msgid "Branch not loaded - %{branchId}" msgstr "" -msgid "Branch prefix" -msgstr "" - msgid "BranchSwitcherPlaceholder|Search branches" msgstr "" @@ -14943,9 +14940,6 @@ msgstr "" msgid "Introducing Your DevOps Report" msgstr "" -msgid "Invalid Git ref" -msgstr "" - msgid "Invalid Insights config file detected" msgstr "" @@ -17876,9 +17870,6 @@ msgstr "" msgid "Mirror user" msgstr "" -msgid "Mirrored branches will have this prefix. If you enabled 'Only mirror protected branches' you need to include this prefix on protected branches in this project or nothing will be mirrored." -msgstr "" - msgid "Mirrored repositories" msgstr "" diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb index e4ac59cf5e0..6cd486bc84b 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb @@ -3,7 +3,7 @@ module QA RSpec.describe 'Manage', :orchestrated, :instance_saml do describe 'Instance wide SAML SSO' do - it 'User logs in to gitlab with SAML SSO', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/671' do + it 'user logs in to gitlab with SAML SSO', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/671' do Runtime::Browser.visit(:gitlab, Page::Main::Login) Page::Main::Login.perform(&:sign_in_with_saml) diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb index 8445fdafdd4..b74f27389a0 100644 --- a/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb @@ -3,7 +3,7 @@ module QA RSpec.describe 'Create', :smoke do describe 'Personal snippet creation' do - it 'User creates a personal snippet', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/840' do + it 'user creates a personal snippet', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/840' do Flow::Login.sign_in Page::Main::Menu.perform do |menu| diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb index 5293fe71873..8c44cd6d642 100644 --- a/qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb @@ -3,7 +3,7 @@ module QA RSpec.describe 'Create' do # to be converted to a smoke test once proved to be stable describe 'Project snippet creation' do - it 'User creates a project snippet', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/839' do + it 'user creates a project snippet', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/839' do Flow::Login.sign_in Resource::ProjectSnippet.fabricate_via_browser_ui! do |snippet| diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb index d76432f71b3..e96113c0133 100644 --- a/spec/controllers/projects/notes_controller_spec.rb +++ b/spec/controllers/projects/notes_controller_spec.rb @@ -426,7 +426,7 @@ RSpec.describe Projects::NotesController do let(:note_text) { "/award :thumbsup:\n/estimate 1d\n/spend 3h" } let(:extra_request_params) { { format: :json } } - it 'includes changes in commands_changes ' do + it 'includes changes in commands_changes' do create! expect(response).to have_gitlab_http_status(:ok) diff --git a/spec/db/production/settings_spec.rb b/spec/db/production/settings_spec.rb index f17720466c0..a7cd7201c89 100644 --- a/spec/db/production/settings_spec.rb +++ b/spec/db/production/settings_spec.rb @@ -31,7 +31,7 @@ RSpec.describe 'seed production settings' do stub_env('GITLAB_PROMETHEUS_METRICS_ENABLED', 'true') end - it 'prometheus_metrics_enabled is set to true ' do + it 'prometheus_metrics_enabled is set to true' do load(settings_file) expect(settings.prometheus_metrics_enabled).to eq(true) diff --git a/spec/features/abuse_report_spec.rb b/spec/features/abuse_report_spec.rb index 5959fcd6306..5fdd0816006 100644 --- a/spec/features/abuse_report_spec.rb +++ b/spec/features/abuse_report_spec.rb @@ -9,7 +9,7 @@ RSpec.describe 'Abuse reports' do sign_in(create(:user)) end - it 'Report abuse' do + it 'report abuse' do visit user_path(another_user) click_link 'Report abuse' diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb index 67a8d6ec601..cd136af8d69 100644 --- a/spec/features/admin/admin_appearance_spec.rb +++ b/spec/features/admin/admin_appearance_spec.rb @@ -6,7 +6,7 @@ RSpec.describe 'Admin Appearance' do let!(:appearance) { create(:appearance) } let(:admin) { create(:admin) } - it 'Create new appearance' do + it 'create new appearance' do sign_in(admin) gitlab_enable_admin_mode_sign_in(admin) visit admin_appearances_path @@ -27,7 +27,7 @@ RSpec.describe 'Admin Appearance' do expect(page).to have_content 'Last edit' end - it 'Preview sign-in page appearance' do + it 'preview sign-in page appearance' do sign_in(admin) gitlab_enable_admin_mode_sign_in(admin) @@ -37,7 +37,7 @@ RSpec.describe 'Admin Appearance' do expect_custom_sign_in_appearance(appearance) end - it 'Preview new project page appearance' do + it 'preview new project page appearance' do sign_in(admin) gitlab_enable_admin_mode_sign_in(admin) @@ -80,13 +80,13 @@ RSpec.describe 'Admin Appearance' do end end - it 'Custom sign-in page' do + it 'custom sign-in page' do visit new_user_session_path expect_custom_sign_in_appearance(appearance) end - it 'Custom new project page' do + it 'custom new project page' do sign_in(admin) gitlab_enable_admin_mode_sign_in(admin) visit new_project_path @@ -111,7 +111,7 @@ RSpec.describe 'Admin Appearance' do end end - it 'Appearance logo' do + it 'appearance logo' do sign_in(admin) gitlab_enable_admin_mode_sign_in(admin) visit admin_appearances_path @@ -124,7 +124,7 @@ RSpec.describe 'Admin Appearance' do expect(page).not_to have_css(logo_selector) end - it 'Header logos' do + it 'header logos' do sign_in(admin) gitlab_enable_admin_mode_sign_in(admin) visit admin_appearances_path diff --git a/spec/features/admin/admin_broadcast_messages_spec.rb b/spec/features/admin/admin_broadcast_messages_spec.rb index 586785272ae..476dd4469bc 100644 --- a/spec/features/admin/admin_broadcast_messages_spec.rb +++ b/spec/features/admin/admin_broadcast_messages_spec.rb @@ -11,7 +11,7 @@ RSpec.describe 'Admin Broadcast Messages' do visit admin_broadcast_messages_path end - it 'See broadcast messages list' do + it 'see broadcast messages list' do expect(page).to have_content 'Migration to new server' end @@ -44,7 +44,7 @@ RSpec.describe 'Admin Broadcast Messages' do expect(page).to have_selector 'strong', text: '4:00 CST to 5:00 CST' end - it 'Edit an existing broadcast message' do + it 'edit an existing broadcast message' do click_link 'Edit' fill_in 'broadcast_message_message', with: 'Application update RIGHT NOW' click_button 'Update broadcast message' @@ -53,7 +53,7 @@ RSpec.describe 'Admin Broadcast Messages' do expect(page).to have_content 'Application update RIGHT NOW' end - it 'Remove an existing broadcast message' do + it 'remove an existing broadcast message' do click_link 'Remove' expect(current_path).to eq admin_broadcast_messages_path diff --git a/spec/features/admin/admin_browse_spam_logs_spec.rb b/spec/features/admin/admin_browse_spam_logs_spec.rb index 9f63c8b5223..471a7e8f0ab 100644 --- a/spec/features/admin/admin_browse_spam_logs_spec.rb +++ b/spec/features/admin/admin_browse_spam_logs_spec.rb @@ -11,7 +11,7 @@ RSpec.describe 'Admin browse spam logs' do gitlab_enable_admin_mode_sign_in(admin) end - it 'Browse spam logs' do + it 'browse spam logs' do visit admin_spam_logs_path expect(page).to have_content('Spam Logs') diff --git a/spec/features/admin/admin_serverless_domains_spec.rb b/spec/features/admin/admin_serverless_domains_spec.rb index fc84df91854..0312e82e1ba 100644 --- a/spec/features/admin/admin_serverless_domains_spec.rb +++ b/spec/features/admin/admin_serverless_domains_spec.rb @@ -12,7 +12,7 @@ RSpec.describe 'Admin Serverless Domains', :js do gitlab_enable_admin_mode_sign_in(admin) end - it 'Add domain with certificate' do + it 'add domain with certificate' do visit admin_serverless_domains_path fill_in 'pages_domain[domain]', with: 'foo.com' @@ -32,7 +32,7 @@ RSpec.describe 'Admin Serverless Domains', :js do expect(page).to have_content '/CN=test-certificate' end - it 'Update domain certificate' do + it 'update domain certificate' do visit admin_serverless_domains_path fill_in 'pages_domain[domain]', with: 'foo.com' @@ -62,7 +62,7 @@ RSpec.describe 'Admin Serverless Domains', :js do context 'when domain exists' do let!(:domain) { create(:pages_domain, :instance_serverless) } - it 'Displays a modal when attempting to delete a domain' do + it 'displays a modal when attempting to delete a domain' do visit admin_serverless_domains_path click_button 'Delete domain' @@ -73,7 +73,7 @@ RSpec.describe 'Admin Serverless Domains', :js do end end - it 'Displays a modal with disabled button if unable to delete a domain' do + it 'displays a modal with disabled button if unable to delete a domain' do create(:serverless_domain_cluster, pages_domain: domain) visit admin_serverless_domains_path diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index 9d1e71092ae..06d31b544ea 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -24,7 +24,7 @@ RSpec.describe 'Admin updates settings' do visit general_admin_application_settings_path end - it 'Change visibility settings' do + it 'change visibility settings' do page.within('.as-visibility-access') do choose "application_setting_default_project_visibility_20" click_button 'Save changes' @@ -33,7 +33,7 @@ RSpec.describe 'Admin updates settings' do expect(page).to have_content "Application settings saved successfully" end - it 'Uncheck all restricted visibility levels' do + it 'uncheck all restricted visibility levels' do page.within('.as-visibility-access') do find('#application_setting_visibility_level_0').set(false) find('#application_setting_visibility_level_10').set(false) @@ -47,7 +47,7 @@ RSpec.describe 'Admin updates settings' do expect(find('#application_setting_visibility_level_20')).not_to be_checked end - it 'Modify import sources' do + it 'modify import sources' do expect(current_settings.import_sources).not_to be_empty page.within('.as-visibility-access') do @@ -70,7 +70,7 @@ RSpec.describe 'Admin updates settings' do expect(current_settings.import_sources).to eq(['git']) end - it 'Change Visibility and Access Controls' do + it 'change Visibility and Access Controls' do page.within('.as-visibility-access') do uncheck 'Project export enabled' click_button 'Save changes' @@ -80,7 +80,7 @@ RSpec.describe 'Admin updates settings' do expect(page).to have_content "Application settings saved successfully" end - it 'Change Keys settings' do + it 'change Keys settings' do page.within('.as-visibility-access') do select 'Are forbidden', from: 'RSA SSH keys' select 'Are allowed', from: 'DSA SSH keys' @@ -98,7 +98,7 @@ RSpec.describe 'Admin updates settings' do expect(find_field('ED25519 SSH keys').value).to eq(forbidden) end - it 'Change Account and Limit Settings' do + it 'change Account and Limit Settings' do page.within('.as-account-limit') do uncheck 'Gravatar enabled' click_button 'Save changes' @@ -108,7 +108,7 @@ RSpec.describe 'Admin updates settings' do expect(page).to have_content "Application settings saved successfully" end - it 'Change Maximum import size' do + it 'change Maximum import size' do page.within('.as-account-limit') do fill_in 'Maximum import size (MB)', with: 15 click_button 'Save changes' @@ -118,7 +118,7 @@ RSpec.describe 'Admin updates settings' do expect(page).to have_content "Application settings saved successfully" end - it 'Change New users set to external', :js do + it 'change New users set to external', :js do user_internal_regex = find('#application_setting_user_default_internal_regex', visible: :all) expect(user_internal_regex).to be_readonly @@ -144,7 +144,7 @@ RSpec.describe 'Admin updates settings' do end end - it 'Change Sign-in restrictions' do + it 'change Sign-in restrictions' do page.within('.as-signin') do fill_in 'Home page URL', with: 'https://about.gitlab.com/' click_button 'Save changes' @@ -154,7 +154,7 @@ RSpec.describe 'Admin updates settings' do expect(page).to have_content "Application settings saved successfully" end - it 'Terms of Service' do + it 'terms of Service' do # Already have the admin accept terms, so they don't need to accept in this spec. _existing_terms = create(:term) accept_terms(admin) @@ -170,7 +170,7 @@ RSpec.describe 'Admin updates settings' do expect(page).to have_content 'Application settings saved successfully' end - it 'Modify oauth providers' do + it 'modify oauth providers' do expect(current_settings.disabled_oauth_sign_in_sources).to be_empty page.within('.as-signin') do @@ -190,7 +190,7 @@ RSpec.describe 'Admin updates settings' do expect(current_settings.disabled_oauth_sign_in_sources).not_to include('google_oauth2') end - it 'Oauth providers do not raise validation errors when saving unrelated changes' do + it 'oauth providers do not raise validation errors when saving unrelated changes' do expect(current_settings.disabled_oauth_sign_in_sources).to be_empty page.within('.as-signin') do @@ -213,7 +213,7 @@ RSpec.describe 'Admin updates settings' do expect(current_settings.disabled_oauth_sign_in_sources).to include('google_oauth2') end - it 'Configure web terminal' do + it 'configure web terminal' do page.within('.as-terminal') do fill_in 'Max session time', with: 15 click_button 'Save changes' @@ -255,7 +255,7 @@ RSpec.describe 'Admin updates settings' do visit general_admin_application_settings_path end - it 'Enable hiding third party offers' do + it 'enable hiding third party offers' do page.within('.as-third-party-offers') do check 'Do not display offers from third parties within GitLab' click_button 'Save changes' @@ -265,7 +265,7 @@ RSpec.describe 'Admin updates settings' do expect(current_settings.hide_third_party_offers).to be true end - it 'Change Slack Notifications Service template settings', :js do + it 'change Slack Notifications Service template settings', :js do first(:link, 'Service Templates').click click_link 'Slack notifications' fill_in 'Webhook', with: 'http://localhost' @@ -315,7 +315,7 @@ RSpec.describe 'Admin updates settings' do end context 'CI/CD page' do - it 'Change CI/CD settings' do + it 'change CI/CD settings' do visit ci_cd_admin_application_settings_path page.within('.as-ci-cd') do @@ -380,7 +380,7 @@ RSpec.describe 'Admin updates settings' do end context 'Repository page' do - it 'Change Repository storage settings' do + it 'change Repository storage settings' do visit repository_admin_application_settings_path page.within('.as-repository-storage') do @@ -393,7 +393,7 @@ RSpec.describe 'Admin updates settings' do end context 'Reporting page' do - it 'Change Spam settings' do + it 'change Spam settings' do visit reporting_admin_application_settings_path page.within('.as-spam') do @@ -421,7 +421,7 @@ RSpec.describe 'Admin updates settings' do visit metrics_and_profiling_admin_application_settings_path end - it 'Change Prometheus settings' do + it 'change Prometheus settings' do page.within('.as-prometheus') do check 'Enable Prometheus Metrics' click_button 'Save changes' @@ -431,7 +431,7 @@ RSpec.describe 'Admin updates settings' do expect(page).to have_content "Application settings saved successfully" end - it 'Change Performance bar settings' do + it 'change Performance bar settings' do group = create(:group) page.within('.as-performance-bar') do @@ -474,7 +474,7 @@ RSpec.describe 'Admin updates settings' do end context 'Network page' do - it 'Changes Outbound requests settings' do + it 'changes Outbound requests settings' do visit network_admin_application_settings_path page.within('.as-outbound') do @@ -492,7 +492,7 @@ RSpec.describe 'Admin updates settings' do expect(current_settings.dns_rebinding_protection_enabled).to be false end - it 'Changes Issues rate limits settings' do + it 'changes Issues rate limits settings' do visit network_admin_application_settings_path page.within('.as-issue-limits') do @@ -510,7 +510,7 @@ RSpec.describe 'Admin updates settings' do visit preferences_admin_application_settings_path end - it 'Change Help page' do + it 'change Help page' do stub_feature_flags(help_page_documentation_redirect: true) new_support_url = 'http://example.com/help' @@ -531,7 +531,7 @@ RSpec.describe 'Admin updates settings' do expect(page).to have_content "Application settings saved successfully" end - it 'Change Pages settings' do + it 'change Pages settings' do page.within('.as-pages') do fill_in 'Maximum size of pages (MB)', with: 15 check 'Require users to prove ownership of custom domains' @@ -543,7 +543,7 @@ RSpec.describe 'Admin updates settings' do expect(page).to have_content "Application settings saved successfully" end - it 'Change Real-time features settings' do + it 'change Real-time features settings' do page.within('.as-realtime') do fill_in 'Polling interval multiplier', with: 5.0 click_button 'Save changes' @@ -564,7 +564,7 @@ RSpec.describe 'Admin updates settings' do .to have_content "The form contains the following error: Polling interval multiplier must be greater than or equal to 0" end - it "Change Pages Let's Encrypt settings" do + it "change Pages Let's Encrypt settings" do visit preferences_admin_application_settings_path page.within('.as-pages') do fill_in 'Email', with: 'my@test.example.com' @@ -578,7 +578,7 @@ RSpec.describe 'Admin updates settings' do end context 'Nav bar' do - it 'Shows default help links in nav' do + it 'shows default help links in nav' do default_support_url = 'https://about.gitlab.com/getting-help/' visit root_dashboard_path @@ -591,7 +591,7 @@ RSpec.describe 'Admin updates settings' do end end - it 'Shows custom support url in nav when set' do + it 'shows custom support url in nav when set' do new_support_url = 'http://example.com/help' stub_application_setting(help_page_support_url: new_support_url) diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb index 332c90df6d7..2af5b787a78 100644 --- a/spec/features/boards/sidebar_spec.rb +++ b/spec/features/boards/sidebar_spec.rb @@ -72,7 +72,7 @@ RSpec.describe 'Issue Boards', :js do end end - it 'removes card from board when clicking ' do + it 'removes card from board when clicking' do click_card(card) page.within('.issue-boards-sidebar') do diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index 97ee891dbb8..f8e84043c1b 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -125,7 +125,7 @@ RSpec.describe 'Commits' do visit pipeline_path(pipeline) end - it 'Renders header', :js do + it 'renders header', :js do expect(page).to have_content pipeline.sha[0..7] expect(page).to have_content pipeline.git_commit_message.gsub!(/\s+/, ' ') expect(page).to have_content pipeline.user.name diff --git a/spec/features/dashboard/shortcuts_spec.rb b/spec/features/dashboard/shortcuts_spec.rb index 58352518d43..b2fda28f0ec 100644 --- a/spec/features/dashboard/shortcuts_spec.rb +++ b/spec/features/dashboard/shortcuts_spec.rb @@ -13,7 +13,7 @@ RSpec.describe 'Dashboard shortcuts', :js do visit root_dashboard_path end - it 'Navigate to tabs' do + it 'navigate to tabs' do find('body').send_keys([:shift, 'I']) check_page_title('Issues') @@ -45,7 +45,7 @@ RSpec.describe 'Dashboard shortcuts', :js do visit explore_root_path end - it 'Navigate to tabs' do + it 'navigate to tabs' do find('body').send_keys([:shift, 'G']) find('.nothing-here-block') diff --git a/spec/features/groups/board_spec.rb b/spec/features/groups/board_spec.rb index 29d0347086c..b25aa26d906 100644 --- a/spec/features/groups/board_spec.rb +++ b/spec/features/groups/board_spec.rb @@ -16,7 +16,7 @@ RSpec.describe 'Group Boards' do visit group_boards_path(group) end - it 'Adds an issue to the backlog' do + it 'adds an issue to the backlog' do page.within(find('.board', match: :first)) do issue_title = 'New Issue' find(:css, '.issue-count-badge-add-button').click diff --git a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb index 0f0146a26a2..d773126e00c 100644 --- a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb +++ b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb @@ -105,7 +105,7 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j visit new_project_issue_path(project, merge_request_to_resolve_discussions_of: merge_request.iid) end - it 'Shows a notice to ask someone else to resolve the threads' do + it 'shows a notice to ask someone else to resolve the threads' do expect(page).to have_content("The threads at #{merge_request.to_reference} will stay unresolved. Ask someone with permission to resolve them.") end end diff --git a/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb b/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb index 841727d6120..99dc71f0559 100644 --- a/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb +++ b/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb @@ -82,7 +82,7 @@ RSpec.describe 'Resolve an open thread in a merge request by creating an issue', discussion_to_resolve: discussion.id) end - it 'Shows a notice to ask someone else to resolve the threads' do + it 'shows a notice to ask someone else to resolve the threads' do expect(page).to have_content("The thread at #{merge_request.to_reference}"\ " (discussion #{discussion.first_note.id}) will stay unresolved."\ " Ask someone with permission to resolve it.") diff --git a/spec/features/issues/issue_header_spec.rb b/spec/features/issues/issue_header_spec.rb index b596415e9e6..cf375d8fb67 100644 --- a/spec/features/issues/issue_header_spec.rb +++ b/spec/features/issues/issue_header_spec.rb @@ -92,7 +92,7 @@ RSpec.describe 'issue header', :js do click_button 'Issue actions' end - it 'has "Submit as spam" item ' do + it 'has "Submit as spam" item' do expect(page).to have_link 'Submit as spam' end end diff --git a/spec/features/markdown/mermaid_spec.rb b/spec/features/markdown/mermaid_spec.rb index 6df844277db..23cdd9d2ce5 100644 --- a/spec/features/markdown/mermaid_spec.rb +++ b/spec/features/markdown/mermaid_spec.rb @@ -48,7 +48,7 @@ RSpec.describe 'Mermaid rendering', :js do expect(page.html.scan(expected).count).to be(4) end - it 'renders only 2 Mermaid blocks and ', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/234081' do + it 'renders only 2 Mermaid blocks and', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/234081' do description = <<~MERMAID ```mermaid graph LR diff --git a/spec/features/milestone_spec.rb b/spec/features/milestone_spec.rb index dce76e4df6d..b9594293996 100644 --- a/spec/features/milestone_spec.rb +++ b/spec/features/milestone_spec.rb @@ -101,7 +101,7 @@ RSpec.describe 'Milestone' do end describe 'Deleting a milestone' do - it "The delete milestone button does not show for unauthorized users" do + it "the delete milestone button does not show for unauthorized users" do create(:milestone, project: project, title: 8.7) sign_out(user) diff --git a/spec/features/profiles/active_sessions_spec.rb b/spec/features/profiles/active_sessions_spec.rb index 45e71320ff2..fd64704b7c8 100644 --- a/spec/features/profiles/active_sessions_spec.rb +++ b/spec/features/profiles/active_sessions_spec.rb @@ -11,7 +11,7 @@ RSpec.describe 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do let(:admin) { create(:admin) } - it 'User sees their active sessions' do + it 'user sees their active sessions' do travel_to(Time.zone.parse('2018-03-12 09:06')) do Capybara::Session.new(:session1) Capybara::Session.new(:session2) @@ -82,7 +82,7 @@ RSpec.describe 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do end end - it 'User can revoke a session', :js do + it 'user can revoke a session', :js do Capybara::Session.new(:session1) Capybara::Session.new(:session2) diff --git a/spec/features/profiles/emails_spec.rb b/spec/features/profiles/emails_spec.rb index fc7de6d8b23..bdf1f8b022a 100644 --- a/spec/features/profiles/emails_spec.rb +++ b/spec/features/profiles/emails_spec.rb @@ -42,7 +42,7 @@ RSpec.describe 'Profile > Emails' do end end - it 'User removes email' do + it 'user removes email' do user.emails.create(email: 'my@email.com') visit profile_emails_path expect(page).to have_content("my@email.com") @@ -51,7 +51,7 @@ RSpec.describe 'Profile > Emails' do expect(page).not_to have_content("my@email.com") end - it 'User confirms email' do + it 'user confirms email' do email = user.emails.create(email: 'my@email.com') visit profile_emails_path expect(page).to have_content("#{email.email} Unverified") @@ -63,7 +63,7 @@ RSpec.describe 'Profile > Emails' do expect(page).to have_content("#{email.email} Verified") end - it 'User re-sends confirmation email' do + it 'user re-sends confirmation email' do email = user.emails.create(email: 'my@email.com') visit profile_emails_path diff --git a/spec/features/profiles/gpg_keys_spec.rb b/spec/features/profiles/gpg_keys_spec.rb index 18ed4e646b3..4eedeeac262 100644 --- a/spec/features/profiles/gpg_keys_spec.rb +++ b/spec/features/profiles/gpg_keys_spec.rb @@ -36,7 +36,7 @@ RSpec.describe 'Profile > GPG Keys' do end end - it 'User sees their key' do + it 'user sees their key' do create(:gpg_key, user: user, key: GpgHelpers::User2.public_key) visit profile_gpg_keys_path @@ -45,7 +45,7 @@ RSpec.describe 'Profile > GPG Keys' do expect(page).to have_content(GpgHelpers::User2.fingerprint) end - it 'User removes a key via the key index' do + it 'user removes a key via the key index' do create(:gpg_key, user: user, key: GpgHelpers::User2.public_key) visit profile_gpg_keys_path @@ -54,7 +54,7 @@ RSpec.describe 'Profile > GPG Keys' do expect(page).to have_content('Your GPG keys (0)') end - it 'User revokes a key via the key index' do + it 'user revokes a key via the key index' do gpg_key = create :gpg_key, user: user, key: GpgHelpers::User2.public_key gpg_signature = create :gpg_signature, gpg_key: gpg_key, verification_status: :verified diff --git a/spec/features/profiles/keys_spec.rb b/spec/features/profiles/keys_spec.rb index 23bbe9c1587..c1e2d19ad9a 100644 --- a/spec/features/profiles/keys_spec.rb +++ b/spec/features/profiles/keys_spec.rb @@ -64,7 +64,7 @@ RSpec.describe 'Profile > SSH Keys' do end end - it 'User sees their keys' do + it 'user sees their keys' do key = create(:key, user: user) visit profile_keys_path diff --git a/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb b/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb index a5b7b1fba9d..1b6215c1308 100644 --- a/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb +++ b/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb @@ -9,7 +9,7 @@ RSpec.describe 'Profile > Notifications > User changes notified_of_own_activity sign_in(user) end - it 'User opts into receiving notifications about their own activity' do + it 'user opts into receiving notifications about their own activity' do visit profile_notifications_path expect(page).not_to have_checked_field('user[notified_of_own_activity]') @@ -20,7 +20,7 @@ RSpec.describe 'Profile > Notifications > User changes notified_of_own_activity expect(page).to have_checked_field('user[notified_of_own_activity]') end - it 'User opts out of receiving notifications about their own activity' do + it 'user opts out of receiving notifications about their own activity' do user.update!(notified_of_own_activity: true) visit profile_notifications_path diff --git a/spec/features/projects/clusters_spec.rb b/spec/features/projects/clusters_spec.rb index 6c6e65005f6..6da66989b09 100644 --- a/spec/features/projects/clusters_spec.rb +++ b/spec/features/projects/clusters_spec.rb @@ -110,7 +110,7 @@ RSpec.describe 'Clusters', :js do visit project_clusters_path(project) end - it 'user sees a add cluster button ' do + it 'user sees a add cluster button' do expect(page).to have_selector('.js-add-cluster:not(.readonly)') end diff --git a/spec/features/projects/labels/issues_sorted_by_priority_spec.rb b/spec/features/projects/labels/issues_sorted_by_priority_spec.rb index 85a08c441ca..0a373b0d51a 100644 --- a/spec/features/projects/labels/issues_sorted_by_priority_spec.rb +++ b/spec/features/projects/labels/issues_sorted_by_priority_spec.rb @@ -15,7 +15,7 @@ RSpec.describe 'Issue prioritization' do # According to https://gitlab.com/gitlab-org/gitlab-foss/issues/14189#note_4360653 context 'when issues have one label', :js do - it 'Are sorted properly' do + it 'are sorted properly' do # Issues issue_1 = create(:issue, title: 'issue_1', project: project) issue_2 = create(:issue, title: 'issue_2', project: project) @@ -45,7 +45,7 @@ RSpec.describe 'Issue prioritization' do end context 'when issues have multiple labels', :js do - it 'Are sorted properly' do + it 'are sorted properly' do # Issues issue_1 = create(:issue, title: 'issue_1', project: project) issue_2 = create(:issue, title: 'issue_2', project: project) diff --git a/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb b/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb index ca8e4fdac57..9b51e867156 100644 --- a/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb +++ b/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb @@ -22,7 +22,7 @@ RSpec.describe 'Projects > Show > User sees setup shortcut buttons' do visit project_path(project) end - it 'Project buttons are not visible' do + it 'project buttons are not visible' do visit project_path(project) page.within('.project-buttons') do diff --git a/spec/features/snippets/private_snippets_spec.rb b/spec/features/snippets/private_snippets_spec.rb index 03745c1025a..7ff27419cf7 100644 --- a/spec/features/snippets/private_snippets_spec.rb +++ b/spec/features/snippets/private_snippets_spec.rb @@ -11,7 +11,7 @@ RSpec.describe 'Private Snippets', :js do sign_in(user) end - it 'Private Snippet renders for creator' do + it 'private Snippet renders for creator' do visit snippet_path(private_snippet) wait_for_requests diff --git a/spec/features/snippets/public_snippets_spec.rb b/spec/features/snippets/public_snippets_spec.rb index d2dc85a9614..0f27d96d8e9 100644 --- a/spec/features/snippets/public_snippets_spec.rb +++ b/spec/features/snippets/public_snippets_spec.rb @@ -6,7 +6,7 @@ RSpec.describe 'Public Snippets', :js do let(:public_snippet) { create(:personal_snippet, :public, :repository) } let(:content) { public_snippet.blobs.first.data.strip! } - it 'Unauthenticated user should see public snippets' do + it 'unauthenticated user should see public snippets' do url = Gitlab::UrlBuilder.build(public_snippet) visit snippet_path(public_snippet) @@ -18,7 +18,7 @@ RSpec.describe 'Public Snippets', :js do expect(page).to have_field('Share', readonly: true, with: url) end - it 'Unauthenticated user should see raw public snippets' do + it 'unauthenticated user should see raw public snippets' do visit raw_snippet_path(public_snippet) expect(page).to have_content(content) diff --git a/spec/features/snippets/search_snippets_spec.rb b/spec/features/snippets/search_snippets_spec.rb index 4f299edc9da..46bc3b7caad 100644 --- a/spec/features/snippets/search_snippets_spec.rb +++ b/spec/features/snippets/search_snippets_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe 'Search Snippets' do - it 'User searches for snippets by title' do + it 'user searches for snippets by title' do public_snippet = create(:personal_snippet, :public, title: 'Beginning and Middle') private_snippet = create(:personal_snippet, :private, title: 'Middle and End') diff --git a/spec/features/snippets/user_creates_snippet_spec.rb b/spec/features/snippets/user_creates_snippet_spec.rb index 1e51210c2b8..ca050daa62a 100644 --- a/spec/features/snippets/user_creates_snippet_spec.rb +++ b/spec/features/snippets/user_creates_snippet_spec.rb @@ -25,7 +25,7 @@ RSpec.describe 'User creates snippet', :js do snippet_fill_in_form(title: title, content: file_content, description: md_description) end - it 'Authenticated user creates a snippet' do + it 'authenticated user creates a snippet' do fill_form click_button('Create snippet') diff --git a/spec/features/snippets/user_snippets_spec.rb b/spec/features/snippets/user_snippets_spec.rb index a313dc3b26a..fe39208213a 100644 --- a/spec/features/snippets/user_snippets_spec.rb +++ b/spec/features/snippets/user_snippets_spec.rb @@ -13,13 +13,13 @@ RSpec.describe 'User Snippets' do visit dashboard_snippets_path end - it 'View all of my snippets' do + it 'view all of my snippets' do expect(page).to have_link(public_snippet.title, href: snippet_path(public_snippet)) expect(page).to have_link(internal_snippet.title, href: snippet_path(internal_snippet)) expect(page).to have_link(private_snippet.title, href: snippet_path(private_snippet)) end - it 'View my public snippets' do + it 'view my public snippets' do page.within('.snippet-scope-menu') do click_link "Public" end @@ -29,7 +29,7 @@ RSpec.describe 'User Snippets' do expect(page).not_to have_content(private_snippet.title) end - it 'View my internal snippets' do + it 'view my internal snippets' do page.within('.snippet-scope-menu') do click_link "Internal" end @@ -39,7 +39,7 @@ RSpec.describe 'User Snippets' do expect(page).not_to have_content(private_snippet.title) end - it 'View my private snippets' do + it 'view my private snippets' do page.within('.snippet-scope-menu') do click_link "Private" end diff --git a/spec/features/users/active_sessions_spec.rb b/spec/features/users/active_sessions_spec.rb index 8e2e16e555e..fab9f0884ae 100644 --- a/spec/features/users/active_sessions_spec.rb +++ b/spec/features/users/active_sessions_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe 'Active user sessions', :clean_gitlab_redis_shared_state do - it 'Successful login adds a new active user login' do + it 'successful login adds a new active user login' do now = Time.zone.parse('2018-03-12 09:06') Timecop.freeze(now) do user = create(:user) @@ -26,7 +26,7 @@ RSpec.describe 'Active user sessions', :clean_gitlab_redis_shared_state do end end - it 'Successful login cleans up obsolete entries' do + it 'successful login cleans up obsolete entries' do user = create(:user) Gitlab::Redis::SharedState.with do |redis| @@ -40,7 +40,7 @@ RSpec.describe 'Active user sessions', :clean_gitlab_redis_shared_state do end end - it 'Sessionless login does not clean up obsolete entries' do + it 'sessionless login does not clean up obsolete entries' do user = create(:user) personal_access_token = create(:personal_access_token, user: user) @@ -56,7 +56,7 @@ RSpec.describe 'Active user sessions', :clean_gitlab_redis_shared_state do end end - it 'Logout deletes the active user login' do + it 'logout deletes the active user login' do user = create(:user) gitlab_sign_in(user) expect(current_path).to eq root_path diff --git a/spec/graphql/mutations/boards/issues/issue_move_list_spec.rb b/spec/graphql/mutations/boards/issues/issue_move_list_spec.rb index ded5776b3cb..24104a20465 100644 --- a/spec/graphql/mutations/boards/issues/issue_move_list_spec.rb +++ b/spec/graphql/mutations/boards/issues/issue_move_list_spec.rb @@ -73,18 +73,6 @@ RSpec.describe Mutations::Boards::Issues::IssueMoveList do it_behaves_like 'raises a resource not available error' end - - context 'when user cannot access board' do - let(:board) { create(:board, group: create(:group, :private)) } - - it_behaves_like 'raises a resource not available error' - end - - context 'when passing board_id as nil' do - let(:board) { nil } - - it_behaves_like 'raises a resource not available error' - end end end end diff --git a/spec/graphql/resolvers/board_lists_resolver_spec.rb b/spec/graphql/resolvers/board_lists_resolver_spec.rb index c1d8041a1e0..71ebec4dc7e 100644 --- a/spec/graphql/resolvers/board_lists_resolver_spec.rb +++ b/spec/graphql/resolvers/board_lists_resolver_spec.rb @@ -29,9 +29,7 @@ RSpec.describe Resolvers::BoardListsResolver do context 'with unauthorized user' do it 'raises an error' do - expect do - resolve_board_lists(current_user: unauth_user) - end.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + expect(resolve_board_lists(current_user: unauth_user)).to be_nil end end @@ -101,12 +99,6 @@ RSpec.describe Resolvers::BoardListsResolver do end def resolve_board_lists(args: {}, current_user: user) - context = GraphQL::Query::Context.new( - query: OpenStruct.new(schema: nil), - values: { current_user: current_user }, - object: nil - ) - - resolve(described_class, obj: board, args: args, ctx: context ) + resolve(described_class, obj: board, args: args, ctx: { current_user: current_user }) end end diff --git a/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb b/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb index deb5ff584cf..84ef906b72f 100644 --- a/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb +++ b/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb @@ -24,7 +24,7 @@ RSpec.describe Resolvers::MergeRequestPipelinesResolver do end def resolve_pipelines - resolve(described_class, obj: merge_request, ctx: { current_user: current_user }) + sync(resolve(described_class, obj: merge_request, ctx: { current_user: current_user })) end it 'resolves only MRs for the passed merge request' do diff --git a/spec/graphql/resolvers/snippets/blobs_resolver_spec.rb b/spec/graphql/resolvers/snippets/blobs_resolver_spec.rb index 16e69f662c0..ebe286769cf 100644 --- a/spec/graphql/resolvers/snippets/blobs_resolver_spec.rb +++ b/spec/graphql/resolvers/snippets/blobs_resolver_spec.rb @@ -16,16 +16,18 @@ RSpec.describe Resolvers::Snippets::BlobsResolver do context 'when user is not authorized' do let(:other_user) { create(:user) } - it 'raises an error' do - expect do - resolve_blobs(snippet, user: other_user) - end.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + it 'redacts the field' do + expect(resolve_blobs(snippet, user: other_user)).to be_nil end end context 'when using no filter' do it 'returns all snippet blobs' do - expect(resolve_blobs(snippet).map(&:path)).to contain_exactly(*snippet.list_files) + result = resolve_blobs(snippet, args: {}) + + expect(result).to match_array(snippet.list_files.map do |file| + have_attributes(path: file) + end) end end @@ -34,7 +36,13 @@ RSpec.describe Resolvers::Snippets::BlobsResolver do it 'returns an array of files' do path = 'CHANGELOG' - expect(resolve_blobs(snippet, args: { paths: path }).first.path).to eq(path) + expect(resolve_blobs(snippet, paths: [path])).to contain_exactly(have_attributes(path: path)) + end + end + + context 'the argument does not match anything' do + it 'returns an empty result' do + expect(resolve_blobs(snippet, paths: ['does not exist'])).to be_empty end end @@ -42,13 +50,15 @@ RSpec.describe Resolvers::Snippets::BlobsResolver do it 'returns an array of files' do paths = ['CHANGELOG', 'README.md'] - expect(resolve_blobs(snippet, args: { paths: paths }).map(&:path)).to contain_exactly(*paths) + expect(resolve_blobs(snippet, paths: paths)).to match_array(paths.map do |file| + have_attributes(path: file) + end) end end end end - def resolve_blobs(snippet, user: current_user, args: {}) + def resolve_blobs(snippet, user: current_user, paths: [], args: { paths: paths }) resolve(described_class, args: args, ctx: { current_user: user }, obj: snippet) end end diff --git a/spec/graphql/types/ci/pipeline_type_spec.rb b/spec/graphql/types/ci/pipeline_type_spec.rb index f13f1c9afb2..d435e337ad7 100644 --- a/spec/graphql/types/ci/pipeline_type_spec.rb +++ b/spec/graphql/types/ci/pipeline_type_spec.rb @@ -6,4 +6,19 @@ RSpec.describe Types::Ci::PipelineType do specify { expect(described_class.graphql_name).to eq('Pipeline') } specify { expect(described_class).to expose_permissions_using(Types::PermissionTypes::Ci::Pipeline) } + + it 'contains attributes related to a pipeline' do + expected_fields = %w[ + id iid sha before_sha status detailed_status config_source duration + coverage created_at updated_at started_at finished_at committed_at + stages user retryable cancelable jobs source_job downstream + upstream path project active user_permissions + ] + + if Gitlab.ee? + expected_fields << 'security_report_summary' + end + + expect(described_class).to have_graphql_fields(*expected_fields) + end end diff --git a/spec/graphql/types/commit_type_spec.rb b/spec/graphql/types/commit_type_spec.rb index e9bc7f6bb94..b43693e5804 100644 --- a/spec/graphql/types/commit_type_spec.rb +++ b/spec/graphql/types/commit_type_spec.rb @@ -9,7 +9,7 @@ RSpec.describe GitlabSchema.types['Commit'] do it 'contains attributes related to commit' do expect(described_class).to have_graphql_fields( - :id, :sha, :title, :description, :description_html, :message, :title_html, :authored_date, + :id, :sha, :short_id, :title, :description, :description_html, :message, :title_html, :authored_date, :author_name, :author_gravatar, :author, :web_url, :web_path, :pipelines, :signature_html ) diff --git a/spec/graphql/types/merge_request_type_spec.rb b/spec/graphql/types/merge_request_type_spec.rb index c929a93a9eb..68387bbc82e 100644 --- a/spec/graphql/types/merge_request_type_spec.rb +++ b/spec/graphql/types/merge_request_type_spec.rb @@ -28,17 +28,26 @@ RSpec.describe GitlabSchema.types['MergeRequest'] do milestone assignees participants subscribed labels discussion_locked time_estimate total_time_spent reference author merged_at commit_count current_user_todos conflicts auto_merge_enabled approved_by source_branch_protected + default_merge_commit_message_with_description squash_on_merge available_auto_merge_strategies + has_ci mergeable commits_without_merge_commits ] if Gitlab.ee? expected_fields << 'approved' expected_fields << 'approvals_left' expected_fields << 'approvals_required' + expected_fields << 'merge_trains_count' end expect(described_class).to have_graphql_fields(*expected_fields) end + describe '#pipelines' do + subject { described_class.fields['pipelines'] } + + it { is_expected.to have_attributes(max_page_size: 500) } + end + describe '#diff_stats_summary' do subject { GitlabSchema.execute(query, context: { current_user: current_user }).as_json } diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb index ff99dd77233..3fcdf910e98 100644 --- a/spec/graphql/types/project_type_spec.rb +++ b/spec/graphql/types/project_type_spec.rb @@ -31,7 +31,7 @@ RSpec.describe GitlabSchema.types['Project'] do container_expiration_policy service_desk_enabled service_desk_address issue_status_counts terraform_states alert_management_integrations container_repositories container_repositories_count - pipeline_analytics total_pipeline_duration + pipeline_analytics total_pipeline_duration squash_read_only ] expect(described_class).to include_graphql_fields(*expected_fields) diff --git a/spec/helpers/clusters_helper_spec.rb b/spec/helpers/clusters_helper_spec.rb index 6b08b6515cf..8c738141063 100644 --- a/spec/helpers/clusters_helper_spec.rb +++ b/spec/helpers/clusters_helper_spec.rb @@ -131,7 +131,7 @@ RSpec.describe ClustersHelper do context 'other values' do let(:cluster_type) { 'not_supported' } - it 'Diplays generic cluster and reports error' do + it 'diplays generic cluster and reports error' do expect(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception).with( an_instance_of(ArgumentError), cluster_error: { error: 'Cluster Type Missing', cluster_type: 'not_supported' } diff --git a/spec/helpers/emails_helper_spec.rb b/spec/helpers/emails_helper_spec.rb index ef8b342a3f6..58ed5901d45 100644 --- a/spec/helpers/emails_helper_spec.rb +++ b/spec/helpers/emails_helper_spec.rb @@ -296,7 +296,7 @@ RSpec.describe EmailsHelper do end with_them do - it 'Produces the right List-Id' do + it 'produces the right List-Id' do project = double("project") allow(project).to receive(:full_path).and_return(full_path) allow(project).to receive(:id).and_return(12345) diff --git a/spec/helpers/icons_helper_spec.rb b/spec/helpers/icons_helper_spec.rb index 357ca0afe46..4784d0aff26 100644 --- a/spec/helpers/icons_helper_spec.rb +++ b/spec/helpers/icons_helper_spec.rb @@ -5,21 +5,6 @@ require 'spec_helper' RSpec.describe IconsHelper do let(:icons_path) { ActionController::Base.helpers.image_path("icons.svg") } - describe 'icon' do - it 'returns aria-hidden by default' do - star = icon('star') - - expect(star['aria-hidden']).to eq 'aria-hidden' - end - - it 'does not return aria-hidden if aria-label is set' do - up = icon('up', 'aria-label' => 'up') - - expect(up['aria-hidden']).to be_nil - expect(up['aria-label']).to eq 'aria-label' - end - end - describe 'sprite_icon_path' do it 'returns relative path' do expect(sprite_icon_path).to eq(icons_path) diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb index 5b7dcd46d27..57845904d32 100644 --- a/spec/helpers/issuables_helper_spec.rb +++ b/spec/helpers/issuables_helper_spec.rb @@ -218,7 +218,7 @@ RSpec.describe IssuablesHelper do assign(:project, issue.project) end - it 'sets sentryIssueIdentifier to nil with no sentry issue ' do + it 'sets sentryIssueIdentifier to nil with no sentry issue' do expect(helper.issuable_initial_data(issue)[:sentryIssueIdentifier]) .to be_nil end diff --git a/spec/lib/banzai/pipeline/jira_import/adf_commonmark_pipeline_spec.rb b/spec/lib/banzai/pipeline/jira_import/adf_commonmark_pipeline_spec.rb index d8841a9753e..74005adf673 100644 --- a/spec/lib/banzai/pipeline/jira_import/adf_commonmark_pipeline_spec.rb +++ b/spec/lib/banzai/pipeline/jira_import/adf_commonmark_pipeline_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe Banzai::Pipeline::JiraImport::AdfCommonmarkPipeline do let_it_be(:fixtures_path) { 'lib/kramdown/atlassian_document_format' } - it 'converts text in Atlassian Document Format ' do + it 'converts text in Atlassian Document Format' do source = fixture_file(File.join(fixtures_path, 'paragraph.json')) target = fixture_file(File.join(fixtures_path, 'paragraph.md')) output = described_class.call(source, {})[:output] diff --git a/spec/lib/gitlab/database/postgres_index_spec.rb b/spec/lib/gitlab/database/postgres_index_spec.rb index d65b638f7bc..cf6e9c80176 100644 --- a/spec/lib/gitlab/database/postgres_index_spec.rb +++ b/spec/lib/gitlab/database/postgres_index_spec.rb @@ -27,7 +27,7 @@ RSpec.describe Gitlab::Database::PostgresIndex do expect(described_class.regular).to all(have_attributes(unique: false)) end - it 'only non partitioned indexes ' do + it 'only non partitioned indexes' do expect(described_class.regular).to all(have_attributes(partitioned: false)) end diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 626798199a2..c917945499c 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -929,7 +929,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do end context 'with max_count' do - it 'returns the number of commits with path ' do + it 'returns the number of commits with path' do options = { ref: 'master', max_count: 5 } expect(repository.count_commits(options)).to eq(5) @@ -937,7 +937,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do end context 'with path' do - it 'returns the number of commits with path ' do + it 'returns the number of commits with path' do options = { ref: 'master', path: 'encoding' } expect(repository.count_commits(options)).to eq(2) @@ -965,7 +965,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do end context 'with max_count' do - it 'returns the number of commits with path ' do + it 'returns the number of commits with path' do options = { from: 'fix-mode', to: 'fix-blob-path', left_right: true, max_count: 1 } expect(repository.count_commits(options)).to eq([1, 1]) diff --git a/spec/lib/gitlab/graphql/timeout_spec.rb b/spec/lib/gitlab/graphql/timeout_spec.rb index 3669a89ba7c..999840019d2 100644 --- a/spec/lib/gitlab/graphql/timeout_spec.rb +++ b/spec/lib/gitlab/graphql/timeout_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Gitlab::Graphql::Timeout do - it 'inherits from ' do + it 'inherits from' do expect(described_class.superclass).to eq GraphQL::Schema::Timeout end diff --git a/spec/lib/gitlab/import_export/json/ndjson_writer_spec.rb b/spec/lib/gitlab/import_export/json/ndjson_writer_spec.rb index 0af74dee604..2a5e802bdc5 100644 --- a/spec/lib/gitlab/import_export/json/ndjson_writer_spec.rb +++ b/spec/lib/gitlab/import_export/json/ndjson_writer_spec.rb @@ -25,7 +25,7 @@ RSpec.describe Gitlab::ImportExport::JSON::NdjsonWriter do describe "#write_relation" do context "when single relation is serialized" do - it "appends json in correct file " do + it "appends json in correct file" do relation = "relation" value = { "key" => "value_1", "key_1" => "value_1" } subject.write_relation(exportable_path, relation, value) diff --git a/spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb index a112655ab49..82db3d94493 100644 --- a/spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb +++ b/spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb @@ -86,7 +86,7 @@ RSpec.describe Gitlab::UsageDataCounters::EditorUniqueCounter, :clean_gitlab_red end end - it 'can return the count of actions per user deduplicated ' do + it 'can return the count of actions per user deduplicated' do described_class.track_web_ide_edit_action(author: user1) described_class.track_snippet_editor_edit_action(author: user1) described_class.track_sfe_edit_action(author: user1) diff --git a/spec/migrations/ensure_namespace_settings_creation_spec.rb b/spec/migrations/ensure_namespace_settings_creation_spec.rb index 8574063f7fe..cececc1e569 100644 --- a/spec/migrations/ensure_namespace_settings_creation_spec.rb +++ b/spec/migrations/ensure_namespace_settings_creation_spec.rb @@ -23,7 +23,7 @@ RSpec.describe EnsureNamespaceSettingsCreation do end end - it 'schedules migrations in batches ' do + it 'schedules migrations in batches' do stub_const("#{described_class.name}::BATCH_SIZE", 2) namespace_3 = namespaces.create!(name: 'gitlab', path: 'gitlab-org3') diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb index dce7b1d30ca..75ed5939724 100644 --- a/spec/models/ci/build_trace_chunk_spec.rb +++ b/spec/models/ci/build_trace_chunk_spec.rb @@ -91,7 +91,7 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do end describe 'CHUNK_SIZE' do - it 'Chunk size can not be changed without special care' do + it 'chunk size can not be changed without special care' do expect(described_class::CHUNK_SIZE).to eq(128.kilobytes) end end diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 7c6b9b2e671..6183edb7828 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -2954,7 +2954,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do stub_feature_flags(ci_store_pipeline_messages: false) end - it ' does not add pipeline error message' do + it 'does not add pipeline error message' do pipeline.add_error_message('The error message') expect(pipeline.messages).to be_empty diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index a3417ee5fc7..6e87ca6dcf7 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -357,7 +357,7 @@ RSpec.describe Note do describe '#confidential?' do context 'when note is not confidential' do context 'when include_noteable is set to true' do - it 'is true when a noteable is confidential ' do + it 'is true when a noteable is confidential' do issue = create(:issue, :confidential) note = build(:note, noteable: issue, project: issue.project) @@ -366,7 +366,7 @@ RSpec.describe Note do end context 'when include_noteable is not set to true' do - it 'is false when a noteable is confidential ' do + it 'is false when a noteable is confidential' do issue = create(:issue, :confidential) note = build(:note, noteable: issue, project: issue.project) diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 1b19eb49374..df556f04c7a 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1087,7 +1087,7 @@ RSpec.describe User do @user.update!(email: 'new_primary@example.com') end - it 'adds old primary to secondary emails when secondary is a new email ' do + it 'adds old primary to secondary emails when secondary is a new email' do @user.update!(email: 'new_primary@example.com') @user.reload diff --git a/spec/presenters/snippet_presenter_spec.rb b/spec/presenters/snippet_presenter_spec.rb index 66c6ba8fa0e..a1d987ed78f 100644 --- a/spec/presenters/snippet_presenter_spec.rb +++ b/spec/presenters/snippet_presenter_spec.rb @@ -72,7 +72,7 @@ RSpec.describe SnippetPresenter do context 'with ProjectSnippet' do let(:snippet) { project_snippet } - it 'checks read_snippet ' do + it 'checks read_snippet' do expect(presenter).to receive(:can?).with(user, :read_snippet, snippet) subject @@ -96,7 +96,7 @@ RSpec.describe SnippetPresenter do context 'with ProjectSnippet' do let(:snippet) { project_snippet } - it 'checks update_snippet ' do + it 'checks update_snippet' do expect(presenter).to receive(:can?).with(user, :update_snippet, snippet) subject @@ -120,7 +120,7 @@ RSpec.describe SnippetPresenter do context 'with ProjectSnippet' do let(:snippet) { project_snippet } - it 'checks admin_snippet ' do + it 'checks admin_snippet' do expect(presenter).to receive(:can?).with(user, :admin_snippet, snippet) subject diff --git a/spec/requests/api/broadcast_messages_spec.rb b/spec/requests/api/broadcast_messages_spec.rb index b5b6ce106e5..b023ec398a2 100644 --- a/spec/requests/api/broadcast_messages_spec.rb +++ b/spec/requests/api/broadcast_messages_spec.rb @@ -112,7 +112,7 @@ RSpec.describe API::BroadcastMessages do expect(response).to have_gitlab_http_status(:bad_request) end - it 'accepts an active dismissable value ' do + it 'accepts an active dismissable value' do attrs = { message: 'new message', dismissable: true } post api('/broadcast_messages', admin), params: attrs @@ -197,7 +197,7 @@ RSpec.describe API::BroadcastMessages do expect(response).to have_gitlab_http_status(:bad_request) end - it 'accepts a new dismissable value ' do + it 'accepts a new dismissable value' do attrs = { message: 'new message', dismissable: true } put api("/broadcast_messages/#{message.id}", admin), params: attrs diff --git a/spec/requests/api/ci/runner/jobs_trace_spec.rb b/spec/requests/api/ci/runner/jobs_trace_spec.rb index 1980c1a9f51..5b7a33d23d8 100644 --- a/spec/requests/api/ci/runner/jobs_trace_spec.rb +++ b/spec/requests/api/ci/runner/jobs_trace_spec.rb @@ -135,7 +135,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do patch_the_trace end - it 'returns Forbidden ' do + it 'returns Forbidden' do expect(response).to have_gitlab_http_status(:forbidden) end end diff --git a/spec/requests/api/graphql/issue/issue_spec.rb b/spec/requests/api/graphql/issue/issue_spec.rb index d7fa680d29b..42c8e0cc9c0 100644 --- a/spec/requests/api/graphql/issue/issue_spec.rb +++ b/spec/requests/api/graphql/issue/issue_spec.rb @@ -125,7 +125,7 @@ RSpec.describe 'Query.issue(id)' do let(:issue_params) { { 'id' => confidential_issue.to_global_id.to_s } } context 'when the user cannot see confidential issues' do - it 'returns nil ' do + it 'returns nil' do post_graphql(query, current_user: current_user) expect(issue_data).to be nil diff --git a/spec/requests/api/graphql/project/merge_request/pipelines_spec.rb b/spec/requests/api/graphql/project/merge_request/pipelines_spec.rb new file mode 100644 index 00000000000..ac0b18a37d6 --- /dev/null +++ b/spec/requests/api/graphql/project/merge_request/pipelines_spec.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Query.project.mergeRequests.pipelines' do + include GraphqlHelpers + + let_it_be(:project) { create(:project, :public, :repository) } + let_it_be(:author) { create(:user) } + let_it_be(:merge_requests) do + %i[with_diffs with_image_diffs conflict].map do |trait| + create(:merge_request, trait, author: author, source_project: project) + end + end + + describe '.count' do + let(:query) do + <<~GQL + query($path: ID!, $first: Int) { + project(fullPath: $path) { + mergeRequests(first: $first) { + nodes { + iid + pipelines { + count + } + } + } + } + } + GQL + end + + def run_query(first = nil) + post_graphql(query, current_user: author, variables: { path: project.full_path, first: first }) + end + + before do + merge_requests.each do |mr| + shas = mr.all_commits.limit(2).pluck(:sha) + + shas.each do |sha| + create(:ci_pipeline, :success, project: project, ref: mr.source_branch, sha: sha) + end + end + end + + it 'produces correct results' do + run_query(2) + + p_nodes = graphql_data_at(:project, :merge_requests, :nodes) + + expect(p_nodes).to all(match('iid' => be_present, 'pipelines' => match('count' => 2))) + end + + it 'is scalable', :request_store, :use_clean_rails_memory_store_caching do + # warm up + run_query + + expect { run_query(2) }.to(issue_same_number_of_queries_as { run_query(1) }.ignoring_cached_queries) + end + end +end diff --git a/spec/requests/api/graphql_spec.rb b/spec/requests/api/graphql_spec.rb index 5dc8edb87e9..4eaf57a7d35 100644 --- a/spec/requests/api/graphql_spec.rb +++ b/spec/requests/api/graphql_spec.rb @@ -105,7 +105,7 @@ RSpec.describe 'GraphQL' do stub_authentication_activity_metrics(debug: false) end - it 'Authenticates users with a PAT' do + it 'authenticates users with a PAT' do expect(authentication_metrics) .to increment(:user_authenticated_counter) .and increment(:user_session_override_counter) diff --git a/spec/rubocop/cop/include_sidekiq_worker_spec.rb b/spec/rubocop/cop/include_sidekiq_worker_spec.rb index 8d056c6a13e..33737babee5 100644 --- a/spec/rubocop/cop/include_sidekiq_worker_spec.rb +++ b/spec/rubocop/cop/include_sidekiq_worker_spec.rb @@ -16,7 +16,7 @@ RSpec.describe RuboCop::Cop::IncludeSidekiqWorker, type: :rubocop do let(:source) { 'include Sidekiq::Worker' } let(:correct_source) { 'include ApplicationWorker' } - it 'registers an offense ' do + it 'registers an offense' do inspect_source(source) aggregate_failures do diff --git a/spec/rubocop/cop/migration/add_column_with_default_spec.rb b/spec/rubocop/cop/migration/add_column_with_default_spec.rb index 50af344e0d4..6deb092f235 100644 --- a/spec/rubocop/cop/migration/add_column_with_default_spec.rb +++ b/spec/rubocop/cop/migration/add_column_with_default_spec.rb @@ -26,7 +26,7 @@ RSpec.describe RuboCop::Cop::Migration::AddColumnWithDefault, type: :rubocop do let(:offense) { '`add_column_with_default` is deprecated, use `add_column` instead' } - it 'registers an offense ' do + it 'registers an offense' do expect_offense(<<~RUBY) def up add_column_with_default(:merge_request_diff_files, :artifacts, :boolean, default: true, allow_null: false) diff --git a/spec/rubocop/cop/migration/create_table_with_foreign_keys_spec.rb b/spec/rubocop/cop/migration/create_table_with_foreign_keys_spec.rb index 93f43b0feb0..eaaa50b8190 100644 --- a/spec/rubocop/cop/migration/create_table_with_foreign_keys_spec.rb +++ b/spec/rubocop/cop/migration/create_table_with_foreign_keys_spec.rb @@ -88,7 +88,7 @@ RSpec.describe RuboCop::Cop::Migration::CreateTableWithForeignKeys, type: :ruboc shared_examples 'target to high traffic table' do |dsl_method, table_name| context 'when the target is defined as option' do - it 'registers an offense ' do + it 'registers an offense' do expect_offense(<<~RUBY) def up create_table(:foo) do |t| @@ -102,7 +102,7 @@ RSpec.describe RuboCop::Cop::Migration::CreateTableWithForeignKeys, type: :ruboc end context 'when the target has implicit definition' do - it 'registers an offense ' do + it 'registers an offense' do expect_offense(<<~RUBY) def up create_table(:foo) do |t| diff --git a/spec/services/application_settings/update_service_spec.rb b/spec/services/application_settings/update_service_spec.rb index 03e55b3ec46..f855661b8b1 100644 --- a/spec/services/application_settings/update_service_spec.rb +++ b/spec/services/application_settings/update_service_spec.rb @@ -49,7 +49,7 @@ RSpec.describe ApplicationSettings::UpdateService do expect(application_settings.terms).to eq('Be nice!') end - it 'Only queries once when the terms are changed' do + it 'only queries once when the terms are changed' do create(:term, terms: 'Other terms') expect(application_settings.terms).to eq('Other terms') @@ -257,7 +257,7 @@ RSpec.describe ApplicationSettings::UpdateService do described_class.new(application_settings, admin, { external_authorization_service_enabled: false }).execute end - it 'does validate labels if external authorization gets enabled ' do + it 'does validate labels if external authorization gets enabled' do expect_any_instance_of(described_class).to receive(:validate_classification_label) described_class.new(application_settings, admin, { external_authorization_service_enabled: true }).execute diff --git a/spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb b/spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb index 7b428550768..eaa5f723bec 100644 --- a/spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb +++ b/spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb @@ -191,7 +191,7 @@ RSpec.describe AutoMerge::MergeWhenPipelineSucceedsService do expect(mr_merge_if_green_enabled.merge_user).to be nil end - it 'Posts a system note' do + it 'posts a system note' do note = mr_merge_if_green_enabled.notes.last expect(note.note).to include 'canceled the automatic merge' end diff --git a/spec/services/files/delete_service_spec.rb b/spec/services/files/delete_service_spec.rb index 17e4645fde6..3823d027812 100644 --- a/spec/services/files/delete_service_spec.rb +++ b/spec/services/files/delete_service_spec.rb @@ -55,7 +55,7 @@ RSpec.describe Files::DeleteService do context "when the file's last commit sha does not match the supplied last_commit_sha" do let(:last_commit_sha) { "foo" } - it "returns a hash with the correct error message and a :error status " do + it "returns a hash with the correct error message and a :error status" do expect { subject.execute } .to raise_error(Files::UpdateService::FileChangedError, "You are attempting to delete a file that has been previously updated.") diff --git a/spec/services/files/update_service_spec.rb b/spec/services/files/update_service_spec.rb index 84d78b4c2bc..6d7459e0b29 100644 --- a/spec/services/files/update_service_spec.rb +++ b/spec/services/files/update_service_spec.rb @@ -34,7 +34,7 @@ RSpec.describe Files::UpdateService do context "when the file's last commit sha does not match the supplied last_commit_sha" do let(:last_commit_sha) { "foo" } - it "returns a hash with the correct error message and a :error status " do + it "returns a hash with the correct error message and a :error status" do expect { subject.execute } .to raise_error(Files::UpdateService::FileChangedError, "You are attempting to update a file that has changed since you started editing it.") @@ -44,7 +44,7 @@ RSpec.describe Files::UpdateService do context "when the file's last commit sha does match the supplied last_commit_sha" do let(:last_commit_sha) { Gitlab::Git::Commit.last_for_path(project.repository, project.default_branch, file_path).sha } - it "returns a hash with the :success status " do + it "returns a hash with the :success status" do results = subject.execute expect(results[:status]).to match(:success) @@ -68,7 +68,7 @@ RSpec.describe Files::UpdateService do end context "when the last_commit_sha is not supplied" do - it "returns a hash with the :success status " do + it "returns a hash with the :success status" do results = subject.execute expect(results[:status]).to match(:success) diff --git a/spec/services/git/tag_hooks_service_spec.rb b/spec/services/git/tag_hooks_service_spec.rb index 4443c46a414..dae2f63f2f9 100644 --- a/spec/services/git/tag_hooks_service_spec.rb +++ b/spec/services/git/tag_hooks_service_spec.rb @@ -19,7 +19,7 @@ RSpec.describe Git::TagHooksService, :service do end describe 'System hooks' do - it 'Executes system hooks' do + it 'executes system hooks' do push_data = service.send(:push_data) expect(project).to receive(:has_active_hooks?).and_return(true) diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb index 1f521ed4a93..e6d1d0e90a7 100644 --- a/spec/services/quick_actions/interpret_service_spec.rb +++ b/spec/services/quick_actions/interpret_service_spec.rb @@ -1584,7 +1584,7 @@ RSpec.describe QuickActions::InterpretService do end end - it 'limits to commands passed ' do + it 'limits to commands passed' do content = "/shrug test\n/close" text, commands = service.execute(content, issue, only: [:shrug]) @@ -1593,7 +1593,7 @@ RSpec.describe QuickActions::InterpretService do expect(text).to eq("test #{described_class::SHRUG}\n/close") end - it 'preserves leading whitespace ' do + it 'preserves leading whitespace' do content = " - list\n\n/close\n\ntest\n\n" text, _ = service.execute(content, issue) diff --git a/spec/services/users/create_service_spec.rb b/spec/services/users/create_service_spec.rb index 9040966215c..74340bac055 100644 --- a/spec/services/users/create_service_spec.rb +++ b/spec/services/users/create_service_spec.rb @@ -59,7 +59,7 @@ RSpec.describe Users::CreateService do service.execute end - it 'executes system hooks ' do + it 'executes system hooks' do system_hook_service = spy(:system_hook_service) expect(service).to receive(:system_hook_service).and_return(system_hook_service) diff --git a/spec/support/shared_examples/features/reportable_note_shared_examples.rb b/spec/support/shared_examples/features/reportable_note_shared_examples.rb index bdaa375721f..288e1df9b2a 100644 --- a/spec/support/shared_examples/features/reportable_note_shared_examples.rb +++ b/spec/support/shared_examples/features/reportable_note_shared_examples.rb @@ -29,7 +29,7 @@ RSpec.shared_examples 'reportable note' do |type| end end - it 'Report button links to a report page' do + it 'report button links to a report page' do dropdown = comment.find(more_actions_selector) open_dropdown(dropdown) diff --git a/spec/support/shared_examples/features/wiki/user_uses_wiki_shortcuts_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_uses_wiki_shortcuts_shared_examples.rb index 0330b345a18..759cfaf6b1f 100644 --- a/spec/support/shared_examples/features/wiki/user_uses_wiki_shortcuts_shared_examples.rb +++ b/spec/support/shared_examples/features/wiki/user_uses_wiki_shortcuts_shared_examples.rb @@ -12,7 +12,7 @@ RSpec.shared_examples 'User uses wiki shortcuts' do visit wiki_page_path(wiki, wiki_page) end - it 'Visit edit wiki page using "e" keyboard shortcut', :js do + it 'visit edit wiki page using "e" keyboard shortcut', :js do find('body').native.send_key('e') expect(find('.wiki-page-title')).to have_content('Edit Page') diff --git a/spec/support/shared_examples/graphql/projects/services_resolver_shared_examples.rb b/spec/support/shared_examples/graphql/projects/services_resolver_shared_examples.rb index 94b7ed1618d..b1de1432a37 100644 --- a/spec/support/shared_examples/graphql/projects/services_resolver_shared_examples.rb +++ b/spec/support/shared_examples/graphql/projects/services_resolver_shared_examples.rb @@ -8,8 +8,6 @@ end RSpec.shared_examples 'cannot access project services' do it 'raises error' do - expect do - resolve_services - end.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + expect(resolve_services).to be_nil end end diff --git a/tooling/overcommit/Gemfile b/tooling/overcommit/Gemfile index 08f08018ffb..e335616d931 100644 --- a/tooling/overcommit/Gemfile +++ b/tooling/overcommit/Gemfile @@ -4,6 +4,6 @@ source 'https://rubygems.org' gem 'overcommit' -gem 'gitlab-styles', '~> 5.1.0', require: false +gem 'gitlab-styles', '~> 5.2.0', require: false gem 'scss_lint', '~> 0.56.0', require: false gem 'haml_lint', '~> 0.34.0', require: false diff --git a/tooling/overcommit/Gemfile.lock b/tooling/overcommit/Gemfile.lock index 5bada88e1dc..b801648ba37 100644 --- a/tooling/overcommit/Gemfile.lock +++ b/tooling/overcommit/Gemfile.lock @@ -11,7 +11,7 @@ GEM childprocess (3.0.0) concurrent-ruby (1.1.7) ffi (1.12.2) - gitlab-styles (5.1.0) + gitlab-styles (5.2.0) rubocop (~> 0.89.1) rubocop-gitlab-security (~> 0.1.0) rubocop-performance (~> 1.8.1) @@ -88,7 +88,7 @@ PLATFORMS ruby DEPENDENCIES - gitlab-styles (~> 5.1.0) + gitlab-styles (~> 5.2.0) haml_lint (~> 0.34.0) overcommit scss_lint (~> 0.56.0) |