Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-06-18 15:08:45 +00:00
parent 8eef083ccd
commit 874c603d7a
68 changed files with 804 additions and 614 deletions

View File

@ -126,7 +126,7 @@ export default {
updateAssigneesDropdown() {
this.isDropdownSearching = true;
return axios
.get(this.buildUrl(gon.relative_url_root, '/autocomplete/users.json'), {
.get(this.buildUrl(gon.relative_url_root, '/-/autocomplete/users.json'), {
params: {
search: this.search,
per_page: 20,

View File

@ -10,7 +10,7 @@ export default class DropdownEmoji extends FilteredSearchDropdown {
super(options);
this.config = {
Ajax: {
endpoint: `${gon.relative_url_root || ''}/autocomplete/award_emojis`,
endpoint: `${gon.relative_url_root || ''}/-/autocomplete/award_emojis`,
method: 'setData',
loadingTemplate: this.loadingTemplate,
onError() {

View File

@ -5,7 +5,7 @@ export default class DropdownUser extends DropdownAjaxFilter {
constructor(options = {}) {
super({
...options,
endpoint: '/autocomplete/users.json',
endpoint: '/-/autocomplete/users.json',
symbol: '@',
});
}

View File

@ -420,7 +420,7 @@ export default {
<transition name="issuable-header-slide">
<div
v-if="shouldShowStickyHeader"
class="issue-sticky-header gl-fixed gl-z-index-2 gl-bg-white gl-border-1 gl-border-b-solid gl-border-b-gray-200 gl-py-3"
class="issue-sticky-header gl-fixed gl-z-index-3 gl-bg-white gl-border-1 gl-border-b-solid gl-border-b-gray-200 gl-py-3"
data-testid="issue-sticky-header"
>
<div

View File

@ -63,17 +63,19 @@ export default {
selectedDailyCoverageName() {
return this.selectedDailyCoverage?.group_name;
},
formattedData() {
if (this.selectedDailyCoverage?.data) {
return this.selectedDailyCoverage.data.map(value => [
dateFormat(value.date, 'mmm dd'),
value.coverage,
]);
}
sortedData() {
// If the fetching failed, we return an empty array which
// allow the graph to render while empty
return [];
if (!this.selectedDailyCoverage?.data) {
return [];
}
return [...this.selectedDailyCoverage.data].sort(
(a, b) => new Date(a.date) - new Date(b.date),
);
},
formattedData() {
return this.sortedData.map(value => [dateFormat(value.date, 'mmm dd'), value.coverage]);
},
chartData() {
return [

View File

@ -28,7 +28,7 @@ export default {
columnClass() {
const positionValues = {
right: 'prepend-left-64',
left: 'append-right-32',
left: 'gl-mr-7',
};
return `graph-position-${this.graphPosition} ${positionValues[this.graphPosition]}`;
},

View File

@ -18,7 +18,7 @@ export default {
fetchAuthors({ dispatch, state }, author = null) {
const { projectId } = state;
return axios
.get(joinPaths(gon.relative_url_root || '', '/autocomplete/users.json'), {
.get(joinPaths(gon.relative_url_root || '', '/-/autocomplete/users.json'), {
params: {
project_id: projectId,
active: true,

View File

@ -21,8 +21,8 @@ function UsersSelect(currentUser, els, options = {}) {
const $els = $(els || '.js-user-search');
this.users = this.users.bind(this);
this.user = this.user.bind(this);
this.usersPath = '/autocomplete/users.json';
this.userPath = '/autocomplete/users/:id.json';
this.usersPath = '/-/autocomplete/users.json';
this.userPath = '/-/autocomplete/users/:id.json';
if (currentUser != null) {
if (typeof currentUser === 'object') {
this.currentUser = currentUser;

View File

@ -415,7 +415,6 @@ img.emoji {
.append-right-15 { margin-right: 15px; }
.append-right-default { margin-right: $gl-padding; }
.append-right-20 { margin-right: 20px; }
.append-right-32 { margin-right: 32px; }
.append-right-48 { margin-right: 48px; }
.prepend-right-32 { margin-right: 32px; }
.append-bottom-5 { margin-bottom: 5px; }

View File

@ -1,8 +1,8 @@
# frozen_string_literal: true
module Types
class ReleaseLinkType < BaseObject
graphql_name 'ReleaseLink'
class ReleaseAssetLinkType < BaseObject
graphql_name 'ReleaseAssetLink'
description 'Represents an asset link associated with a release'
authorize :read_release
@ -13,7 +13,7 @@ module Types
description: 'Name of the link'
field :url, GraphQL::STRING_TYPE, null: true,
description: 'URL of the link'
field :link_type, Types::ReleaseLinkTypeEnum, null: true,
field :link_type, Types::ReleaseAssetLinkTypeEnum, null: true,
description: 'Type of the link: `other`, `runbook`, `image`, `package`; defaults to `other`'
field :external, GraphQL::BOOLEAN_TYPE, null: true, method: :external?,
description: 'Indicates the link points to an external resource'

View File

@ -1,8 +1,8 @@
# frozen_string_literal: true
module Types
class ReleaseLinkTypeEnum < BaseEnum
graphql_name 'ReleaseLinkType'
class ReleaseAssetLinkTypeEnum < BaseEnum
graphql_name 'ReleaseAssetLinkType'
description 'Type of the link: `other`, `runbook`, `image`, `package`; defaults to `other`'
::Releases::Link.link_types.keys.each do |link_type|

View File

@ -13,7 +13,7 @@ module Types
field :count, GraphQL::INT_TYPE, null: true, method: :assets_count,
description: 'Number of assets of the release'
field :links, Types::ReleaseLinkType.connection_type, null: true,
field :links, Types::ReleaseAssetLinkType.connection_type, null: true,
description: 'Asset links of the release'
field :sources, Types::ReleaseSourceType.connection_type, null: true,
description: 'Sources of the release'

View File

@ -37,7 +37,7 @@ module BulkInsertSafe
# These are the callbacks we think safe when used on models that are
# written to the database in bulk
CALLBACK_NAME_WHITELIST = Set[
ALLOWED_CALLBACKS = Set[
:initialize,
:validate,
:validation,
@ -179,16 +179,12 @@ module BulkInsertSafe
end
def _bulk_insert_callback_allowed?(name, args)
_bulk_insert_whitelisted?(name) || _bulk_insert_saved_from_belongs_to?(name, args)
ALLOWED_CALLBACKS.include?(name) || _bulk_insert_saved_from_belongs_to?(name, args)
end
# belongs_to associations will install a before_save hook during class loading
def _bulk_insert_saved_from_belongs_to?(name, args)
args.first == :before && args.second.to_s.start_with?('autosave_associated_records_for_')
end
def _bulk_insert_whitelisted?(name)
CALLBACK_NAME_WHITELIST.include?(name)
end
end
end

View File

@ -10,7 +10,8 @@ module Metrics
STAGES = ::Gitlab::Metrics::Dashboard::Stages
SEQUENCE = [
STAGES::CommonMetricsInserter,
STAGES::EndpointInserter,
STAGES::MetricEndpointInserter,
STAGES::VariableEndpointInserter,
STAGES::PanelIdsInserter,
STAGES::Sorter,
STAGES::AlertsInserter,

View File

@ -11,7 +11,7 @@ module Metrics
include Gitlab::Utils::StrongMemoize
SEQUENCE = [
STAGES::EndpointInserter,
STAGES::MetricEndpointInserter,
STAGES::PanelIdsInserter
].freeze

View File

@ -10,7 +10,8 @@ module Metrics
DASHBOARD_NAME = nil
SEQUENCE = [
STAGES::EndpointInserter,
STAGES::MetricEndpointInserter,
STAGES::VariableEndpointInserter,
STAGES::PanelIdsInserter,
STAGES::Sorter
].freeze

View File

@ -10,7 +10,8 @@ module Metrics
SEQUENCE = [
STAGES::CustomMetricsInserter,
STAGES::EndpointInserter,
STAGES::MetricEndpointInserter,
STAGES::VariableEndpointInserter,
STAGES::PanelIdsInserter,
STAGES::Sorter
].freeze

View File

@ -12,7 +12,8 @@ module Metrics
STAGES::CommonMetricsInserter,
STAGES::CustomMetricsInserter,
STAGES::CustomMetricsDetailsInserter,
STAGES::EndpointInserter,
STAGES::MetricEndpointInserter,
STAGES::VariableEndpointInserter,
STAGES::PanelIdsInserter,
STAGES::Sorter,
STAGES::AlertsInserter

View File

@ -30,7 +30,7 @@ module Metrics
override :sequence
def sequence
[STAGES::EndpointInserter]
[STAGES::MetricEndpointInserter]
end
override :identifiers

View File

@ -22,16 +22,20 @@ module Prometheus
attr_accessor :proxyable, :method, :path, :params
PROMETHEUS_QUERY_API = 'query'
PROMETHEUS_QUERY_RANGE_API = 'query_range'
PROMETHEUS_SERIES_API = 'series'
PROXY_SUPPORT = {
'query' => {
PROMETHEUS_QUERY_API => {
method: ['GET'],
params: %w(query time timeout)
},
'query_range' => {
PROMETHEUS_QUERY_RANGE_API => {
method: ['GET'],
params: %w(query start end step timeout)
},
'series' => {
PROMETHEUS_SERIES_API => {
method: %w(GET),
params: %w(match start end)
}

View File

@ -1,5 +0,0 @@
---
title: Remove Rails Optimistic Locking monkeypatch
merge_request: 25566
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Improve error message when unconfirmed user tries to log in
merge_request: 34818
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Sort code coverage graph in ascending order
merge_request: 34750
author:
type: fixed

View File

@ -0,0 +1,46 @@
# frozen_string_literal: true
# ensure ActiveRecord's version has been required already
require 'active_record/locking/optimistic'
# rubocop:disable Lint/RescueException
module ActiveRecord
module Locking
module Optimistic
private
def _update_row(attribute_names, attempted_action = "update")
return super unless locking_enabled?
begin
locking_column = self.class.locking_column
previous_lock_value = read_attribute_before_type_cast(locking_column)
attribute_names << locking_column
self[locking_column] += 1
# Patched because when `lock_version` is read as `0`, it may actually be `NULL` in the DB.
possible_previous_lock_value = previous_lock_value.to_i == 0 ? [nil, 0] : previous_lock_value
affected_rows = self.class.unscoped.where(
locking_column => possible_previous_lock_value,
self.class.primary_key => id_in_database
).update_all(
attributes_with_values(attribute_names)
)
if affected_rows != 1
raise ActiveRecord::StaleObjectError.new(self, attempted_action)
end
affected_rows
# If something went wrong, revert the locking_column value.
rescue Exception
self[locking_column] = previous_lock_value.to_i
raise
end
end
end
end
end

View File

@ -15,7 +15,7 @@ en:
not_found_in_database: "Invalid %{authentication_keys} or password."
timeout: "Your session expired. Please sign in again to continue."
unauthenticated: "You need to sign in or sign up before continuing."
unconfirmed: "You have to confirm your email address before continuing."
unconfirmed: "You have to confirm your email address before continuing. Please check your email for the link we sent you, or click 'Resend confirmation email'."
mailer:
confirmation_instructions:
subject: "Confirmation instructions"

View File

@ -4,8 +4,13 @@ THROUGHPUT_LABELS = [
'Community contribution',
'security',
'bug',
'backstage', # To be removed by https://gitlab.com/gitlab-org/gitlab/-/issues/222360.
'feature',
'backstage',
'feature::addition',
'feature::maintenance',
'tooling',
'tooling::pipelines',
'tooling::workflow',
'documentation'
].freeze

View File

@ -1,6 +1,13 @@
# frozen_string_literal: true
NO_SPECS_LABELS = %w[backstage documentation QA].freeze
NO_SPECS_LABELS = [
'backstage', # To be removed by https://gitlab.com/gitlab-org/gitlab/-/issues/222360.
'tooling',
'tooling::pipelines',
'tooling::workflow',
'documentation',
'QA'
].freeze
NO_NEW_SPEC_MESSAGE = <<~MSG
You've made some app changes, but didn't add any tests.
That's OK as long as you're refactoring existing code,

View File

@ -1,23 +1,20 @@
# frozen_string_literal: true
class SetLockVersionNotNullConstraint < ActiveRecord::Migration[6.0]
class RestorePreviousSchemaWithoutLockVersionNullConstraint < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
TABLES = %i(epics merge_requests issues ci_stages ci_builds ci_pipelines).freeze
disable_ddl_transaction!
TABLES = %i(epics merge_requests issues ci_stages ci_builds ci_pipelines).freeze
def up
TABLES.each do |table|
add_not_null_constraint table, :lock_version, validate: false
end
end
def down
TABLES.each do |table|
remove_not_null_constraint table, :lock_version
end
end
def down
# no-op
end
end

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
class RestorePreviousSchemaWithLockVersionIndices < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :issues, :lock_version, where: "lock_version IS NULL"
add_concurrent_index :merge_requests, :lock_version, where: "lock_version IS NULL"
add_concurrent_index :epics, :lock_version, where: "lock_version IS NULL"
add_concurrent_index :ci_stages, :id, where: "lock_version IS NULL", name: "tmp_index_ci_stages_lock_version"
add_concurrent_index :ci_builds, :id, where: "lock_version IS NULL", name: "tmp_index_ci_builds_lock_version"
add_concurrent_index :ci_pipelines, :id, where: "lock_version IS NULL", name: "tmp_index_ci_pipelines_lock_version"
end
def down
# no-op
end
end

View File

@ -1,19 +0,0 @@
# frozen_string_literal: true
class SetLockVersionToNotNull < ActiveRecord::Migration[6.0]
DOWNTIME = false
MODELS = [Epic, MergeRequest, Issue, Ci::Stage, Ci::Build, Ci::Pipeline].freeze
disable_ddl_transaction!
def up
MODELS.each do |model|
model.where(lock_version: nil).update_all(lock_version: 0)
end
end
def down
# Nothing to do...
end
end

View File

@ -1,18 +0,0 @@
# frozen_string_literal: true
class LockVersionCleanupForEpics < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
validate_not_null_constraint :epics, :lock_version
remove_concurrent_index :epics, :lock_version, where: "lock_version IS NULL"
end
def down
add_concurrent_index :epics, :lock_version, where: "lock_version IS NULL"
end
end

View File

@ -1,18 +0,0 @@
# frozen_string_literal: true
class LockVersionCleanupForMergeRequests < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
validate_not_null_constraint :merge_requests, :lock_version
remove_concurrent_index :merge_requests, :lock_version, where: "lock_version IS NULL"
end
def down
add_concurrent_index :merge_requests, :lock_version, where: "lock_version IS NULL"
end
end

View File

@ -1,18 +0,0 @@
# frozen_string_literal: true
class LockVersionCleanupForIssues < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
validate_not_null_constraint :issues, :lock_version
remove_concurrent_index :issues, :lock_version, where: "lock_version IS NULL"
end
def down
add_concurrent_index :issues, :lock_version, where: "lock_version IS NULL"
end
end

View File

@ -1,18 +0,0 @@
# frozen_string_literal: true
class LockVersionCleanupForCiStages < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
validate_not_null_constraint :ci_stages, :lock_version
remove_concurrent_index :ci_stages, :id, where: "lock_version IS NULL", name: "tmp_index_ci_stages_lock_version"
end
def down
add_concurrent_index :ci_stages, :id, where: "lock_version IS NULL", name: "tmp_index_ci_stages_lock_version"
end
end

View File

@ -1,18 +0,0 @@
# frozen_string_literal: true
class LockVersionCleanupForCiBuilds < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
validate_not_null_constraint :ci_builds, :lock_version
remove_concurrent_index :ci_builds, :id, where: "lock_version IS NULL", name: "tmp_index_ci_builds_lock_version"
end
def down
add_concurrent_index :ci_builds, :id, where: "lock_version IS NULL", name: "tmp_index_ci_builds_lock_version"
end
end

View File

@ -1,18 +0,0 @@
# frozen_string_literal: true
class LockVersionCleanupForCiPipelines < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
validate_not_null_constraint :ci_pipelines, :lock_version
remove_concurrent_index :ci_pipelines, :id, where: "lock_version IS NULL", name: "tmp_index_ci_pipelines_lock_version"
end
def down
add_concurrent_index :ci_pipelines, :id, where: "lock_version IS NULL", name: "tmp_index_ci_pipelines_lock_version"
end
end

View File

@ -1056,8 +1056,7 @@ CREATE TABLE public.ci_builds (
resource_group_id bigint,
waiting_for_resource_at timestamp with time zone,
processed boolean,
scheduling_type smallint,
CONSTRAINT check_1e2fbd1b39 CHECK ((lock_version IS NOT NULL))
scheduling_type smallint
);
CREATE SEQUENCE public.ci_builds_id_seq
@ -1368,8 +1367,7 @@ CREATE TABLE public.ci_pipelines (
source_sha bytea,
target_sha bytea,
external_pull_request_id bigint,
ci_ref_id bigint,
CONSTRAINT check_d7e99a025e CHECK ((lock_version IS NOT NULL))
ci_ref_id bigint
);
CREATE TABLE public.ci_pipelines_config (
@ -1554,8 +1552,7 @@ CREATE TABLE public.ci_stages (
name character varying,
status integer,
lock_version integer DEFAULT 0,
"position" integer,
CONSTRAINT check_81b431e49b CHECK ((lock_version IS NOT NULL))
"position" integer
);
CREATE SEQUENCE public.ci_stages_id_seq
@ -2514,8 +2511,7 @@ CREATE TABLE public.epics (
start_date_sourcing_epic_id integer,
due_date_sourcing_epic_id integer,
confidential boolean DEFAULT false NOT NULL,
external_key character varying(255),
CONSTRAINT check_fcfb4a93ff CHECK ((lock_version IS NOT NULL))
external_key character varying(255)
);
CREATE SEQUENCE public.epics_id_seq
@ -3558,8 +3554,7 @@ CREATE TABLE public.issues (
promoted_to_epic_id integer,
health_status smallint,
external_key character varying(255),
sprint_id bigint,
CONSTRAINT check_fba63f706d CHECK ((lock_version IS NOT NULL))
sprint_id bigint
);
CREATE SEQUENCE public.issues_id_seq
@ -4139,8 +4134,7 @@ CREATE TABLE public.merge_requests (
state_id smallint DEFAULT 1 NOT NULL,
rebase_jid character varying,
squash_commit_sha bytea,
sprint_id bigint,
CONSTRAINT check_970d272570 CHECK ((lock_version IS NOT NULL))
sprint_id bigint
);
CREATE TABLE public.merge_requests_closing_issues (
@ -9892,6 +9886,8 @@ CREATE INDEX index_epics_on_iid ON public.epics USING btree (iid);
CREATE INDEX index_epics_on_last_edited_by_id ON public.epics USING btree (last_edited_by_id);
CREATE INDEX index_epics_on_lock_version ON public.epics USING btree (lock_version) WHERE (lock_version IS NULL);
CREATE INDEX index_epics_on_parent_id ON public.epics USING btree (parent_id);
CREATE INDEX index_epics_on_start_date ON public.epics USING btree (start_date);
@ -10138,6 +10134,8 @@ CREATE INDEX index_issues_on_duplicated_to_id ON public.issues USING btree (dupl
CREATE INDEX index_issues_on_last_edited_by_id ON public.issues USING btree (last_edited_by_id);
CREATE INDEX index_issues_on_lock_version ON public.issues USING btree (lock_version) WHERE (lock_version IS NULL);
CREATE INDEX index_issues_on_milestone_id ON public.issues USING btree (milestone_id);
CREATE INDEX index_issues_on_moved_to_id ON public.issues USING btree (moved_to_id) WHERE (moved_to_id IS NOT NULL);
@ -10304,6 +10302,8 @@ CREATE INDEX index_merge_requests_on_head_pipeline_id ON public.merge_requests U
CREATE INDEX index_merge_requests_on_latest_merge_request_diff_id ON public.merge_requests USING btree (latest_merge_request_diff_id);
CREATE INDEX index_merge_requests_on_lock_version ON public.merge_requests USING btree (lock_version) WHERE (lock_version IS NULL);
CREATE INDEX index_merge_requests_on_merge_user_id ON public.merge_requests USING btree (merge_user_id) WHERE (merge_user_id IS NOT NULL);
CREATE INDEX index_merge_requests_on_milestone_id ON public.merge_requests USING btree (milestone_id);
@ -11308,6 +11308,12 @@ CREATE INDEX tmp_build_stage_position_index ON public.ci_builds USING btree (sta
CREATE INDEX tmp_idx_on_user_id_where_bio_is_filled ON public.users USING btree (id) WHERE ((COALESCE(bio, ''::character varying))::text IS DISTINCT FROM ''::text);
CREATE INDEX tmp_index_ci_builds_lock_version ON public.ci_builds USING btree (id) WHERE (lock_version IS NULL);
CREATE INDEX tmp_index_ci_pipelines_lock_version ON public.ci_pipelines USING btree (id) WHERE (lock_version IS NULL);
CREATE INDEX tmp_index_ci_stages_lock_version ON public.ci_stages USING btree (id) WHERE (lock_version IS NULL);
CREATE UNIQUE INDEX users_security_dashboard_projects_unique_index ON public.users_security_dashboard_projects USING btree (project_id, user_id);
CREATE UNIQUE INDEX vulnerability_feedback_unique_idx ON public.vulnerability_feedback USING btree (project_id, category, feedback_type, project_fingerprint);
@ -14053,14 +14059,6 @@ COPY "schema_migrations" (version) FROM STDIN;
20200605093113
20200608072931
20200608075553
20200608195222
20200608205813
20200608212030
20200608212435
20200608212549
20200608212652
20200608212807
20200608212824
20200608214008
20200609002841
20200609142506
@ -14080,5 +14078,7 @@ COPY "schema_migrations" (version) FROM STDIN;
20200617001637
20200617001848
20200617002030
20200618134223
20200618134723
\.

View File

@ -10207,6 +10207,96 @@ type Release {
tagPath: String
}
"""
Represents an asset link associated with a release
"""
type ReleaseAssetLink {
"""
Indicates the link points to an external resource
"""
external: Boolean
"""
ID of the link
"""
id: ID!
"""
Type of the link: `other`, `runbook`, `image`, `package`; defaults to `other`
"""
linkType: ReleaseAssetLinkType
"""
Name of the link
"""
name: String
"""
URL of the link
"""
url: String
}
"""
The connection type for ReleaseAssetLink.
"""
type ReleaseAssetLinkConnection {
"""
A list of edges.
"""
edges: [ReleaseAssetLinkEdge]
"""
A list of nodes.
"""
nodes: [ReleaseAssetLink]
"""
Information to aid in pagination.
"""
pageInfo: PageInfo!
}
"""
An edge in a connection.
"""
type ReleaseAssetLinkEdge {
"""
A cursor for use in pagination.
"""
cursor: String!
"""
The item at the end of the edge.
"""
node: ReleaseAssetLink
}
"""
Type of the link: `other`, `runbook`, `image`, `package`; defaults to `other`
"""
enum ReleaseAssetLinkType {
"""
Image link type
"""
IMAGE
"""
Other link type
"""
OTHER
"""
Package link type
"""
PACKAGE
"""
Runbook link type
"""
RUNBOOK
}
"""
A container for all assets associated with a release
"""
@ -10239,7 +10329,7 @@ type ReleaseAssets {
Returns the last _n_ elements from the list.
"""
last: Int
): ReleaseLinkConnection
): ReleaseAssetLinkConnection
"""
Sources of the release
@ -10362,96 +10452,6 @@ type ReleaseEvidenceEdge {
node: ReleaseEvidence
}
"""
Represents an asset link associated with a release
"""
type ReleaseLink {
"""
Indicates the link points to an external resource
"""
external: Boolean
"""
ID of the link
"""
id: ID!
"""
Type of the link: `other`, `runbook`, `image`, `package`; defaults to `other`
"""
linkType: ReleaseLinkType
"""
Name of the link
"""
name: String
"""
URL of the link
"""
url: String
}
"""
The connection type for ReleaseLink.
"""
type ReleaseLinkConnection {
"""
A list of edges.
"""
edges: [ReleaseLinkEdge]
"""
A list of nodes.
"""
nodes: [ReleaseLink]
"""
Information to aid in pagination.
"""
pageInfo: PageInfo!
}
"""
An edge in a connection.
"""
type ReleaseLinkEdge {
"""
A cursor for use in pagination.
"""
cursor: String!
"""
The item at the end of the edge.
"""
node: ReleaseLink
}
"""
Type of the link: `other`, `runbook`, `image`, `package`; defaults to `other`
"""
enum ReleaseLinkType {
"""
Image link type
"""
IMAGE
"""
Other link type
"""
OTHER
"""
Package link type
"""
PACKAGE
"""
Runbook link type
"""
RUNBOOK
}
"""
Represents the source code attached to a release in a particular format
"""

View File

@ -29844,6 +29844,240 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "ReleaseAssetLink",
"description": "Represents an asset link associated with a release",
"fields": [
{
"name": "external",
"description": "Indicates the link points to an external resource",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "id",
"description": "ID of the link",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "linkType",
"description": "Type of the link: `other`, `runbook`, `image`, `package`; defaults to `other`",
"args": [
],
"type": {
"kind": "ENUM",
"name": "ReleaseAssetLinkType",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "name",
"description": "Name of the link",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "url",
"description": "URL of the link",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "ReleaseAssetLinkConnection",
"description": "The connection type for ReleaseAssetLink.",
"fields": [
{
"name": "edges",
"description": "A list of edges.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "ReleaseAssetLinkEdge",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "nodes",
"description": "A list of nodes.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "ReleaseAssetLink",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "pageInfo",
"description": "Information to aid in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "PageInfo",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "ReleaseAssetLinkEdge",
"description": "An edge in a connection.",
"fields": [
{
"name": "cursor",
"description": "A cursor for use in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "node",
"description": "The item at the end of the edge.",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "ReleaseAssetLink",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "ENUM",
"name": "ReleaseAssetLinkType",
"description": "Type of the link: `other`, `runbook`, `image`, `package`; defaults to `other`",
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": [
{
"name": "OTHER",
"description": "Other link type",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "RUNBOOK",
"description": "Runbook link type",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "PACKAGE",
"description": "Package link type",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "IMAGE",
"description": "Image link type",
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "ReleaseAssets",
@ -29910,7 +30144,7 @@
],
"type": {
"kind": "OBJECT",
"name": "ReleaseLinkConnection",
"name": "ReleaseAssetLinkConnection",
"ofType": null
},
"isDeprecated": false,
@ -30274,240 +30508,6 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "ReleaseLink",
"description": "Represents an asset link associated with a release",
"fields": [
{
"name": "external",
"description": "Indicates the link points to an external resource",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "id",
"description": "ID of the link",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "linkType",
"description": "Type of the link: `other`, `runbook`, `image`, `package`; defaults to `other`",
"args": [
],
"type": {
"kind": "ENUM",
"name": "ReleaseLinkType",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "name",
"description": "Name of the link",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "url",
"description": "URL of the link",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "ReleaseLinkConnection",
"description": "The connection type for ReleaseLink.",
"fields": [
{
"name": "edges",
"description": "A list of edges.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "ReleaseLinkEdge",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "nodes",
"description": "A list of nodes.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "ReleaseLink",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "pageInfo",
"description": "Information to aid in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "PageInfo",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "ReleaseLinkEdge",
"description": "An edge in a connection.",
"fields": [
{
"name": "cursor",
"description": "A cursor for use in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "node",
"description": "The item at the end of the edge.",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "ReleaseLink",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "ENUM",
"name": "ReleaseLinkType",
"description": "Type of the link: `other`, `runbook`, `image`, `package`; defaults to `other`",
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": [
{
"name": "OTHER",
"description": "Other link type",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "RUNBOOK",
"description": "Runbook link type",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "PACKAGE",
"description": "Package link type",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "IMAGE",
"description": "Image link type",
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "ReleaseSource",

View File

@ -1415,6 +1415,18 @@ Represents a release
| `tagName` | String | Name of the tag associated with the release |
| `tagPath` | String | Relative web path to the tag associated with the release |
## ReleaseAssetLink
Represents an asset link associated with a release
| Name | Type | Description |
| --- | ---- | ---------- |
| `external` | Boolean | Indicates the link points to an external resource |
| `id` | ID! | ID of the link |
| `linkType` | ReleaseAssetLinkType | Type of the link: `other`, `runbook`, `image`, `package`; defaults to `other` |
| `name` | String | Name of the link |
| `url` | String | URL of the link |
## ReleaseAssets
A container for all assets associated with a release
@ -1434,18 +1446,6 @@ Evidence for a release
| `id` | ID! | ID of the evidence |
| `sha` | String | SHA1 ID of the evidence hash |
## ReleaseLink
Represents an asset link associated with a release
| Name | Type | Description |
| --- | ---- | ---------- |
| `external` | Boolean | Indicates the link points to an external resource |
| `id` | ID! | ID of the link |
| `linkType` | ReleaseLinkType | Type of the link: `other`, `runbook`, `image`, `package`; defaults to `other` |
| `name` | String | Name of the link |
| `url` | String | URL of the link |
## ReleaseSource
Represents the source code attached to a release in a particular format

View File

@ -67,6 +67,6 @@ close the terminal window.
![finished job with terminal open](img/finished_job_with_terminal_open.png)
## Interactive Web Terminals for the Web IDE **(ULTIMATE ONLY)**
## Interactive Web Terminals for the Web IDE
Read the Web IDE docs to learn how to run [Interactive Terminals through the Web IDE](../../user/project/web_ide/index.md).
Read the Web IDE docs to learn how to run [Interactive Terminals through the Web IDE](../../user/project/web_ide/index.md#interactive-web-terminals-for-the-web-ide).

View File

@ -121,10 +121,10 @@ These callbacks cannot be used with bulk insertions, since they are meant to be
every instance that is saved or created. Since these events do not fire when
records are inserted in bulk, we currently disallow their use.
The specifics around which callbacks are disallowed are defined in
The specifics around which callbacks are explicitly allowed are defined in
[`BulkInsertSafe`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/concerns/bulk_insert_safe.rb).
Consult the module source code for details. If your class uses any of the blacklisted
functionality, and you `include BulkInsertSafe`, the application will fail with an error.
Consult the module source code for details. If your class uses callbacks that are not explicitly designated
safe and you `include BulkInsertSafe` the application will fail with an error.
### `BulkInsertSafe` versus `InsertAll`

View File

@ -213,16 +213,19 @@ to work:
- The Runner needs to have
[`[session_server]` configured properly](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-session_server-section).
This section requires at least a `session_timeout` value (which defaults to 1800
seconds) and a `listen_address` value. If `advertise_address` is not defined, `listen_address` is used.
- If you are using a reverse proxy with your GitLab instance, web terminals need to be
[enabled](../../../administration/integration/terminal.md#enabling-and-disabling-terminal-support). **(ULTIMATE ONLY)**
If you have the terminal open and the job has finished with its tasks, the
terminal will block the job from finishing for the duration configured in
[`[session_server].terminal_max_retention_time`](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-session_server-section)
[`[session_server].session_timeout`](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-session_server-section)
until you close the terminal window.
NOTE: **Note:** Not all executors are
[supported](https://docs.gitlab.com/runner/executors/#compatibility-chart)
[supported](https://docs.gitlab.com/runner/executors/#compatibility-chart).
The [File Sync](#file-syncing-to-web-terminal) feature is supported on Kubernetes runners only.
### Web IDE configuration file
@ -246,6 +249,8 @@ In the code below there is an example of this configuration file:
```yaml
terminal:
# This can be any image that has the necessary runtime environment for your project.
image: node:10-alpine
before_script:
- apt-get update
script: sleep 60

View File

@ -3,7 +3,14 @@
module Gitlab
module Danger
module Changelog
NO_CHANGELOG_LABELS = %w[backstage ci-build meta].freeze
NO_CHANGELOG_LABELS = [
'backstage', # To be removed by https://gitlab.com/gitlab-org/gitlab/-/issues/222360.
'tooling',
'tooling::pipelines',
'tooling::workflow',
'ci-build',
'meta'
].freeze
NO_CHANGELOG_CATEGORIES = %i[docs none].freeze
def needed?

View File

@ -48,6 +48,14 @@ module Gitlab
end
end
def for_variables
return unless dashboard.dig(:templating, :variables).is_a?(Hash)
dashboard.dig(:templating, :variables).each do |variable_name, variable|
yield variable_name, variable
end
end
def for_panel_groups
dashboard[:panel_groups].each do |panel_group|
yield panel_group

View File

@ -4,9 +4,9 @@ module Gitlab
module Metrics
module Dashboard
module Stages
class EndpointInserter < BaseStage
class MetricEndpointInserter < BaseStage
def transform!
raise Errors::DashboardProcessingError.new('Environment is required for Stages::EndpointInserter') unless params[:environment]
raise Errors::DashboardProcessingError.new('Environment is required for Stages::MetricEndpointInserter') unless params[:environment]
for_metrics do |metric|
metric[:prometheus_endpoint_path] = endpoint_for_metric(metric)

View File

@ -0,0 +1,34 @@
# frozen_string_literal: true
module Gitlab
module Metrics
module Dashboard
module Stages
class VariableEndpointInserter < BaseStage
VARIABLE_TYPE_METRIC_LABEL_VALUES = 'metric_label_values'
def transform!
raise Errors::DashboardProcessingError.new(_('Environment is required for Stages::VariableEndpointInserter')) unless params[:environment]
for_variables do |variable_name, variable|
if variable.is_a?(Hash) && variable[:type] == VARIABLE_TYPE_METRIC_LABEL_VALUES
variable[:options][:prometheus_endpoint_path] = endpoint_for_variable(variable.dig(:options, :series_selector))
end
end
end
private
def endpoint_for_variable(series_selector)
Gitlab::Routing.url_helpers.prometheus_api_project_environment_path(
project,
params[:environment],
proxy_path: ::Prometheus::ProxyService::PROMETHEUS_SERIES_API,
match: Array(series_selector)
)
end
end
end
end
end
end

View File

@ -8555,6 +8555,9 @@ msgstr ""
msgid "Environment does not have deployments"
msgstr ""
msgid "Environment is required for Stages::VariableEndpointInserter"
msgstr ""
msgid "Environment scope"
msgstr ""

View File

@ -110,7 +110,7 @@ RSpec.describe 'Login' do
gitlab_sign_in(user)
expect(page).not_to have_content('You have to confirm your email address before continuing.')
expect(page).not_to have_content(I18n.t('devise.failure.unconfirmed'))
expect(page).not_to have_link('Resend confirmation email', href: new_user_confirmation_path)
end
end
@ -124,7 +124,7 @@ RSpec.describe 'Login' do
gitlab_sign_in(user)
expect(page).to have_content('You have to confirm your email address before continuing.')
expect(page).to have_content(I18n.t('devise.failure.unconfirmed'))
expect(page).to have_link('Resend confirmation email', href: new_user_confirmation_path)
end
end
@ -820,7 +820,7 @@ RSpec.describe 'Login' do
gitlab_sign_in(user)
expect(current_path).to eq new_user_session_path
expect(page).to have_content('You have to confirm your email address before continuing.')
expect(page).to have_content(I18n.t('devise.failure.unconfirmed'))
end
end
end

View File

@ -24,6 +24,12 @@ templating:
- value: 'value_option_2'
text: 'Option 2'
default: true
metric_label_values_variable:
label: 'Variable 3'
type: metric_label_values
options:
series_selector: 'backend:haproxy_backend_availability:ratio{env="{{env}}"}'
label: 'backend'
panel_groups:
- group: Group A
priority: 1

View File

@ -0,0 +1,12 @@
{
"type": "object",
"required": [
"type", "options"
],
"properties": {
"type": { "enum": "metric_label_values" },
"label": { "type": "string" },
"options": { "$ref": "metric_label_values_variable_options.json" }
},
"additionalProperties": false
}

View File

@ -0,0 +1,12 @@
{
"type": "object",
"required": [
"series_selector", "label", "prometheus_endpoint_path"
],
"properties": {
"series_selector": { "type": "string" },
"label": { "type": "string" },
"prometheus_endpoint_path": { "type": "string" }
},
"additionalProperties": false
}

View File

@ -9,7 +9,8 @@
"type": "array",
"items": { "type": "string" }
},
{ "$ref": "custom_variable_full_syntax.json" }
{ "$ref": "custom_variable_full_syntax.json" },
{ "$ref": "metric_label_values_variable_full_syntax.json" }
]
}
},

View File

@ -70,7 +70,7 @@ describe('Alert Details Sidebar Assignees', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
const path = '/autocomplete/users.json';
const path = '/-/autocomplete/users.json';
const users = [
{
avatar_url:

View File

@ -48,13 +48,13 @@ describe('Dropdown User', () => {
};
const dropdown = new DropdownUser();
expect(dropdown.config.AjaxFilter.endpoint).toBe('/autocomplete/users.json');
expect(dropdown.config.AjaxFilter.endpoint).toBe('/-/autocomplete/users.json');
});
it('should return endpoint when relative_url_root is undefined', () => {
const dropdown = new DropdownUser();
expect(dropdown.config.AjaxFilter.endpoint).toBe('/autocomplete/users.json');
expect(dropdown.config.AjaxFilter.endpoint).toBe('/-/autocomplete/users.json');
});
it('should return endpoint with relative url when available', () => {
@ -63,7 +63,9 @@ describe('Dropdown User', () => {
};
const dropdown = new DropdownUser();
expect(dropdown.config.AjaxFilter.endpoint).toBe('/gitlab_directory/autocomplete/users.json');
expect(dropdown.config.AjaxFilter.endpoint).toBe(
'/gitlab_directory/-/autocomplete/users.json',
);
});
afterEach(() => {

View File

@ -5,7 +5,7 @@ import { GlAreaChart } from '@gitlab/ui/dist/charts';
import axios from '~/lib/utils/axios_utils';
import CodeCoverage from '~/pages/projects/graphs/components/code_coverage.vue';
import codeCoverageMockData from './mock_data';
import { codeCoverageMockData, sortedDataByDates } from './mock_data';
import waitForPromises from 'helpers/wait_for_promises';
import httpStatusCodes from '~/lib/utils/http_status';
@ -52,6 +52,10 @@ describe('Code Coverage', () => {
expect(findAreaChart().exists()).toBe(true);
});
it('sorts the dates in ascending order', () => {
expect(wrapper.vm.sortedData).toEqual(sortedDataByDates);
});
it('matches the snapshot', () => {
expect(wrapper.element).toMatchSnapshot();
});

View File

@ -1,60 +1,69 @@
export default [
export const codeCoverageMockData = [
{
group_name: 'rspec',
data: [
{ date: '2020-04-30', coverage: 40.0 },
{ date: '2020-05-01', coverage: 80.0 },
{ date: '2020-05-02', coverage: 99.0 },
{ date: '2020-05-10', coverage: 80.0 },
{ date: '2020-05-15', coverage: 70.0 },
{ date: '2020-05-20', coverage: 69.0 },
{ date: '2020-05-15', coverage: 70.0 },
{ date: '2020-05-10', coverage: 80.0 },
{ date: '2020-05-02', coverage: 99.0 },
{ date: '2020-05-01', coverage: 80.0 },
{ date: '2020-04-30', coverage: 40.0 },
],
},
{
group_name: 'cypress',
data: [
{ date: '2022-07-30', coverage: 1.0 },
{ date: '2022-08-01', coverage: 2.4 },
{ date: '2022-08-02', coverage: 5.0 },
{ date: '2022-08-10', coverage: 15.0 },
{ date: '2022-08-15', coverage: 30.0 },
{ date: '2022-08-20', coverage: 40.0 },
{ date: '2022-08-15', coverage: 30.0 },
{ date: '2022-08-10', coverage: 15.0 },
{ date: '2022-08-02', coverage: 5.0 },
{ date: '2022-08-01', coverage: 2.4 },
{ date: '2022-07-30', coverage: 1.0 },
],
},
{
group_name: 'karma',
data: [
{ date: '2020-05-01', coverage: 94.0 },
{ date: '2020-05-02', coverage: 94.0 },
{ date: '2020-05-03', coverage: 94.0 },
{ date: '2020-05-04', coverage: 94.0 },
{ date: '2020-05-05', coverage: 92.0 },
{ date: '2020-05-06', coverage: 91.0 },
{ date: '2020-05-07', coverage: 78.0 },
{ date: '2020-05-08', coverage: 94.0 },
{ date: '2020-05-09', coverage: 94.0 },
{ date: '2020-05-10', coverage: 94.0 },
{ date: '2020-05-11', coverage: 94.0 },
{ date: '2020-05-12', coverage: 94.0 },
{ date: '2020-05-13', coverage: 92.0 },
{ date: '2020-05-14', coverage: 91.0 },
{ date: '2020-05-15', coverage: 78.0 },
{ date: '2020-05-16', coverage: 94.0 },
{ date: '2020-05-17', coverage: 94.0 },
{ date: '2020-05-18', coverage: 93.0 },
{ date: '2020-05-19', coverage: 92.0 },
{ date: '2020-05-20', coverage: 91.0 },
{ date: '2020-05-21', coverage: 90.0 },
{ date: '2020-05-22', coverage: 91.0 },
{ date: '2020-05-23', coverage: 92.0 },
{ date: '2020-05-24', coverage: 75.0 },
{ date: '2020-05-25', coverage: 74.0 },
{ date: '2020-05-26', coverage: 74.0 },
{ date: '2020-05-27', coverage: 74.0 },
{ date: '2020-05-28', coverage: 80.0 },
{ date: '2020-05-29', coverage: 85.0 },
{ date: '2020-05-30', coverage: 92.0 },
{ date: '2020-05-31', coverage: 91.0 },
{ date: '2020-05-30', coverage: 94.0 },
{ date: '2020-05-29', coverage: 94.0 },
{ date: '2020-05-28', coverage: 92.0 },
{ date: '2020-05-27', coverage: 91.0 },
{ date: '2020-05-26', coverage: 78.0 },
{ date: '2020-05-25', coverage: 94.0 },
{ date: '2020-05-24', coverage: 94.0 },
{ date: '2020-05-23', coverage: 94.0 },
{ date: '2020-05-22', coverage: 94.0 },
{ date: '2020-05-21', coverage: 94.0 },
{ date: '2020-05-20', coverage: 92.0 },
{ date: '2020-05-19', coverage: 91.0 },
{ date: '2020-05-18', coverage: 78.0 },
{ date: '2020-05-17', coverage: 94.0 },
{ date: '2020-05-16', coverage: 94.0 },
{ date: '2020-05-15', coverage: 93.0 },
{ date: '2020-05-14', coverage: 92.0 },
{ date: '2020-05-13', coverage: 91.0 },
{ date: '2020-05-12', coverage: 90.0 },
{ date: '2020-05-11', coverage: 91.0 },
{ date: '2020-05-10', coverage: 92.0 },
{ date: '2020-05-09', coverage: 75.0 },
{ date: '2020-05-08', coverage: 74.0 },
{ date: '2020-05-07', coverage: 74.0 },
{ date: '2020-05-06', coverage: 74.0 },
{ date: '2020-05-05', coverage: 80.0 },
{ date: '2020-05-04', coverage: 85.0 },
{ date: '2020-05-03', coverage: 92.0 },
{ date: '2020-05-02', coverage: 94.0 },
{ date: '2020-05-01', coverage: 94.0 },
],
},
];
export const sortedDataByDates = [
{ date: '2020-04-30', coverage: 40.0 },
{ date: '2020-05-01', coverage: 80.0 },
{ date: '2020-05-02', coverage: 99.0 },
{ date: '2020-05-10', coverage: 80.0 },
{ date: '2020-05-15', coverage: 70.0 },
{ date: '2020-05-20', coverage: 69.0 },
];

View File

@ -45,7 +45,7 @@ describe('Project commits actions', () => {
describe('fetchAuthors', () => {
it('dispatches request/receive', () => {
const path = '/autocomplete/users.json';
const path = '/-/autocomplete/users.json';
state.projectId = '8';
const data = [{ id: 1 }];
@ -60,7 +60,7 @@ describe('Project commits actions', () => {
});
it('dispatches request/receive on error', () => {
const path = '/autocomplete/users.json';
const path = '/-/autocomplete/users.json';
mock.onGet(path).replyOnce(500);
testAction(actions.fetchAuthors, null, state, [], [{ type: 'receiveAuthorsError' }]);

View File

@ -2,7 +2,7 @@
require 'spec_helper'
describe GitlabSchema.types['ReleaseLink'] do
describe GitlabSchema.types['ReleaseAssetLink'] do
it { expect(described_class).to require_graphql_authorizations(:read_release) }
it 'has the expected fields' do

View File

@ -16,7 +16,7 @@ describe GitlabSchema.types['ReleaseAssets'] do
describe 'links field' do
subject { described_class.fields['links'] }
it { is_expected.to have_graphql_type(Types::ReleaseLinkType.connection_type) }
it { is_expected.to have_graphql_type(Types::ReleaseAssetLinkType.connection_type) }
end
describe 'sources field' do

View File

@ -13,7 +13,7 @@ describe Gitlab::Metrics::Dashboard::Processor do
Gitlab::Metrics::Dashboard::Stages::CommonMetricsInserter,
Gitlab::Metrics::Dashboard::Stages::CustomMetricsInserter,
Gitlab::Metrics::Dashboard::Stages::CustomMetricsDetailsInserter,
Gitlab::Metrics::Dashboard::Stages::EndpointInserter,
Gitlab::Metrics::Dashboard::Stages::MetricEndpointInserter,
Gitlab::Metrics::Dashboard::Stages::Sorter,
Gitlab::Metrics::Dashboard::Stages::AlertsInserter,
Gitlab::Metrics::Dashboard::Stages::PanelIdsInserter,
@ -98,7 +98,7 @@ describe Gitlab::Metrics::Dashboard::Processor do
let(:sequence) do
[
Gitlab::Metrics::Dashboard::Stages::CommonMetricsInserter,
Gitlab::Metrics::Dashboard::Stages::EndpointInserter,
Gitlab::Metrics::Dashboard::Stages::MetricEndpointInserter,
Gitlab::Metrics::Dashboard::Stages::Sorter
]
end

View File

@ -0,0 +1,77 @@
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Metrics::Dashboard::Stages::VariableEndpointInserter do
include MetricsDashboardHelpers
let(:project) { build_stubbed(:project) }
let(:environment) { build_stubbed(:environment, project: project) }
describe '#transform!' do
subject(:transform!) { described_class.new(project, dashboard, environment: environment).transform! }
let(:dashboard) { load_sample_dashboard.deep_symbolize_keys }
context 'when dashboard variables are present' do
it 'assigns prometheus_endpoint_path to metric_label_values variable type' do
endpoint_path = Gitlab::Routing.url_helpers.prometheus_api_project_environment_path(
project,
environment,
proxy_path: :series,
match: ['backend:haproxy_backend_availability:ratio{env="{{env}}"}']
)
transform!
expect(
dashboard.dig(:templating, :variables, :metric_label_values_variable, :options)
).to include(prometheus_endpoint_path: endpoint_path)
end
it 'does not modify other variable types' do
original_text_variable = dashboard[:templating][:variables][:text_variable_full_syntax].deep_dup
transform!
expect(dashboard[:templating][:variables][:text_variable_full_syntax]).to eq(original_text_variable)
end
context 'when variable does not have the required series_selector' do
it 'adds prometheus_endpoint_path without match parameter' do
dashboard[:templating][:variables][:metric_label_values_variable][:options].delete(:series_selector)
endpoint_path = Gitlab::Routing.url_helpers.prometheus_api_project_environment_path(
project,
environment,
proxy_path: :series
)
transform!
expect(
dashboard.dig(:templating, :variables, :metric_label_values_variable, :options)
).to include(prometheus_endpoint_path: endpoint_path)
end
end
end
context 'when no variables are present' do
it 'does not fail' do
dashboard.delete(:templating)
expect { transform! }.not_to raise_error
end
end
context 'with no environment' do
subject(:transform!) { described_class.new(project, dashboard, {}).transform! }
it 'raises error' do
expect { transform! }.to raise_error(
Gitlab::Metrics::Dashboard::Errors::DashboardProcessingError,
'Environment is required for Stages::VariableEndpointInserter'
)
end
end
end
end

View File

@ -151,7 +151,7 @@ describe UpdateIndexApprovalRuleNameForCodeOwnersRuleType do
expect(MergeRequests::SyncCodeOwnerApprovalRules)
.to receive(:new).with(MergeRequest.find(merge_request.id)).once.and_call_original
# We expect approval_rules.count to be changed by -2 as we're deleting the
# We expect approval_rules.count to be changed by -3 as we're deleting the
# 3 rules created above, and MergeRequests::SyncCodeOwnerApprovalRules
# will not be able to create new one with an empty (or missing)
# CODEOWNERS file.

View File

@ -3,7 +3,7 @@
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20200427064130_cleanup_optimistic_locking_nulls_pt2_fixed.rb')
describe CleanupOptimisticLockingNullsPt2Fixed, :migration, schema: 20200219193117 do
describe CleanupOptimisticLockingNullsPt2Fixed, :migration do
test_tables = %w(ci_stages ci_builds ci_pipelines).freeze
test_tables.each do |table|
let(table.to_sym) { table(table.to_sym) }

View File

@ -95,6 +95,29 @@ describe Issue do
end
end
describe 'locking' do
using RSpec::Parameterized::TableSyntax
where(:lock_version) do
[
[0],
["0"]
]
end
with_them do
it 'works when an issue has a NULL lock_version' do
issue = create(:issue)
described_class.where(id: issue.id).update_all('lock_version = NULL')
issue.update!(lock_version: lock_version, title: 'locking test')
expect(issue.reload.title).to eq('locking test')
end
end
end
describe '.simple_sorts' do
it 'includes all keys' do
expect(described_class.simple_sorts.keys).to include(

View File

@ -55,6 +55,29 @@ describe MergeRequest do
end
end
describe 'locking' do
using RSpec::Parameterized::TableSyntax
where(:lock_version) do
[
[0],
["0"]
]
end
with_them do
it 'works when a merge request has a NULL lock_version' do
merge_request = create(:merge_request)
described_class.where(id: merge_request.id).update_all('lock_version = NULL')
merge_request.update!(lock_version: lock_version, title: 'locking test')
expect(merge_request.reload.title).to eq('locking test')
end
end
end
describe '#squash_in_progress?' do
let(:repo_path) do
Gitlab::GitalyClient::StorageSettings.allow_disk_access do

View File

@ -18,6 +18,10 @@ module MetricsDashboardHelpers
project.repository.refresh_method_caches([:metrics_dashboard])
end
def load_sample_dashboard
YAML.safe_load(fixture_file('lib/gitlab/metrics/dashboard/sample_dashboard.yml'))
end
def system_dashboard_path
Metrics::Dashboard::SystemDashboardService::DASHBOARD_PATH
end

View File

@ -7,17 +7,17 @@ RSpec.shared_examples 'a BulkInsertSafe model' do |klass|
let(:target_class) { klass.dup }
# We consider all callbacks unsafe for bulk insertions unless we have explicitly
# whitelisted them (esp. anything related to :save, :create, :commit etc.)
let(:callback_method_blacklist) do
# allowed them (especially anything related to :save, :create, :commit, etc.)
let(:unsafe_callbacks) do
ActiveRecord::Callbacks::CALLBACKS.reject do |callback|
cb_name = callback.to_s.gsub(/(before_|after_|around_)/, '').to_sym
BulkInsertSafe::CALLBACK_NAME_WHITELIST.include?(cb_name)
BulkInsertSafe::ALLOWED_CALLBACKS.include?(cb_name)
end.to_set
end
context 'when calling class methods directly' do
it 'raises an error when method is not bulk-insert safe' do
callback_method_blacklist.each do |m|
unsafe_callbacks.each do |m|
expect { target_class.send(m, nil) }.to(
raise_error(BulkInsertSafe::MethodNotAllowedError),
"Expected call to #{m} to raise an error, but it didn't"
@ -26,7 +26,7 @@ RSpec.shared_examples 'a BulkInsertSafe model' do |klass|
end
it 'does not raise an error when method is bulk-insert safe' do
BulkInsertSafe::CALLBACK_NAME_WHITELIST.each do |name|
BulkInsertSafe::ALLOWED_CALLBACKS.each do |name|
expect { target_class.set_callback(name) {} }.not_to raise_error
end
end