diff options
159 files changed, 1335 insertions, 330 deletions
diff --git a/.gitlab/ci/review.gitlab-ci.yml b/.gitlab/ci/review.gitlab-ci.yml index 33c9a8ba987..e2fe5f467f8 100644 --- a/.gitlab/ci/review.gitlab-ci.yml +++ b/.gitlab/ci/review.gitlab-ci.yml @@ -43,7 +43,7 @@ review-build-cng: HOST_SUFFIX: "${CI_ENVIRONMENT_SLUG}" REVIEW_APPS_DOMAIN: "temp.gitlab-review.app" # FIXME: using temporary domain DOMAIN: "-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN}" - GITLAB_HELM_CHART_REF: "v4.1.3" + GITLAB_HELM_CHART_REF: "v4.3.0" environment: name: review/${CI_COMMIT_REF_NAME} url: https://gitlab-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN} diff --git a/app/controllers/projects/metrics_dashboard_controller.rb b/app/controllers/projects/metrics_dashboard_controller.rb index ae3f0a7a0c2..9d0cbfd2a20 100644 --- a/app/controllers/projects/metrics_dashboard_controller.rb +++ b/app/controllers/projects/metrics_dashboard_controller.rb @@ -18,7 +18,13 @@ module Projects if environment render 'projects/environments/metrics' elsif default_environment - redirect_to project_metrics_dashboard_path(project, environment: default_environment) + redirect_to project_metrics_dashboard_path( + project, + **permitted_params + .to_h + .symbolize_keys + .merge(environment: default_environment) + ) else render 'projects/environments/empty_metrics' end @@ -26,9 +32,14 @@ module Projects private + def permitted_params + @permitted_params ||= params.permit(:dashboard_path, :environment, :page) + end + def environment strong_memoize(:environment) do - project.environments.find(params[:environment]) if params[:environment] + env = permitted_params[:environment] + project.environments.find(env) if env end end diff --git a/app/finders/users_finder.rb b/app/finders/users_finder.rb index f87e0c67604..cc94536bf79 100644 --- a/app/finders/users_finder.rb +++ b/app/finders/users_finder.rb @@ -17,6 +17,7 @@ # without_projects: boolean # sort: string # id: integer +# non_internal: boolean # class UsersFinder include CreatedAtFilter @@ -42,6 +43,7 @@ class UsersFinder users = by_created_at(users) users = by_without_projects(users) users = by_custom_attributes(users) + users = by_non_internal(users) order(users) end @@ -112,6 +114,12 @@ class UsersFinder users.without_projects end + def by_non_internal(users) + return users unless params[:non_internal] + + users.non_internal + end + # rubocop: disable CodeReuse/ActiveRecord def order(users) return users unless params[:sort] diff --git a/app/graphql/mutations/snippets/create.rb b/app/graphql/mutations/snippets/create.rb index 37a281871c2..a8aeb15afcd 100644 --- a/app/graphql/mutations/snippets/create.rb +++ b/app/graphql/mutations/snippets/create.rb @@ -51,6 +51,11 @@ module Mutations snippet = service_response.payload[:snippet] + # Only when the user is not an api user and the operation was successful + if !api_user? && service_response.success? + ::Gitlab::UsageDataCounters::EditorUniqueCounter.track_snippet_editor_edit_action(author: current_user) + end + { snippet: service_response.success? ? snippet : nil, errors: errors_on_object(snippet) diff --git a/app/graphql/mutations/snippets/update.rb b/app/graphql/mutations/snippets/update.rb index fee2f61e927..d0db5fa2eb9 100644 --- a/app/graphql/mutations/snippets/update.rb +++ b/app/graphql/mutations/snippets/update.rb @@ -34,6 +34,11 @@ module Mutations update_params(args)).execute(snippet) snippet = result.payload[:snippet] + # Only when the user is not an api user and the operation was successful + if !api_user? && result.success? + ::Gitlab::UsageDataCounters::EditorUniqueCounter.track_snippet_editor_edit_action(author: current_user) + end + { snippet: result.success? ? snippet : snippet.reset, errors: errors_on_object(snippet) diff --git a/app/models/concerns/relative_positioning.rb b/app/models/concerns/relative_positioning.rb index a6fd912e135..3f65ebe3db7 100644 --- a/app/models/concerns/relative_positioning.rb +++ b/app/models/concerns/relative_positioning.rb @@ -83,9 +83,9 @@ module RelativePositioning if gap_width < MIN_GAP raise NoSpaceLeft elsif gap_width > MAX_GAP - if pos_before == MIN_POSITION + if pos_before <= MIN_POSITION pos_after - IDEAL_DISTANCE - elsif pos_after == MAX_POSITION + elsif pos_after >= MAX_POSITION pos_before + IDEAL_DISTANCE else midpoint diff --git a/app/models/namespace/root_storage_statistics.rb b/app/models/namespace/root_storage_statistics.rb index 2ad6ea59588..5723a823e98 100644 --- a/app/models/namespace/root_storage_statistics.rb +++ b/app/models/namespace/root_storage_statistics.rb @@ -2,7 +2,16 @@ class Namespace::RootStorageStatistics < ApplicationRecord SNIPPETS_SIZE_STAT_NAME = 'snippets_size'.freeze - STATISTICS_ATTRIBUTES = %W(storage_size repository_size wiki_size lfs_objects_size build_artifacts_size packages_size #{SNIPPETS_SIZE_STAT_NAME}).freeze + STATISTICS_ATTRIBUTES = %W( + storage_size + repository_size + wiki_size + lfs_objects_size + build_artifacts_size + packages_size + #{SNIPPETS_SIZE_STAT_NAME} + pipeline_artifacts_size + ).freeze self.primary_key = :namespace_id @@ -40,7 +49,8 @@ class Namespace::RootStorageStatistics < ApplicationRecord 'COALESCE(SUM(ps.lfs_objects_size), 0) AS lfs_objects_size', 'COALESCE(SUM(ps.build_artifacts_size), 0) AS build_artifacts_size', 'COALESCE(SUM(ps.packages_size), 0) AS packages_size', - "COALESCE(SUM(ps.snippets_size), 0) AS #{SNIPPETS_SIZE_STAT_NAME}" + "COALESCE(SUM(ps.snippets_size), 0) AS #{SNIPPETS_SIZE_STAT_NAME}", + 'COALESCE(SUM(ps.pipeline_artifacts_size), 0) AS pipeline_artifacts_size' ) end diff --git a/app/models/user.rb b/app/models/user.rb index 1a67116c1f2..300e918513a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -107,7 +107,7 @@ class User < ApplicationRecord has_many :group_deploy_keys has_many :gpg_keys - has_many :emails, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :emails has_many :personal_access_tokens, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :identities, dependent: :destroy, autosave: true # rubocop:disable Cop/ActiveRecordDependent has_many :u2f_registrations, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent diff --git a/app/services/git/process_ref_changes_service.rb b/app/services/git/process_ref_changes_service.rb index c012c61a337..d039ed00efc 100644 --- a/app/services/git/process_ref_changes_service.rb +++ b/app/services/git/process_ref_changes_service.rb @@ -42,7 +42,7 @@ module Git push_service_class = push_service_class_for(ref_type) create_bulk_push_event = changes.size > Gitlab::CurrentSettings.push_event_activities_limit - merge_request_branches = merge_request_branches_for(changes) + merge_request_branches = merge_request_branches_for(ref_type, changes) changes.each do |change| push_service_class.new( @@ -74,8 +74,10 @@ module Git Git::BranchPushService end - def merge_request_branches_for(changes) - @merge_requests_branches ||= MergeRequests::PushedBranchesService.new(project, current_user, changes: changes).execute + def merge_request_branches_for(ref_type, changes) + return [] if ref_type == :tag + + MergeRequests::PushedBranchesService.new(project, current_user, changes: changes).execute end end end diff --git a/app/views/admin/dev_ops_score/_callout.html.haml b/app/views/admin/dev_ops_score/_callout.html.haml index 31ae7721f5f..1b50c27834d 100644 --- a/app/views/admin/dev_ops_score/_callout.html.haml +++ b/app/views/admin/dev_ops_score/_callout.html.haml @@ -2,12 +2,12 @@ .user-callout{ data: { uid: 'dev_ops_score_intro_callout_dismissed' } } .bordered-box.landing.content-block %button.btn.btn-default.close.js-close-callout{ type: 'button', - 'aria-label' => _('Dismiss DevOps Score introduction') } + 'aria-label' => _('Dismiss DevOps Report introduction') } = icon('times', class: 'dismiss-icon', 'aria-hidden' => 'true') .user-callout-copy %h4 - = _('Introducing Your DevOps Score') + = _('Introducing Your DevOps Report') %p - = _('Your DevOps Score gives an overview of how you are using GitLab from a feature perspective. View how you compare with other organizations, discover features you are not using, and learn best practices through blog posts and white papers.') + = _('Your DevOps Report gives an overview of how you are using GitLab from a feature perspective. View how you compare with other organizations, discover features you are not using, and learn best practices through blog posts and white papers.') .svg-container.devops = custom_icon('dev_ops_score_overview') diff --git a/app/views/admin/dev_ops_score/_no_data.html.haml b/app/views/admin/dev_ops_score/_no_data.html.haml index 36368ceca2b..8ea3797b1cc 100644 --- a/app/views/admin/dev_ops_score/_no_data.html.haml +++ b/app/views/admin/dev_ops_score/_no_data.html.haml @@ -4,4 +4,4 @@ %h4= _('Data is still calculating...') %p = _('In order to gather accurate feature usage data, it can take 1 to 2 weeks to see your index.') - = link_to _('Learn more'), help_page_path('user/admin_area/analytics/dev_ops_score'), target: '_blank' + = link_to _('Learn more'), help_page_path('user/admin_area/analytics/dev_ops_report'), target: '_blank' diff --git a/app/views/admin/dev_ops_score/show.html.haml b/app/views/admin/dev_ops_score/show.html.haml index 9c056384164..520194acc88 100644 --- a/app/views/admin/dev_ops_score/show.html.haml +++ b/app/views/admin/dev_ops_score/show.html.haml @@ -1,4 +1,4 @@ -- page_title _('DevOps Score') +- page_title _('DevOps Report') - usage_ping_enabled = Gitlab::CurrentSettings.usage_ping_enabled .container @@ -16,10 +16,10 @@ %h2.devops-header-title{ class: "devops-#{score_level(@metric.average_percentage_score)}-score" } = number_to_percentage(@metric.average_percentage_score, precision: 1) .devops-header-subtitle - = _('index') + = _('DevOps') %br - = _('score') - = link_to icon('question-circle', 'aria-hidden' => 'true'), help_page_path('user/admin_area/analytics/dev_ops_score') + = _('Score') + = link_to icon('question-circle', 'aria-hidden' => 'true'), help_page_path('user/admin_area/analytics/dev_ops_report') .devops-cards.board-card-container - @metric.cards.each do |card| diff --git a/app/views/layouts/nav/sidebar/_admin.html.haml b/app/views/layouts/nav/sidebar/_admin.html.haml index 2e915a38c46..960a4a93019 100644 --- a/app/views/layouts/nav/sidebar/_admin.html.haml +++ b/app/views/layouts/nav/sidebar/_admin.html.haml @@ -62,9 +62,9 @@ = _('Analytics') %li.divider.fly-out-top-item = nav_link(controller: :dev_ops_score) do - = link_to admin_dev_ops_score_path, title: _('DevOps Score') do + = link_to admin_dev_ops_score_path, title: _('DevOps Report') do %span - = _('DevOps Score') + = _('DevOps Report') = nav_link(controller: :cohorts) do = link_to admin_cohorts_path, title: _('Cohorts') do %span diff --git a/changelogs/unreleased/227929-metrics-dashboard-embeds-with-new-urls.yml b/changelogs/unreleased/227929-metrics-dashboard-embeds-with-new-urls.yml new file mode 100644 index 00000000000..46f36353614 --- /dev/null +++ b/changelogs/unreleased/227929-metrics-dashboard-embeds-with-new-urls.yml @@ -0,0 +1,5 @@ +--- +title: Fix Metrics dashboard embeds when using new URLs +merge_request: 39876 +author: +type: fixed diff --git a/changelogs/unreleased/232841-fj-track-snippet-edit-events.yml b/changelogs/unreleased/232841-fj-track-snippet-edit-events.yml new file mode 100644 index 00000000000..9ee49bef669 --- /dev/null +++ b/changelogs/unreleased/232841-fj-track-snippet-edit-events.yml @@ -0,0 +1,5 @@ +--- +title: Track snippet editor actions +merge_request: 40277 +author: +type: changed diff --git a/changelogs/unreleased/241144-fj-add-non-internal-filter-to-users-rest-endpoint.yml b/changelogs/unreleased/241144-fj-add-non-internal-filter-to-users-rest-endpoint.yml new file mode 100644 index 00000000000..1b0cd053975 --- /dev/null +++ b/changelogs/unreleased/241144-fj-add-non-internal-filter-to-users-rest-endpoint.yml @@ -0,0 +1,5 @@ +--- +title: Add filter to exclude non internal users in REST API +merge_request: 40372 +author: +type: changed diff --git a/changelogs/unreleased/dblessing-emails-cascade-delete.yml b/changelogs/unreleased/dblessing-emails-cascade-delete.yml new file mode 100644 index 00000000000..cdba61207ab --- /dev/null +++ b/changelogs/unreleased/dblessing-emails-cascade-delete.yml @@ -0,0 +1,5 @@ +--- +title: Add emails user_id foreign key with cascade delete +merge_request: 39899 +author: +type: other diff --git a/changelogs/unreleased/id-remove-memoize-on-processing-ref-changes.yml b/changelogs/unreleased/id-remove-memoize-on-processing-ref-changes.yml new file mode 100644 index 00000000000..2c52d5f3f61 --- /dev/null +++ b/changelogs/unreleased/id-remove-memoize-on-processing-ref-changes.yml @@ -0,0 +1,5 @@ +--- +title: Fix wrong caching logic in ProcessRefChangesService +merge_request: 40821 +author: +type: fixed diff --git a/changelogs/unreleased/mw-rename-devops-score-to-devops-report.yml b/changelogs/unreleased/mw-rename-devops-score-to-devops-report.yml new file mode 100644 index 00000000000..b470ced0329 --- /dev/null +++ b/changelogs/unreleased/mw-rename-devops-score-to-devops-report.yml @@ -0,0 +1,5 @@ +--- +title: Rename DevOps Score to DevOps Report +merge_request: 39953 +author: +type: changed diff --git a/db/migrate/20200819192143_add_emails_user_id_foreign_key.rb b/db/migrate/20200819192143_add_emails_user_id_foreign_key.rb new file mode 100644 index 00000000000..7f00aa6341b --- /dev/null +++ b/db/migrate/20200819192143_add_emails_user_id_foreign_key.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class AddEmailsUserIdForeignKey < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + CONSTRAINT_NAME = 'fk_emails_user_id' + + def up + with_lock_retries do + add_foreign_key :emails, :users, on_delete: :cascade, validate: false, name: CONSTRAINT_NAME + end + end + + def down + with_lock_retries do + remove_foreign_key_if_exists :emails, column: :user_id, name: CONSTRAINT_NAME + end + end +end diff --git a/db/post_migrate/20200819202048_remove_orphaned_emails.rb b/db/post_migrate/20200819202048_remove_orphaned_emails.rb new file mode 100644 index 00000000000..82cba244ad6 --- /dev/null +++ b/db/post_migrate/20200819202048_remove_orphaned_emails.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class RemoveOrphanedEmails < ActiveRecord::Migration[6.0] + DOWNTIME = false + + def up + execute <<~SQL + DELETE FROM emails + WHERE not exists ( + SELECT 1 FROM users WHERE users.id = emails.user_id + ); + SQL + + execute 'DELETE FROM emails WHERE user_id IS NULL;' + end + + def down + # no-op + end +end diff --git a/db/post_migrate/20200819202222_validate_emails_user_id_foreign_key.rb b/db/post_migrate/20200819202222_validate_emails_user_id_foreign_key.rb new file mode 100644 index 00000000000..4f7f9deb540 --- /dev/null +++ b/db/post_migrate/20200819202222_validate_emails_user_id_foreign_key.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class ValidateEmailsUserIdForeignKey < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + CONSTRAINT_NAME = 'fk_emails_user_id' + + def up + validate_foreign_key :emails, :user_id, name: CONSTRAINT_NAME + end + + def down + # no op + end +end diff --git a/db/schema_migrations/20200819192143 b/db/schema_migrations/20200819192143 new file mode 100644 index 00000000000..e0934e15f1f --- /dev/null +++ b/db/schema_migrations/20200819192143 @@ -0,0 +1 @@ +5a5278fdd9539d33a6de226a84ed39b7c5a26929cec68ec5e8d193afb3cfafa2
\ No newline at end of file diff --git a/db/schema_migrations/20200819202048 b/db/schema_migrations/20200819202048 new file mode 100644 index 00000000000..a5428c7b40a --- /dev/null +++ b/db/schema_migrations/20200819202048 @@ -0,0 +1 @@ +476bce9b18177f37b31e15d42f5a1391c0bfbbd312a513c1d5b43085b90afb3e
\ No newline at end of file diff --git a/db/schema_migrations/20200819202222 b/db/schema_migrations/20200819202222 new file mode 100644 index 00000000000..f633502d0bb --- /dev/null +++ b/db/schema_migrations/20200819202222 @@ -0,0 +1 @@ +5e2dfdf725ad0a3d90b240ced74cf5a872f7126b716847f9f9e99b4ad2a22109
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index cb6c551bd43..1951b9385a7 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -22016,6 +22016,9 @@ ALTER TABLE ONLY public.events ALTER TABLE ONLY public.vulnerabilities ADD CONSTRAINT fk_efb96ab1e2 FOREIGN KEY (project_id) REFERENCES public.projects(id) ON DELETE CASCADE; +ALTER TABLE ONLY public.emails + ADD CONSTRAINT fk_emails_user_id FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + ALTER TABLE ONLY public.clusters ADD CONSTRAINT fk_f05c5e5a42 FOREIGN KEY (management_project_id) REFERENCES public.projects(id) ON DELETE SET NULL; diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql index 321bb9d7f84..0523e95535d 100644 --- a/doc/api/graphql/reference/gitlab_schema.graphql +++ b/doc/api/graphql/reference/gitlab_schema.graphql @@ -1725,11 +1725,73 @@ type ClusterAgentDeletePayload { errors: [String!]! } +type ClusterAgentToken { + """ + Cluster agent this token is associated with + """ + clusterAgent: ClusterAgent + + """ + Timestamp the token was created + """ + createdAt: Time + + """ + Global ID of the token + """ + id: ClustersAgentTokenID! +} + +""" +Autogenerated input type of ClusterAgentTokenCreate +""" +input ClusterAgentTokenCreateInput { + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + + """ + Global ID of the cluster agent that will be associated with the new token + """ + clusterAgentId: ClustersAgentID! +} + +""" +Autogenerated return type of ClusterAgentTokenCreate +""" +type ClusterAgentTokenCreatePayload { + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + + """ + Errors encountered during execution of the mutation. + """ + errors: [String!]! + + """ + Token secret value. Make sure you save it - you won't be able to access it again + """ + secret: String + + """ + Token created after mutation + """ + token: ClusterAgentToken +} + """ Identifier of Clusters::Agent """ scalar ClustersAgentID +""" +Identifier of Clusters::AgentToken +""" +scalar ClustersAgentTokenID + type Commit { """ Author of the commit @@ -8548,6 +8610,11 @@ type MergeRequest implements Noteable { allowCollaboration: Boolean """ + Indicates if the merge request has all the required approvals. Returns true if no required approvals are configured. + """ + approved: Boolean! + + """ Users who approved the merge request """ approvedBy( @@ -9792,6 +9859,7 @@ type Mutation { boardListCreate(input: BoardListCreateInput!): BoardListCreatePayload boardListUpdateLimitMetrics(input: BoardListUpdateLimitMetricsInput!): BoardListUpdateLimitMetricsPayload clusterAgentDelete(input: ClusterAgentDeleteInput!): ClusterAgentDeletePayload + clusterAgentTokenCreate(input: ClusterAgentTokenCreateInput!): ClusterAgentTokenCreatePayload commitCreate(input: CommitCreateInput!): CommitCreatePayload configureSast(input: ConfigureSastInput!): ConfigureSastPayload createAlertIssue(input: CreateAlertIssueInput!): CreateAlertIssuePayload diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json index 1772c92c3b2..a2c9a369e29 100644 --- a/doc/api/graphql/reference/gitlab_schema.json +++ b/doc/api/graphql/reference/gitlab_schema.json @@ -4730,6 +4730,181 @@ "possibleTypes": null }, { + "kind": "OBJECT", + "name": "ClusterAgentToken", + "description": null, + "fields": [ + { + "name": "clusterAgent", + "description": "Cluster agent this token is associated with", + "args": [ + + ], + "type": { + "kind": "OBJECT", + "name": "ClusterAgent", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "createdAt", + "description": "Timestamp the token was created", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "Time", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "id", + "description": "Global ID of the token", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ClustersAgentTokenID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "ClusterAgentTokenCreateInput", + "description": "Autogenerated input type of ClusterAgentTokenCreate", + "fields": null, + "inputFields": [ + { + "name": "clusterAgentId", + "description": "Global ID of the cluster agent that will be associated with the new token", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ClustersAgentID", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "clientMutationId", + "description": "A unique identifier for the client performing the mutation.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "ClusterAgentTokenCreatePayload", + "description": "Autogenerated return type of ClusterAgentTokenCreate", + "fields": [ + { + "name": "clientMutationId", + "description": "A unique identifier for the client performing the mutation.", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "errors", + "description": "Errors encountered during execution of the mutation.", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "secret", + "description": "Token secret value. Make sure you save it - you won't be able to access it again", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "token", + "description": "Token created after mutation", + "args": [ + + ], + "type": { + "kind": "OBJECT", + "name": "ClusterAgentToken", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + + ], + "enumValues": null, + "possibleTypes": null + }, + { "kind": "SCALAR", "name": "ClustersAgentID", "description": "Identifier of Clusters::Agent", @@ -4740,6 +4915,16 @@ "possibleTypes": null }, { + "kind": "SCALAR", + "name": "ClustersAgentTokenID", + "description": "Identifier of Clusters::AgentToken", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { "kind": "OBJECT", "name": "Commit", "description": null, @@ -23819,6 +24004,24 @@ "deprecationReason": null }, { + "name": "approved", + "description": "Indicates if the merge request has all the required approvals. Returns true if no required approvals are configured.", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "approvedBy", "description": "Users who approved the merge request", "args": [ @@ -27730,6 +27933,33 @@ "deprecationReason": null }, { + "name": "clusterAgentTokenCreate", + "description": null, + "args": [ + { + "name": "input", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "ClusterAgentTokenCreateInput", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "ClusterAgentTokenCreatePayload", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "commitCreate", "description": null, "args": [ diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 0ab83a1d5f7..0c75917db10 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -295,6 +295,25 @@ Autogenerated return type of ClusterAgentDelete | `clientMutationId` | String | A unique identifier for the client performing the mutation. | | `errors` | String! => Array | Errors encountered during execution of the mutation. | +## ClusterAgentToken + +| Name | Type | Description | +| --- | ---- | ---------- | +| `clusterAgent` | ClusterAgent | Cluster agent this token is associated with | +| `createdAt` | Time | Timestamp the token was created | +| `id` | ClustersAgentTokenID! | Global ID of the token | + +## ClusterAgentTokenCreatePayload + +Autogenerated return type of ClusterAgentTokenCreate + +| Name | Type | Description | +| --- | ---- | ---------- | +| `clientMutationId` | String | A unique identifier for the client performing the mutation. | +| `errors` | String! => Array | Errors encountered during execution of the mutation. | +| `secret` | String | Token secret value. Make sure you save it - you won't be able to access it again | +| `token` | ClusterAgentToken | Token created after mutation | + ## Commit | Name | Type | Description | @@ -1319,6 +1338,7 @@ Autogenerated return type of MarkAsSpamSnippet | Name | Type | Description | | --- | ---- | ---------- | | `allowCollaboration` | Boolean | Indicates if members of the target project can push to the fork | +| `approved` | Boolean! | Indicates if the merge request has all the required approvals. Returns true if no required approvals are configured. | | `author` | User | User who created this merge request | | `commitCount` | Int | Number of commits in the merge request | | `createdAt` | Time! | Timestamp of when the merge request was created | diff --git a/doc/api/users.md b/doc/api/users.md index 76075e8b7be..4c762958b52 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -61,6 +61,15 @@ GET /users?active=true GET /users?blocked=true ``` +GitLab supports bot users such as the [alert bot](../operations/incident_management/generic_alerts.md) +or the [support bot](../user/project/service_desk.md#support-bot-user). +To exclude these users from the users' list, you can use the parameter `exclude_internal=true` +([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/241144) in GitLab 13.4). + +```plaintext +GET /users?exclude_internal=true +``` + NOTE: **Note:** Username search is case insensitive. diff --git a/doc/development/contributing/issue_workflow.md b/doc/development/contributing/issue_workflow.md index b4bad20438f..33004037fae 100644 --- a/doc/development/contributing/issue_workflow.md +++ b/doc/development/contributing/issue_workflow.md @@ -43,7 +43,7 @@ Most issues will have labels for at least one of the following: - Type: `~feature`, `~bug`, `~tooling`, `~documentation`, etc. - Stage: `~"devops::plan"`, `~"devops::create"`, etc. - Group: `~"group::source code"`, `~"group::knowledge"`, `~"group::editor"`, etc. -- Category: `~"Category:Code Analytics"`, `~"Category:DevOps Score"`, `~"Category:Templates"`, etc. +- Category: `~"Category:Code Analytics"`, `~"Category:DevOps Reports"`, `~"Category:Templates"`, etc. - Feature: `~wiki`, `~ldap`, `~api`, `~issues`, `~"merge requests"`, etc. - Department: `~UX`, `~Quality` - Team: `~"Technical Writing"`, `~Delivery` @@ -188,9 +188,9 @@ their color is `#428BCA`. `<Category Name>` is the category name as it is in the single source of truth for categories at <https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/categories.yml>. -For instance, the "DevOps Score" category is represented by the -~"Category:DevOps Score" label in the `gitlab-org` group since its -`devops_score.name` value is "DevOps Score". +For instance, the "DevOps Report" category is represented by the +~"Category:DevOps Reports" label in the `gitlab-org` group since its +`devops_score.name` value is "DevOps Reports". If a category's label doesn't respect this naming convention, it should be specified with [the `label` attribute](https://about.gitlab.com/handbook/marketing/website/#category-attributes) diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md index 283060ba8d4..c864e59c898 100644 --- a/doc/development/documentation/index.md +++ b/doc/development/documentation/index.md @@ -750,3 +750,64 @@ code review. For docs changes in merge requests, whenever a change to files unde is made, Danger Bot leaves a comment with further instructions about the documentation process. This is configured in the `Dangerfile` in the GitLab repository under [/danger/documentation/](https://gitlab.com/gitlab-org/gitlab/tree/master/danger/documentation). + +## Automatic screenshot generator + +You can now set up an automatic screenshot generator to take and compress screenshots, with the +help of a configuration file known as **screenshot generator**. + +### Use the tool + +To run the tool on an existing screenshot generator, take the following steps: + +1. Set up the [GitLab Development Kit (GDK)](https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/howto/gitlab_docs.md). +1. Navigate to the subdirectory with your cloned GitLab repository, typically `gdk/gitlab`. +1. Make sure that your GDK database is fully migrated: `bin/rake db:migrate RAILS_ENV=development`. +1. Install pngquant, see the tool website for more info: [`pngquant`](https://pngquant.org/) +1. Run `scripts/docs_screenshots.rb spec/docs_screenshots/<name_of_screenshot_generator>.rb <milestone-version>`. +1. Identify the location of the screenshots, based on the `gitlab/doc` location defined by the `it` parameter in your script. +1. Commit the newly created screenshots. + +### Extending the tool + +To add an additional **screenshot generator**, take the following steps: + +- Locate the `spec/docs_screenshots` directory. +- Add a new file with a `_docs.rb` extension. +- Be sure to include the following bits in the file: + +```ruby +require 'spec_helper' + +RSpec.describe '<What I am taking screenshots of>', :js do + include DocsScreenshotHelpers # Helper that enables the screenshots taking mechanism + + before do + page.driver.browser.manage.window.resize_to(1366, 1024) # length and width of the page + end +``` + +- In addition, every `it` block must include the path where the screenshot is saved + +```ruby + it 'user/packages/container_registry/img/project_image_repositories_list' +``` + +#### Full page screenshots + +To take a full page screenshot simply `visit the page` and perform any expectation on real content (to have capybara wait till the page is ready and not take a white screenshot). + +#### Element screenshot + +To have the screenshot focuses few more steps are needed: + +- **find the area**: `screenshot_area = find('#js-registry-policies')` +- **scroll the area in focus**: `scroll_to screenshot_area` +- **wait for the content**: `expect(screenshot_area).to have_content 'Expiration interval'` +- **set the crop area**: `set_crop_data(screenshot_area, 20)` + +In particular `set_crop_data` accepts as arguments: a `DOM` element and a padding, the padding will be added around the element enlarging the screenshot area. + +#### Live example + +Please use `spec/docs_screenshots/container_registry_docs.rb` as a guide and as an example to create your own scripts. diff --git a/doc/development/telemetry/usage_ping.md b/doc/development/telemetry/usage_ping.md index 7a3a501fa94..771486944c6 100644 --- a/doc/development/telemetry/usage_ping.md +++ b/doc/development/telemetry/usage_ping.md @@ -37,7 +37,7 @@ More useful links: - The main purpose of Usage Ping is to build a better GitLab. Data about how GitLab is used is collected to better understand feature/stage adoption and usage, which helps us understand how GitLab is adding value and helps our team better understand the reasons why people use GitLab and with this knowledge we're able to make better product decisions. - As a benefit of having the usage ping active, GitLab lets you analyze the users’ activities over time of your GitLab installation. -- As a benefit of having the usage ping active, GitLab provides you with The DevOps Score,which gives you an overview of your entire instance’s adoption of Concurrent DevOps from planning to monitoring. +- As a benefit of having the usage ping active, GitLab provides you with The DevOps Report,which gives you an overview of your entire instance’s adoption of Concurrent DevOps from planning to monitoring. - You will get better, more proactive support. (assuming that our TAMs and support organization used the data to deliver more value) - You will get insight and advice into how to get the most value out of your investment in GitLab. Wouldn't you want to know that a number of features or values are not being adopted in your organization? - You get a report that illustrates how you compare against other similar organizations (anonymized), with specific advice and recommendations on how to improve your DevOps processes. @@ -108,7 +108,7 @@ sequenceDiagram S3 Bucket->>Snowflake DW: Import data Snowflake DW->>Snowflake DW: Transform data using dbt Snowflake DW->>Sisense Dashboards: Data available for querying - Versions Application->>GitLab Instance: DevOps Score (Conversational Development Index) + Versions Application->>GitLab Instance: DevOps Report (Conversational Development Index) ``` ## How Usage Ping works diff --git a/doc/operations/metrics/embed.md b/doc/operations/metrics/embed.md index 62d60921c85..fcf9679d164 100644 --- a/doc/operations/metrics/embed.md +++ b/doc/operations/metrics/embed.md @@ -20,15 +20,28 @@ metrics to others, and you want to have relevant information directly available. NOTE: **Note:** Requires [Kubernetes](../../user/project/integrations/prometheus_library/kubernetes.md) metrics. +Note: **Note:** +In GitLab versions 13.3 and earlier, metrics dashboard links were in the form +`https://<root_url>/<project>/-/environments/<environment_id>/metrics`. These links +are still supported, and can be used to embed metric charts. + To display metric charts, include a link of the form -`https://<root_url>/<project>/-/environments/<environment_id>/metrics` in a field +`https://<root_url>/<project>/-/metrics?environment=<environment_id>` in a field that supports GitLab-flavored Markdown: -![Embedded Metrics Markdown](img/embedded_metrics_markdown_v12_8.png) +```markdown +### Summary + +**Start time:** 2020-01-21T12:00:31+00:00 + +### Metrics + +https://gitlab.com/gitlab-org/monitor/tanuki-inc/-/metrics?environment=1118134 +``` GitLab unfurls the link as an embedded metrics panel: -![Embedded Metrics Rendered](img/embedded_metrics_rendered_v12_8.png) +![Embedded Metrics Rendered](img/embedded_metrics_rendered_v13_4.png) You can also embed a single chart. To get a link to a chart, click the **{ellipsis_v}** **More actions** menu in the upper right corner of the chart, diff --git a/doc/operations/metrics/img/embedded_metrics_markdown_v12_8.png b/doc/operations/metrics/img/embedded_metrics_markdown_v12_8.png Binary files differdeleted file mode 100644 index ffd34705464..00000000000 --- a/doc/operations/metrics/img/embedded_metrics_markdown_v12_8.png +++ /dev/null diff --git a/doc/operations/metrics/img/embedded_metrics_rendered_v12_8.png b/doc/operations/metrics/img/embedded_metrics_rendered_v12_8.png Binary files differdeleted file mode 100644 index b024daaaa8e..00000000000 --- a/doc/operations/metrics/img/embedded_metrics_rendered_v12_8.png +++ /dev/null diff --git a/doc/operations/metrics/img/embedded_metrics_rendered_v13_4.png b/doc/operations/metrics/img/embedded_metrics_rendered_v13_4.png Binary files differnew file mode 100644 index 00000000000..876972dc721 --- /dev/null +++ b/doc/operations/metrics/img/embedded_metrics_rendered_v13_4.png diff --git a/doc/user/admin_area/analytics/convdev.md b/doc/user/admin_area/analytics/convdev.md index 9df8cbe021d..3ffda3f4400 100644 --- a/doc/user/admin_area/analytics/convdev.md +++ b/doc/user/admin_area/analytics/convdev.md @@ -1,5 +1,5 @@ --- -redirect_to: 'dev_ops_score.md' +redirect_to: 'dev_ops_report.md' --- -This document was moved to [another location](dev_ops_score.md). +This document was moved to [another location](dev_ops_report.md). diff --git a/doc/user/admin_area/analytics/dev_ops_score.md b/doc/user/admin_area/analytics/dev_ops_report.md index 5f4ae21b5f0..8c21570937d 100644 --- a/doc/user/admin_area/analytics/dev_ops_score.md +++ b/doc/user/admin_area/analytics/dev_ops_report.md @@ -1,4 +1,4 @@ -# DevOps Score +# DevOps Report > - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/30469) in GitLab 9.3. > - [Renamed from Conversational Development Index](https://gitlab.com/gitlab-org/gitlab/-/issues/20976) in GitLab 12.6. @@ -6,7 +6,7 @@ NOTE: **Note:** Your GitLab instance's [usage ping](../settings/usage_statistics.md#usage-ping-core-only) must be activated in order to use this feature. -The DevOps Score gives you an overview of your entire instance's adoption of +The DevOps Report gives you an overview of your entire instance's adoption of [Concurrent DevOps](https://about.gitlab.com/topics/concurrent-devops/) from planning to monitoring. @@ -15,9 +15,9 @@ the last 30 days, averaged over the number of active users in that time period. provides a Lead score per feature, which is calculated based on GitLab's analysis of top-performing instances based on [usage ping data](../settings/usage_statistics.md#usage-ping-core-only) that GitLab has collected. Your score is compared to the lead score of each feature and then expressed as a percentage at the bottom of said feature. -Your overall **index score** is an average of your feature scores. You can use this score to compare your DevOps status to other organizations. +Your overall **DevOps Score** is an average of your feature scores. You can use this score to compare your DevOps status to other organizations. -![DevOps Score](img/dev_ops_score_v12_6.png) +![DevOps Report](img/dev_ops_report_v13_4.png) The page also provides helpful links to articles and GitLab docs, to help you improve your scores. diff --git a/doc/user/admin_area/analytics/img/dev_ops_report_v13_4.png b/doc/user/admin_area/analytics/img/dev_ops_report_v13_4.png Binary files differnew file mode 100644 index 00000000000..1fa070a6915 --- /dev/null +++ b/doc/user/admin_area/analytics/img/dev_ops_report_v13_4.png diff --git a/doc/user/admin_area/analytics/img/dev_ops_score_v12_6.png b/doc/user/admin_area/analytics/img/dev_ops_score_v12_6.png Binary files differdeleted file mode 100644 index 3d697a30f66..00000000000 --- a/doc/user/admin_area/analytics/img/dev_ops_score_v12_6.png +++ /dev/null diff --git a/doc/user/admin_area/analytics/index.md b/doc/user/admin_area/analytics/index.md index b7fc8a656d0..b3336b471f8 100644 --- a/doc/user/admin_area/analytics/index.md +++ b/doc/user/admin_area/analytics/index.md @@ -6,5 +6,5 @@ Administrators have access to instance-wide analytics, as shown in **Admin Area There are two kinds of statistics: -- [Dev Ops Score](dev_ops_score.md): Provides an overview of your entire instance's feature usage. +- [DevOps Report](dev_ops_report.md): Provides an overview of your entire instance's feature usage. - [User Cohorts](user_cohorts.md): Display the monthly cohorts of new users and their activities over time. diff --git a/doc/user/admin_area/monitoring/convdev.md b/doc/user/admin_area/monitoring/convdev.md index afdf3dc5849..d9c3950f2e4 100644 --- a/doc/user/admin_area/monitoring/convdev.md +++ b/doc/user/admin_area/monitoring/convdev.md @@ -1,5 +1,5 @@ --- -redirect_to: '../analytics/dev_ops_score.md' +redirect_to: '../analytics/dev_ops_report.md' --- -Conversational Development Index was renamed to [DevOps Score](../analytics/dev_ops_score.md) in GitLab 12.6. +Conversational Development Index was renamed to [DevOps Report](../analytics/dev_ops_report.md) in GitLab 12.6. diff --git a/doc/user/admin_area/monitoring/dev_ops_score.md b/doc/user/admin_area/monitoring/dev_ops_report.md index 70c668870fd..9ad9830ed59 100644 --- a/doc/user/admin_area/monitoring/dev_ops_score.md +++ b/doc/user/admin_area/monitoring/dev_ops_report.md @@ -1,5 +1,5 @@ --- -redirect_to: '../analytics/dev_ops_score.md' +redirect_to: '../analytics/dev_ops_report.md' --- -This document was moved to [another location](../analytics/dev_ops_score.md). +This document was moved to [another location](../analytics/dev_ops_report.md). diff --git a/doc/user/admin_area/settings/img/domain_blacklist.png b/doc/user/admin_area/settings/img/domain_denylist.png Binary files differindex a7e972b7c0a..a7e972b7c0a 100644 --- a/doc/user/admin_area/settings/img/domain_blacklist.png +++ b/doc/user/admin_area/settings/img/domain_denylist.png diff --git a/doc/user/admin_area/settings/sign_up_restrictions.md b/doc/user/admin_area/settings/sign_up_restrictions.md index 8ef5ac8dc8f..99fc82f94ed 100644 --- a/doc/user/admin_area/settings/sign_up_restrictions.md +++ b/doc/user/admin_area/settings/sign_up_restrictions.md @@ -8,7 +8,7 @@ You can use sign-up restrictions to: - Disable new signups. - Require user email confirmation. -- Blacklist or whitelist email addresses belonging to specific domains. +- Denylist or allowlist email addresses belonging to specific domains. NOTE: **Note:** These restrictions are only applied during sign-up from an external user. An admin is @@ -31,7 +31,7 @@ consider disabling new signups if you do not expect public users to sign up for account. Alternatively, you could also consider setting up a -[whitelist](#whitelist-email-domains) or [blacklist](#blacklist-email-domains) on +[allowlist](#allowlist-email-domains) or [denylist](#denylist-email-domains) on email domains to prevent malicious users from creating accounts. ## Require email confirmation @@ -48,14 +48,14 @@ their email address before they are allowed to sign in. You can [change](../../../security/password_length_limits.md#modify-minimum-password-length-using-gitlab-ui) the minimum number of characters a user must have in their password using the GitLab UI. -## Whitelist email domains +## Allowlist email domains > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/598) in GitLab 7.11.0 You can restrict users to only sign up using email addresses matching the given domains list. -## Blacklist email domains +## Denylist email domains > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/5259) in GitLab 8.10. @@ -71,17 +71,17 @@ To access this feature: 1. Navigate to the **Admin Area > Settings > General**. 1. Expand the **Sign-up restrictions** section. -For the blacklist, you can enter the list manually or upload a `.txt` file that +For the denylist, you can enter the list manually or upload a `.txt` file that contains list entries. -For the whitelist, you must enter the list manually. +For the allowlist, you must enter the list manually. -Both the whitelist and blacklist accept wildcards. For example, you can use +Both the allowlist and denylist accept wildcards. For example, you can use `*.company.com` to accept every `company.com` subdomain, or `*.io` to block all domains ending in `.io`. Domains should be separated by a whitespace, semicolon, comma, or a new line. -![Domain Blacklist](img/domain_blacklist.png) +![Domain Denylist](img/domain_denylist.png) <!-- ## Troubleshooting diff --git a/lib/api/users.rb b/lib/api/users.rb index 335624963aa..aa3ecf4019d 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -84,6 +84,7 @@ module API optional :created_after, type: DateTime, desc: 'Return users created after the specified time' optional :created_before, type: DateTime, desc: 'Return users created before the specified time' optional :without_projects, type: Boolean, default: false, desc: 'Filters only users without projects' + optional :exclude_internal, as: :non_internal, type: Boolean, default: false, desc: 'Filters only non internal users' all_or_none_of :extern_uid, :provider use :sort_params diff --git a/lib/banzai/filter/inline_metrics_filter.rb b/lib/banzai/filter/inline_metrics_filter.rb index 543d98e62be..2872ad7b632 100644 --- a/lib/banzai/filter/inline_metrics_filter.rb +++ b/lib/banzai/filter/inline_metrics_filter.rb @@ -10,7 +10,6 @@ module Banzai # the cost of doing a full regex match. def xpath_search "descendant-or-self::a[contains(@href,'metrics') and \ - contains(@href,'environments') and \ starts-with(@href, '#{gitlab_domain}')]" end @@ -29,7 +28,7 @@ module Banzai params['project'], params['environment'], embedded: true, - **query_params(params['url']) + **query_params(params['url']).except(:environment) ) end end diff --git a/lib/gitlab/metrics/dashboard/url.rb b/lib/gitlab/metrics/dashboard/url.rb index 160ecfb85c9..6dcc73c0f6a 100644 --- a/lib/gitlab/metrics/dashboard/url.rb +++ b/lib/gitlab/metrics/dashboard/url.rb @@ -10,20 +10,23 @@ module Gitlab QUERY_PATTERN = '(?<query>\?[a-zA-Z0-9%.()+_=-]+(&[a-zA-Z0-9%.()+_=-]+)*)?' ANCHOR_PATTERN = '(?<anchor>\#[a-z0-9_-]+)?' - OPTIONAL_DASH_PATTERN = '(?:/-)?' + DASH_PATTERN = '(?:/-)' - # Matches urls for a metrics dashboard. This could be - # either the /metrics endpoint or the /metrics_dashboard - # endpoint. + # Matches urls for a metrics dashboard. + # This regex needs to match the old metrics URL, the new metrics URL, + # and the dashboard URL (inline_metrics_redactor_filter.rb + # uses this regex to match against the dashboard URL.) # - # EX - https://<host>/<namespace>/<project>/environments/<env_id>/metrics + # EX - Old URL: https://<host>/<namespace>/<project>/environments/<env_id>/metrics + # OR + # New URL: https://<host>/<namespace>/<project>/-/metrics?environment=<env_id> + # OR + # dashboard URL: https://<host>/<namespace>/<project>/environments/<env_id>/metrics_dashboard def metrics_regex strong_memoize(:metrics_regex) do regex_for_project_metrics( %r{ - /environments - /(?<environment>\d+) - /(metrics_dashboard|metrics) + ( #{environment_metrics_regex} ) | ( #{non_environment_metrics_regex} ) }x ) end @@ -36,6 +39,7 @@ module Gitlab strong_memoize(:grafana_regex) do regex_for_project_metrics( %r{ + #{DASH_PATTERN}? /grafana /metrics_dashboard }x @@ -44,16 +48,22 @@ module Gitlab end # Matches dashboard urls for a metric chart embed - # for cluster metrics + # for cluster metrics. + # This regex needs to match the dashboard URL as well, not just the trigger URL. + # The inline_metrics_redactor_filter.rb uses this regex to match against + # the dashboard URL. # # EX - https://<host>/<namespace>/<project>/-/clusters/<cluster_id>/?group=Cluster%20Health&title=Memory%20Usage&y_label=Memory%20(GiB) + # dashboard URL - https://<host>/<namespace>/<project>/-/clusters/<cluster_id>/metrics_dashboard?group=Cluster%20Health&title=Memory%20Usage&y_label=Memory%20(GiB) def clusters_regex strong_memoize(:clusters_regex) do regex_for_project_metrics( %r{ + #{DASH_PATTERN}? /clusters /(?<cluster_id>\d+) /? + ( (/metrics) | ( /metrics_dashboard\.json ) )? }x ) end @@ -67,10 +77,11 @@ module Gitlab strong_memoize(:alert_regex) do regex_for_project_metrics( %r{ + #{DASH_PATTERN}? /prometheus /alerts /(?<alert>\d+) - /metrics_dashboard + /metrics_dashboard(\.json)? }x ) end @@ -95,16 +106,37 @@ module Gitlab private + def environment_metrics_regex + %r{ + #{DASH_PATTERN}? + /environments + /(?<environment>\d+) + /(metrics_dashboard|metrics) + }x + end + + def non_environment_metrics_regex + %r{ + #{DASH_PATTERN} + /metrics + (?= # Lookahead to ensure there is an environment query param + \? + .* + environment=(?<environment>\d+) + .* + ) + }x + end + def regex_for_project_metrics(path_suffix_pattern) %r{ - (?<url> + ^(?<url> #{gitlab_host_pattern} #{project_path_pattern} - #{OPTIONAL_DASH_PATTERN} #{path_suffix_pattern} #{QUERY_PATTERN} #{ANCHOR_PATTERN} - ) + )$ }x end diff --git a/lib/tasks/pngquant.rake b/lib/tasks/pngquant.rake index ceb4de55373..63bc1c7c16e 100644 --- a/lib/tasks/pngquant.rake +++ b/lib/tasks/pngquant.rake @@ -2,10 +2,10 @@ return if Rails.env.production? require 'png_quantizator' require 'parallel' +require_relative '../../tooling/lib/tooling/images' # The amount of variance (in bytes) allowed in # file size when testing for compression size -TOLERANCE = 10000 namespace :pngquant do # Returns an array of all images eligible for compression @@ -13,55 +13,13 @@ namespace :pngquant do Dir.glob('doc/**/*.png', File::FNM_CASEFOLD) end - # Runs pngquant on an image and optionally - # writes the result to disk - def compress_image(file, overwrite_original) - compressed_file = "#{file}.compressed" - FileUtils.copy(file, compressed_file) - - pngquant_file = PngQuantizator::Image.new(compressed_file) - - # Run the image repeatedly through pngquant until - # the change in file size is within TOLERANCE - loop do - before = File.size(compressed_file) - pngquant_file.quantize! - after = File.size(compressed_file) - break if before - after <= TOLERANCE - end - - savings = File.size(file) - File.size(compressed_file) - is_uncompressed = savings > TOLERANCE - - if is_uncompressed && overwrite_original - FileUtils.copy(compressed_file, file) - end - - FileUtils.remove(compressed_file) - - [is_uncompressed, savings] - end - - # Ensures pngquant is available and prints an error if not - def check_executable - unless system('pngquant --version', out: File::NULL) - warn( - 'Error: pngquant executable was not detected in the system.'.color(:red), - 'Download pngquant at https://pngquant.org/ and place the executable in /usr/local/bin'.color(:green) - ) - abort - end - end - desc 'GitLab | Pngquant | Compress all documentation PNG images using pngquant' task :compress do - check_executable - files = doc_images puts "Compressing #{files.size} PNG files in doc/**" Parallel.each(files) do |file| - was_uncompressed, savings = compress_image(file, true) + was_uncompressed, savings = Tooling::Image.compress_image(file) if was_uncompressed puts "#{file} was reduced by #{savings} bytes" @@ -71,13 +29,11 @@ namespace :pngquant do desc 'GitLab | Pngquant | Checks that all documentation PNG images have been compressed with pngquant' task :lint do - check_executable - files = doc_images puts "Checking #{files.size} PNG files in doc/**" uncompressed_files = Parallel.map(files) do |file| - is_uncompressed, _ = compress_image(file, false) + is_uncompressed, _ = Tooling::Image.compress_image(file, true) if is_uncompressed puts "Uncompressed file detected: ".color(:red) + file file diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 8b819f2acb7..85e76c9137b 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -5156,6 +5156,9 @@ msgstr "" msgid "ClusterAgent|This feature is only available for premium plans" msgstr "" +msgid "ClusterAgent|User has insufficient permissions to create a token for this project" +msgstr "" + msgid "ClusterAgent|You have insufficient permissions to create a cluster agent for this project" msgstr "" @@ -8583,7 +8586,10 @@ msgstr "" msgid "Detect host keys" msgstr "" -msgid "DevOps Score" +msgid "DevOps" +msgstr "" + +msgid "DevOps Report" msgstr "" msgid "Diff content limits" @@ -8729,7 +8735,7 @@ msgid_plural "Dismiss %d selected vulnerabilities as" msgstr[0] "" msgstr[1] "" -msgid "Dismiss DevOps Score introduction" +msgid "Dismiss DevOps Report introduction" msgstr "" msgid "Dismiss Merge Request promotion" @@ -13318,7 +13324,7 @@ msgstr "" msgid "Introducing Value Stream Analytics" msgstr "" -msgid "Introducing Your DevOps Score" +msgid "Introducing Your DevOps Report" msgstr "" msgid "Invalid Git ref" @@ -21469,6 +21475,9 @@ msgstr "" msgid "Scopes can't be blank" msgstr "" +msgid "Score" +msgstr "" + msgid "Scroll down" msgstr "" @@ -28572,7 +28581,7 @@ msgstr "" msgid "Your Default Notification Email will be used for account notifications if a %{openingTag}group-specific email address%{closingTag} is not set." msgstr "" -msgid "Your DevOps Score gives an overview of how you are using GitLab from a feature perspective. View how you compare with other organizations, discover features you are not using, and learn best practices through blog posts and white papers." +msgid "Your DevOps Report gives an overview of how you are using GitLab from a feature perspective. View how you compare with other organizations, discover features you are not using, and learn best practices through blog posts and white papers." msgstr "" msgid "Your GPG keys (%{count})" @@ -29323,9 +29332,6 @@ msgstr "" msgid "in project %{link_to_project}" msgstr "" -msgid "index" -msgstr "" - msgid "instance completed" msgid_plural "instances completed" msgstr[0] "" @@ -29997,9 +30003,6 @@ msgstr "" msgid "satisfied" msgstr "" -msgid "score" -msgstr "" - msgid "security Reports|There was an error creating the merge request" msgstr "" diff --git a/rubocop/cop/rspec/top_level_describe_path.rb b/rubocop/cop/rspec/top_level_describe_path.rb index 61796e23af0..3cc1ee8df90 100644 --- a/rubocop/cop/rspec/top_level_describe_path.rb +++ b/rubocop/cop/rspec/top_level_describe_path.rb @@ -21,7 +21,7 @@ module RuboCop private def acceptable_file_path?(path) - File.fnmatch?('*_spec.rb', path) || File.fnmatch?('*/frontend/fixtures/*', path) + File.fnmatch?('*_spec.rb', path) || File.fnmatch?('*/frontend/fixtures/*', path) || File.fnmatch?('*/docs_screenshots/*_docs.rb', path) end def shared_example?(node) diff --git a/scripts/docs_screenshots.rb b/scripts/docs_screenshots.rb new file mode 100755 index 00000000000..e02d67de748 --- /dev/null +++ b/scripts/docs_screenshots.rb @@ -0,0 +1,60 @@ +#!/usr/bin/env ruby + +# frozen_string_literal: true + +require 'png_quantizator' +require 'open3' +require 'parallel' +require_relative '../tooling/lib/tooling/images.rb' + +generator = ARGV[0] +milestone = ARGV[1] + +unless generator + warn('Error: missing generator, please supply one') + abort +end + +unless milestone + warn('Error: missing milestone, please supply one') + abort +end + +def rename_image(file, milestone) + path = File.dirname(file) + basename = File.basename(file) + final_name = File.join(path, "#{basename}_v#{milestone}.png") + FileUtils.mv(file, final_name) +end + +system('spring', 'rspec', generator) + +files = [] + +Open3.popen3("git diff --name-only -- '*.png'") do |stdin, stdout, stderr, thread| + files.concat stdout.read.chomp.split("\n") +end + +Open3.popen3("git status --porcelain -- '*.png'") do |stdin, stdout, stderr, thread| + files.concat stdout.read.chomp.split("?? ") +end + +files.reject!(&:empty?) + +if files.empty? + puts "No file generated, did you select the right screenshot generator?" +else + puts "Compressing newly generated screenshots" + + Parallel.each(files) do |file| + file_path = File.join(Dir.pwd, file.to_s.strip) + was_uncompressed, savings = Tooling::Image.compress_image(file_path) + rename_image(file_path, milestone) + + if was_uncompressed + puts "#{file} was reduced by #{savings} bytes." + else + puts "Skipping already compressed file: #{file}." + end + end +end diff --git a/spec/controllers/projects/graphs_controller_spec.rb b/spec/controllers/projects/graphs_controller_spec.rb index 49def8f80b0..be89fa0d361 100644 --- a/spec/controllers/projects/graphs_controller_spec.rb +++ b/spec/controllers/projects/graphs_controller_spec.rb @@ -44,7 +44,7 @@ RSpec.describe Projects::GraphsController do context 'when anonymous users can read build report results' do it 'sets the daily coverage options' do - Timecop.freeze do + freeze_time do get(:charts, params: { namespace_id: project.namespace.path, project_id: project.path, id: 'master' }) expect(assigns[:daily_coverage_options]).to eq( diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index a0e478ef368..f851418ae09 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -6,9 +6,9 @@ RSpec.describe Projects::IssuesController do include ProjectForksHelper include_context 'includes Spam constants' - let(:project) { create(:project) } - let(:user) { create(:user) } - let(:issue) { create(:issue, project: project) } + let_it_be(:project, reload: true) { create(:project) } + let_it_be(:user, reload: true) { create(:user) } + let(:issue) { create(:issue, project: project) } describe "GET #index" do context 'external issue tracker' do @@ -39,8 +39,8 @@ RSpec.describe Projects::IssuesController do end context 'when project has moved' do - let(:new_project) { create(:project) } - let(:issue) { create(:issue, project: new_project) } + let_it_be(:new_project) { create(:project) } + let_it_be(:issue) { create(:issue, project: new_project) } before do project.route.destroy @@ -297,6 +297,7 @@ RSpec.describe Projects::IssuesController do project.add_developer(developer) end + let_it_be(:issue) { create(:issue, project: project) } let(:developer) { user } let(:params) do { @@ -401,7 +402,7 @@ RSpec.describe Projects::IssuesController do end context 'when moving issue to another private project' do - let(:another_project) { create(:project, :private) } + let_it_be(:another_project) { create(:project, :private) } context 'when user has access to move issue' do before do @@ -438,10 +439,10 @@ RSpec.describe Projects::IssuesController do end describe 'PUT #reorder' do - let(:group) { create(:group, projects: [project]) } - let!(:issue1) { create(:issue, project: project, relative_position: 10) } - let!(:issue2) { create(:issue, project: project, relative_position: 20) } - let!(:issue3) { create(:issue, project: project, relative_position: 30) } + let_it_be(:group) { create(:group, projects: [project]) } + let_it_be(:issue1) { create(:issue, project: project, relative_position: 10) } + let_it_be(:issue2) { create(:issue, project: project, relative_position: 20) } + let_it_be(:issue3) { create(:issue, project: project, relative_position: 30) } before do sign_in(user) @@ -657,15 +658,15 @@ RSpec.describe Projects::IssuesController do end describe 'Confidential Issues' do - let(:project) { create(:project_empty_repo, :public) } - let(:assignee) { create(:assignee) } - let(:author) { create(:user) } - let(:non_member) { create(:user) } - let(:member) { create(:user) } - let(:admin) { create(:admin) } - let!(:issue) { create(:issue, project: project) } - let!(:unescaped_parameter_value) { create(:issue, :confidential, project: project, author: author) } - let!(:request_forgery_timing_attack) { create(:issue, :confidential, project: project, assignees: [assignee]) } + let_it_be(:project) { create(:project_empty_repo, :public) } + let_it_be(:assignee) { create(:assignee) } + let_it_be(:author) { create(:user) } + let_it_be(:non_member) { create(:user) } + let_it_be(:member) { create(:user) } + let_it_be(:admin) { create(:admin) } + let_it_be(:issue) { create(:issue, project: project) } + let_it_be(:unescaped_parameter_value) { create(:issue, :confidential, project: project, author: author) } + let_it_be(:request_forgery_timing_attack) { create(:issue, :confidential, project: project, assignees: [assignee]) } describe 'GET #index' do it 'does not list confidential issues for guests' do @@ -1077,7 +1078,7 @@ RSpec.describe Projects::IssuesController do end context 'resolving discussions in MergeRequest' do - let(:discussion) { create(:diff_note_on_merge_request).to_discussion } + let_it_be(:discussion) { create(:diff_note_on_merge_request).to_discussion } let(:merge_request) { discussion.noteable } let(:project) { merge_request.source_project } @@ -1376,9 +1377,9 @@ RSpec.describe Projects::IssuesController do end context "when the user is owner" do - let(:owner) { create(:user) } - let(:namespace) { create(:namespace, owner: owner) } - let(:project) { create(:project, namespace: namespace) } + let_it_be(:owner) { create(:user) } + let_it_be(:namespace) { create(:namespace, owner: owner) } + let_it_be(:project) { create(:project, namespace: namespace) } before do sign_in(owner) @@ -1461,7 +1462,8 @@ RSpec.describe Projects::IssuesController do describe 'POST create_merge_request' do let(:target_project_id) { nil } - let(:project) { create(:project, :repository, :public) } + + let_it_be(:project) { create(:project, :repository, :public) } before do project.add_developer(user) @@ -1539,7 +1541,7 @@ RSpec.describe Projects::IssuesController do end describe 'POST #import_csv' do - let(:project) { create(:project, :public) } + let_it_be(:project) { create(:project, :public) } let(:file) { fixture_file_upload('spec/fixtures/csv_comma.csv') } context 'unauthorized' do @@ -1621,7 +1623,7 @@ RSpec.describe Projects::IssuesController do end context 'when not logged in' do - let(:project) { create(:project_empty_repo, :public) } + let(:empty_project) { create(:project_empty_repo, :public) } it 'redirects to the sign in page' do request_csv @@ -1738,7 +1740,7 @@ RSpec.describe Projects::IssuesController do end context 'with cross-reference system note', :request_store do - let(:new_issue) { create(:issue) } + let_it_be(:new_issue) { create(:issue) } let(:cross_reference) { "mentioned in #{new_issue.to_reference(issue.project)}" } before do @@ -1836,7 +1838,7 @@ RSpec.describe Projects::IssuesController do end context 'private project with token authentication' do - let(:private_project) { create(:project, :private) } + let_it_be(:private_project) { create(:project, :private) } it_behaves_like 'authenticates sessionless user', :index, :atom, ignore_incrementing: true do before do @@ -1856,7 +1858,7 @@ RSpec.describe Projects::IssuesController do end context 'public project with token authentication' do - let(:public_project) { create(:project, :public) } + let_it_be(:public_project) { create(:project, :public) } it_behaves_like 'authenticates sessionless user', :index, :atom, public: true do before do diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb index 570d65dba4f..8c59b2b378f 100644 --- a/spec/controllers/projects/notes_controller_spec.rb +++ b/spec/controllers/projects/notes_controller_spec.rb @@ -98,7 +98,7 @@ RSpec.describe Projects::NotesController do let(:page_2_boundary) { microseconds(page_2.last.updated_at + NotesFinder::FETCH_OVERLAP) } around do |example| - Timecop.freeze do + freeze_time do example.run end end diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb index 1c9167ef025..ff39bf81d3f 100644 --- a/spec/db/schema_spec.rb +++ b/spec/db/schema_spec.rb @@ -35,7 +35,6 @@ RSpec.describe 'Database schema' do deploy_keys_projects: %w[deploy_key_id], deployments: %w[deployable_id environment_id user_id], draft_notes: %w[discussion_id commit_id], - emails: %w[user_id], epics: %w[updated_by_id last_edited_by_id state_id], events: %w[target_id], forked_project_links: %w[forked_from_project_id], diff --git a/spec/docs_screenshots/container_registry_docs.rb b/spec/docs_screenshots/container_registry_docs.rb new file mode 100644 index 00000000000..7e533bdf1d7 --- /dev/null +++ b/spec/docs_screenshots/container_registry_docs.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Container Registry', :js do + include DocsScreenshotHelpers + + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:project) { create(:project, namespace: group) } + + before do + page.driver.browser.manage.window.resize_to(1366, 1024) + + group.add_owner(user) + sign_in(user) + + stub_container_registry_config(enabled: true) + stub_container_registry_tags(repository: :any, tags: []) + end + + context 'expiration policy settings' do + it 'user/packages/container_registry/img/expiration_policy_form' do + visit project_settings_ci_cd_path(project) + screenshot_area = find('#js-registry-policies') + scroll_to screenshot_area + expect(screenshot_area).to have_content 'Expiration interval' + set_crop_data(screenshot_area, 20) + end + end + + context 'project container_registry' do + it 'user/packages/container_registry/img/project_empty_page' do + visit_project_container_registry + + expect(page).to have_content _('There are no container images stored for this project') + end + + context 'with a list of repositories' do + before do + stub_container_registry_tags(repository: %r{my/image}, tags: %w[latest], with_manifest: true) + create_list(:container_repository, 12, project: project) + end + + it 'user/packages/container_registry/img/project_image_repositories_list' do + visit_project_container_registry + + expect(page).to have_content 'Image Repositories' + end + + it 'user/packages/container_registry/img/project_image_repositories_list_with_commands_open' do + visit_project_container_registry + + click_on 'CLI Commands' + end + end + end + + context 'group container_registry' do + it 'user/packages/container_registry/img/group_empty_page' do + visit_group_container_registry + + expect(page).to have_content 'There are no container images available in this group' + end + + context 'with a list of repositories' do + before do + stub_container_registry_tags(repository: %r{my/image}, tags: %w[latest], with_manifest: true) + create_list(:container_repository, 12, project: project) + end + + it 'user/packages/container_registry/img/group_image_repositories_list' do + visit_group_container_registry + + expect(page).to have_content 'Image Repositories' + end + end + end + + def visit_project_container_registry + visit project_container_registry_index_path(project) + end + + def visit_group_container_registry + visit group_container_registries_path(group) + end +end diff --git a/spec/features/admin/admin_dev_ops_score_spec.rb b/spec/features/admin/admin_dev_ops_score_spec.rb index 72e82156526..31a2b4bbe72 100644 --- a/spec/features/admin/admin_dev_ops_score_spec.rb +++ b/spec/features/admin/admin_dev_ops_score_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'DevOps Score page' do +RSpec.describe 'DevOps Report page' do before do sign_in(create(:admin)) end @@ -10,11 +10,11 @@ RSpec.describe 'DevOps Score page' do it 'has dismissable intro callout', :js do visit admin_dev_ops_score_path - expect(page).to have_content 'Introducing Your DevOps Score' + expect(page).to have_content 'Introducing Your DevOps Report' find('.js-close-callout').click - expect(page).not_to have_content 'Introducing Your DevOps Score' + expect(page).not_to have_content 'Introducing Your DevOps Report' end context 'when usage ping is disabled' do @@ -31,7 +31,7 @@ RSpec.describe 'DevOps Score page' do it 'hides the intro callout' do visit admin_dev_ops_score_path - expect(page).not_to have_content 'Introducing Your DevOps Score' + expect(page).not_to have_content 'Introducing Your DevOps Report' end end diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb index 65f2e5dfc0d..4b4cb444903 100644 --- a/spec/features/boards/sidebar_spec.rb +++ b/spec/features/boards/sidebar_spec.rb @@ -23,7 +23,7 @@ RSpec.describe 'Issue Boards', :js do let(:application_settings) { {} } around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end before do diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb index 2217bd9d6b5..3ae9a2b7555 100644 --- a/spec/features/groups/milestone_spec.rb +++ b/spec/features/groups/milestone_spec.rb @@ -8,7 +8,7 @@ RSpec.describe 'Group milestones' do let_it_be(:user) { create(:group_member, :maintainer, user: create(:user), group: group ).user } around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end before do diff --git a/spec/features/projects/members/invite_group_spec.rb b/spec/features/projects/members/invite_group_spec.rb index 058cbfff662..30e32ad1366 100644 --- a/spec/features/projects/members/invite_group_spec.rb +++ b/spec/features/projects/members/invite_group_spec.rb @@ -112,7 +112,7 @@ RSpec.describe 'Project > Members > Invite group', :js do let!(:group) { create(:group) } around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end before do diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb index 2d0fcfe84e6..a2fd1caf6a3 100644 --- a/spec/features/users/login_spec.rb +++ b/spec/features/users/login_spec.rb @@ -503,7 +503,7 @@ RSpec.describe 'Login' do context 'within the grace period' do it 'redirects to two-factor configuration page' do - Timecop.freeze do + freeze_time do expect(authentication_metrics) .to increment(:user_authenticated_counter) diff --git a/spec/finders/users_finder_spec.rb b/spec/finders/users_finder_spec.rb index 17b36247b05..a04f5452fcd 100644 --- a/spec/finders/users_finder_spec.rb +++ b/spec/finders/users_finder_spec.rb @@ -12,7 +12,7 @@ RSpec.describe UsersFinder do it 'returns all users' do users = described_class.new(user).execute - expect(users).to contain_exactly(user, normal_user, blocked_user, omniauth_user) + expect(users).to contain_exactly(user, normal_user, blocked_user, omniauth_user, internal_user) end it 'filters by username' do @@ -54,7 +54,7 @@ RSpec.describe UsersFinder do it 'returns no external users' do users = described_class.new(user, external: true).execute - expect(users).to contain_exactly(user, normal_user, blocked_user, omniauth_user) + expect(users).to contain_exactly(user, normal_user, blocked_user, omniauth_user, internal_user) end it 'filters by created_at' do @@ -68,19 +68,25 @@ RSpec.describe UsersFinder do expect(users.map(&:username)).not_to include([filtered_user_before.username, filtered_user_after.username]) end + it 'filters by non internal users' do + users = described_class.new(user, non_internal: true).execute + + expect(users).to contain_exactly(user, normal_user, blocked_user, omniauth_user) + end + it 'does not filter by custom attributes' do users = described_class.new( user, custom_attributes: { foo: 'bar' } ).execute - expect(users).to contain_exactly(user, normal_user, blocked_user, omniauth_user) + expect(users).to contain_exactly(user, normal_user, blocked_user, omniauth_user, internal_user) end it 'orders returned results' do users = described_class.new(user, sort: 'id_asc').execute - expect(users).to eq([normal_user, blocked_user, omniauth_user, user]) + expect(users).to eq([normal_user, blocked_user, omniauth_user, internal_user, user]) end end @@ -96,13 +102,14 @@ RSpec.describe UsersFinder do it 'returns all users' do users = described_class.new(admin).execute - expect(users).to contain_exactly(admin, normal_user, blocked_user, external_user, omniauth_user) + expect(users).to contain_exactly(admin, normal_user, blocked_user, external_user, omniauth_user, internal_user) end it 'filters by custom attributes' do create :user_custom_attribute, user: normal_user, key: 'foo', value: 'foo' create :user_custom_attribute, user: normal_user, key: 'bar', value: 'bar' create :user_custom_attribute, user: blocked_user, key: 'foo', value: 'foo' + create :user_custom_attribute, user: internal_user, key: 'foo', value: 'foo' users = described_class.new( admin, diff --git a/spec/graphql/types/merge_request_type_spec.rb b/spec/graphql/types/merge_request_type_spec.rb index b11951190e0..a9a74114dda 100644 --- a/spec/graphql/types/merge_request_type_spec.rb +++ b/spec/graphql/types/merge_request_type_spec.rb @@ -27,7 +27,10 @@ RSpec.describe GitlabSchema.types['MergeRequest'] do total_time_spent reference author merged_at commit_count ] - expected_fields << 'approved_by' if Gitlab.ee? + if Gitlab.ee? + expected_fields << 'approved' + expected_fields << 'approved_by' + end expect(described_class).to have_graphql_fields(*expected_fields) end diff --git a/spec/lib/banzai/filter/inline_metrics_filter_spec.rb b/spec/lib/banzai/filter/inline_metrics_filter_spec.rb index 9b0b95b9da2..cdebd886b16 100644 --- a/spec/lib/banzai/filter/inline_metrics_filter_spec.rb +++ b/spec/lib/banzai/filter/inline_metrics_filter_spec.rb @@ -5,25 +5,68 @@ require 'spec_helper' RSpec.describe Banzai::Filter::InlineMetricsFilter do include FilterSpecHelper - let(:params) { ['foo', 'bar', 12] } - let(:query_params) { {} } - - let(:trigger_url) { urls.metrics_namespace_project_environment_url(*params, query_params) } + let(:environment_id) { 12 } let(:dashboard_url) { urls.metrics_dashboard_namespace_project_environment_url(*params, **query_params, embedded: true) } - it_behaves_like 'a metrics embed filter' + let(:query_params) do + { + dashboard: 'config/prometheus/common_metrics.yml', + group: 'System metrics (Kubernetes)', + title: 'Core Usage (Pod Average)', + y_label: 'Cores per Pod' + } + end + + context 'with /-/environments/:environment_id/metrics URL' do + let(:params) { ['group', 'project', environment_id] } + let(:trigger_url) { urls.metrics_namespace_project_environment_url(*params, **query_params) } + + context 'with no query params' do + let(:query_params) { {} } + + it_behaves_like 'a metrics embed filter' + end + + context 'with query params' do + it_behaves_like 'a metrics embed filter' + end + end - context 'with query params specified' do - let(:query_params) do - { - dashboard: 'config/prometheus/common_metrics.yml', - group: 'System metrics (Kubernetes)', - title: 'Core Usage (Pod Average)', - y_label: 'Cores per Pod' - } + context 'with /-/metrics?environment=:environment_id URL' do + let(:params) { %w(group project) } + let(:trigger_url) { urls.namespace_project_metrics_dashboard_url(*params, **query_params) } + let(:dashboard_url) do + urls.metrics_dashboard_namespace_project_environment_url( + *params.append(environment_id), + **query_params.except(:environment), + embedded: true + ) end - it_behaves_like 'a metrics embed filter' + context 'with query params' do + it_behaves_like 'a metrics embed filter' do + before do + query_params.merge!(environment: environment_id) + end + end + end + + context 'with only environment in query params' do + let(:query_params) { { environment: environment_id } } + + it_behaves_like 'a metrics embed filter' + end + + context 'with no query params' do + let(:query_params) { {} } + + it 'ignores metrics URL without environment parameter' do + input = %(<a href="#{trigger_url}">example</a>) + filtered_input = filter(input).to_s + + expect(CGI.unescape_html(filtered_input)).to eq(input) + end + end end it 'leaves links to other dashboards unchanged' do diff --git a/spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb b/spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb index 5f66844f498..3c736b46131 100644 --- a/spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb +++ b/spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb @@ -22,6 +22,13 @@ RSpec.describe Banzai::Filter::InlineMetricsRedactorFilter do it_behaves_like 'redacts the embed placeholder' it_behaves_like 'retains the embed placeholder when applicable' + context 'with /-/metrics?environment=:environment_id URL' do + let(:url) { urls.project_metrics_dashboard_url(project, embedded: true, environment: 1) } + + it_behaves_like 'redacts the embed placeholder' + it_behaves_like 'retains the embed placeholder when applicable' + end + context 'for a grafana dashboard' do let(:url) { urls.project_grafana_api_metrics_dashboard_url(project, embedded: true) } @@ -33,7 +40,7 @@ RSpec.describe Banzai::Filter::InlineMetricsRedactorFilter do let_it_be(:cluster) { create(:cluster, :provided_by_gcp, :project, projects: [project]) } let(:params) { [project.namespace.path, project.path, cluster.id] } let(:query_params) { { group: 'Cluster Health', title: 'CPU Usage', y_label: 'CPU (cores)' } } - let(:url) { urls.metrics_dashboard_namespace_project_cluster_url(*params, **query_params) } + let(:url) { urls.metrics_dashboard_namespace_project_cluster_url(*params, **query_params, format: :json) } context 'with user who can read cluster' do it_behaves_like 'redacts the embed placeholder' diff --git a/spec/lib/gitlab/alert_management/payload/prometheus_spec.rb b/spec/lib/gitlab/alert_management/payload/prometheus_spec.rb index 2ddcad4134c..457db58a28b 100644 --- a/spec/lib/gitlab/alert_management/payload/prometheus_spec.rb +++ b/spec/lib/gitlab/alert_management/payload/prometheus_spec.rb @@ -45,7 +45,7 @@ RSpec.describe Gitlab::AlertManagement::Payload::Prometheus do let(:current_time) { Time.current.utc } around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end subject { parsed_payload.starts_at } diff --git a/spec/lib/gitlab/app_text_logger_spec.rb b/spec/lib/gitlab/app_text_logger_spec.rb index 04c2e946640..e8bee0f9903 100644 --- a/spec/lib/gitlab/app_text_logger_spec.rb +++ b/spec/lib/gitlab/app_text_logger_spec.rb @@ -17,7 +17,7 @@ RSpec.describe Gitlab::AppTextLogger do end it 'logs time in UTC with ISO8601.3 standard' do - Timecop.freeze do + freeze_time do expect(subject.format_message('INFO', Time.now, nil, string_message)) .to include(Time.now.utc.iso8601(3)) end diff --git a/spec/lib/gitlab/ci/jwt_spec.rb b/spec/lib/gitlab/ci/jwt_spec.rb index a15f3310dab..9b133efad9c 100644 --- a/spec/lib/gitlab/ci/jwt_spec.rb +++ b/spec/lib/gitlab/ci/jwt_spec.rb @@ -20,7 +20,7 @@ RSpec.describe Gitlab::Ci::Jwt do subject(:payload) { described_class.new(build, ttl: 30).payload } it 'has correct values for the standard JWT attributes' do - Timecop.freeze do + freeze_time do now = Time.now.to_i aggregate_failures do diff --git a/spec/lib/gitlab/conan_token_spec.rb b/spec/lib/gitlab/conan_token_spec.rb index b17f2eaa8d8..be1d3e757f5 100644 --- a/spec/lib/gitlab/conan_token_spec.rb +++ b/spec/lib/gitlab/conan_token_spec.rb @@ -85,7 +85,7 @@ RSpec.describe Gitlab::ConanToken do it 'returns the encoded JWT' do allow(SecureRandom).to receive(:uuid).and_return('u-u-i-d') - Timecop.freeze do + freeze_time do jwt = build_jwt(access_token_id: 123, user_id: 456) token = described_class.new(access_token_id: 123, user_id: 456) diff --git a/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb index afab19de2ab..17104715580 100644 --- a/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb @@ -34,7 +34,7 @@ RSpec.describe Gitlab::CycleAnalytics::CodeStage do describe '#project_median' do around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end it 'counts median from issues with metrics' do @@ -76,7 +76,7 @@ RSpec.describe Gitlab::CycleAnalytics::CodeStage do describe '#group_median' do around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end it 'counts median from issues with metrics' do diff --git a/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb index 9ec71e6ed72..c7ab2b9b84b 100644 --- a/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb @@ -29,7 +29,7 @@ RSpec.describe Gitlab::CycleAnalytics::IssueStage do describe '#median' do around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end it 'counts median from issues with metrics' do @@ -65,7 +65,7 @@ RSpec.describe Gitlab::CycleAnalytics::IssueStage do describe '#group_median' do around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end it 'counts median from issues with metrics' do @@ -87,7 +87,7 @@ RSpec.describe Gitlab::CycleAnalytics::IssueStage do describe '#group_median' do around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end it 'counts median from issues with metrics' do diff --git a/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb index 66d00edacb7..2547c05c025 100644 --- a/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb @@ -29,7 +29,7 @@ RSpec.describe Gitlab::CycleAnalytics::PlanStage do describe '#project_median' do around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end it 'counts median from issues with metrics' do @@ -67,7 +67,7 @@ RSpec.describe Gitlab::CycleAnalytics::PlanStage do describe '#group_median' do around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end it 'counts median from issues with metrics' do diff --git a/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb index cdd1cca6837..5593013740e 100644 --- a/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb @@ -27,7 +27,7 @@ RSpec.describe Gitlab::CycleAnalytics::ReviewStage do describe '#project_median' do around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end it 'counts median from issues with metrics' do @@ -70,7 +70,7 @@ RSpec.describe Gitlab::CycleAnalytics::ReviewStage do describe '#group_median' do around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end it 'counts median from issues with metrics' do diff --git a/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb b/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb index 9ece24074e7..719d4a69985 100644 --- a/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb @@ -231,7 +231,7 @@ RSpec.describe Gitlab::CycleAnalytics::StageSummary do context 'when `from` and `to` are within a day' do it 'returns the number of deployments made on that day' do - Timecop.freeze(Time.now) do + freeze_time do create(:deployment, :success, project: project) options[:from] = options[:to] = Time.now diff --git a/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb index 69e42adb139..852f7041dc6 100644 --- a/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb @@ -32,7 +32,7 @@ RSpec.describe Gitlab::CycleAnalytics::StagingStage do describe '#project_median' do around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end it 'counts median from issues with metrics' do @@ -79,7 +79,7 @@ RSpec.describe Gitlab::CycleAnalytics::StagingStage do describe '#group_median' do around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end it 'counts median from issues with metrics' do diff --git a/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb index 9a207d32167..49ee6624260 100644 --- a/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb @@ -37,7 +37,7 @@ RSpec.describe Gitlab::CycleAnalytics::TestStage do end around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end it 'counts median from issues with metrics' do diff --git a/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb b/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb index 042ac498373..48132d68031 100644 --- a/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb +++ b/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb @@ -86,7 +86,7 @@ RSpec.describe Gitlab::Database::Migrations::BackgroundMigrationHelpers do let!(:id3) { create(:user).id } around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end before do diff --git a/spec/lib/gitlab/external_authorization/access_spec.rb b/spec/lib/gitlab/external_authorization/access_spec.rb index 4bb81230ac0..a6773cc19e1 100644 --- a/spec/lib/gitlab/external_authorization/access_spec.rb +++ b/spec/lib/gitlab/external_authorization/access_spec.rb @@ -7,7 +7,7 @@ RSpec.describe Gitlab::ExternalAuthorization::Access, :clean_gitlab_redis_cache describe '#loaded?' do it 'is `true` when it was loaded recently' do - Timecop.freeze do + freeze_time do allow(access).to receive(:loaded_at).and_return(5.minutes.ago) expect(access).to be_loaded @@ -19,7 +19,7 @@ RSpec.describe Gitlab::ExternalAuthorization::Access, :clean_gitlab_redis_cache end it 'is `false` when there the result was loaded a long time ago' do - Timecop.freeze do + freeze_time do allow(access).to receive(:loaded_at).and_return(2.weeks.ago) expect(access).not_to be_loaded @@ -70,7 +70,7 @@ RSpec.describe Gitlab::ExternalAuthorization::Access, :clean_gitlab_redis_cache end it 'stores the result in redis' do - Timecop.freeze do + freeze_time do fake_cache = double expect(fake_cache).to receive(:store).with(true, nil, Time.now) expect(access).to receive(:cache).and_return(fake_cache) @@ -118,7 +118,7 @@ RSpec.describe Gitlab::ExternalAuthorization::Access, :clean_gitlab_redis_cache end it 'does not load from the webservice' do - Timecop.freeze do + freeze_time do expect(fake_cache).to receive(:load).and_return([true, nil, Time.now]) expect(access).to receive(:load_from_cache).and_call_original @@ -129,7 +129,7 @@ RSpec.describe Gitlab::ExternalAuthorization::Access, :clean_gitlab_redis_cache end it 'loads from the webservice when the cached result was too old' do - Timecop.freeze do + freeze_time do expect(fake_cache).to receive(:load).and_return([true, nil, 2.days.ago]) expect(access).to receive(:load_from_cache).and_call_original diff --git a/spec/lib/gitlab/external_authorization/cache_spec.rb b/spec/lib/gitlab/external_authorization/cache_spec.rb index 9037c04cf2b..a8e7932b82c 100644 --- a/spec/lib/gitlab/external_authorization/cache_spec.rb +++ b/spec/lib/gitlab/external_authorization/cache_spec.rb @@ -22,7 +22,7 @@ RSpec.describe Gitlab::ExternalAuthorization::Cache, :clean_gitlab_redis_cache d describe '#load' do it 'reads stored info from redis' do - Timecop.freeze do + freeze_time do set_in_redis(:access, false) set_in_redis(:reason, 'Access denied for now') set_in_redis(:refreshed_at, Time.now) @@ -38,7 +38,7 @@ RSpec.describe Gitlab::ExternalAuthorization::Cache, :clean_gitlab_redis_cache d describe '#store' do it 'sets the values in redis' do - Timecop.freeze do + freeze_time do cache.store(true, 'the reason', Time.now) expect(read_from_redis(:access)).to eq('true') diff --git a/spec/lib/gitlab/github_import/importer/label_links_importer_spec.rb b/spec/lib/gitlab/github_import/importer/label_links_importer_spec.rb index 4d3245fc988..6d143f78c66 100644 --- a/spec/lib/gitlab/github_import/importer/label_links_importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer/label_links_importer_spec.rb @@ -38,7 +38,7 @@ RSpec.describe Gitlab::GithubImport::Importer::LabelLinksImporter do .to receive(:find_target_id) .and_return(1) - Timecop.freeze do + freeze_time do expect(Gitlab::Database) .to receive(:bulk_insert) .with( diff --git a/spec/lib/gitlab/github_import/importer/labels_importer_spec.rb b/spec/lib/gitlab/github_import/importer/labels_importer_spec.rb index 0010b959a49..ca9d3e1e21c 100644 --- a/spec/lib/gitlab/github_import/importer/labels_importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer/labels_importer_spec.rb @@ -85,13 +85,13 @@ RSpec.describe Gitlab::GithubImport::Importer::LabelsImporter, :clean_gitlab_red end it 'includes the created timestamp' do - Timecop.freeze do + freeze_time do expect(label_hash[:created_at]).to eq(Time.zone.now) end end it 'includes the updated timestamp' do - Timecop.freeze do + freeze_time do expect(label_hash[:updated_at]).to eq(Time.zone.now) end end diff --git a/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb index 05ac0248ec9..0835c6155b9 100644 --- a/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb @@ -164,7 +164,7 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestsImporter do .to receive(:increment) .and_call_original - Timecop.freeze do + freeze_time do importer.update_repository expect(project.last_repository_updated_at).to be_like_time(Time.zone.now) diff --git a/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb b/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb index 65dba2711b9..180c6d9e420 100644 --- a/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb @@ -261,7 +261,7 @@ RSpec.describe Gitlab::GithubImport::Importer::RepositoryImporter do describe '#update_clone_time' do it 'sets the timestamp for when the cloning process finished' do - Timecop.freeze do + freeze_time do expect(project) .to receive(:update_column) .with(:last_repository_updated_at, Time.zone.now) diff --git a/spec/lib/gitlab/incident_management/pager_duty/incident_issue_description_spec.rb b/spec/lib/gitlab/incident_management/pager_duty/incident_issue_description_spec.rb index 6dc96217f09..535cce6aa04 100644 --- a/spec/lib/gitlab/incident_management/pager_duty/incident_issue_description_spec.rb +++ b/spec/lib/gitlab/incident_management/pager_duty/incident_issue_description_spec.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require 'fast_spec_helper' -require 'timecop' RSpec.describe Gitlab::IncidentManagement::PagerDuty::IncidentIssueDescription do describe '#to_s' do @@ -50,7 +49,7 @@ RSpec.describe Gitlab::IncidentManagement::PagerDuty::IncidentIssueDescription d let(:created_at) { nil } it 'description contains current time in UTC' do - Timecop.freeze do + freeze_time do now = Time.current.utc.strftime('%d %B %Y, %-l:%M%p (%Z)') expect(to_s).to include( diff --git a/spec/lib/gitlab/log_timestamp_formatter_spec.rb b/spec/lib/gitlab/log_timestamp_formatter_spec.rb index e06baa2324f..b51d0fec15e 100644 --- a/spec/lib/gitlab/log_timestamp_formatter_spec.rb +++ b/spec/lib/gitlab/log_timestamp_formatter_spec.rb @@ -8,7 +8,7 @@ RSpec.describe Gitlab::LogTimestampFormatter do let(:formatted_timestamp) { Time.now.utc.iso8601(3) } it 'logs the timestamp in UTC and ISO8601.3 format' do - Timecop.freeze(Time.now) do + freeze_time do expect(subject.call('', Time.now, '', '')).to include formatted_timestamp end end diff --git a/spec/lib/gitlab/metrics/dashboard/url_spec.rb b/spec/lib/gitlab/metrics/dashboard/url_spec.rb index 205e1000376..830d43169a9 100644 --- a/spec/lib/gitlab/metrics/dashboard/url_spec.rb +++ b/spec/lib/gitlab/metrics/dashboard/url_spec.rb @@ -6,11 +6,12 @@ RSpec.describe Gitlab::Metrics::Dashboard::Url do include Gitlab::Routing.url_helpers describe '#metrics_regex' do + let(:environment_id) { 1 } let(:url_params) do [ 'foo', 'bar', - 1, + environment_id, { start: '2019-08-02T05:43:09.000Z', dashboard: 'config/prometheus/common_metrics.yml', @@ -33,12 +34,42 @@ RSpec.describe Gitlab::Metrics::Dashboard::Url do subject { described_class.metrics_regex } - context 'for metrics route' do + context 'for /-/environments/:environment_id/metrics route' do let(:url) { metrics_namespace_project_environment_url(*url_params) } it_behaves_like 'regex which matches url when expected' end + context 'for /-/metrics?environment=:environment_id route' do + let(:url) { namespace_project_metrics_dashboard_url(*url_params) } + let(:url_params) do + [ + 'namespace1', + 'project1', + { + environment: environment_id, + start: '2019-08-02T05:43:09.000Z', + dashboard: 'config/prometheus/common_metrics.yml', + group: 'awesome group', + anchor: 'title' + } + ] + end + + let(:expected_params) do + { + 'url' => url, + 'namespace' => 'namespace1', + 'project' => 'project1', + 'environment' => "#{environment_id}", + 'query' => "?dashboard=config%2Fprometheus%2Fcommon_metrics.yml&environment=#{environment_id}&group=awesome+group&start=2019-08-02T05%3A43%3A09.000Z", + 'anchor' => '#title' + } + end + + it_behaves_like 'regex which matches url when expected' + end + context 'for metrics_dashboard route' do let(:url) { metrics_dashboard_namespace_project_environment_url(*url_params) } @@ -47,16 +78,19 @@ RSpec.describe Gitlab::Metrics::Dashboard::Url do end describe '#clusters_regex' do - let(:url) do - Gitlab::Routing.url_helpers.namespace_project_cluster_url( + let(:url) { Gitlab::Routing.url_helpers.namespace_project_cluster_url(*url_params) } + let(:url_params) do + [ 'foo', 'bar', '1', - group: 'Cluster Health', - title: 'Memory Usage', - y_label: 'Memory 20(GiB)', - anchor: 'title' - ) + { + group: 'Cluster Health', + title: 'Memory Usage', + y_label: 'Memory 20(GiB)', + anchor: 'title' + } + ] end let(:expected_params) do @@ -73,6 +107,27 @@ RSpec.describe Gitlab::Metrics::Dashboard::Url do subject { described_class.clusters_regex } it_behaves_like 'regex which matches url when expected' + + context 'for metrics_dashboard route' do + let(:url) do + metrics_dashboard_namespace_project_cluster_url( + *url_params, cluster_type: :project, embedded: true, format: :json + ) + end + + let(:expected_params) do + { + 'url' => url, + 'namespace' => 'foo', + 'project' => 'bar', + 'cluster_id' => '1', + 'query' => '?cluster_type=project&embedded=true', + 'anchor' => nil + } + end + + it_behaves_like 'regex which matches url when expected' + end end describe '#grafana_regex' do @@ -103,15 +158,18 @@ RSpec.describe Gitlab::Metrics::Dashboard::Url do end describe '#alert_regex' do - let(:url) do - Gitlab::Routing.url_helpers.metrics_dashboard_namespace_project_prometheus_alert_url( + let(:url) { Gitlab::Routing.url_helpers.metrics_dashboard_namespace_project_prometheus_alert_url(*url_params) } + let(:url_params) do + [ 'foo', 'bar', '1', - start: '2020-02-10T12:59:49.938Z', - end: '2020-02-10T20:59:49.938Z', - anchor: "anchor" - ) + { + start: '2020-02-10T12:59:49.938Z', + end: '2020-02-10T20:59:49.938Z', + anchor: "anchor" + } + ] end let(:expected_params) do @@ -128,6 +186,21 @@ RSpec.describe Gitlab::Metrics::Dashboard::Url do subject { described_class.alert_regex } it_behaves_like 'regex which matches url when expected' + + it_behaves_like 'regex which matches url when expected' do + let(:url) { Gitlab::Routing.url_helpers.metrics_dashboard_namespace_project_prometheus_alert_url(*url_params, format: :json) } + + let(:expected_params) do + { + 'url' => url, + 'namespace' => 'foo', + 'project' => 'bar', + 'alert' => '1', + 'query' => nil, + 'anchor' => nil + } + end + end end describe '#build_dashboard_url' do diff --git a/spec/lib/gitlab/metrics/method_call_spec.rb b/spec/lib/gitlab/metrics/method_call_spec.rb index 825c91b6cb4..fb5436a90e3 100644 --- a/spec/lib/gitlab/metrics/method_call_spec.rb +++ b/spec/lib/gitlab/metrics/method_call_spec.rb @@ -30,7 +30,7 @@ RSpec.describe Gitlab::Metrics::MethodCall do end around do |example| - Timecop.freeze do + freeze_time do example.run end end diff --git a/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb b/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb index 59a70ac74a5..eb6c83096b9 100644 --- a/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb +++ b/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb @@ -12,7 +12,7 @@ RSpec.describe Gitlab::Metrics::Samplers::RubySampler do describe '#initialize' do it 'sets process_start_time_seconds' do - Timecop.freeze do + freeze_time do expect(sampler.metrics[:process_start_time_seconds].get).to eq(Time.now.to_i) end end diff --git a/spec/lib/gitlab/phabricator_import/cache/map_spec.rb b/spec/lib/gitlab/phabricator_import/cache/map_spec.rb index 0f760852a68..08ac85c2625 100644 --- a/spec/lib/gitlab/phabricator_import/cache/map_spec.rb +++ b/spec/lib/gitlab/phabricator_import/cache/map_spec.rb @@ -50,7 +50,7 @@ RSpec.describe Gitlab::PhabricatorImport::Cache::Map, :clean_gitlab_redis_cache describe '#set_gitlab_model' do around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end it 'sets the class and id in redis with a ttl' do diff --git a/spec/lib/gitlab/prometheus/queries/additional_metrics_environment_query_spec.rb b/spec/lib/gitlab/prometheus/queries/additional_metrics_environment_query_spec.rb index f5911963108..d0dee2ad366 100644 --- a/spec/lib/gitlab/prometheus/queries/additional_metrics_environment_query_spec.rb +++ b/spec/lib/gitlab/prometheus/queries/additional_metrics_environment_query_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.describe Gitlab::Prometheus::Queries::AdditionalMetricsEnvironmentQuery do around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end include_examples 'additional metrics query' do diff --git a/spec/lib/gitlab/prometheus/queries/validate_query_spec.rb b/spec/lib/gitlab/prometheus/queries/validate_query_spec.rb index 045c063ab34..e3706a4b106 100644 --- a/spec/lib/gitlab/prometheus/queries/validate_query_spec.rb +++ b/spec/lib/gitlab/prometheus/queries/validate_query_spec.rb @@ -28,7 +28,7 @@ RSpec.describe Gitlab::Prometheus::Queries::ValidateQuery do let(:error_message) { "invalid parameter 'query': 1:9: parse error: unexpected identifier \"query\"" } it 'returns invalid' do - Timecop.freeze do + freeze_time do stub_prometheus_query_error( prometheus_query_with_time_url(query, Time.now), error_message @@ -53,7 +53,7 @@ RSpec.describe Gitlab::Prometheus::Queries::ValidateQuery do end it 'catches exception and returns invalid' do - Timecop.freeze do + freeze_time do expect(subject.query(query)).to eq(valid: false, error: message) end end diff --git a/spec/lib/gitlab/prometheus_client_spec.rb b/spec/lib/gitlab/prometheus_client_spec.rb index 0774c2f3144..7c0c2962aed 100644 --- a/spec/lib/gitlab/prometheus_client_spec.rb +++ b/spec/lib/gitlab/prometheus_client_spec.rb @@ -136,7 +136,7 @@ RSpec.describe Gitlab::PrometheusClient do let(:query_url) { prometheus_query_with_time_url(prometheus_query, Time.now.utc) } around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end context 'when request returns vector results' do @@ -195,7 +195,7 @@ RSpec.describe Gitlab::PrometheusClient do let(:query_url) { prometheus_query_with_time_url(query, Time.now.utc) } around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end context 'when request returns vector results' do @@ -228,7 +228,7 @@ RSpec.describe Gitlab::PrometheusClient do let(:query_url) { prometheus_series_url('series_name', 'other_service') } around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end it 'calls endpoint and returns list of series' do @@ -259,7 +259,7 @@ RSpec.describe Gitlab::PrometheusClient do let(:query_url) { prometheus_query_range_url(prometheus_query) } around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end context 'when non utc time is passed' do @@ -358,7 +358,7 @@ RSpec.describe Gitlab::PrometheusClient do let(:query_url) { prometheus_query_url(prometheus_query) } around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end context 'when response status code is 200' do diff --git a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/until_executing_spec.rb b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/until_executing_spec.rb index 77d760d1ae3..10b18052e9a 100644 --- a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/until_executing_spec.rb +++ b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/until_executing_spec.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require 'fast_spec_helper' -require 'timecop' RSpec.describe Gitlab::SidekiqMiddleware::DuplicateJobs::Strategies::UntilExecuting do let(:fake_duplicate_job) do @@ -77,7 +76,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::DuplicateJobs::Strategies::UntilExecut context 'scheduled in the future' do it 'adds the jid of the existing job to the job hash' do - Timecop.freeze do + freeze_time do allow(fake_duplicate_job).to receive(:scheduled?).twice.and_return(true) allow(fake_duplicate_job).to receive(:scheduled_at).and_return(Time.now + time_diff) allow(fake_duplicate_job).to receive(:options).and_return({ including_scheduled: true }) diff --git a/spec/lib/gitlab/updated_notes_paginator_spec.rb b/spec/lib/gitlab/updated_notes_paginator_spec.rb index eedc11777d4..ce6a7719fb4 100644 --- a/spec/lib/gitlab/updated_notes_paginator_spec.rb +++ b/spec/lib/gitlab/updated_notes_paginator_spec.rb @@ -15,7 +15,7 @@ RSpec.describe Gitlab::UpdatedNotesPaginator do let(:page_1_boundary) { page_1.last.updated_at + NotesFinder::FETCH_OVERLAP } around do |example| - Timecop.freeze do + freeze_time do example.run end end diff --git a/spec/migrations/20190924152703_migrate_issue_trackers_data_spec.rb b/spec/migrations/20190924152703_migrate_issue_trackers_data_spec.rb index 6c957ee1428..196f5ead8d2 100644 --- a/spec/migrations/20190924152703_migrate_issue_trackers_data_spec.rb +++ b/spec/migrations/20190924152703_migrate_issue_trackers_data_spec.rb @@ -52,7 +52,7 @@ RSpec.describe MigrateIssueTrackersData do it 'schedules background migrations at correct time' do Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! expect(migration_name).to be_scheduled_delayed_migration(3.minutes, jira_service.id, bugzilla_service.id) diff --git a/spec/migrations/20200122123016_backfill_project_settings_spec.rb b/spec/migrations/20200122123016_backfill_project_settings_spec.rb index 0992ddde0c1..60c6206a83b 100644 --- a/spec/migrations/20200122123016_backfill_project_settings_spec.rb +++ b/spec/migrations/20200122123016_backfill_project_settings_spec.rb @@ -19,7 +19,7 @@ RSpec.describe BackfillProjectSettings, :sidekiq, schema: 20200114113341 do it 'schedules BackfillProjectSettings background jobs' do Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! expect(described_class::MIGRATION).to be_scheduled_delayed_migration(2.minutes, 1, 2) diff --git a/spec/migrations/20200130145430_reschedule_migrate_issue_trackers_data_spec.rb b/spec/migrations/20200130145430_reschedule_migrate_issue_trackers_data_spec.rb index d5208976928..e140be476ab 100644 --- a/spec/migrations/20200130145430_reschedule_migrate_issue_trackers_data_spec.rb +++ b/spec/migrations/20200130145430_reschedule_migrate_issue_trackers_data_spec.rb @@ -53,7 +53,7 @@ RSpec.describe RescheduleMigrateIssueTrackersData do describe "#up" do it 'schedules background migrations at correct time' do Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! expect(migration_name).to be_scheduled_delayed_migration(3.minutes, jira_service.id, bugzilla_service.id) diff --git a/spec/migrations/20200406102120_backfill_deployment_clusters_from_deployments_spec.rb b/spec/migrations/20200406102120_backfill_deployment_clusters_from_deployments_spec.rb index 47bc7428286..c6b221b09a5 100644 --- a/spec/migrations/20200406102120_backfill_deployment_clusters_from_deployments_spec.rb +++ b/spec/migrations/20200406102120_backfill_deployment_clusters_from_deployments_spec.rb @@ -27,7 +27,7 @@ RSpec.describe BackfillDeploymentClustersFromDeployments, :migration, :sidekiq, batch_2_end = create_deployment(**deployment_data) Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! # batch 1 diff --git a/spec/migrations/20200703125016_backfill_namespace_settings_spec.rb b/spec/migrations/20200703125016_backfill_namespace_settings_spec.rb index 7b84ef9e236..9ff88009d8a 100644 --- a/spec/migrations/20200703125016_backfill_namespace_settings_spec.rb +++ b/spec/migrations/20200703125016_backfill_namespace_settings_spec.rb @@ -17,7 +17,7 @@ RSpec.describe BackfillNamespaceSettings, :sidekiq, schema: 20200703124823 do it 'schedules BackfillNamespaceSettings background jobs' do Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! expect(described_class::MIGRATION).to be_scheduled_delayed_migration(2.minutes, 1, 2) diff --git a/spec/migrations/backfill_imported_snippet_repositories_spec.rb b/spec/migrations/backfill_imported_snippet_repositories_spec.rb index 208bda274e2..998a691bf77 100644 --- a/spec/migrations/backfill_imported_snippet_repositories_spec.rb +++ b/spec/migrations/backfill_imported_snippet_repositories_spec.rb @@ -30,7 +30,7 @@ RSpec.describe BackfillImportedSnippetRepositories do create_snippet(10) Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! expect(described_class::MIGRATION) diff --git a/spec/migrations/backfill_snippet_repositories_spec.rb b/spec/migrations/backfill_snippet_repositories_spec.rb index 084faa16a37..6d417669ad6 100644 --- a/spec/migrations/backfill_snippet_repositories_spec.rb +++ b/spec/migrations/backfill_snippet_repositories_spec.rb @@ -28,7 +28,7 @@ RSpec.describe BackfillSnippetRepositories do stub_const("#{described_class.name}::BATCH_SIZE", 2) Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! expect(described_class::MIGRATION) diff --git a/spec/migrations/enqueue_reset_merge_status_second_run_spec.rb b/spec/migrations/enqueue_reset_merge_status_second_run_spec.rb index f5728534675..d4189326366 100644 --- a/spec/migrations/enqueue_reset_merge_status_second_run_spec.rb +++ b/spec/migrations/enqueue_reset_merge_status_second_run_spec.rb @@ -33,7 +33,7 @@ RSpec.describe EnqueueResetMergeStatusSecondRun do stub_const("#{described_class.name}::BATCH_SIZE", 2) Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! expect(described_class::MIGRATION) diff --git a/spec/migrations/enqueue_reset_merge_status_spec.rb b/spec/migrations/enqueue_reset_merge_status_spec.rb index 683d2caf9ca..c581293ddcd 100644 --- a/spec/migrations/enqueue_reset_merge_status_spec.rb +++ b/spec/migrations/enqueue_reset_merge_status_spec.rb @@ -33,7 +33,7 @@ RSpec.describe EnqueueResetMergeStatus do stub_const("#{described_class.name}::BATCH_SIZE", 2) Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! expect(described_class::MIGRATION) diff --git a/spec/migrations/fix_projects_without_project_feature_spec.rb b/spec/migrations/fix_projects_without_project_feature_spec.rb index 2a4ba3f3ca5..1ec67c07ba3 100644 --- a/spec/migrations/fix_projects_without_project_feature_spec.rb +++ b/spec/migrations/fix_projects_without_project_feature_spec.rb @@ -20,7 +20,7 @@ RSpec.describe FixProjectsWithoutProjectFeature do around do |example| Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do example.call end end diff --git a/spec/migrations/fix_projects_without_prometheus_services_spec.rb b/spec/migrations/fix_projects_without_prometheus_services_spec.rb index 73ede2ad90c..a4504ab6773 100644 --- a/spec/migrations/fix_projects_without_prometheus_services_spec.rb +++ b/spec/migrations/fix_projects_without_prometheus_services_spec.rb @@ -20,7 +20,7 @@ RSpec.describe FixProjectsWithoutPrometheusService, :migration do around do |example| Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do example.call end end diff --git a/spec/migrations/fix_wrong_pages_access_level_spec.rb b/spec/migrations/fix_wrong_pages_access_level_spec.rb index 9ee5fa783a4..80787e0f115 100644 --- a/spec/migrations/fix_wrong_pages_access_level_spec.rb +++ b/spec/migrations/fix_wrong_pages_access_level_spec.rb @@ -29,7 +29,7 @@ RSpec.describe FixWrongPagesAccessLevel, :sidekiq_might_not_need_inline, schema: it 'correctly schedules background migrations' do Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do first_id = create_project_feature("project1", project_class::PRIVATE, feature_class::PRIVATE).id last_id = create_project_feature("project2", project_class::PRIVATE, feature_class::PUBLIC).id diff --git a/spec/migrations/migrate_discussion_id_on_promoted_epics_spec.rb b/spec/migrations/migrate_discussion_id_on_promoted_epics_spec.rb index e3119be495d..92a49046193 100644 --- a/spec/migrations/migrate_discussion_id_on_promoted_epics_spec.rb +++ b/spec/migrations/migrate_discussion_id_on_promoted_epics_spec.rb @@ -53,7 +53,7 @@ RSpec.describe MigrateDiscussionIdOnPromotedEpics do stub_const("#{described_class.name}::BATCH_SIZE", 2) Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! expect(migration_name).to be_scheduled_delayed_migration(2.minutes, %w(id1 id2)) @@ -69,7 +69,7 @@ RSpec.describe MigrateDiscussionIdOnPromotedEpics do create_note(create_epic, 'id3') Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! expect(migration_name).to be_scheduled_delayed_migration(2.minutes, %w(id1)) diff --git a/spec/migrations/schedule_calculate_wiki_sizes_spec.rb b/spec/migrations/schedule_calculate_wiki_sizes_spec.rb index c60ffc3619e..0af491d863b 100644 --- a/spec/migrations/schedule_calculate_wiki_sizes_spec.rb +++ b/spec/migrations/schedule_calculate_wiki_sizes_spec.rb @@ -21,7 +21,7 @@ RSpec.describe ScheduleCalculateWikiSizes do let!(:project_statistic3) { project_statistics.create!(project_id: project3.id, namespace_id: namespace.id, wiki_size: nil) } it 'schedules a background migration' do - Timecop.freeze do + freeze_time do migrate! expect(migration_name).to be_scheduled_delayed_migration(5.minutes, project_statistic2.id, project_statistic3.id) @@ -49,7 +49,7 @@ RSpec.describe ScheduleCalculateWikiSizes do it 'does not schedule a background migration' do Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! expect(BackgroundMigrationWorker.jobs.size).to eq 0 diff --git a/spec/migrations/schedule_fill_valid_time_for_pages_domain_certificates_spec.rb b/spec/migrations/schedule_fill_valid_time_for_pages_domain_certificates_spec.rb index a51bc374d5f..539b9ee96a8 100644 --- a/spec/migrations/schedule_fill_valid_time_for_pages_domain_certificates_spec.rb +++ b/spec/migrations/schedule_fill_valid_time_for_pages_domain_certificates_spec.rb @@ -22,7 +22,7 @@ RSpec.describe ScheduleFillValidTimeForPagesDomainCertificates do it 'correctly schedules background migrations' do Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! first_id = domains_table.find_by_domain("domain3.example.com").id diff --git a/spec/migrations/schedule_migrate_security_scans_spec.rb b/spec/migrations/schedule_migrate_security_scans_spec.rb index 4fd17b20666..61b14f239ae 100644 --- a/spec/migrations/schedule_migrate_security_scans_spec.rb +++ b/spec/migrations/schedule_migrate_security_scans_spec.rb @@ -40,7 +40,7 @@ RSpec.describe ScheduleMigrateSecurityScans, :sidekiq do it 'schedules migration of security scans' do Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migration.up expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, job_artifact_1.id, job_artifact_1.id) @@ -57,7 +57,7 @@ RSpec.describe ScheduleMigrateSecurityScans, :sidekiq do it 'schedules migration of security scans' do Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migration.up expect(BackgroundMigrationWorker.jobs).to be_empty diff --git a/spec/migrations/schedule_pages_metadata_migration_spec.rb b/spec/migrations/schedule_pages_metadata_migration_spec.rb index c37e19eb71c..94311237cf4 100644 --- a/spec/migrations/schedule_pages_metadata_migration_spec.rb +++ b/spec/migrations/schedule_pages_metadata_migration_spec.rb @@ -17,7 +17,7 @@ RSpec.describe SchedulePagesMetadataMigration do it 'schedules pages metadata migration' do Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! expect(described_class::MIGRATION).to be_scheduled_delayed_migration(2.minutes, 111, 111) diff --git a/spec/migrations/schedule_populate_merge_request_assignees_table_spec.rb b/spec/migrations/schedule_populate_merge_request_assignees_table_spec.rb index 8b26cd589fd..93185b330c7 100644 --- a/spec/migrations/schedule_populate_merge_request_assignees_table_spec.rb +++ b/spec/migrations/schedule_populate_merge_request_assignees_table_spec.rb @@ -31,7 +31,7 @@ RSpec.describe SchedulePopulateMergeRequestAssigneesTable do stub_const("#{described_class.name}::BATCH_SIZE", 2) Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! expect(described_class::MIGRATION) diff --git a/spec/migrations/schedule_populate_personal_snippet_statistics_spec.rb b/spec/migrations/schedule_populate_personal_snippet_statistics_spec.rb index ce618449884..8678361fc64 100644 --- a/spec/migrations/schedule_populate_personal_snippet_statistics_spec.rb +++ b/spec/migrations/schedule_populate_personal_snippet_statistics_spec.rb @@ -38,7 +38,7 @@ RSpec.describe SchedulePopulatePersonalSnippetStatistics do stub_const("#{described_class}::BATCH_SIZE", 4) Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! aggregate_failures do diff --git a/spec/migrations/schedule_populate_project_snippet_statistics_spec.rb b/spec/migrations/schedule_populate_project_snippet_statistics_spec.rb index 05e9d4d2f79..d5f048ed5cb 100644 --- a/spec/migrations/schedule_populate_project_snippet_statistics_spec.rb +++ b/spec/migrations/schedule_populate_project_snippet_statistics_spec.rb @@ -43,7 +43,7 @@ RSpec.describe SchedulePopulateProjectSnippetStatistics do stub_const("#{described_class}::BATCH_SIZE", 4) Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! aggregate_failures do diff --git a/spec/migrations/schedule_populate_user_highest_roles_table_spec.rb b/spec/migrations/schedule_populate_user_highest_roles_table_spec.rb index c7e5c6f30a6..32def8ab47d 100644 --- a/spec/migrations/schedule_populate_user_highest_roles_table_spec.rb +++ b/spec/migrations/schedule_populate_user_highest_roles_table_spec.rb @@ -32,7 +32,7 @@ RSpec.describe SchedulePopulateUserHighestRolesTable do stub_const("#{described_class.name}::BATCH_SIZE", 2) Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, 1, 4) diff --git a/spec/migrations/schedule_recalculate_project_authorizations_second_run_spec.rb b/spec/migrations/schedule_recalculate_project_authorizations_second_run_spec.rb index 06f1a7e28eb..a02e00de1e3 100644 --- a/spec/migrations/schedule_recalculate_project_authorizations_second_run_spec.rb +++ b/spec/migrations/schedule_recalculate_project_authorizations_second_run_spec.rb @@ -16,7 +16,7 @@ RSpec.describe ScheduleRecalculateProjectAuthorizationsSecondRun do it 'schedules background migration' do Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! expect(BackgroundMigrationWorker.jobs.size).to eq(2) diff --git a/spec/migrations/schedule_recalculate_project_authorizations_spec.rb b/spec/migrations/schedule_recalculate_project_authorizations_spec.rb index 9519b103284..378e6aa133d 100644 --- a/spec/migrations/schedule_recalculate_project_authorizations_spec.rb +++ b/spec/migrations/schedule_recalculate_project_authorizations_spec.rb @@ -26,7 +26,7 @@ RSpec.describe ScheduleRecalculateProjectAuthorizations do it 'schedules background migration' do Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! expect(BackgroundMigrationWorker.jobs.size).to eq(2) @@ -45,7 +45,7 @@ RSpec.describe ScheduleRecalculateProjectAuthorizations do access_level: 30) Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! expect(BackgroundMigrationWorker.jobs.size).to eq(2) diff --git a/spec/migrations/schedule_recalculate_project_authorizations_third_run_spec.rb b/spec/migrations/schedule_recalculate_project_authorizations_third_run_spec.rb index 300bb940dd8..5328abf7ea7 100644 --- a/spec/migrations/schedule_recalculate_project_authorizations_third_run_spec.rb +++ b/spec/migrations/schedule_recalculate_project_authorizations_third_run_spec.rb @@ -16,7 +16,7 @@ RSpec.describe ScheduleRecalculateProjectAuthorizationsThirdRun do it 'schedules background migration' do Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! expect(BackgroundMigrationWorker.jobs.size).to eq(2) diff --git a/spec/migrations/schedule_sync_issuables_state_id_spec.rb b/spec/migrations/schedule_sync_issuables_state_id_spec.rb index ecfebbde348..d22d636e084 100644 --- a/spec/migrations/schedule_sync_issuables_state_id_spec.rb +++ b/spec/migrations/schedule_sync_issuables_state_id_spec.rb @@ -20,7 +20,7 @@ RSpec.describe ScheduleSyncIssuablesStateId do it 'correctly schedules issuable sync background migration' do Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! expect(migration).to be_scheduled_delayed_migration(120.seconds, resource_1.id, resource_2.id) diff --git a/spec/migrations/schedule_sync_issuables_state_id_where_nil_spec.rb b/spec/migrations/schedule_sync_issuables_state_id_where_nil_spec.rb index d23f5b69d22..3d450f0e6bc 100644 --- a/spec/migrations/schedule_sync_issuables_state_id_where_nil_spec.rb +++ b/spec/migrations/schedule_sync_issuables_state_id_where_nil_spec.rb @@ -20,7 +20,7 @@ RSpec.describe ScheduleSyncIssuablesStateIdWhereNil do it 'correctly schedules issuable sync background migration' do Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! expect(migration).to be_scheduled_delayed_migration(120.seconds, resource_1.id, resource_3.id) diff --git a/spec/migrations/schedule_update_existing_subgroup_to_match_visibility_level_of_parent_spec.rb b/spec/migrations/schedule_update_existing_subgroup_to_match_visibility_level_of_parent_spec.rb index 949d8d8794f..deb7aae737a 100644 --- a/spec/migrations/schedule_update_existing_subgroup_to_match_visibility_level_of_parent_spec.rb +++ b/spec/migrations/schedule_update_existing_subgroup_to_match_visibility_level_of_parent_spec.rb @@ -14,7 +14,7 @@ RSpec.describe ScheduleUpdateExistingSubgroupToMatchVisibilityLevelOfParent do create_namespace('child', Gitlab::VisibilityLevel::PUBLIC, parent_id: parent.id) Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! expect(BackgroundMigrationWorker.jobs.size).to eq(1) @@ -30,7 +30,7 @@ RSpec.describe ScheduleUpdateExistingSubgroupToMatchVisibilityLevelOfParent do create_namespace('child', Gitlab::VisibilityLevel::PUBLIC, parent_id: middle_group.id) Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! expect(BackgroundMigrationWorker.jobs.size).to eq(1) @@ -47,7 +47,7 @@ RSpec.describe ScheduleUpdateExistingSubgroupToMatchVisibilityLevelOfParent do create_namespace('child', Gitlab::VisibilityLevel::PUBLIC, parent_id: middle_group.id) Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! expect(BackgroundMigrationWorker.jobs.size).to eq(1) @@ -66,7 +66,7 @@ RSpec.describe ScheduleUpdateExistingSubgroupToMatchVisibilityLevelOfParent do create_namespace('child', Gitlab::VisibilityLevel::PUBLIC, parent_id: middle_group.id) Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do migrate! expect(BackgroundMigrationWorker.jobs.size).to eq(2) diff --git a/spec/models/board_group_recent_visit_spec.rb b/spec/models/board_group_recent_visit_spec.rb index 4d16e1ff839..c6fbd263072 100644 --- a/spec/models/board_group_recent_visit_spec.rb +++ b/spec/models/board_group_recent_visit_spec.rb @@ -28,7 +28,7 @@ RSpec.describe BoardGroupRecentVisit do let!(:visit) { create :board_group_recent_visit, group: board.group, board: board, user: user, updated_at: 7.days.ago } it 'updates the timestamp' do - Timecop.freeze do + freeze_time do described_class.visited!(user, board) expect(described_class.count).to eq 1 diff --git a/spec/models/board_project_recent_visit_spec.rb b/spec/models/board_project_recent_visit_spec.rb index 8e74405fd8c..145a4f5b1a7 100644 --- a/spec/models/board_project_recent_visit_spec.rb +++ b/spec/models/board_project_recent_visit_spec.rb @@ -28,7 +28,7 @@ RSpec.describe BoardProjectRecentVisit do let!(:visit) { create :board_project_recent_visit, project: board.project, board: board, user: user, updated_at: 7.days.ago } it 'updates the timestamp' do - Timecop.freeze do + freeze_time do described_class.visited!(user, board) expect(described_class.count).to eq 1 diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 069ac23c5a4..c4934da73fc 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -448,7 +448,7 @@ RSpec.describe Ci::Build do end it 'schedules BuildScheduleWorker at the right time' do - Timecop.freeze do + freeze_time do expect(Ci::BuildScheduleWorker) .to receive(:perform_at).with(be_like_time(1.minute.since), build.id) @@ -496,7 +496,7 @@ RSpec.describe Ci::Build do let(:option) { { start_in: '1 day' } } it 'returns date after 1 day' do - Timecop.freeze do + freeze_time do is_expected.to eq(1.day.since) end end @@ -506,7 +506,7 @@ RSpec.describe Ci::Build do let(:option) { { start_in: '1 week' } } it 'returns date after 1 week' do - Timecop.freeze do + freeze_time do is_expected.to eq(1.week.since) end end @@ -4087,7 +4087,7 @@ RSpec.describe Ci::Build do let(:path) { 'other_artifacts_0.1.2/another-subdirectory/banana_sample.gif' } around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end before do diff --git a/spec/models/clusters/applications/prometheus_spec.rb b/spec/models/clusters/applications/prometheus_spec.rb index 6ae4b37d5ca..82971596176 100644 --- a/spec/models/clusters/applications/prometheus_spec.rb +++ b/spec/models/clusters/applications/prometheus_spec.rb @@ -46,7 +46,7 @@ RSpec.describe Clusters::Applications::Prometheus do subject { create(:clusters_applications_prometheus, :installed, cluster: cluster) } it 'sets last_update_started_at to now' do - Timecop.freeze do + freeze_time do expect { subject.make_updating }.to change { subject.reload.last_update_started_at }.to be_within(1.second).of(Time.current) end end @@ -353,7 +353,7 @@ RSpec.describe Clusters::Applications::Prometheus do let(:timestamp) { Time.current - 5.minutes } around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end before do diff --git a/spec/models/concerns/prometheus_adapter_spec.rb b/spec/models/concerns/prometheus_adapter_spec.rb index e795e2b06cb..235e505c6e9 100644 --- a/spec/models/concerns/prometheus_adapter_spec.rb +++ b/spec/models/concerns/prometheus_adapter_spec.rb @@ -25,7 +25,7 @@ RSpec.describe PrometheusAdapter, :use_clean_rails_memory_store_caching do let(:validation_respone) { { data: { valid: true } } } around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end context 'with valid data' do @@ -45,7 +45,7 @@ RSpec.describe PrometheusAdapter, :use_clean_rails_memory_store_caching do let(:environment) { build_stubbed(:environment, slug: 'env-slug') } around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end context 'with valid data' do @@ -85,7 +85,7 @@ RSpec.describe PrometheusAdapter, :use_clean_rails_memory_store_caching do let(:deployment_query) { Gitlab::Prometheus::Queries::DeploymentQuery } around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end context 'with valid data' do @@ -107,7 +107,7 @@ RSpec.describe PrometheusAdapter, :use_clean_rails_memory_store_caching do let(:time_window) { [1552642245.067, 1552642095.831] } around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end context 'with valid data' do @@ -137,7 +137,7 @@ RSpec.describe PrometheusAdapter, :use_clean_rails_memory_store_caching do end around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end context 'when service is inactive' do diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb index b320390711e..6ea94b4cd1a 100644 --- a/spec/models/deployment_spec.rb +++ b/spec/models/deployment_spec.rb @@ -99,7 +99,7 @@ RSpec.describe Deployment do end it 'starts running' do - Timecop.freeze do + freeze_time do expect(deployment).to be_running expect(deployment.finished_at).to be_nil end @@ -110,7 +110,7 @@ RSpec.describe Deployment do let(:deployment) { create(:deployment, :running) } it 'has correct status' do - Timecop.freeze do + freeze_time do deployment.succeed! expect(deployment).to be_success @@ -137,7 +137,7 @@ RSpec.describe Deployment do let(:deployment) { create(:deployment, :running) } it 'has correct status' do - Timecop.freeze do + freeze_time do deployment.drop! expect(deployment).to be_failed @@ -157,7 +157,7 @@ RSpec.describe Deployment do let(:deployment) { create(:deployment, :running) } it 'has correct status' do - Timecop.freeze do + freeze_time do deployment.cancel! expect(deployment).to be_canceled @@ -584,7 +584,7 @@ RSpec.describe Deployment do end it 'updates finished_at when transitioning to a finished status' do - Timecop.freeze do + freeze_time do deploy.update_status('success') expect(deploy.read_attribute(:finished_at)).to eq(Time.current) diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb index 2696d144db4..bb0b3748b76 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -1222,7 +1222,7 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do let(:environment) { build(:environment, :will_auto_stop) } it 'returns when it will expire' do - Timecop.freeze { is_expected.to eq(1.day.to_i) } + freeze_time { is_expected.to eq(1.day.to_i) } end end @@ -1248,7 +1248,7 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do end with_them do it 'sets correct auto_stop_in' do - Timecop.freeze do + freeze_time do if expected_result.is_a?(Integer) || expected_result.nil? subject diff --git a/spec/models/metrics/dashboard/annotation_spec.rb b/spec/models/metrics/dashboard/annotation_spec.rb index bd4baeb8851..4b7492016f3 100644 --- a/spec/models/metrics/dashboard/annotation_spec.rb +++ b/spec/models/metrics/dashboard/annotation_spec.rb @@ -100,7 +100,7 @@ RSpec.describe Metrics::Dashboard::Annotation do describe '#ending_before' do it 'returns annotations only for appointed dashboard' do - Timecop.freeze do + freeze_time do twelve_minutes_old_annotation = create(:metrics_dashboard_annotation, starting_at: 15.minutes.ago, ending_at: 12.minutes.ago) create(:metrics_dashboard_annotation, starting_at: 15.minutes.ago, ending_at: 11.minutes.ago) diff --git a/spec/models/namespace/root_storage_statistics_spec.rb b/spec/models/namespace/root_storage_statistics_spec.rb index ce6f875ee09..92a8d17a2a8 100644 --- a/spec/models/namespace/root_storage_statistics_spec.rb +++ b/spec/models/namespace/root_storage_statistics_spec.rb @@ -44,6 +44,7 @@ RSpec.describe Namespace::RootStorageStatistics, type: :model do total_packages_size = stat1.packages_size + stat2.packages_size total_storage_size = stat1.storage_size + stat2.storage_size total_snippets_size = stat1.snippets_size + stat2.snippets_size + total_pipeline_artifacts_size = stat1.pipeline_artifacts_size + stat2.pipeline_artifacts_size expect(root_storage_statistics.repository_size).to eq(total_repository_size) expect(root_storage_statistics.wiki_size).to eq(total_wiki_size) @@ -52,6 +53,7 @@ RSpec.describe Namespace::RootStorageStatistics, type: :model do expect(root_storage_statistics.packages_size).to eq(total_packages_size) expect(root_storage_statistics.storage_size).to eq(total_storage_size) expect(root_storage_statistics.snippets_size).to eq(total_snippets_size) + expect(root_storage_statistics.pipeline_artifacts_size).to eq(total_pipeline_artifacts_size) end it 'works when there are no projects' do @@ -67,6 +69,7 @@ RSpec.describe Namespace::RootStorageStatistics, type: :model do expect(root_storage_statistics.packages_size).to eq(0) expect(root_storage_statistics.storage_size).to eq(0) expect(root_storage_statistics.snippets_size).to eq(0) + expect(root_storage_statistics.pipeline_artifacts_size).to eq(0) end end diff --git a/spec/models/remote_mirror_spec.rb b/spec/models/remote_mirror_spec.rb index ebc9760ab14..c7dbf3c0e92 100644 --- a/spec/models/remote_mirror_spec.rb +++ b/spec/models/remote_mirror_spec.rb @@ -283,7 +283,7 @@ RSpec.describe RemoteMirror, :mailer do let(:remote_mirror) { create(:project, :repository, :remote_mirror).remote_mirrors.first } around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end context 'with remote mirroring disabled' do @@ -397,7 +397,7 @@ RSpec.describe RemoteMirror, :mailer do let(:timestamp) { Time.current - 5.minutes } around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end before do diff --git a/spec/presenters/ci/build_presenter_spec.rb b/spec/presenters/ci/build_presenter_spec.rb index 8d302b242b3..1ff2ea3d225 100644 --- a/spec/presenters/ci/build_presenter_spec.rb +++ b/spec/presenters/ci/build_presenter_spec.rb @@ -228,7 +228,7 @@ RSpec.describe Ci::BuildPresenter do let(:build) { create(:ci_build, :scheduled) } it 'returns execution time' do - Timecop.freeze do + freeze_time do is_expected.to be_like_time(60.0) end end @@ -238,7 +238,7 @@ RSpec.describe Ci::BuildPresenter do let(:build) { create(:ci_build, :expired_scheduled) } it 'returns execution time' do - Timecop.freeze do + freeze_time do is_expected.to eq(0) end end @@ -249,7 +249,7 @@ RSpec.describe Ci::BuildPresenter do let(:build) { create(:ci_build) } it 'does not return execution time' do - Timecop.freeze do + freeze_time do is_expected.to be_falsy end end diff --git a/spec/requests/api/conan_packages_spec.rb b/spec/requests/api/conan_packages_spec.rb index 6569ce91bb8..58c360d055b 100644 --- a/spec/requests/api/conan_packages_spec.rb +++ b/spec/requests/api/conan_packages_spec.rb @@ -153,7 +153,7 @@ RSpec.describe API::ConanPackages do end it 'token has valid validity time' do - Timecop.freeze do + freeze_time do subject payload = JSONWebToken::HMACToken.decode( diff --git a/spec/requests/api/graphql/mutations/snippets/create_spec.rb b/spec/requests/api/graphql/mutations/snippets/create_spec.rb index 705e3647f4a..1bb446de708 100644 --- a/spec/requests/api/graphql/mutations/snippets/create_spec.rb +++ b/spec/requests/api/graphql/mutations/snippets/create_spec.rb @@ -99,6 +99,8 @@ RSpec.describe 'Creating a Snippet' do it_behaves_like 'a mutation that returns errors in the response', errors: ['Snippet actions have invalid data'] it_behaves_like 'does not create snippet' end + + it_behaves_like 'snippet edit usage data counters' end context 'with PersonalSnippet' do @@ -129,6 +131,8 @@ RSpec.describe 'Creating a Snippet' do it_behaves_like 'a mutation that returns top-level errors', errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR] end + + it_behaves_like 'snippet edit usage data counters' end context 'when there are ActiveRecord validation errors' do diff --git a/spec/requests/api/graphql/mutations/snippets/update_spec.rb b/spec/requests/api/graphql/mutations/snippets/update_spec.rb index 9564bbc46de..58ce74b9263 100644 --- a/spec/requests/api/graphql/mutations/snippets/update_spec.rb +++ b/spec/requests/api/graphql/mutations/snippets/update_spec.rb @@ -131,6 +131,7 @@ RSpec.describe 'Updating a Snippet' do it_behaves_like 'graphql update actions' it_behaves_like 'when the snippet is not found' + it_behaves_like 'snippet edit usage data counters' end describe 'ProjectSnippet' do @@ -173,6 +174,8 @@ RSpec.describe 'Updating a Snippet' do expect(errors.first['message']).to eq(Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR) end end + + it_behaves_like 'snippet edit usage data counters' end it_behaves_like 'when the snippet is not found' diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb index 873189af397..bb7288ed227 100644 --- a/spec/requests/api/internal/base_spec.rb +++ b/spec/requests/api/internal/base_spec.rb @@ -415,7 +415,7 @@ RSpec.describe API::Internal::Base do let(:env) { {} } around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end before do diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index 6c6497a240b..39cff2ad74e 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -348,6 +348,26 @@ RSpec.describe API::Users, :do_not_mock_admin_mode do expect(response).to match_response_schema('public_api/v4/user/basics') expect(json_response.first.keys).not_to include 'is_admin' end + + context 'exclude_internal param' do + let_it_be(:internal_user) { User.alert_bot } + + it 'returns all users when it is not set' do + get api("/users?exclude_internal=false", user) + + expect(response).to match_response_schema('public_api/v4/user/basics') + expect(response).to include_pagination_headers + expect(json_response.map { |u| u['id'] }).to include(internal_user.id) + end + + it 'returns all non internal users when it is set' do + get api("/users?exclude_internal=true", user) + + expect(response).to match_response_schema('public_api/v4/user/basics') + expect(response).to include_pagination_headers + expect(json_response.map { |u| u['id'] }).not_to include(internal_user.id) + end + end end context "when admin" do diff --git a/spec/requests/projects/metrics_dashboard_spec.rb b/spec/requests/projects/metrics_dashboard_spec.rb index f35af571e79..0b6e2ef5151 100644 --- a/spec/requests/projects/metrics_dashboard_spec.rb +++ b/spec/requests/projects/metrics_dashboard_spec.rb @@ -14,7 +14,7 @@ RSpec.describe 'Projects::MetricsDashboardController' do end describe 'GET /:namespace/:project/-/metrics' do - it 'returns 200' do + it "redirects to default environment's metrics dashboard" do send_request expect(response).to redirect_to(dashboard_route(environment: environment)) end @@ -24,6 +24,18 @@ RSpec.describe 'Projects::MetricsDashboardController' do expect(assigns(:default_environment).id).to eq(environment.id) end + it 'retains existing parameters when redirecting' do + get "#{dashboard_route(dashboard_path: '.gitlab/dashboards/dashboard_path.yml')}/panel/new" + + expect(response).to redirect_to( + dashboard_route( + dashboard_path: '.gitlab/dashboards/dashboard_path.yml', + page: 'panel/new', + environment: environment + ) + ) + end + context 'with anonymous user and public dashboard visibility' do let(:anonymous_user) { create(:user) } let(:project) do diff --git a/spec/serializers/analytics_build_entity_spec.rb b/spec/serializers/analytics_build_entity_spec.rb index 20bd017d1cf..09804681f5d 100644 --- a/spec/serializers/analytics_build_entity_spec.rb +++ b/spec/serializers/analytics_build_entity_spec.rb @@ -16,7 +16,7 @@ RSpec.describe AnalyticsBuildEntity do subject { entity.as_json } around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end it 'contains the URL' do diff --git a/spec/services/clusters/applications/schedule_update_service_spec.rb b/spec/services/clusters/applications/schedule_update_service_spec.rb index f559fb1b7aa..01a75a334e6 100644 --- a/spec/services/clusters/applications/schedule_update_service_spec.rb +++ b/spec/services/clusters/applications/schedule_update_service_spec.rb @@ -7,7 +7,7 @@ RSpec.describe Clusters::Applications::ScheduleUpdateService do let(:project) { create(:project) } around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end context 'when application is able to be updated' do diff --git a/spec/services/deployments/after_create_service_spec.rb b/spec/services/deployments/after_create_service_spec.rb index 3287eed03b7..6cdb4c88191 100644 --- a/spec/services/deployments/after_create_service_spec.rb +++ b/spec/services/deployments/after_create_service_spec.rb @@ -122,7 +122,7 @@ RSpec.describe Deployments::AfterCreateService do end it 'renews auto stop at' do - Timecop.freeze do + freeze_time do environment.update!(auto_stop_at: nil) expect { subject.execute } diff --git a/spec/services/git/process_ref_changes_service_spec.rb b/spec/services/git/process_ref_changes_service_spec.rb index fc313bf6eb9..087f4ba372b 100644 --- a/spec/services/git/process_ref_changes_service_spec.rb +++ b/spec/services/git/process_ref_changes_service_spec.rb @@ -172,23 +172,31 @@ RSpec.describe Git::ProcessRefChangesService do [ { index: 0, oldrev: Gitlab::Git::BLANK_SHA, newrev: '789012', ref: "#{ref_prefix}/create1" }, { index: 1, oldrev: Gitlab::Git::BLANK_SHA, newrev: '789013', ref: "#{ref_prefix}/create2" }, - { index: 2, oldrev: Gitlab::Git::BLANK_SHA, newrev: '789014', ref: "#{ref_prefix}/create3" } + { index: 2, oldrev: Gitlab::Git::BLANK_SHA, newrev: '789014', ref: "#{ref_prefix}/create3" }, + { index: 3, oldrev: '789015', newrev: '789016', ref: "#{ref_prefix}/changed1" }, + { index: 4, oldrev: '789017', newrev: '789018', ref: "#{ref_prefix}/changed2" }, + { index: 5, oldrev: '789019', newrev: Gitlab::Git::BLANK_SHA, ref: "#{ref_prefix}/removed1" }, + { index: 6, oldrev: '789020', newrev: Gitlab::Git::BLANK_SHA, ref: "#{ref_prefix}/removed2" } ] end let(:git_changes) { double(branch_changes: branch_changes, tag_changes: tag_changes) } - it 'schedules job for existing merge requests' do - expect_next_instance_of(MergeRequests::PushedBranchesService) do |service| - expect(service).to receive(:execute).and_return(%w(create1 create2)) - end + before do + allow(MergeRequests::PushedBranchesService).to receive(:new).and_return( + double(execute: %w(create1 create2)), double(execute: %w(changed1)), double(execute: %w(removed2)) + ) + end + it 'schedules job for existing merge requests' do expect(UpdateMergeRequestsWorker).to receive(:perform_async) .with(project.id, user.id, Gitlab::Git::BLANK_SHA, '789012', "#{ref_prefix}/create1").ordered expect(UpdateMergeRequestsWorker).to receive(:perform_async) .with(project.id, user.id, Gitlab::Git::BLANK_SHA, '789013', "#{ref_prefix}/create2").ordered - expect(UpdateMergeRequestsWorker).not_to receive(:perform_async) - .with(project.id, user.id, Gitlab::Git::BLANK_SHA, '789014', "#{ref_prefix}/create3").ordered + expect(UpdateMergeRequestsWorker).to receive(:perform_async) + .with(project.id, user.id, '789015', '789016', "#{ref_prefix}/changed1").ordered + expect(UpdateMergeRequestsWorker).to receive(:perform_async) + .with(project.id, user.id, '789020', Gitlab::Git::BLANK_SHA, "#{ref_prefix}/removed2").ordered subject.execute end diff --git a/spec/services/merge_requests/delete_non_latest_diffs_service_spec.rb b/spec/services/merge_requests/delete_non_latest_diffs_service_spec.rb index 377615bbc6f..cdaacaf5fca 100644 --- a/spec/services/merge_requests/delete_non_latest_diffs_service_spec.rb +++ b/spec/services/merge_requests/delete_non_latest_diffs_service_spec.rb @@ -19,7 +19,7 @@ RSpec.describe MergeRequests::DeleteNonLatestDiffsService, :clean_gitlab_redis_s expect(diffs.count).to eq(4) - Timecop.freeze do + freeze_time do expect(DeleteDiffFilesWorker) .to receive(:bulk_perform_in) .with(5.minutes, [[diffs.first.id], [diffs.second.id]]) diff --git a/spec/services/note_summary_spec.rb b/spec/services/note_summary_spec.rb index 38174748b19..ad244f62292 100644 --- a/spec/services/note_summary_spec.rb +++ b/spec/services/note_summary_spec.rb @@ -23,7 +23,7 @@ RSpec.describe NoteSummary do describe '#note' do it 'returns note hash' do - Timecop.freeze do + freeze_time do expect(create_note_summary.note).to eq(noteable: noteable, project: project, author: user, note: 'note', created_at: Time.current) end diff --git a/spec/services/projects/hashed_storage/base_attachment_service_spec.rb b/spec/services/projects/hashed_storage/base_attachment_service_spec.rb index 5e1b6f2e404..969381b8748 100644 --- a/spec/services/projects/hashed_storage/base_attachment_service_spec.rb +++ b/spec/services/projects/hashed_storage/base_attachment_service_spec.rb @@ -30,7 +30,7 @@ RSpec.describe Projects::HashedStorage::BaseAttachmentService do target_path = Dir.mktmpdir expect(Dir.exist?(target_path)).to be_truthy - Timecop.freeze do + freeze_time do suffix = Time.current.utc.to_i subject.send(:discard_path!, target_path) diff --git a/spec/services/releases/create_service_spec.rb b/spec/services/releases/create_service_spec.rb index ad4696b0074..90648340b66 100644 --- a/spec/services/releases/create_service_spec.rb +++ b/spec/services/releases/create_service_spec.rb @@ -202,7 +202,7 @@ RSpec.describe Releases::CreateService do let(:last_release) { project.releases.last } around do |example| - Timecop.freeze { example.run } + freeze_time { example.run } end subject { service.execute } diff --git a/spec/services/submit_usage_ping_service_spec.rb b/spec/services/submit_usage_ping_service_spec.rb index 450af68d383..401d1ed5adc 100644 --- a/spec/services/submit_usage_ping_service_spec.rb +++ b/spec/services/submit_usage_ping_service_spec.rb @@ -68,7 +68,7 @@ RSpec.describe SubmitUsagePingService do end end - shared_examples 'saves DevOps score data from the response' do + shared_examples 'saves DevOps report data from the response' do it do expect { subject.execute } .to change { DevOpsScore::Metric.count } @@ -123,15 +123,15 @@ RSpec.describe SubmitUsagePingService do stub_response(body: with_conv_index_params) end - it_behaves_like 'saves DevOps score data from the response' + it_behaves_like 'saves DevOps report data from the response' end - context 'when DevOps score data is passed' do + context 'when DevOps report data is passed' do before do stub_response(body: with_dev_ops_score_params) end - it_behaves_like 'saves DevOps score data from the response' + it_behaves_like 'saves DevOps report data from the response' end context 'with save_raw_usage_data feature enabled' do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index adf21c1c965..de4bd178b9d 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -100,6 +100,10 @@ RSpec.configure do |config| metadata[:enable_admin_mode] = true if location =~ %r{(ee)?/spec/controllers/admin/} end + config.define_derived_metadata(file_path: %r{(ee)?/spec/.+_docs\.rb\z}) do |metadata| + metadata[:type] = :feature + end + config.include LicenseHelpers config.include ActiveJob::TestHelper config.include ActiveSupport::Testing::TimeHelpers diff --git a/spec/support/helpers/docs_screenshot_helpers.rb b/spec/support/helpers/docs_screenshot_helpers.rb new file mode 100644 index 00000000000..aa3aad0a740 --- /dev/null +++ b/spec/support/helpers/docs_screenshot_helpers.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require 'fileutils' +require 'mini_magick' + +module DocsScreenshotHelpers + extend ActiveSupport::Concern + + def set_crop_data(element, padding) + @crop_element = element + @crop_padding = padding + end + + def crop_image_screenshot(path) + element_rect = @crop_element.evaluate_script("this.getBoundingClientRect()") + + width = element_rect['width'] + (@crop_padding * 2) + height = element_rect['height'] + (@crop_padding * 2) + + x = element_rect['x'] - @crop_padding + y = element_rect['y'] - @crop_padding + + image = MiniMagick::Image.new(path) + image.crop "#{width}x#{height}+#{x}+#{y}" + end + + included do |base| + after do |example| + filename = "#{example.description}.png" + path = File.expand_path(filename, 'doc/') + page.save_screenshot(path) + + if @crop_element + crop_image_screenshot(path) + set_crop_data(nil, nil) + end + end + end +end diff --git a/spec/support/shared_contexts/finders/users_finder_shared_contexts.rb b/spec/support/shared_contexts/finders/users_finder_shared_contexts.rb index fc8f9d2f407..54022aeb494 100644 --- a/spec/support/shared_contexts/finders/users_finder_shared_contexts.rb +++ b/spec/support/shared_contexts/finders/users_finder_shared_contexts.rb @@ -5,4 +5,5 @@ RSpec.shared_context 'UsersFinder#execute filter by project context' do let_it_be(:blocked_user) { create(:user, :blocked, username: 'notsorandom') } let_it_be(:external_user) { create(:user, :external) } let_it_be(:omniauth_user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') } + let_it_be(:internal_user) { User.alert_bot } end diff --git a/spec/support/shared_examples/lib/gitlab/background_migration/mentions_migration_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/background_migration/mentions_migration_shared_examples.rb index 8cf6babe146..e93077c42e1 100644 --- a/spec/support/shared_examples/lib/gitlab/background_migration/mentions_migration_shared_examples.rb +++ b/spec/support/shared_examples/lib/gitlab/background_migration/mentions_migration_shared_examples.rb @@ -63,7 +63,7 @@ RSpec.shared_examples 'schedules resource mentions migration' do |resource_class it 'schedules background migrations' do Sidekiq::Testing.fake! do - Timecop.freeze do + freeze_time do resource_count = is_for_notes ? Note.count : resource_class.count expect(resource_count).to eq 5 diff --git a/spec/support/shared_examples/models/relative_positioning_shared_examples.rb b/spec/support/shared_examples/models/relative_positioning_shared_examples.rb index ebb91a2fbad..0ad8d8cbde1 100644 --- a/spec/support/shared_examples/models/relative_positioning_shared_examples.rb +++ b/spec/support/shared_examples/models/relative_positioning_shared_examples.rb @@ -568,6 +568,12 @@ RSpec.shared_examples 'a class that supports relative positioning' do end end + it 'places items at most IDEAL_DISTANCE from the start when the range is open' do + n = item1.send(:scoped_items).count + + expect([item1, item2].map(&:relative_position)).to all(be >= (RelativePositioning::START_POSITION - (n * RelativePositioning::IDEAL_DISTANCE))) + end + it 'moves item to the end' do new_item.move_to_start @@ -613,6 +619,12 @@ RSpec.shared_examples 'a class that supports relative positioning' do end end + it 'places items at most IDEAL_DISTANCE from the start when the range is open' do + n = item1.send(:scoped_items).count + + expect([item1, item2].map(&:relative_position)).to all(be <= (RelativePositioning::START_POSITION + (n * RelativePositioning::IDEAL_DISTANCE))) + end + it 'moves item to the end' do new_item.move_to_end diff --git a/spec/support/shared_examples/models/throttled_touch_shared_examples.rb b/spec/support/shared_examples/models/throttled_touch_shared_examples.rb index fc4f6053bb9..14b851d2828 100644 --- a/spec/support/shared_examples/models/throttled_touch_shared_examples.rb +++ b/spec/support/shared_examples/models/throttled_touch_shared_examples.rb @@ -3,7 +3,7 @@ RSpec.shared_examples 'throttled touch' do describe '#touch' do it 'updates the updated_at timestamp' do - Timecop.freeze do + freeze_time do subject.touch expect(subject.updated_at).to be_like_time(Time.zone.now) end diff --git a/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb index 48824a4b0d2..62dbac3fd4d 100644 --- a/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb @@ -8,3 +8,42 @@ RSpec.shared_examples 'when the snippet is not found' do it_behaves_like 'a mutation that returns top-level errors', errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR] end + +RSpec.shared_examples 'snippet edit usage data counters' do + context 'when user is sessionless' do + it 'does not track usage data actions' do + expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).not_to receive(:track_snippet_editor_edit_action) + + post_graphql_mutation(mutation, current_user: current_user) + end + end + + context 'when user is not sessionless' do + before do + session_id = Rack::Session::SessionId.new('6919a6f1bb119dd7396fadc38fd18d0d') + session_hash = { 'warden.user.user.key' => [[current_user.id], current_user.encrypted_password[0, 29]] } + + Gitlab::Redis::SharedState.with do |redis| + redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash)) + end + + cookies[Gitlab::Application.config.session_options[:key]] = session_id.public_id + end + + it 'tracks usage data actions', :clean_gitlab_redis_shared_state do + expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).to receive(:track_snippet_editor_edit_action) + + post_graphql_mutation(mutation) + end + + context 'when mutation result raises an error' do + it 'does not track usage data actions' do + mutation_vars[:title] = nil + + expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).not_to receive(:track_snippet_editor_edit_action) + + post_graphql_mutation(mutation) + end + end + end +end diff --git a/spec/workers/cluster_update_app_worker_spec.rb b/spec/workers/cluster_update_app_worker_spec.rb index c24f40024fd..8b8c1c82099 100644 --- a/spec/workers/cluster_update_app_worker_spec.rb +++ b/spec/workers/cluster_update_app_worker_spec.rb @@ -12,7 +12,7 @@ RSpec.describe ClusterUpdateAppWorker do subject { described_class.new } around do |example| - Timecop.freeze(Time.current) { example.run } + freeze_time { example.run } end before do diff --git a/spec/workers/repository_update_remote_mirror_worker_spec.rb b/spec/workers/repository_update_remote_mirror_worker_spec.rb index c6e667097ec..858f5226c48 100644 --- a/spec/workers/repository_update_remote_mirror_worker_spec.rb +++ b/spec/workers/repository_update_remote_mirror_worker_spec.rb @@ -9,7 +9,7 @@ RSpec.describe RepositoryUpdateRemoteMirrorWorker, :clean_gitlab_redis_shared_st let(:scheduled_time) { Time.current - 5.minutes } around do |example| - Timecop.freeze(Time.current) { example.run } + freeze_time { example.run } end def expect_mirror_service_to_return(mirror, result, tries = 0) diff --git a/tooling/lib/tooling/images.rb b/tooling/lib/tooling/images.rb new file mode 100644 index 00000000000..d0c464b983c --- /dev/null +++ b/tooling/lib/tooling/images.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +module Tooling + module Image + # Determine the tolerance till when we run pngquant in a loop + TOLERANCE = 10000 + + def self.check_executables + unless system('pngquant --version', out: File::NULL) + warn( + 'Error: pngquant executable was not detected in the system.', + 'Download pngquant at https://pngquant.org/ and place the executable in /usr/local/bin' + ) + abort + end + + unless system('gm version', out: File::NULL) + warn( + 'Error: gm executable was not detected in the system.', + 'Please install imagemagick: brew install imagemagick or sudo apt install imagemagick' + ) + abort + end + end + + def self.compress_image(file, keep_original = false) + check_executables + + compressed_file = "#{file}.compressed" + FileUtils.copy(file, compressed_file) + + pngquant_file = PngQuantizator::Image.new(compressed_file) + + # Run the image repeatedly through pngquant until + # the change in file size is within TOLERANCE + # or the loop count is above 1000 + 1000.times do + before = File.size(compressed_file) + pngquant_file.quantize! + after = File.size(compressed_file) + break if before - after <= TOLERANCE + end + + savings = File.size(file) - File.size(compressed_file) + is_uncompressed = savings > TOLERANCE + + if is_uncompressed && !keep_original + FileUtils.copy(compressed_file, file) + end + + FileUtils.remove(compressed_file) + + [is_uncompressed, savings] + end + end +end |