Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
681ca59b6f
commit
89eff770d2
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { isEmpty, omit, throttle } from 'lodash';
|
import { isEmpty, omit, throttle } from 'lodash';
|
||||||
import { GlLink, GlDeprecatedButton, GlTooltip, GlResizeObserverDirective } from '@gitlab/ui';
|
import { GlLink, GlTooltip, GlResizeObserverDirective } from '@gitlab/ui';
|
||||||
import { GlAreaChart, GlLineChart, GlChartSeriesLabel } from '@gitlab/ui/dist/charts';
|
import { GlAreaChart, GlLineChart, GlChartSeriesLabel } from '@gitlab/ui/dist/charts';
|
||||||
import { s__ } from '~/locale';
|
import { s__ } from '~/locale';
|
||||||
import { getSvgIconPathContent } from '~/lib/utils/icon_utils';
|
import { getSvgIconPathContent } from '~/lib/utils/icon_utils';
|
||||||
|
@ -25,7 +25,6 @@ export default {
|
||||||
GlAreaChart,
|
GlAreaChart,
|
||||||
GlLineChart,
|
GlLineChart,
|
||||||
GlTooltip,
|
GlTooltip,
|
||||||
GlDeprecatedButton,
|
|
||||||
GlChartSeriesLabel,
|
GlChartSeriesLabel,
|
||||||
GlLink,
|
GlLink,
|
||||||
Icon,
|
Icon,
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require "json"
|
|
||||||
|
|
||||||
module Resolvers
|
|
||||||
module CiConfiguration
|
|
||||||
class SastResolver < BaseResolver
|
|
||||||
SAST_UI_SCHEMA_PATH = 'app/validators/json_schemas/security_ci_configuration_schemas/sast_ui_schema.json'
|
|
||||||
|
|
||||||
type ::Types::CiConfiguration::Sast::Type, null: true
|
|
||||||
|
|
||||||
def resolve(**args)
|
|
||||||
Gitlab::Json.parse(File.read(Rails.root.join(SAST_UI_SCHEMA_PATH)))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -10,7 +10,7 @@ module Resolvers
|
||||||
|
|
||||||
def resolve(iid:)
|
def resolve(iid:)
|
||||||
BatchLoader::GraphQL.for(iid).batch(key: project) do |iids, loader, args|
|
BatchLoader::GraphQL.for(iid).batch(key: project) do |iids, loader, args|
|
||||||
args[:key].ci_pipelines.for_iid(iids).each { |pl| loader.call(pl.iid.to_s, pl) }
|
args[:key].all_pipelines.for_iid(iids).each { |pl| loader.call(pl.iid.to_s, pl) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Types
|
||||||
|
module Ci
|
||||||
|
class PipelineConfigSourceEnum < BaseEnum
|
||||||
|
::Ci::PipelineEnums.config_sources.keys.each do |state_symbol|
|
||||||
|
value state_symbol.to_s.upcase, value: state_symbol.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -25,6 +25,8 @@ module Types
|
||||||
field :detailed_status, Types::Ci::DetailedStatusType, null: false,
|
field :detailed_status, Types::Ci::DetailedStatusType, null: false,
|
||||||
description: 'Detailed status of the pipeline',
|
description: 'Detailed status of the pipeline',
|
||||||
resolve: -> (obj, _args, ctx) { obj.detailed_status(ctx[:current_user]) }
|
resolve: -> (obj, _args, ctx) { obj.detailed_status(ctx[:current_user]) }
|
||||||
|
field :config_source, PipelineConfigSourceEnum, null: true,
|
||||||
|
description: "Config source of the pipeline (#{::Ci::PipelineEnums.config_sources.keys.join(', ').upcase})"
|
||||||
field :duration, GraphQL::INT_TYPE, null: true,
|
field :duration, GraphQL::INT_TYPE, null: true,
|
||||||
description: 'Duration of the pipeline in seconds'
|
description: 'Duration of the pipeline in seconds'
|
||||||
field :coverage, GraphQL::FLOAT_TYPE, null: true,
|
field :coverage, GraphQL::FLOAT_TYPE, null: true,
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Types
|
|
||||||
module CiConfiguration
|
|
||||||
module Sast
|
|
||||||
# rubocop: disable Graphql/AuthorizeTypes
|
|
||||||
class AnalyzersEntityType < BaseObject
|
|
||||||
graphql_name 'SastCiConfigurationAnalyzersEntity'
|
|
||||||
description 'Represents an analyzer entity in SAST CI configuration'
|
|
||||||
|
|
||||||
field :name, GraphQL::STRING_TYPE, null: true,
|
|
||||||
description: 'Name of the analyzer.'
|
|
||||||
|
|
||||||
field :label, GraphQL::STRING_TYPE, null: true,
|
|
||||||
description: 'Analyzer label used in the config UI.'
|
|
||||||
|
|
||||||
field :enabled, GraphQL::BOOLEAN_TYPE, null: true,
|
|
||||||
description: 'Indicates whether an analyzer is enabled.'
|
|
||||||
|
|
||||||
field :description, GraphQL::STRING_TYPE, null: true,
|
|
||||||
description: 'Analyzer description that is displayed on the form.'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,34 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Types
|
|
||||||
module CiConfiguration
|
|
||||||
module Sast
|
|
||||||
# rubocop: disable Graphql/AuthorizeTypes
|
|
||||||
class EntityType < BaseObject
|
|
||||||
graphql_name 'SastCiConfigurationEntity'
|
|
||||||
description 'Represents an entity in SAST CI configuration'
|
|
||||||
|
|
||||||
field :field, GraphQL::STRING_TYPE, null: true,
|
|
||||||
description: 'CI keyword of entity.'
|
|
||||||
|
|
||||||
field :label, GraphQL::STRING_TYPE, null: true,
|
|
||||||
description: 'Label for entity used in the form.'
|
|
||||||
|
|
||||||
field :type, GraphQL::STRING_TYPE, null: true,
|
|
||||||
description: 'Type of the field value.'
|
|
||||||
|
|
||||||
field :options, ::Types::CiConfiguration::Sast::OptionsEntityType.connection_type, null: true,
|
|
||||||
description: 'Different possible values of the field.'
|
|
||||||
|
|
||||||
field :default_value, GraphQL::STRING_TYPE, null: true,
|
|
||||||
description: 'Default value that is used if value is empty.'
|
|
||||||
|
|
||||||
field :description, GraphQL::STRING_TYPE, null: true,
|
|
||||||
description: 'Entity description that is displayed on the form.'
|
|
||||||
|
|
||||||
field :value, GraphQL::STRING_TYPE, null: true,
|
|
||||||
description: 'Current value of the entity.'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,19 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Types
|
|
||||||
module CiConfiguration
|
|
||||||
module Sast
|
|
||||||
# rubocop: disable Graphql/AuthorizeTypes
|
|
||||||
class OptionsEntityType < BaseObject
|
|
||||||
graphql_name 'SastCiConfigurationOptionsEntity'
|
|
||||||
description 'Represents an entity for options in SAST CI configuration'
|
|
||||||
|
|
||||||
field :label, GraphQL::STRING_TYPE, null: true,
|
|
||||||
description: 'Label of option entity.'
|
|
||||||
|
|
||||||
field :value, GraphQL::STRING_TYPE, null: true,
|
|
||||||
description: 'Value of option entity.'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,22 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Types
|
|
||||||
module CiConfiguration
|
|
||||||
module Sast
|
|
||||||
# rubocop: disable Graphql/AuthorizeTypes
|
|
||||||
class Type < BaseObject
|
|
||||||
graphql_name 'SastCiConfiguration'
|
|
||||||
description 'Represents a CI configuration of SAST'
|
|
||||||
|
|
||||||
field :global, ::Types::CiConfiguration::Sast::EntityType.connection_type, null: true,
|
|
||||||
description: 'List of global entities related to SAST configuration.'
|
|
||||||
|
|
||||||
field :pipeline, ::Types::CiConfiguration::Sast::EntityType.connection_type, null: true,
|
|
||||||
description: 'List of pipeline entities related to SAST configuration.'
|
|
||||||
|
|
||||||
field :analyzers, ::Types::CiConfiguration::Sast::AnalyzersEntityType.connection_type, null: true,
|
|
||||||
description: 'List of analyzers entities attached to SAST configuration.'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -175,10 +175,6 @@ module Types
|
||||||
description: 'A single environment of the project',
|
description: 'A single environment of the project',
|
||||||
resolver: Resolvers::EnvironmentsResolver.single
|
resolver: Resolvers::EnvironmentsResolver.single
|
||||||
|
|
||||||
field :sast_ci_configuration, ::Types::CiConfiguration::Sast::Type, null: true,
|
|
||||||
description: 'SAST CI configuration for the project',
|
|
||||||
resolver: ::Resolvers::CiConfiguration::SastResolver
|
|
||||||
|
|
||||||
field :issue,
|
field :issue,
|
||||||
Types::IssueType,
|
Types::IssueType,
|
||||||
null: true,
|
null: true,
|
||||||
|
|
|
@ -4,83 +4,43 @@
|
||||||
"field" : "SECURE_ANALYZERS_PREFIX",
|
"field" : "SECURE_ANALYZERS_PREFIX",
|
||||||
"label" : "Image prefix",
|
"label" : "Image prefix",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default_value": "registry.gitlab.com/gitlab-org/security-products/analyzers",
|
"default_value": "",
|
||||||
"value": ""
|
"value": "",
|
||||||
|
"description": "Analyzer image's registry prefix (or Name of the registry providing the analyzers' image)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"field" : "SAST_EXCLUDED_PATHS",
|
"field" : "SAST_EXCLUDED_PATHS",
|
||||||
"label" : "Excluded Paths",
|
"label" : "Excluded Paths",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default_value": "spec, test, tests, tmp",
|
"default_value": "",
|
||||||
"value": ""
|
"value": "",
|
||||||
|
"description": "Comma-separated list of paths to be excluded from analyzer output. Patterns can be globs, file paths, or folder paths."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"field" : "SAST_ANALYZER_IMAGE_TAG",
|
"field" : "SAST_ANALYZER_IMAGE_TAG",
|
||||||
"label" : "Image tag",
|
"label" : "Image tag",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"options": [],
|
"default_value": "",
|
||||||
"default_value": "2",
|
"value": "",
|
||||||
"value": ""
|
"description": "Analyzer image's tag"
|
||||||
},
|
|
||||||
{
|
|
||||||
"field" : "SAST_DISABLED",
|
|
||||||
"label" : "Disable SAST",
|
|
||||||
"type": "options",
|
|
||||||
"options": [
|
|
||||||
{
|
|
||||||
"value" :"true",
|
|
||||||
"label" : "true (disables SAST)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value":"false",
|
|
||||||
"label":"false (enables SAST)"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"default_value": "false",
|
|
||||||
"value": ""
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"pipeline": [
|
"pipeline": [
|
||||||
{
|
{
|
||||||
"field" : "stage",
|
"field" : "stage",
|
||||||
"label" : "Stage",
|
"label" : "Stage",
|
||||||
"type": "dropdown",
|
"type": "string",
|
||||||
"options": [
|
|
||||||
{
|
|
||||||
"value" :"test",
|
|
||||||
"label" : "test"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value":"build",
|
|
||||||
"label":"build"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"default_value": "test",
|
|
||||||
"value": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"field" : "allow_failure",
|
|
||||||
"label" : "Allow Failure",
|
|
||||||
"type": "options",
|
|
||||||
"options": [
|
|
||||||
{
|
|
||||||
"value" :"true",
|
|
||||||
"label" : "Allows pipeline failure"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "false",
|
|
||||||
"label": "Does not allow pipeline failure"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"default_value": "true",
|
|
||||||
"value": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"field" : "rules",
|
|
||||||
"label" : "Rules",
|
|
||||||
"type": "multiline",
|
|
||||||
"default_value": "",
|
"default_value": "",
|
||||||
"value": ""
|
"value": "",
|
||||||
|
"description": "Pipeline stage in which the scan jobs run"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field" : "SEARCH_MAX_DEPTH",
|
||||||
|
"label" : "Search maximum depth",
|
||||||
|
"type": "string",
|
||||||
|
"default_value": "",
|
||||||
|
"value": "",
|
||||||
|
"description": "Maximum depth of language and framework detection"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"analyzers": [
|
"analyzers": [
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Remove not-null constraint on type column in audit_events
|
||||||
|
merge_request: 39192
|
||||||
|
author:
|
||||||
|
type: other
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Allow GraphQL pipeline to resolve non-CI pipelines and expose configSource field
|
||||||
|
merge_request: 39275
|
||||||
|
author:
|
||||||
|
type: added
|
|
@ -0,0 +1,27 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class RemoveNotNullConstraintOnTypeFromAuditEvents < ActiveRecord::Migration[6.0]
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
|
||||||
|
DOWNTIME = false
|
||||||
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
# rubocop:disable Migration/WithLockRetriesDisallowedMethod
|
||||||
|
# To avoid deadlock on audit_event and audit_event_part... since there is a trigger to insert record from audit_events
|
||||||
|
# to audit_events_part..., we need to ensure each ALTER TABLE command run in its own transaction.
|
||||||
|
def up
|
||||||
|
with_lock_retries do
|
||||||
|
change_column_null :audit_events_part_5fc467ac26, :type, true
|
||||||
|
end
|
||||||
|
|
||||||
|
with_lock_retries do
|
||||||
|
change_column_null :audit_events, :type, true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
# rubocop:enable Migration/WithLockRetriesDisallowedMethod
|
||||||
|
|
||||||
|
def down
|
||||||
|
# no-op -- null values might be added after this constraint is removed.
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1 @@
|
||||||
|
b7477fbcba166d848e3b1bd7f4a184f50f3b2fafe80bfcf44fd6f4f9979ffcb7
|
|
@ -68,7 +68,7 @@ COMMENT ON FUNCTION public.table_sync_function_2be879775d() IS 'Partitioning mig
|
||||||
CREATE TABLE public.audit_events_part_5fc467ac26 (
|
CREATE TABLE public.audit_events_part_5fc467ac26 (
|
||||||
id bigint NOT NULL,
|
id bigint NOT NULL,
|
||||||
author_id integer NOT NULL,
|
author_id integer NOT NULL,
|
||||||
type character varying NOT NULL,
|
type character varying,
|
||||||
entity_id integer NOT NULL,
|
entity_id integer NOT NULL,
|
||||||
entity_type character varying NOT NULL,
|
entity_type character varying NOT NULL,
|
||||||
details text,
|
details text,
|
||||||
|
@ -9466,7 +9466,7 @@ CREATE TABLE public.ar_internal_metadata (
|
||||||
CREATE TABLE public.audit_events (
|
CREATE TABLE public.audit_events (
|
||||||
id integer NOT NULL,
|
id integer NOT NULL,
|
||||||
author_id integer NOT NULL,
|
author_id integer NOT NULL,
|
||||||
type character varying NOT NULL,
|
type character varying,
|
||||||
entity_id integer NOT NULL,
|
entity_id integer NOT NULL,
|
||||||
entity_type character varying NOT NULL,
|
entity_type character varying NOT NULL,
|
||||||
details text,
|
details text,
|
||||||
|
|
|
@ -10261,6 +10261,13 @@ type Pipeline {
|
||||||
"""
|
"""
|
||||||
committedAt: Time
|
committedAt: Time
|
||||||
|
|
||||||
|
"""
|
||||||
|
Config source of the pipeline (UNKNOWN_SOURCE, REPOSITORY_SOURCE,
|
||||||
|
AUTO_DEVOPS_SOURCE, WEBIDE_SOURCE, REMOTE_SOURCE, EXTERNAL_PROJECT_SOURCE,
|
||||||
|
BRIDGE_SOURCE, PARAMETER_SOURCE)
|
||||||
|
"""
|
||||||
|
configSource: PipelineConfigSourceEnum
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Coverage percentage
|
Coverage percentage
|
||||||
"""
|
"""
|
||||||
|
@ -10353,6 +10360,17 @@ type Pipeline {
|
||||||
userPermissions: PipelinePermissions!
|
userPermissions: PipelinePermissions!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum PipelineConfigSourceEnum {
|
||||||
|
AUTO_DEVOPS_SOURCE
|
||||||
|
BRIDGE_SOURCE
|
||||||
|
EXTERNAL_PROJECT_SOURCE
|
||||||
|
PARAMETER_SOURCE
|
||||||
|
REMOTE_SOURCE
|
||||||
|
REPOSITORY_SOURCE
|
||||||
|
UNKNOWN_SOURCE
|
||||||
|
WEBIDE_SOURCE
|
||||||
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
The connection type for Pipeline.
|
The connection type for Pipeline.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -30652,6 +30652,20 @@
|
||||||
"isDeprecated": false,
|
"isDeprecated": false,
|
||||||
"deprecationReason": null
|
"deprecationReason": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "configSource",
|
||||||
|
"description": "Config source of the pipeline (UNKNOWN_SOURCE, REPOSITORY_SOURCE, AUTO_DEVOPS_SOURCE, WEBIDE_SOURCE, REMOTE_SOURCE, EXTERNAL_PROJECT_SOURCE, BRIDGE_SOURCE, PARAMETER_SOURCE)",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "ENUM",
|
||||||
|
"name": "PipelineConfigSourceEnum",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "coverage",
|
"name": "coverage",
|
||||||
"description": "Coverage percentage",
|
"description": "Coverage percentage",
|
||||||
|
@ -30927,6 +30941,65 @@
|
||||||
"enumValues": null,
|
"enumValues": null,
|
||||||
"possibleTypes": null
|
"possibleTypes": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"kind": "ENUM",
|
||||||
|
"name": "PipelineConfigSourceEnum",
|
||||||
|
"description": null,
|
||||||
|
"fields": null,
|
||||||
|
"inputFields": null,
|
||||||
|
"interfaces": null,
|
||||||
|
"enumValues": [
|
||||||
|
{
|
||||||
|
"name": "UNKNOWN_SOURCE",
|
||||||
|
"description": null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "REPOSITORY_SOURCE",
|
||||||
|
"description": null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AUTO_DEVOPS_SOURCE",
|
||||||
|
"description": null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "WEBIDE_SOURCE",
|
||||||
|
"description": null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "REMOTE_SOURCE",
|
||||||
|
"description": null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "EXTERNAL_PROJECT_SOURCE",
|
||||||
|
"description": null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "BRIDGE_SOURCE",
|
||||||
|
"description": null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "PARAMETER_SOURCE",
|
||||||
|
"description": null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"possibleTypes": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "OBJECT",
|
"kind": "OBJECT",
|
||||||
"name": "PipelineConnection",
|
"name": "PipelineConnection",
|
||||||
|
|
|
@ -1571,6 +1571,7 @@ Information about pagination in a connection.
|
||||||
| --- | ---- | ---------- |
|
| --- | ---- | ---------- |
|
||||||
| `beforeSha` | String | Base SHA of the source branch |
|
| `beforeSha` | String | Base SHA of the source branch |
|
||||||
| `committedAt` | Time | Timestamp of the pipeline's commit |
|
| `committedAt` | Time | Timestamp of the pipeline's commit |
|
||||||
|
| `configSource` | PipelineConfigSourceEnum | Config source of the pipeline (UNKNOWN_SOURCE, REPOSITORY_SOURCE, AUTO_DEVOPS_SOURCE, WEBIDE_SOURCE, REMOTE_SOURCE, EXTERNAL_PROJECT_SOURCE, BRIDGE_SOURCE, PARAMETER_SOURCE) |
|
||||||
| `coverage` | Float | Coverage percentage |
|
| `coverage` | Float | Coverage percentage |
|
||||||
| `createdAt` | Time! | Timestamp of the pipeline's creation |
|
| `createdAt` | Time! | Timestamp of the pipeline's creation |
|
||||||
| `detailedStatus` | DetailedStatus! | Detailed status of the pipeline |
|
| `detailedStatus` | DetailedStatus! | Detailed status of the pipeline |
|
||||||
|
|
|
@ -306,6 +306,7 @@ module Gitlab
|
||||||
Gitlab::UsageData::Topology.new.topology_usage_data
|
Gitlab::UsageData::Topology.new.topology_usage_data
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# rubocop: disable UsageData/DistinctCountByLargeForeignKey
|
||||||
def ingress_modsecurity_usage
|
def ingress_modsecurity_usage
|
||||||
##
|
##
|
||||||
# This method measures usage of the Modsecurity Web Application Firewall across the entire
|
# This method measures usage of the Modsecurity Web Application Firewall across the entire
|
||||||
|
@ -326,6 +327,7 @@ module Gitlab
|
||||||
ingress_modsecurity_not_installed: distinct_count(successful_deployments_with_cluster(::Clusters::Applications::Ingress.modsecurity_not_installed), column)
|
ingress_modsecurity_not_installed: distinct_count(successful_deployments_with_cluster(::Clusters::Applications::Ingress.modsecurity_not_installed), column)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
# rubocop: enable UsageData/DistinctCountByLargeForeignKey
|
||||||
|
|
||||||
# rubocop: disable CodeReuse/ActiveRecord
|
# rubocop: disable CodeReuse/ActiveRecord
|
||||||
def container_expiration_policies_usage
|
def container_expiration_policies_usage
|
||||||
|
@ -729,9 +731,11 @@ module Gitlab
|
||||||
end
|
end
|
||||||
|
|
||||||
# rubocop: disable CodeReuse/ActiveRecord
|
# rubocop: disable CodeReuse/ActiveRecord
|
||||||
|
# rubocop: disable UsageData/DistinctCountByLargeForeignKey
|
||||||
def cluster_applications_user_distinct_count(applications, time_period)
|
def cluster_applications_user_distinct_count(applications, time_period)
|
||||||
distinct_count(applications.where(time_period).available.joins(:cluster), 'clusters.user_id')
|
distinct_count(applications.where(time_period).available.joins(:cluster), 'clusters.user_id')
|
||||||
end
|
end
|
||||||
|
# rubocop: enable UsageData/DistinctCountByLargeForeignKey
|
||||||
|
|
||||||
def clusters_user_distinct_count(clusters, time_period)
|
def clusters_user_distinct_count(clusters, time_period)
|
||||||
distinct_count(clusters.where(time_period), :user_id)
|
distinct_count(clusters.where(time_period), :user_id)
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module RuboCop
|
||||||
|
module Cop
|
||||||
|
module UsageData
|
||||||
|
# Allows counts only for selected tables' foreign keys for `distinct_count` method.
|
||||||
|
#
|
||||||
|
# Because distinct_counts over large tables' foreign keys will take a long time
|
||||||
|
#
|
||||||
|
# @example
|
||||||
|
#
|
||||||
|
# # bad because pipeline_id points to a large table
|
||||||
|
# distinct_count(Ci::Build, :commit_id)
|
||||||
|
#
|
||||||
|
class DistinctCountByLargeForeignKey < RuboCop::Cop::Cop
|
||||||
|
MSG = 'Avoid doing `%s` for large foreign keys.'.freeze
|
||||||
|
|
||||||
|
def_node_matcher :distinct_count?, <<-PATTERN
|
||||||
|
(send _ $:distinct_count $...)
|
||||||
|
PATTERN
|
||||||
|
|
||||||
|
def on_send(node)
|
||||||
|
distinct_count?(node) do |method_name, method_arguments|
|
||||||
|
next unless method_arguments && method_arguments.length >= 2
|
||||||
|
next if allowed_foreign_key?(method_arguments[1])
|
||||||
|
|
||||||
|
add_offense(node, location: :selector, message: format(MSG, method_name))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def allowed_foreign_key?(key)
|
||||||
|
key.type == :sym && allowed_foreign_keys.include?(key.value)
|
||||||
|
end
|
||||||
|
|
||||||
|
def allowed_foreign_keys
|
||||||
|
cop_config['AllowedForeignKeys'] || []
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -30,3 +30,16 @@ UsageData/LargeTable:
|
||||||
- :arel_table
|
- :arel_table
|
||||||
- :minimum
|
- :minimum
|
||||||
- :maximum
|
- :maximum
|
||||||
|
UsageData/DistinctCountByLargeForeignKey:
|
||||||
|
Enabled: true
|
||||||
|
Include:
|
||||||
|
- 'lib/gitlab/usage_data.rb'
|
||||||
|
- 'ee/lib/ee/gitlab/usage_data.rb'
|
||||||
|
AllowedForeignKeys:
|
||||||
|
- :user_id
|
||||||
|
- :author_id
|
||||||
|
- :creator_id
|
||||||
|
- :owner_id
|
||||||
|
- :project_id
|
||||||
|
- :issue_id
|
||||||
|
- :merge_request_id
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
RSpec.describe Resolvers::CiConfiguration::SastResolver do
|
|
||||||
include GraphqlHelpers
|
|
||||||
|
|
||||||
let_it_be(:user) { create(:user) }
|
|
||||||
let_it_be(:project) { create(:project) }
|
|
||||||
|
|
||||||
describe '#resolve' do
|
|
||||||
subject(:sast_config) { resolve(described_class, ctx: { current_user: user }, obj: project) }
|
|
||||||
|
|
||||||
it 'returns global variable informations related to SAST' do
|
|
||||||
expect(sast_config['global'].first['field']).to eql("SECURE_ANALYZERS_PREFIX")
|
|
||||||
expect(sast_config['global'].first['label']).to eql("Image prefix")
|
|
||||||
expect(sast_config['global'].first['type']).to eql("string")
|
|
||||||
|
|
||||||
expect(sast_config['pipeline'].first['field']).to eql("stage")
|
|
||||||
expect(sast_config['pipeline'].first['label']).to eql("Stage")
|
|
||||||
expect(sast_config['pipeline'].first['type']).to eql("dropdown")
|
|
||||||
|
|
||||||
expect(sast_config['analyzers'].first['name']).to eql("brakeman")
|
|
||||||
expect(sast_config['analyzers'].first['label']).to eql("Brakeman")
|
|
||||||
expect(sast_config['analyzers'].first['enabled']).to be true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -33,4 +33,21 @@ RSpec.describe Resolvers::ProjectPipelineResolver do
|
||||||
it 'errors when no iid is passed' do
|
it 'errors when no iid is passed' do
|
||||||
expect { resolve_pipeline(project, {}) }.to raise_error(ArgumentError)
|
expect { resolve_pipeline(project, {}) }.to raise_error(ArgumentError)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when the pipeline is not a ci_config_source' do
|
||||||
|
let(:pipeline) do
|
||||||
|
config_source_value = Ci::PipelineEnums.non_ci_config_source_values.first
|
||||||
|
config_source = Ci::PipelineEnums.config_sources.key(config_source_value)
|
||||||
|
|
||||||
|
create(:ci_pipeline, config_source: config_source, project: project)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'resolves pipeline for the passed iid' do
|
||||||
|
result = batch_sync do
|
||||||
|
resolve_pipeline(project, { iid: pipeline.iid.to_s })
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(result).to eq(pipeline)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
RSpec.describe GitlabSchema.types['SastCiConfigurationAnalyzersEntity'] do
|
|
||||||
let(:fields) { %i[name label enabled description] }
|
|
||||||
|
|
||||||
it { expect(described_class.graphql_name).to eq('SastCiConfigurationAnalyzersEntity') }
|
|
||||||
|
|
||||||
it { expect(described_class).to have_graphql_fields(fields) }
|
|
||||||
end
|
|
|
@ -1,11 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
RSpec.describe GitlabSchema.types['SastCiConfigurationEntity'] do
|
|
||||||
let(:fields) { %i[field label description type options default_value value] }
|
|
||||||
|
|
||||||
it { expect(described_class.graphql_name).to eq('SastCiConfigurationEntity') }
|
|
||||||
|
|
||||||
it { expect(described_class).to have_graphql_fields(fields) }
|
|
||||||
end
|
|
|
@ -1,11 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
RSpec.describe GitlabSchema.types['SastCiConfigurationOptionsEntity'] do
|
|
||||||
let(:fields) { %i[label value] }
|
|
||||||
|
|
||||||
it { expect(described_class.graphql_name).to eq('SastCiConfigurationOptionsEntity') }
|
|
||||||
|
|
||||||
it { expect(described_class).to have_graphql_fields(fields) }
|
|
||||||
end
|
|
|
@ -1,11 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
RSpec.describe GitlabSchema.types['SastCiConfiguration'] do
|
|
||||||
let(:fields) { %i[global pipeline analyzers] }
|
|
||||||
|
|
||||||
it { expect(described_class.graphql_name).to eq('SastCiConfiguration') }
|
|
||||||
|
|
||||||
it { expect(described_class).to have_graphql_fields(fields) }
|
|
||||||
end
|
|
|
@ -26,7 +26,7 @@ RSpec.describe GitlabSchema.types['Project'] do
|
||||||
grafanaIntegration autocloseReferencedIssues suggestion_commit_message environments
|
grafanaIntegration autocloseReferencedIssues suggestion_commit_message environments
|
||||||
environment boards jira_import_status jira_imports services releases release
|
environment boards jira_import_status jira_imports services releases release
|
||||||
alert_management_alerts alert_management_alert alert_management_alert_status_counts
|
alert_management_alerts alert_management_alert alert_management_alert_status_counts
|
||||||
container_expiration_policy sast_ci_configuration service_desk_enabled service_desk_address
|
container_expiration_policy service_desk_enabled service_desk_address
|
||||||
issue_status_counts
|
issue_status_counts
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -150,93 +150,5 @@ RSpec.describe GitlabSchema.types['Project'] do
|
||||||
it { is_expected.to have_graphql_type(Types::ContainerExpirationPolicyType) }
|
it { is_expected.to have_graphql_type(Types::ContainerExpirationPolicyType) }
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'sast_ci_configuration' do
|
|
||||||
let_it_be(:project) { create(:project) }
|
|
||||||
let_it_be(:user) { create(:user) }
|
|
||||||
let_it_be(:query) do
|
|
||||||
%(
|
|
||||||
query {
|
|
||||||
project(fullPath: "#{project.full_path}") {
|
|
||||||
sastCiConfiguration {
|
|
||||||
global {
|
|
||||||
nodes {
|
|
||||||
type
|
|
||||||
options {
|
|
||||||
nodes {
|
|
||||||
label
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
field
|
|
||||||
label
|
|
||||||
defaultValue
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pipeline {
|
|
||||||
nodes {
|
|
||||||
type
|
|
||||||
options {
|
|
||||||
nodes {
|
|
||||||
label
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
field
|
|
||||||
label
|
|
||||||
defaultValue
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
analyzers {
|
|
||||||
nodes {
|
|
||||||
name
|
|
||||||
label
|
|
||||||
enabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
subject { GitlabSchema.execute(query, context: { current_user: user }).as_json }
|
|
||||||
|
|
||||||
before do
|
|
||||||
project.add_developer(user)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns the project's sast configuration for global variables" do
|
|
||||||
query_result = subject.dig('data', 'project', 'sastCiConfiguration', 'global', 'nodes')
|
|
||||||
first_config = query_result.first
|
|
||||||
fourth_config = query_result[3]
|
|
||||||
expect(first_config['type']).to eq('string')
|
|
||||||
expect(first_config['field']).to eq('SECURE_ANALYZERS_PREFIX')
|
|
||||||
expect(first_config['label']).to eq('Image prefix')
|
|
||||||
expect(first_config['defaultValue']).to eq('registry.gitlab.com/gitlab-org/security-products/analyzers')
|
|
||||||
expect(first_config['value']).to eq('')
|
|
||||||
expect(first_config['options']).to be_nil
|
|
||||||
expect(fourth_config['options']['nodes']).to match([{ "value" => "true", "label" => "true (disables SAST)" },
|
|
||||||
{ "value" => "false", "label" => "false (enables SAST)" }])
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns the project's sast configuration for pipeline variables" do
|
|
||||||
configuration = subject.dig('data', 'project', 'sastCiConfiguration', 'pipeline', 'nodes').first
|
|
||||||
expect(configuration['type']).to eq('dropdown')
|
|
||||||
expect(configuration['field']).to eq('stage')
|
|
||||||
expect(configuration['label']).to eq('Stage')
|
|
||||||
expect(configuration['defaultValue']).to eq('test')
|
|
||||||
expect(configuration['value']).to eq('')
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns the project's sast configuration for analyzer variables" do
|
|
||||||
configuration = subject.dig('data', 'project', 'sastCiConfiguration', 'analyzers', 'nodes').first
|
|
||||||
expect(configuration['name']).to eq('brakeman')
|
|
||||||
expect(configuration['label']).to eq('Brakeman')
|
|
||||||
expect(configuration['enabled']).to eq(true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it_behaves_like 'a GraphQL type with labels'
|
it_behaves_like 'a GraphQL type with labels'
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,6 +10,13 @@ RSpec.describe AuditEventPartitioned do
|
||||||
expect(partitioned_table.column_names).to match_array(source_table.column_names)
|
expect(partitioned_table.column_names).to match_array(source_table.column_names)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'has the same null constraints as the source table' do
|
||||||
|
constraints_from_source_table = null_constraints(source_table)
|
||||||
|
constraints_from_partitioned_table = null_constraints(partitioned_table)
|
||||||
|
|
||||||
|
expect(constraints_from_partitioned_table.to_a).to match_array(constraints_from_source_table.to_a)
|
||||||
|
end
|
||||||
|
|
||||||
it 'inserts the same record as the one in the source table', :aggregate_failures do
|
it 'inserts the same record as the one in the source table', :aggregate_failures do
|
||||||
expect { create(:audit_event) }.to change { partitioned_table.count }.by(1)
|
expect { create(:audit_event) }.to change { partitioned_table.count }.by(1)
|
||||||
|
|
||||||
|
@ -22,4 +29,13 @@ RSpec.describe AuditEventPartitioned do
|
||||||
|
|
||||||
expect(event_from_partitioned_table).to eq(event_from_source_table)
|
expect(event_from_partitioned_table).to eq(event_from_source_table)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def null_constraints(table)
|
||||||
|
table.connection.select_all(<<~SQL)
|
||||||
|
SELECT c.column_name, c.is_nullable
|
||||||
|
FROM information_schema.columns c
|
||||||
|
WHERE c.table_name = '#{table.table_name}'
|
||||||
|
AND c.column_name != 'created_at'
|
||||||
|
SQL
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -29,4 +29,10 @@ RSpec.describe 'getting pipeline information nested in a project' do
|
||||||
|
|
||||||
expect(pipeline_graphql_data).not_to be_nil
|
expect(pipeline_graphql_data).not_to be_nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'contains configSource' do
|
||||||
|
post_graphql(query, current_user: current_user)
|
||||||
|
|
||||||
|
expect(pipeline_graphql_data.dig('configSource')).to eq('UNKNOWN_SOURCE')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'fast_spec_helper'
|
||||||
|
|
||||||
|
require 'rubocop'
|
||||||
|
require 'rubocop/rspec/support'
|
||||||
|
|
||||||
|
require_relative '../../../../rubocop/cop/usage_data/distinct_count_by_large_foreign_key'
|
||||||
|
|
||||||
|
RSpec.describe RuboCop::Cop::UsageData::DistinctCountByLargeForeignKey, type: :rubocop do
|
||||||
|
include CopHelper
|
||||||
|
|
||||||
|
let(:allowed_foreign_keys) { %i[author_id user_id] }
|
||||||
|
|
||||||
|
let(:config) do
|
||||||
|
RuboCop::Config.new('UsageData/DistinctCountByLargeForeignKey' => {
|
||||||
|
'AllowedForeignKeys' => allowed_foreign_keys
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
subject(:cop) { described_class.new(config) }
|
||||||
|
|
||||||
|
context 'when counting by disallowed key' do
|
||||||
|
it 'register an offence' do
|
||||||
|
inspect_source('distinct_count(Issue, :creator_id)')
|
||||||
|
|
||||||
|
expect(cop.offenses.size).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when calling by allowed key' do
|
||||||
|
it 'does not register an offence' do
|
||||||
|
inspect_source('distinct_count(Issue, :author_id)')
|
||||||
|
|
||||||
|
expect(cop.offenses).to be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,13 @@
|
||||||
|
include:
|
||||||
|
- template: SAST.gitlab-ci.yml
|
||||||
|
|
||||||
|
variables:
|
||||||
|
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers2"
|
||||||
|
SAST_EXCLUDED_PATHS: "spec, executables"
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- our_custom_security_stage
|
||||||
|
sast:
|
||||||
|
stage: our_custom_security_stage
|
||||||
|
variables:
|
||||||
|
SEARCH_MAX_DEPTH: 8
|
|
@ -0,0 +1,9 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
RSpec.shared_context 'read ci configuration for sast enabled project' do
|
||||||
|
let_it_be(:gitlab_ci_yml_content) do
|
||||||
|
File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci_for_sast.yml'))
|
||||||
|
end
|
||||||
|
|
||||||
|
let_it_be(:project) { create(:project, :repository) }
|
||||||
|
end
|
Loading…
Reference in New Issue