Add latest changes from gitlab-org/gitlab@master
|
@ -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}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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|
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix Metrics dashboard embeds when using new URLs
|
||||
merge_request: 39876
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Track snippet editor actions
|
||||
merge_request: 40277
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add filter to exclude non internal users in REST API
|
||||
merge_request: 40372
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add emails user_id foreign key with cascade delete
|
||||
merge_request: 39899
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix wrong caching logic in ProcessRefChangesService
|
||||
merge_request: 40821
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Rename DevOps Score to DevOps Report
|
||||
merge_request: 39953
|
||||
author:
|
||||
type: changed
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
5a5278fdd9539d33a6de226a84ed39b7c5a26929cec68ec5e8d193afb3cfafa2
|
|
@ -0,0 +1 @@
|
|||
476bce9b18177f37b31e15d42f5a1391c0bfbbd312a513c1d5b43085b90afb3e
|
|
@ -0,0 +1 @@
|
|||
5e2dfdf725ad0a3d90b240ced74cf5a872f7126b716847f9f9e99b4ad2a22109
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -8547,6 +8609,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
|
||||
"""
|
||||
|
@ -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
|
||||
|
|
|
@ -4729,6 +4729,181 @@
|
|||
"enumValues": null,
|
||||
"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",
|
||||
|
@ -4739,6 +4914,16 @@
|
|||
"enumValues": null,
|
||||
"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",
|
||||
|
@ -23818,6 +24003,24 @@
|
|||
"isDeprecated": false,
|
||||
"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",
|
||||
|
@ -27729,6 +27932,33 @@
|
|||
"isDeprecated": false,
|
||||
"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,
|
||||
|
|
|
@ -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 |
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 64 KiB |
After Width: | Height: | Size: 24 KiB |
|
@ -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).
|
||||
|
|
|
@ -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.
|
After Width: | Height: | Size: 164 KiB |
Before Width: | Height: | Size: 153 KiB |
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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).
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ""
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 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 /-/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
|
||||
|
||||
it_behaves_like 'a metrics embed filter'
|
||||
context 'with query params' do
|
||||
it_behaves_like 'a metrics embed filter'
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -30,7 +30,7 @@ RSpec.describe Gitlab::Metrics::MethodCall do
|
|||
end
|
||||
|
||||
around do |example|
|
||||
Timecop.freeze do
|
||||
freeze_time do
|
||||
example.run
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 })
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|