summaryrefslogtreecommitdiff
path: root/lib/gitlab/ci
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-11-19 22:11:55 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2019-11-19 22:11:55 +0000
commit5a8431feceba47fd8e1804d9aa1b1730606b71d5 (patch)
treee5df8e0ceee60f4af8093f5c4c2f934b8abced05 /lib/gitlab/ci
parent4d477238500c347c6553d335d920bedfc5a46869 (diff)
downloadgitlab-ce-5a8431feceba47fd8e1804d9aa1b1730606b71d5.tar.gz
Add latest changes from gitlab-org/gitlab@12-5-stable-ee
Diffstat (limited to 'lib/gitlab/ci')
-rw-r--r--lib/gitlab/ci/ansi2json/converter.rb101
-rw-r--r--lib/gitlab/ci/ansi2json/line.rb7
-rw-r--r--lib/gitlab/ci/ansi2json/state.rb4
-rw-r--r--lib/gitlab/ci/ansi2json/style.rb21
-rw-r--r--lib/gitlab/ci/build/context/base.rb35
-rw-r--r--lib/gitlab/ci/build/context/build.rb41
-rw-r--r--lib/gitlab/ci/build/context/global.rb41
-rw-r--r--lib/gitlab/ci/build/policy/changes.rb2
-rw-r--r--lib/gitlab/ci/build/policy/kubernetes.rb2
-rw-r--r--lib/gitlab/ci/build/policy/refs.rb2
-rw-r--r--lib/gitlab/ci/build/policy/specification.rb2
-rw-r--r--lib/gitlab/ci/build/policy/variables.rb4
-rw-r--r--lib/gitlab/ci/build/rules.rb14
-rw-r--r--lib/gitlab/ci/build/rules/rule.rb4
-rw-r--r--lib/gitlab/ci/build/rules/rule/clause.rb2
-rw-r--r--lib/gitlab/ci/build/rules/rule/clause/changes.rb2
-rw-r--r--lib/gitlab/ci/build/rules/rule/clause/exists.rb2
-rw-r--r--lib/gitlab/ci/build/rules/rule/clause/if.rb7
-rw-r--r--lib/gitlab/ci/config/entry/artifacts.rb17
-rw-r--r--lib/gitlab/ci/config/entry/boolean.rb20
-rw-r--r--lib/gitlab/ci/config/entry/commands.rb4
-rw-r--r--lib/gitlab/ci/config/entry/default.rb34
-rw-r--r--lib/gitlab/ci/config/entry/files.rb26
-rw-r--r--lib/gitlab/ci/config/entry/job.rb67
-rw-r--r--lib/gitlab/ci/config/entry/key.rb45
-rw-r--r--lib/gitlab/ci/config/entry/need.rb44
-rw-r--r--lib/gitlab/ci/config/entry/needs.rb55
-rw-r--r--lib/gitlab/ci/config/entry/prefix.rb20
-rw-r--r--lib/gitlab/ci/config/entry/root.rb5
-rw-r--r--lib/gitlab/ci/config/entry/rules/rule.rb15
-rw-r--r--lib/gitlab/ci/config/entry/script.rb6
-rw-r--r--lib/gitlab/ci/config/entry/workflow.rb25
-rw-r--r--lib/gitlab/ci/config/normalizer.rb20
-rw-r--r--lib/gitlab/ci/pipeline/chain/base.rb2
-rw-r--r--lib/gitlab/ci/pipeline/chain/build.rb2
-rw-r--r--lib/gitlab/ci/pipeline/chain/command.rb4
-rw-r--r--lib/gitlab/ci/pipeline/chain/config/content.rb59
-rw-r--r--lib/gitlab/ci/pipeline/chain/config/process.rb41
-rw-r--r--lib/gitlab/ci/pipeline/chain/evaluate_workflow_rules.rb50
-rw-r--r--lib/gitlab/ci/pipeline/chain/populate.rb21
-rw-r--r--lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs.rb6
-rw-r--r--lib/gitlab/ci/pipeline/chain/seed.rb64
-rw-r--r--lib/gitlab/ci/pipeline/chain/validate/config.rb33
-rw-r--r--lib/gitlab/ci/pipeline/seed/build.rb47
-rw-r--r--lib/gitlab/ci/pipeline/seed/build/cache.rb77
-rw-r--r--lib/gitlab/ci/status/build/failed.rb4
-rw-r--r--lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml10
-rw-r--r--lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml13
-rw-r--r--lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml67
-rw-r--r--lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml69
-rw-r--r--lib/gitlab/ci/yaml_processor.rb46
53 files changed, 1020 insertions, 295 deletions
diff --git a/lib/gitlab/ci/ansi2json/converter.rb b/lib/gitlab/ci/ansi2json/converter.rb
index 8d25b66af9c..cbda3808b86 100644
--- a/lib/gitlab/ci/ansi2json/converter.rb
+++ b/lib/gitlab/ci/ansi2json/converter.rb
@@ -22,11 +22,11 @@ module Gitlab
start_offset = @state.offset
- @state.set_current_line!(style: Style.new(@state.inherited_style))
+ @state.new_line!(
+ style: Style.new(@state.inherited_style))
stream.each_line do |line|
- s = StringScanner.new(line)
- convert_line(s)
+ consume_line(line)
end
# This must be assigned before flushing the current line
@@ -52,26 +52,41 @@ module Gitlab
private
- def convert_line(scanner)
- until scanner.eos?
-
- if scanner.scan(Gitlab::Regex.build_trace_section_regex)
- handle_section(scanner)
- elsif scanner.scan(/\e([@-_])(.*?)([@-~])/)
- handle_sequence(scanner)
- elsif scanner.scan(/\e(([@-_])(.*?)?)?$/)
- break
- elsif scanner.scan(/</)
- @state.current_line << '&lt;'
- elsif scanner.scan(/\r?\n/)
- # we advance the offset of the next current line
- # so it does not start from \n
- flush_current_line(advance_offset: scanner.matched_size)
- else
- @state.current_line << scanner.scan(/./m)
- end
-
- @state.offset += scanner.matched_size
+ def consume_line(line)
+ scanner = StringScanner.new(line)
+
+ consume_token(scanner) until scanner.eos?
+ end
+
+ def consume_token(scanner)
+ if scan_token(scanner, Gitlab::Regex.build_trace_section_regex, consume: false)
+ handle_section(scanner)
+ elsif scan_token(scanner, /\e([@-_])(.*?)([@-~])/)
+ handle_sequence(scanner)
+ elsif scan_token(scanner, /\e(([@-_])(.*?)?)?$/)
+ # stop scanning
+ scanner.terminate
+ elsif scan_token(scanner, /\r?\n/)
+ flush_current_line
+ elsif scan_token(scanner, /\r/)
+ # drop last line
+ @state.current_line.clear!
+ elsif scan_token(scanner, /.[^\e\r\ns]*/m)
+ # this is a join from all previous tokens and first letters
+ # it always matches at least one character `.`
+ # it matches everything that is not start of:
+ # `\e`, `<`, `\r`, `\n`, `s` (for section_start)
+ @state.current_line << scanner[0]
+ else
+ raise 'invalid parser state'
+ end
+ end
+
+ def scan_token(scanner, match, consume: true)
+ scanner.scan(match).tap do |result|
+ # we need to move offset as soon
+ # as we match the token
+ @state.offset += scanner.matched_size if consume && result
end
end
@@ -96,32 +111,50 @@ module Gitlab
section_name = sanitize_section_name(section)
if action == "start"
- handle_section_start(section_name, timestamp)
+ handle_section_start(scanner, section_name, timestamp)
elsif action == "end"
- handle_section_end(section_name, timestamp)
+ handle_section_end(scanner, section_name, timestamp)
+ else
+ raise 'unsupported action'
end
end
- def handle_section_start(section, timestamp)
- flush_current_line unless @state.current_line.empty?
+ def handle_section_start(scanner, section, timestamp)
+ # We make a new line for new section
+ flush_current_line
+
@state.open_section(section, timestamp)
+
+ # we need to consume match after handling
+ # the open of section, as we want the section
+ # marker to be refresh on incremental update
+ @state.offset += scanner.matched_size
end
- def handle_section_end(section, timestamp)
+ def handle_section_end(scanner, section, timestamp)
return unless @state.section_open?(section)
- flush_current_line unless @state.current_line.empty?
+ # We flush the content to make the end
+ # of section to be a new line
+ flush_current_line
+
@state.close_section(section, timestamp)
- # ensure that section end is detached from the last
- # line in the section
+ # we need to consume match before handling
+ # as we want the section close marker
+ # not to be refreshed on incremental update
+ @state.offset += scanner.matched_size
+
+ # this flushes an empty line with `section_duration`
flush_current_line
end
- def flush_current_line(advance_offset: 0)
- @lines << @state.current_line.to_h
+ def flush_current_line
+ unless @state.current_line.empty?
+ @lines << @state.current_line.to_h
+ end
- @state.set_current_line!(advance_offset: advance_offset)
+ @state.new_line!
end
def sanitize_section_name(section)
diff --git a/lib/gitlab/ci/ansi2json/line.rb b/lib/gitlab/ci/ansi2json/line.rb
index 173fb1df88e..21aa1f84353 100644
--- a/lib/gitlab/ci/ansi2json/line.rb
+++ b/lib/gitlab/ci/ansi2json/line.rb
@@ -47,12 +47,17 @@ module Gitlab
@current_segment.text << data
end
+ def clear!
+ @segments.clear
+ @current_segment = Segment.new(style: style)
+ end
+
def style
@current_segment.style
end
def empty?
- @segments.empty? && @current_segment.empty?
+ @segments.empty? && @current_segment.empty? && @section_duration.nil?
end
def update_style(ansi_commands)
diff --git a/lib/gitlab/ci/ansi2json/state.rb b/lib/gitlab/ci/ansi2json/state.rb
index db7a9035b8b..7e1a8102a35 100644
--- a/lib/gitlab/ci/ansi2json/state.rb
+++ b/lib/gitlab/ci/ansi2json/state.rb
@@ -46,9 +46,9 @@ module Gitlab
@open_sections.key?(section)
end
- def set_current_line!(style: nil, advance_offset: 0)
+ def new_line!(style: nil)
new_line = Line.new(
- offset: @offset + advance_offset,
+ offset: @offset,
style: style || @current_line.style,
sections: @open_sections.keys
)
diff --git a/lib/gitlab/ci/ansi2json/style.rb b/lib/gitlab/ci/ansi2json/style.rb
index 2739ffdfa5d..77f61178b37 100644
--- a/lib/gitlab/ci/ansi2json/style.rb
+++ b/lib/gitlab/ci/ansi2json/style.rb
@@ -15,14 +15,10 @@ module Gitlab
end
def update(ansi_commands)
- command = ansi_commands.shift
- return unless command
-
- if changes = Gitlab::Ci::Ansi2json::Parser.new(command, ansi_commands).changes
- apply_changes(changes)
- end
+ # treat e\[m as \e[0m
+ ansi_commands = ['0'] if ansi_commands.empty?
- update(ansi_commands)
+ evaluate_stack_command(ansi_commands)
end
def set?
@@ -50,6 +46,17 @@ module Gitlab
private
+ def evaluate_stack_command(ansi_commands)
+ command = ansi_commands.shift
+ return unless command
+
+ if changes = Gitlab::Ci::Ansi2json::Parser.new(command, ansi_commands).changes
+ apply_changes(changes)
+ end
+
+ evaluate_stack_command(ansi_commands)
+ end
+
def apply_changes(changes)
case
when changes[:reset]
diff --git a/lib/gitlab/ci/build/context/base.rb b/lib/gitlab/ci/build/context/base.rb
new file mode 100644
index 00000000000..02b97ea76e9
--- /dev/null
+++ b/lib/gitlab/ci/build/context/base.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Build
+ module Context
+ class Base
+ attr_reader :pipeline
+
+ def initialize(pipeline)
+ @pipeline = pipeline
+ end
+
+ def variables
+ raise NotImplementedError
+ end
+
+ protected
+
+ def pipeline_attributes
+ {
+ pipeline: pipeline,
+ project: pipeline.project,
+ user: pipeline.user,
+ ref: pipeline.ref,
+ tag: pipeline.tag,
+ trigger_request: pipeline.legacy_trigger,
+ protected: pipeline.protected_ref?
+ }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/build/context/build.rb b/lib/gitlab/ci/build/context/build.rb
new file mode 100644
index 00000000000..dfd86d3ad72
--- /dev/null
+++ b/lib/gitlab/ci/build/context/build.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Build
+ module Context
+ class Build < Base
+ include Gitlab::Utils::StrongMemoize
+
+ attr_reader :attributes
+
+ def initialize(pipeline, attributes = {})
+ super(pipeline)
+
+ @attributes = attributes
+ end
+
+ def variables
+ strong_memoize(:variables) do
+ # This is a temporary piece of technical debt to allow us access
+ # to the CI variables to evaluate rules before we persist a Build
+ # with the result. We should refactor away the extra Build.new,
+ # but be able to get CI Variables directly from the Seed::Build.
+ stub_build.scoped_variables_hash
+ end
+ end
+
+ private
+
+ def stub_build
+ ::Ci::Build.new(build_attributes)
+ end
+
+ def build_attributes
+ attributes.merge(pipeline_attributes)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/build/context/global.rb b/lib/gitlab/ci/build/context/global.rb
new file mode 100644
index 00000000000..fdd3ac358d5
--- /dev/null
+++ b/lib/gitlab/ci/build/context/global.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Build
+ module Context
+ class Global < Base
+ include Gitlab::Utils::StrongMemoize
+
+ def initialize(pipeline, yaml_variables:)
+ super(pipeline)
+
+ @yaml_variables = yaml_variables.to_a
+ end
+
+ def variables
+ strong_memoize(:variables) do
+ # This is a temporary piece of technical debt to allow us access
+ # to the CI variables to evaluate workflow:rules
+ # with the result. We should refactor away the extra Build.new,
+ # but be able to get CI Variables directly from the Seed::Build.
+ stub_build.scoped_variables_hash
+ .reject { |key, _value| key =~ /\ACI_(JOB|BUILD)/ }
+ end
+ end
+
+ private
+
+ def stub_build
+ ::Ci::Build.new(build_attributes)
+ end
+
+ def build_attributes
+ pipeline_attributes.merge(
+ yaml_variables: @yaml_variables)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/build/policy/changes.rb b/lib/gitlab/ci/build/policy/changes.rb
index 9c705a1cd3e..9ae4198bbf7 100644
--- a/lib/gitlab/ci/build/policy/changes.rb
+++ b/lib/gitlab/ci/build/policy/changes.rb
@@ -9,7 +9,7 @@ module Gitlab
@globs = Array(globs)
end
- def satisfied_by?(pipeline, seed)
+ def satisfied_by?(pipeline, context)
return true if pipeline.modified_paths.nil?
pipeline.modified_paths.any? do |path|
diff --git a/lib/gitlab/ci/build/policy/kubernetes.rb b/lib/gitlab/ci/build/policy/kubernetes.rb
index 4c7dc947cd0..4e8693724e5 100644
--- a/lib/gitlab/ci/build/policy/kubernetes.rb
+++ b/lib/gitlab/ci/build/policy/kubernetes.rb
@@ -11,7 +11,7 @@ module Gitlab
end
end
- def satisfied_by?(pipeline, seed = nil)
+ def satisfied_by?(pipeline, context = nil)
pipeline.has_kubernetes_active?
end
end
diff --git a/lib/gitlab/ci/build/policy/refs.rb b/lib/gitlab/ci/build/policy/refs.rb
index c3005303fd8..afe0ccb361e 100644
--- a/lib/gitlab/ci/build/policy/refs.rb
+++ b/lib/gitlab/ci/build/policy/refs.rb
@@ -9,7 +9,7 @@ module Gitlab
@patterns = Array(refs)
end
- def satisfied_by?(pipeline, seed = nil)
+ def satisfied_by?(pipeline, context = nil)
@patterns.any? do |pattern|
pattern, path = pattern.split('@', 2)
diff --git a/lib/gitlab/ci/build/policy/specification.rb b/lib/gitlab/ci/build/policy/specification.rb
index ceb5210cfb5..1394340ce1f 100644
--- a/lib/gitlab/ci/build/policy/specification.rb
+++ b/lib/gitlab/ci/build/policy/specification.rb
@@ -17,7 +17,7 @@ module Gitlab
@spec = spec
end
- def satisfied_by?(pipeline, seed = nil)
+ def satisfied_by?(pipeline, context = nil)
raise NotImplementedError
end
end
diff --git a/lib/gitlab/ci/build/policy/variables.rb b/lib/gitlab/ci/build/policy/variables.rb
index e9c8864123f..7b1ce6330f0 100644
--- a/lib/gitlab/ci/build/policy/variables.rb
+++ b/lib/gitlab/ci/build/policy/variables.rb
@@ -9,8 +9,8 @@ module Gitlab
@expressions = Array(expressions)
end
- def satisfied_by?(pipeline, seed)
- variables = seed.scoped_variables_hash
+ def satisfied_by?(pipeline, context)
+ variables = context.variables
statements = @expressions.map do |statement|
::Gitlab::Ci::Pipeline::Expression::Statement
diff --git a/lib/gitlab/ci/build/rules.rb b/lib/gitlab/ci/build/rules.rb
index 43399c74457..c705b6f86c7 100644
--- a/lib/gitlab/ci/build/rules.rb
+++ b/lib/gitlab/ci/build/rules.rb
@@ -13,17 +13,21 @@ module Gitlab
options: { start_in: start_in }.compact
}.compact
end
+
+ def pass?
+ self.when != 'never'
+ end
end
- def initialize(rule_hashes, default_when = 'on_success')
+ def initialize(rule_hashes, default_when:)
@rule_list = Rule.fabricate_list(rule_hashes)
@default_when = default_when
end
- def evaluate(pipeline, build)
+ def evaluate(pipeline, context)
if @rule_list.nil?
Result.new(@default_when)
- elsif matched_rule = match_rule(pipeline, build)
+ elsif matched_rule = match_rule(pipeline, context)
Result.new(
matched_rule.attributes[:when] || @default_when,
matched_rule.attributes[:start_in]
@@ -35,8 +39,8 @@ module Gitlab
private
- def match_rule(pipeline, build)
- @rule_list.find { |rule| rule.matches?(pipeline, build) }
+ def match_rule(pipeline, context)
+ @rule_list.find { |rule| rule.matches?(pipeline, context) }
end
end
end
diff --git a/lib/gitlab/ci/build/rules/rule.rb b/lib/gitlab/ci/build/rules/rule.rb
index 8d52158c8d2..077e4d150fb 100644
--- a/lib/gitlab/ci/build/rules/rule.rb
+++ b/lib/gitlab/ci/build/rules/rule.rb
@@ -23,8 +23,8 @@ module Gitlab
end
end
- def matches?(pipeline, build)
- @clauses.all? { |clause| clause.satisfied_by?(pipeline, build) }
+ def matches?(pipeline, context)
+ @clauses.all? { |clause| clause.satisfied_by?(pipeline, context) }
end
end
end
diff --git a/lib/gitlab/ci/build/rules/rule/clause.rb b/lib/gitlab/ci/build/rules/rule/clause.rb
index bf787fe95a6..6d4bbbb8c21 100644
--- a/lib/gitlab/ci/build/rules/rule/clause.rb
+++ b/lib/gitlab/ci/build/rules/rule/clause.rb
@@ -20,7 +20,7 @@ module Gitlab
@spec = spec
end
- def satisfied_by?(pipeline, seed = nil)
+ def satisfied_by?(pipeline, context = nil)
raise NotImplementedError
end
end
diff --git a/lib/gitlab/ci/build/rules/rule/clause/changes.rb b/lib/gitlab/ci/build/rules/rule/clause/changes.rb
index 81d2ee6c24c..728a66ca87f 100644
--- a/lib/gitlab/ci/build/rules/rule/clause/changes.rb
+++ b/lib/gitlab/ci/build/rules/rule/clause/changes.rb
@@ -8,7 +8,7 @@ module Gitlab
@globs = Array(globs)
end
- def satisfied_by?(pipeline, seed)
+ def satisfied_by?(pipeline, context)
return true if pipeline.modified_paths.nil?
pipeline.modified_paths.any? do |path|
diff --git a/lib/gitlab/ci/build/rules/rule/clause/exists.rb b/lib/gitlab/ci/build/rules/rule/clause/exists.rb
index 62f8371283f..85e77438f51 100644
--- a/lib/gitlab/ci/build/rules/rule/clause/exists.rb
+++ b/lib/gitlab/ci/build/rules/rule/clause/exists.rb
@@ -15,7 +15,7 @@ module Gitlab
@exact_globs, @pattern_globs = globs.partition(&method(:exact_glob?))
end
- def satisfied_by?(pipeline, seed)
+ def satisfied_by?(pipeline, context)
paths = worktree_paths(pipeline)
exact_matches?(paths) || pattern_matches?(paths)
diff --git a/lib/gitlab/ci/build/rules/rule/clause/if.rb b/lib/gitlab/ci/build/rules/rule/clause/if.rb
index 18c3b450f95..6143a736ca6 100644
--- a/lib/gitlab/ci/build/rules/rule/clause/if.rb
+++ b/lib/gitlab/ci/build/rules/rule/clause/if.rb
@@ -8,10 +8,9 @@ module Gitlab
@expression = expression
end
- def satisfied_by?(pipeline, seed)
- variables = seed.scoped_variables_hash
-
- ::Gitlab::Ci::Pipeline::Expression::Statement.new(@expression, variables).truthful?
+ def satisfied_by?(pipeline, context)
+ ::Gitlab::Ci::Pipeline::Expression::Statement.new(
+ @expression, context.variables).truthful?
end
end
end
diff --git a/lib/gitlab/ci/config/entry/artifacts.rb b/lib/gitlab/ci/config/entry/artifacts.rb
index 41613369ca2..9d8d7675234 100644
--- a/lib/gitlab/ci/config/entry/artifacts.rb
+++ b/lib/gitlab/ci/config/entry/artifacts.rb
@@ -12,7 +12,9 @@ module Gitlab
include ::Gitlab::Config::Entry::Validatable
include ::Gitlab::Config::Entry::Attributable
- ALLOWED_KEYS = %i[name untracked paths reports when expire_in].freeze
+ ALLOWED_KEYS = %i[name untracked paths reports when expire_in expose_as].freeze
+ EXPOSE_AS_REGEX = /\A\w[-\w ]*\z/.freeze
+ EXPOSE_AS_ERROR_MESSAGE = "can contain only letters, digits, '-', '_' and spaces"
attributes ALLOWED_KEYS
@@ -21,11 +23,18 @@ module Gitlab
validations do
validates :config, type: Hash
validates :config, allowed_keys: ALLOWED_KEYS
+ validates :paths, presence: true, if: :expose_as_present?
with_options allow_nil: true do
validates :name, type: String
validates :untracked, boolean: true
validates :paths, array_of_strings: true
+ validates :paths, array_of_strings: {
+ with: /\A[^*]*\z/,
+ message: "can't contain '*' when used with 'expose_as'"
+ }, if: :expose_as_present?
+ validates :expose_as, type: String, length: { maximum: 100 }, if: :expose_as_present?
+ validates :expose_as, format: { with: EXPOSE_AS_REGEX, message: EXPOSE_AS_ERROR_MESSAGE }, if: :expose_as_present?
validates :reports, type: Hash
validates :when,
inclusion: { in: %w[on_success on_failure always],
@@ -41,6 +50,12 @@ module Gitlab
@config[:reports] = reports_value if @config.key?(:reports)
@config
end
+
+ def expose_as_present?
+ return false unless Feature.enabled?(:ci_expose_arbitrary_artifacts_in_mr, default_enabled: true)
+
+ !@config[:expose_as].nil?
+ end
end
end
end
diff --git a/lib/gitlab/ci/config/entry/boolean.rb b/lib/gitlab/ci/config/entry/boolean.rb
new file mode 100644
index 00000000000..10619ef9f8d
--- /dev/null
+++ b/lib/gitlab/ci/config/entry/boolean.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module Entry
+ ##
+ # Entry that represents the interrutible value.
+ #
+ class Boolean < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Validatable
+
+ validations do
+ validates :config, boolean: true
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/entry/commands.rb b/lib/gitlab/ci/config/entry/commands.rb
index 02e368c1813..7a86fca3056 100644
--- a/lib/gitlab/ci/config/entry/commands.rb
+++ b/lib/gitlab/ci/config/entry/commands.rb
@@ -11,11 +11,11 @@ module Gitlab
include ::Gitlab::Config::Entry::Validatable
validations do
- validates :config, array_of_strings_or_string: true
+ validates :config, string_or_nested_array_of_strings: true
end
def value
- Array(@config)
+ Array(@config).flatten(1)
end
end
end
diff --git a/lib/gitlab/ci/config/entry/default.rb b/lib/gitlab/ci/config/entry/default.rb
index 6200d7c7f87..83127bde6e4 100644
--- a/lib/gitlab/ci/config/entry/default.rb
+++ b/lib/gitlab/ci/config/entry/default.rb
@@ -11,11 +11,10 @@ module Gitlab
#
class Default < ::Gitlab::Config::Entry::Node
include ::Gitlab::Config::Entry::Configurable
-
- DuplicateError = Class.new(Gitlab::Config::Loader::FormatError)
+ include ::Gitlab::Config::Entry::Inheritable
ALLOWED_KEYS = %i[before_script image services
- after_script cache].freeze
+ after_script cache interruptible].freeze
validations do
validates :config, allowed_keys: ALLOWED_KEYS
@@ -41,31 +40,22 @@ module Gitlab
description: 'Configure caching between build jobs.',
inherit: true
- helpers :before_script, :image, :services, :after_script, :cache
-
- def compose!(deps = nil)
- super(self)
+ entry :interruptible, Entry::Boolean,
+ description: 'Set jobs interruptible default value.',
+ inherit: false
- inherit!(deps)
- end
+ helpers :before_script, :image, :services, :after_script, :cache, :interruptible
private
- def inherit!(deps)
- return unless deps
-
- self.class.nodes.each do |key, factory|
- next unless factory.inheritable?
+ def overwrite_entry(deps, key, current_entry)
+ inherited_entry = deps[key]
- root_entry = deps[key]
- next unless root_entry.specified?
-
- if self[key].specified?
- raise DuplicateError, "#{key} is defined in top-level and `default:` entry"
- end
-
- @entries[key] = root_entry
+ if inherited_entry.specified? && current_entry.specified?
+ raise InheritError, "#{key} is defined in top-level and `default:` entry"
end
+
+ inherited_entry unless current_entry.specified?
end
end
end
diff --git a/lib/gitlab/ci/config/entry/files.rb b/lib/gitlab/ci/config/entry/files.rb
new file mode 100644
index 00000000000..d0d6a36d754
--- /dev/null
+++ b/lib/gitlab/ci/config/entry/files.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module Entry
+ ##
+ # Entry that represents an array of file paths.
+ #
+ class Files < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Validatable
+
+ validations do
+ validates :config, array_of_strings: true
+ validates :config, length: {
+ minimum: 1,
+ maximum: 2,
+ too_short: 'requires at least %{count} item',
+ too_long: 'has too many items (maximum is %{count})'
+ }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/entry/job.rb b/lib/gitlab/ci/config/entry/job.rb
index 07d5be86b1e..c75ae87a985 100644
--- a/lib/gitlab/ci/config/entry/job.rb
+++ b/lib/gitlab/ci/config/entry/job.rb
@@ -10,6 +10,7 @@ module Gitlab
class Job < ::Gitlab::Config::Entry::Node
include ::Gitlab::Config::Entry::Configurable
include ::Gitlab::Config::Entry::Attributable
+ include ::Gitlab::Config::Entry::Inheritable
ALLOWED_WHEN = %w[on_success on_failure always manual delayed].freeze
ALLOWED_KEYS = %i[tags script only except rules type image services
@@ -37,7 +38,6 @@ module Gitlab
with_options allow_nil: true do
validates :tags, array_of_strings: true
validates :allow_failure, boolean: true
- validates :interruptible, boolean: true
validates :parallel, numericality: { only_integer: true,
greater_than_or_equal_to: 2,
less_than_or_equal_to: 50 }
@@ -49,7 +49,6 @@ module Gitlab
validates :timeout, duration: { limit: ChronicDuration.output(Project::MAX_BUILD_TIMEOUT) }
validates :dependencies, array_of_strings: true
- validates :needs, array_of_strings: true
validates :extends, array_of_strings_or_string: true
validates :rules, array_of_hashes: true
end
@@ -73,13 +72,16 @@ module Gitlab
inherit: true
entry :script, Entry::Commands,
- description: 'Commands that will be executed in this job.'
+ description: 'Commands that will be executed in this job.',
+ inherit: false
entry :stage, Entry::Stage,
- description: 'Pipeline stage this job will be executed into.'
+ description: 'Pipeline stage this job will be executed into.',
+ inherit: false
entry :type, Entry::Stage,
- description: 'Deprecated: stage this job will be executed into.'
+ description: 'Deprecated: stage this job will be executed into.',
+ inherit: false
entry :after_script, Entry::Script,
description: 'Commands that will be executed when finishing job.',
@@ -97,30 +99,50 @@ module Gitlab
description: 'Services that will be used to execute this job.',
inherit: true
+ entry :interruptible, Entry::Boolean,
+ description: 'Set jobs interruptible value.',
+ inherit: true
+
entry :only, Entry::Policy,
description: 'Refs policy this job will be executed for.',
- default: Entry::Policy::DEFAULT_ONLY
+ default: Entry::Policy::DEFAULT_ONLY,
+ inherit: false
entry :except, Entry::Policy,
- description: 'Refs policy this job will be executed for.'
+ description: 'Refs policy this job will be executed for.',
+ inherit: false
entry :rules, Entry::Rules,
- description: 'List of evaluable Rules to determine job inclusion.'
+ description: 'List of evaluable Rules to determine job inclusion.',
+ inherit: false,
+ metadata: {
+ allowed_when: %w[on_success on_failure always never manual delayed].freeze
+ }
+
+ entry :needs, Entry::Needs,
+ description: 'Needs configuration for this job.',
+ metadata: { allowed_needs: %i[job] },
+ inherit: false
entry :variables, Entry::Variables,
- description: 'Environment variables available for this job.'
+ description: 'Environment variables available for this job.',
+ inherit: false
entry :artifacts, Entry::Artifacts,
- description: 'Artifacts configuration for this job.'
+ description: 'Artifacts configuration for this job.',
+ inherit: false
entry :environment, Entry::Environment,
- description: 'Environment configuration for this job.'
+ description: 'Environment configuration for this job.',
+ inherit: false
entry :coverage, Entry::Coverage,
- description: 'Coverage configuration for this job.'
+ description: 'Coverage configuration for this job.',
+ inherit: false
entry :retry, Entry::Retry,
- description: 'Retry configuration for this job.'
+ description: 'Retry configuration for this job.',
+ inherit: false
helpers :before_script, :script, :stage, :type, :after_script,
:cache, :image, :services, :only, :except, :variables,
@@ -155,8 +177,6 @@ module Gitlab
@entries.delete(:except)
end
end
-
- inherit!(deps)
end
def name
@@ -185,21 +205,8 @@ module Gitlab
private
- # We inherit config entries from `default:`
- # if the entry has the `inherit: true` flag set
- def inherit!(deps)
- return unless deps
-
- self.class.nodes.each do |key, factory|
- next unless factory.inheritable?
-
- default_entry = deps.default[key]
- job_entry = self[key]
-
- if default_entry.specified? && !job_entry.specified?
- @entries[key] = default_entry
- end
- end
+ def overwrite_entry(deps, key, current_entry)
+ deps.default[key] unless current_entry.specified?
end
def to_hash
diff --git a/lib/gitlab/ci/config/entry/key.rb b/lib/gitlab/ci/config/entry/key.rb
index 0c10967e629..f12f0919348 100644
--- a/lib/gitlab/ci/config/entry/key.rb
+++ b/lib/gitlab/ci/config/entry/key.rb
@@ -7,11 +7,48 @@ module Gitlab
##
# Entry that represents a key.
#
- class Key < ::Gitlab::Config::Entry::Node
- include ::Gitlab::Config::Entry::Validatable
+ class Key < ::Gitlab::Config::Entry::Simplifiable
+ strategy :SimpleKey, if: -> (config) { config.is_a?(String) || config.is_a?(Symbol) }
+ strategy :ComplexKey, if: -> (config) { config.is_a?(Hash) }
- validations do
- validates :config, key: true
+ class SimpleKey < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Validatable
+
+ validations do
+ validates :config, key: true
+ end
+
+ def self.default
+ 'default'
+ end
+
+ def value
+ super.to_s
+ end
+ end
+
+ class ComplexKey < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Attributable
+ include ::Gitlab::Config::Entry::Configurable
+
+ ALLOWED_KEYS = %i[files prefix].freeze
+ REQUIRED_KEYS = %i[files].freeze
+
+ validations do
+ validates :config, allowed_keys: ALLOWED_KEYS
+ validates :config, required_keys: REQUIRED_KEYS
+ end
+
+ entry :files, Entry::Files,
+ description: 'Files that should be used to build the key'
+ entry :prefix, Entry::Prefix,
+ description: 'Prefix that is added to the final cache key'
+ end
+
+ class UnknownStrategy < ::Gitlab::Config::Entry::Node
+ def errors
+ ["#{location} should be a hash, a string or a symbol"]
+ end
end
def self.default
diff --git a/lib/gitlab/ci/config/entry/need.rb b/lib/gitlab/ci/config/entry/need.rb
new file mode 100644
index 00000000000..b6db546d8ff
--- /dev/null
+++ b/lib/gitlab/ci/config/entry/need.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module Entry
+ class Need < ::Gitlab::Config::Entry::Simplifiable
+ strategy :Job, if: -> (config) { config.is_a?(String) }
+
+ class Job < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Validatable
+
+ validations do
+ validates :config, presence: true
+ validates :config, type: String
+ end
+
+ def type
+ :job
+ end
+
+ def value
+ { name: @config }
+ end
+ end
+
+ class UnknownStrategy < ::Gitlab::Config::Entry::Node
+ def type
+ end
+
+ def value
+ end
+
+ def errors
+ ["#{location} has an unsupported type"]
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+::Gitlab::Ci::Config::Entry::Need.prepend_if_ee('::EE::Gitlab::Ci::Config::Entry::Need')
diff --git a/lib/gitlab/ci/config/entry/needs.rb b/lib/gitlab/ci/config/entry/needs.rb
new file mode 100644
index 00000000000..28452aaaa16
--- /dev/null
+++ b/lib/gitlab/ci/config/entry/needs.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module Entry
+ ##
+ # Entry that represents a set of needs dependencies.
+ #
+ class Needs < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Validatable
+
+ validations do
+ validates :config, presence: true
+
+ validate do
+ unless config.is_a?(Hash) || config.is_a?(Array)
+ errors.add(:config, 'can only be a Hash or an Array')
+ end
+ end
+
+ validate on: :composed do
+ extra_keys = value.keys - opt(:allowed_needs)
+ if extra_keys.any?
+ errors.add(:config, "uses invalid types: #{extra_keys.join(', ')}")
+ end
+ end
+ end
+
+ def compose!(deps = nil)
+ super(deps) do
+ [@config].flatten.each_with_index do |need, index|
+ @entries[index] = ::Gitlab::Config::Entry::Factory.new(Entry::Need)
+ .value(need)
+ .with(key: "need", parent: self, description: "need definition.") # rubocop:disable CodeReuse/ActiveRecord
+ .create!
+ end
+
+ @entries.each_value do |entry|
+ entry.compose!(deps)
+ end
+ end
+ end
+
+ def value
+ values = @entries.values.select(&:type)
+ values.group_by(&:type).transform_values do |values|
+ values.map(&:value)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/entry/prefix.rb b/lib/gitlab/ci/config/entry/prefix.rb
new file mode 100644
index 00000000000..3244ad6d611
--- /dev/null
+++ b/lib/gitlab/ci/config/entry/prefix.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module Entry
+ ##
+ # Entry that represents a key prefix.
+ #
+ class Prefix < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Validatable
+
+ validations do
+ validates :config, key: true
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/entry/root.rb b/lib/gitlab/ci/config/entry/root.rb
index 07022ff7b54..25fb278d9b8 100644
--- a/lib/gitlab/ci/config/entry/root.rb
+++ b/lib/gitlab/ci/config/entry/root.rb
@@ -12,7 +12,7 @@ module Gitlab
include ::Gitlab::Config::Entry::Configurable
ALLOWED_KEYS = %i[default include before_script image services
- after_script variables stages types cache].freeze
+ after_script variables stages types cache workflow].freeze
validations do
validates :config, allowed_keys: ALLOWED_KEYS
@@ -64,6 +64,9 @@ module Gitlab
description: 'Configure caching between build jobs.',
reserved: true
+ entry :workflow, Entry::Workflow,
+ description: 'List of evaluable rules to determine Pipeline status'
+
helpers :default, :jobs, :stages, :types, :variables
delegate :before_script_value,
diff --git a/lib/gitlab/ci/config/entry/rules/rule.rb b/lib/gitlab/ci/config/entry/rules/rule.rb
index 5d6d1c026e3..59e0ef583ae 100644
--- a/lib/gitlab/ci/config/entry/rules/rule.rb
+++ b/lib/gitlab/ci/config/entry/rules/rule.rb
@@ -8,9 +8,9 @@ module Gitlab
include ::Gitlab::Config::Entry::Validatable
include ::Gitlab::Config::Entry::Attributable
- CLAUSES = %i[if changes exists].freeze
- ALLOWED_KEYS = %i[if changes exists when start_in].freeze
- ALLOWED_WHEN = %w[on_success on_failure always never manual delayed].freeze
+ CLAUSES = %i[if changes exists].freeze
+ ALLOWED_KEYS = %i[if changes exists when start_in].freeze
+ ALLOWABLE_WHEN = %w[on_success on_failure always never manual delayed].freeze
attributes :if, :changes, :exists, :when, :start_in
@@ -25,7 +25,14 @@ module Gitlab
with_options allow_nil: true do
validates :if, expression: true
validates :changes, :exists, array_of_strings: true, length: { maximum: 50 }
- validates :when, allowed_values: { in: ALLOWED_WHEN }
+ validates :when, allowed_values: { in: ALLOWABLE_WHEN }
+ end
+
+ validate do
+ validates_with Gitlab::Config::Entry::Validators::AllowedValuesValidator,
+ attributes: %i[when],
+ allow_nil: true,
+ in: opt(:allowed_when)
end
end
diff --git a/lib/gitlab/ci/config/entry/script.rb b/lib/gitlab/ci/config/entry/script.rb
index 9d25a82b521..285e18218b3 100644
--- a/lib/gitlab/ci/config/entry/script.rb
+++ b/lib/gitlab/ci/config/entry/script.rb
@@ -11,7 +11,11 @@ module Gitlab
include ::Gitlab::Config::Entry::Validatable
validations do
- validates :config, array_of_strings: true
+ validates :config, nested_array_of_strings: true
+ end
+
+ def value
+ config.flatten(1)
end
end
end
diff --git a/lib/gitlab/ci/config/entry/workflow.rb b/lib/gitlab/ci/config/entry/workflow.rb
new file mode 100644
index 00000000000..a51a3fbdcd2
--- /dev/null
+++ b/lib/gitlab/ci/config/entry/workflow.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module Entry
+ class Workflow < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Configurable
+
+ ALLOWED_KEYS = %i[rules].freeze
+
+ validations do
+ validates :config, type: Hash
+ validates :config, allowed_keys: ALLOWED_KEYS
+ validates :config, presence: true
+ end
+
+ entry :rules, Entry::Rules,
+ description: 'List of evaluable Rules to determine Pipeline status.',
+ metadata: { allowed_when: %w[always never] }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/normalizer.rb b/lib/gitlab/ci/config/normalizer.rb
index 09f9bf5f69f..e714ef225f5 100644
--- a/lib/gitlab/ci/config/normalizer.rb
+++ b/lib/gitlab/ci/config/normalizer.rb
@@ -18,8 +18,8 @@ module Gitlab
config[:dependencies] = expand_names(config[:dependencies])
end
- if config[:needs]
- config[:needs] = expand_names(config[:needs])
+ if job_needs = config.dig(:needs, :job)
+ config[:needs][:job] = expand_needs(job_needs)
end
config
@@ -36,6 +36,22 @@ module Gitlab
end
end
+ def expand_needs(job_needs)
+ return unless job_needs
+
+ job_needs.flat_map do |job_need|
+ job_need_name = job_need[:name].to_sym
+
+ if all_job_names = parallelized_jobs[job_need_name]
+ all_job_names.map do |job_name|
+ { name: job_name }
+ end
+ else
+ job_need
+ end
+ end
+ end
+
def parallelized_jobs
strong_memoize(:parallelized_jobs) do
@jobs_config.each_with_object({}) do |(job_name, config), hash|
diff --git a/lib/gitlab/ci/pipeline/chain/base.rb b/lib/gitlab/ci/pipeline/chain/base.rb
index bab1c73e2f1..aabdf7ce47d 100644
--- a/lib/gitlab/ci/pipeline/chain/base.rb
+++ b/lib/gitlab/ci/pipeline/chain/base.rb
@@ -5,7 +5,7 @@ module Gitlab
module Pipeline
module Chain
class Base
- attr_reader :pipeline, :command
+ attr_reader :pipeline, :command, :config
delegate :project, :current_user, to: :command
diff --git a/lib/gitlab/ci/pipeline/chain/build.rb b/lib/gitlab/ci/pipeline/chain/build.rb
index 899df81ea5c..9662209f88e 100644
--- a/lib/gitlab/ci/pipeline/chain/build.rb
+++ b/lib/gitlab/ci/pipeline/chain/build.rb
@@ -22,8 +22,6 @@ module Gitlab
external_pull_request: @command.external_pull_request,
variables_attributes: Array(@command.variables_attributes)
)
-
- @pipeline.set_config_source
end
def break?
diff --git a/lib/gitlab/ci/pipeline/chain/command.rb b/lib/gitlab/ci/pipeline/chain/command.rb
index 58f89a6be5e..c2df419cca0 100644
--- a/lib/gitlab/ci/pipeline/chain/command.rb
+++ b/lib/gitlab/ci/pipeline/chain/command.rb
@@ -10,7 +10,9 @@ module Gitlab
:trigger_request, :schedule, :merge_request, :external_pull_request,
:ignore_skip_ci, :save_incompleted,
:seeds_block, :variables_attributes, :push_options,
- :chat_data, :allow_mirror_update
+ :chat_data, :allow_mirror_update,
+ # These attributes are set by Chains during processing:
+ :config_content, :config_processor, :stage_seeds
) do
include Gitlab::Utils::StrongMemoize
diff --git a/lib/gitlab/ci/pipeline/chain/config/content.rb b/lib/gitlab/ci/pipeline/chain/config/content.rb
new file mode 100644
index 00000000000..a8cd99b8e92
--- /dev/null
+++ b/lib/gitlab/ci/pipeline/chain/config/content.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Pipeline
+ module Chain
+ module Config
+ class Content < Chain::Base
+ include Chain::Helpers
+
+ def perform!
+ return if @command.config_content
+
+ if content = content_from_repo
+ @command.config_content = content
+ @pipeline.config_source = :repository_source
+ # TODO: we should persist ci_config_path
+ # @pipeline.config_path = ci_config_path
+ elsif content = content_from_auto_devops
+ @command.config_content = content
+ @pipeline.config_source = :auto_devops_source
+ end
+
+ unless @command.config_content
+ return error("Missing #{ci_config_path} file")
+ end
+ end
+
+ def break?
+ @pipeline.errors.any? || @pipeline.persisted?
+ end
+
+ private
+
+ def content_from_repo
+ return unless project
+ return unless @pipeline.sha
+ return unless ci_config_path
+
+ project.repository.gitlab_ci_yml_for(@pipeline.sha, ci_config_path)
+ rescue GRPC::NotFound, GRPC::Internal
+ nil
+ end
+
+ def content_from_auto_devops
+ return unless project&.auto_devops_enabled?
+
+ Gitlab::Template::GitlabCiYmlTemplate.find('Auto-DevOps').content
+ end
+
+ def ci_config_path
+ project.ci_config_path.presence || '.gitlab-ci.yml'
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/pipeline/chain/config/process.rb b/lib/gitlab/ci/pipeline/chain/config/process.rb
new file mode 100644
index 00000000000..731b0fdb286
--- /dev/null
+++ b/lib/gitlab/ci/pipeline/chain/config/process.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Pipeline
+ module Chain
+ module Config
+ class Process < Chain::Base
+ include Chain::Helpers
+
+ def perform!
+ raise ArgumentError, 'missing config content' unless @command.config_content
+
+ @command.config_processor = ::Gitlab::Ci::YamlProcessor.new(
+ @command.config_content, {
+ project: project,
+ sha: @pipeline.sha,
+ user: current_user
+ }
+ )
+ rescue Gitlab::Ci::YamlProcessor::ValidationError => ex
+ error(ex.message, config_error: true)
+ rescue => ex
+ Gitlab::Sentry.track_acceptable_exception(ex, extra: {
+ project_id: project.id,
+ sha: @pipeline.sha
+ })
+
+ error("Undefined error (#{Labkit::Correlation::CorrelationId.current_id})",
+ config_error: true)
+ end
+
+ def break?
+ @pipeline.errors.any? || @pipeline.persisted?
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/pipeline/chain/evaluate_workflow_rules.rb b/lib/gitlab/ci/pipeline/chain/evaluate_workflow_rules.rb
new file mode 100644
index 00000000000..0ee9485eebc
--- /dev/null
+++ b/lib/gitlab/ci/pipeline/chain/evaluate_workflow_rules.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Pipeline
+ module Chain
+ class EvaluateWorkflowRules < Chain::Base
+ include ::Gitlab::Utils::StrongMemoize
+ include Chain::Helpers
+
+ def perform!
+ return unless Feature.enabled?(:workflow_rules, @pipeline.project)
+
+ unless workflow_passed?
+ error('Pipeline filtered out by workflow rules.')
+ end
+ end
+
+ def break?
+ return false unless Feature.enabled?(:workflow_rules, @pipeline.project)
+
+ !workflow_passed?
+ end
+
+ private
+
+ def workflow_passed?
+ strong_memoize(:workflow_passed) do
+ workflow_rules.evaluate(@pipeline, global_context).pass?
+ end
+ end
+
+ def workflow_rules
+ Gitlab::Ci::Build::Rules.new(
+ workflow_config[:rules], default_when: 'always')
+ end
+
+ def global_context
+ Gitlab::Ci::Build::Context::Global.new(
+ @pipeline, yaml_variables: workflow_config[:yaml_variables])
+ end
+
+ def workflow_config
+ @command.config_processor.workflow_attributes || {}
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/pipeline/chain/populate.rb b/lib/gitlab/ci/pipeline/chain/populate.rb
index 13eca5a9d28..3a40c7b167c 100644
--- a/lib/gitlab/ci/pipeline/chain/populate.rb
+++ b/lib/gitlab/ci/pipeline/chain/populate.rb
@@ -10,29 +10,12 @@ module Gitlab
PopulateError = Class.new(StandardError)
def perform!
- # Allocate next IID. This operation must be outside of transactions of pipeline creations.
- pipeline.ensure_project_iid!
-
- # Protect the pipeline. This is assigned in Populate instead of
- # Build to prevent erroring out on ambiguous refs.
- pipeline.protected = @command.protected_ref?
-
- ##
- # Populate pipeline with block argument of CreatePipelineService#execute.
- #
- @command.seeds_block&.call(pipeline)
-
- ##
- # Gather all runtime build/stage errors
- #
- if seeds_errors = pipeline.stage_seeds.flat_map(&:errors).compact.presence
- return error(seeds_errors.join("\n"), config_error: true)
- end
+ raise ArgumentError, 'missing stage seeds' unless @command.stage_seeds
##
# Populate pipeline with all stages, and stages with builds.
#
- pipeline.stages = pipeline.stage_seeds.map(&:to_resource)
+ pipeline.stages = @command.stage_seeds.map(&:to_resource)
if pipeline.stages.none?
return error('No stages / jobs for this pipeline.')
diff --git a/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs.rb b/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs.rb
index 1e09b417311..9267c72efa4 100644
--- a/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs.rb
+++ b/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs.rb
@@ -6,11 +6,13 @@ module Gitlab
module Chain
class RemoveUnwantedChatJobs < Chain::Base
def perform!
- return unless pipeline.config_processor && pipeline.chat?
+ raise ArgumentError, 'missing config processor' unless @command.config_processor
+
+ return unless pipeline.chat?
# When scheduling a chat pipeline we only want to run the build
# that matches the chat command.
- pipeline.config_processor.jobs.select! do |name, _|
+ @command.config_processor.jobs.select! do |name, _|
name.to_s == command.chat_data[:command].to_s
end
end
diff --git a/lib/gitlab/ci/pipeline/chain/seed.rb b/lib/gitlab/ci/pipeline/chain/seed.rb
new file mode 100644
index 00000000000..2e177cfec7e
--- /dev/null
+++ b/lib/gitlab/ci/pipeline/chain/seed.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Pipeline
+ module Chain
+ class Seed < Chain::Base
+ include Chain::Helpers
+ include Gitlab::Utils::StrongMemoize
+
+ def perform!
+ raise ArgumentError, 'missing config processor' unless @command.config_processor
+
+ # Allocate next IID. This operation must be outside of transactions of pipeline creations.
+ pipeline.ensure_project_iid!
+
+ # Protect the pipeline. This is assigned in Populate instead of
+ # Build to prevent erroring out on ambiguous refs.
+ pipeline.protected = @command.protected_ref?
+
+ ##
+ # Populate pipeline with block argument of CreatePipelineService#execute.
+ #
+ @command.seeds_block&.call(pipeline)
+
+ ##
+ # Gather all runtime build/stage errors
+ #
+ if stage_seeds_errors
+ return error(stage_seeds_errors.join("\n"), config_error: true)
+ end
+
+ @command.stage_seeds = stage_seeds
+ end
+
+ def break?
+ pipeline.errors.any?
+ end
+
+ private
+
+ def stage_seeds_errors
+ stage_seeds.flat_map(&:errors).compact.presence
+ end
+
+ def stage_seeds
+ strong_memoize(:stage_seeds) do
+ seeds = stages_attributes.inject([]) do |previous_stages, attributes|
+ seed = Gitlab::Ci::Pipeline::Seed::Stage.new(pipeline, attributes, previous_stages)
+ previous_stages + [seed]
+ end
+
+ seeds.select(&:included?)
+ end
+ end
+
+ def stages_attributes
+ @command.config_processor.stages_attributes
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/pipeline/chain/validate/config.rb b/lib/gitlab/ci/pipeline/chain/validate/config.rb
deleted file mode 100644
index 28c38cc3d18..00000000000
--- a/lib/gitlab/ci/pipeline/chain/validate/config.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module Ci
- module Pipeline
- module Chain
- module Validate
- class Config < Chain::Base
- include Chain::Helpers
-
- def perform!
- unless @pipeline.config_processor
- unless @pipeline.ci_yaml_file
- return error("Missing #{@pipeline.ci_yaml_file_path} file")
- end
-
- if @command.save_incompleted && @pipeline.has_yaml_errors?
- @pipeline.drop!(:config_error)
- end
-
- error(@pipeline.yaml_errors)
- end
- end
-
- def break?
- @pipeline.errors.any? || @pipeline.persisted?
- end
- end
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/ci/pipeline/seed/build.rb b/lib/gitlab/ci/pipeline/seed/build.rb
index fc9c540088b..dce56b22666 100644
--- a/lib/gitlab/ci/pipeline/seed/build.rb
+++ b/lib/gitlab/ci/pipeline/seed/build.rb
@@ -28,7 +28,9 @@ module Gitlab
@except = Gitlab::Ci::Build::Policy
.fabricate(attributes.delete(:except))
@rules = Gitlab::Ci::Build::Rules
- .new(attributes.delete(:rules))
+ .new(attributes.delete(:rules), default_when: 'on_success')
+ @cache = Seed::Build::Cache
+ .new(pipeline, attributes.delete(:cache))
end
def name
@@ -38,7 +40,7 @@ module Gitlab
def included?
strong_memoize(:inclusion) do
if @using_rules
- included_by_rules?
+ rules_result.pass?
elsif @using_only || @using_except
all_of_only? && none_of_except?
else
@@ -59,6 +61,7 @@ module Gitlab
@seed_attributes
.deep_merge(pipeline_attributes)
.deep_merge(rules_attributes)
+ .deep_merge(cache_attributes)
end
def bridge?
@@ -80,26 +83,14 @@ module Gitlab
end
end
- def scoped_variables_hash
- strong_memoize(:scoped_variables_hash) do
- # This is a temporary piece of technical debt to allow us access
- # to the CI variables to evaluate rules before we persist a Build
- # with the result. We should refactor away the extra Build.new,
- # but be able to get CI Variables directly from the Seed::Build.
- ::Ci::Build.new(
- @seed_attributes.merge(pipeline_attributes)
- ).scoped_variables_hash
- end
- end
-
private
def all_of_only?
- @only.all? { |spec| spec.satisfied_by?(@pipeline, self) }
+ @only.all? { |spec| spec.satisfied_by?(@pipeline, evaluate_context) }
end
def none_of_except?
- @except.none? { |spec| spec.satisfied_by?(@pipeline, self) }
+ @except.none? { |spec| spec.satisfied_by?(@pipeline, evaluate_context) }
end
def needs_errors
@@ -141,13 +132,27 @@ module Gitlab
}
end
- def included_by_rules?
- rules_attributes[:when] != 'never'
+ def rules_attributes
+ return {} unless @using_rules
+
+ rules_result.build_attributes
end
- def rules_attributes
- strong_memoize(:rules_attributes) do
- @using_rules ? @rules.evaluate(@pipeline, self).build_attributes : {}
+ def rules_result
+ strong_memoize(:rules_result) do
+ @rules.evaluate(@pipeline, evaluate_context)
+ end
+ end
+
+ def evaluate_context
+ strong_memoize(:evaluate_context) do
+ Gitlab::Ci::Build::Context::Build.new(@pipeline, @seed_attributes)
+ end
+ end
+
+ def cache_attributes
+ strong_memoize(:cache_attributes) do
+ @cache.build_attributes
end
end
end
diff --git a/lib/gitlab/ci/pipeline/seed/build/cache.rb b/lib/gitlab/ci/pipeline/seed/build/cache.rb
new file mode 100644
index 00000000000..7671035b896
--- /dev/null
+++ b/lib/gitlab/ci/pipeline/seed/build/cache.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Pipeline
+ module Seed
+ class Build
+ class Cache
+ def initialize(pipeline, cache)
+ @pipeline = pipeline
+ local_cache = cache.to_h.deep_dup
+ @key = local_cache.delete(:key)
+ @paths = local_cache.delete(:paths)
+ @policy = local_cache.delete(:policy)
+ @untracked = local_cache.delete(:untracked)
+
+ raise ArgumentError, "unknown cache keys: #{local_cache.keys}" if local_cache.any?
+ end
+
+ def build_attributes
+ {
+ options: {
+ cache: {
+ key: key_string,
+ paths: @paths,
+ policy: @policy,
+ untracked: @untracked
+ }.compact.presence
+ }.compact
+ }
+ end
+
+ private
+
+ def key_string
+ key_from_string || key_from_files
+ end
+
+ def key_from_string
+ @key.to_s if @key.is_a?(String) || @key.is_a?(Symbol)
+ end
+
+ def key_from_files
+ return unless @key.is_a?(Hash)
+
+ [@key[:prefix], files_digest].select(&:present?).join('-')
+ end
+
+ def files_digest
+ hash_of_the_latest_changes || 'default'
+ end
+
+ def hash_of_the_latest_changes
+ return unless Feature.enabled?(:ci_file_based_cache, @pipeline.project, default_enabled: true)
+
+ ids = files.map { |path| last_commit_id_for_path(path) }
+ ids = ids.compact.sort.uniq
+
+ Digest::SHA1.hexdigest(ids.join('-')) if ids.any?
+ end
+
+ def files
+ @key[:files]
+ .to_a
+ .select(&:present?)
+ .uniq
+ end
+
+ def last_commit_id_for_path(path)
+ @pipeline.project.repository.last_commit_id_for_path(@pipeline.sha, path)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/status/build/failed.rb b/lib/gitlab/ci/status/build/failed.rb
index 961012c2cee..910d93f54ce 100644
--- a/lib/gitlab/ci/status/build/failed.rb
+++ b/lib/gitlab/ci/status/build/failed.rb
@@ -16,7 +16,9 @@ module Gitlab
stale_schedule: 'stale schedule',
job_execution_timeout: 'job execution timeout',
archived_failure: 'archived failure',
- unmet_prerequisites: 'unmet prerequisites'
+ unmet_prerequisites: 'unmet prerequisites',
+ scheduler_failure: 'scheduler failure',
+ data_integrity_failure: 'data integrity failure'
}.freeze
private_constant :REASONS
diff --git a/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml
index 3cdb7b5420c..a60b00b2ee8 100644
--- a/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml
@@ -18,7 +18,7 @@ code_quality:
--env SOURCE_CODE="$PWD"
--volume "$PWD":/code
--volume /var/run/docker.sock:/var/run/docker.sock
- "registry.gitlab.com/gitlab-org/security-products/codequality:12-0-stable" /code
+ "registry.gitlab.com/gitlab-org/security-products/codequality:12-5-stable" /code
artifacts:
reports:
codequality: gl-code-quality-report.json
diff --git a/lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml
index ae2ff9992f9..7a672f910dd 100644
--- a/lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml
@@ -1,8 +1,8 @@
-.auto-deploy:
- image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:v0.1.0"
+.dast-auto-deploy:
+ image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:v0.6.0"
dast_environment_deploy:
- extends: .auto-deploy
+ extends: .dast-auto-deploy
stage: review
script:
- auto-deploy check_kube_domain
@@ -28,10 +28,10 @@ dast_environment_deploy:
variables:
- $CI_DEFAULT_BRANCH != $CI_COMMIT_REF_NAME
- $DAST_DISABLED || $DAST_DISABLED_FOR_DEFAULT_BRANCH
- - $DAST_WEBSITE # we don't need to create a review app if a URL is already given
+ - $DAST_WEBSITE # we don't need to create a review app if a URL is already given
stop_dast_environment:
- extends: .auto-deploy
+ extends: .dast-auto-deploy
stage: cleanup
variables:
GIT_STRATEGY: none
diff --git a/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
index a8ec2d4781d..738be44d5f4 100644
--- a/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
@@ -1,5 +1,5 @@
.auto-deploy:
- image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:v0.1.0"
+ image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:v0.7.0"
review:
extends: .auto-deploy
diff --git a/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml
index f058468ed8e..ef2fc561201 100644
--- a/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml
@@ -9,16 +9,17 @@ container_scanning:
name: registry.gitlab.com/gitlab-org/security-products/analyzers/klar:$CS_MAJOR_VERSION
entrypoint: []
variables:
- # By default, use the latest clair vulnerabilities database, however, allow it to be overridden here
- # with a specific version to provide consistency for integration testing purposes
- CLAIR_DB_IMAGE_TAG: latest
- # Override this variable in your `.gitlab-ci.yml` file and set it to `fetch` if you want to provide a `clair-whitelist.yaml` file.
- # See https://docs.gitlab.com/ee/user/application_security/container_scanning/index.html#overriding-the-container-scanning-template
+ # By default, use the latest clair vulnerabilities database, however, allow it to be overridden here with a specific image
+ # to enable container scanning to run offline, or to provide a consistent list of vulnerabilities for integration testing purposes
+ CLAIR_DB_IMAGE_TAG: "latest"
+ CLAIR_DB_IMAGE: "arminc/clair-db:$CLAIR_DB_IMAGE_TAG"
+ # Override the GIT_STRATEGY variable in your `.gitlab-ci.yml` file and set it to `fetch` if you want to provide a `clair-whitelist.yml`
+ # file. See https://docs.gitlab.com/ee/user/application_security/container_scanning/index.html#overriding-the-container-scanning-template
# for details
GIT_STRATEGY: none
allow_failure: true
services:
- - name: arminc/clair-db:$CLAIR_DB_IMAGE_TAG
+ - name: $CLAIR_DB_IMAGE
alias: clair-vulnerabilities-db
script:
# the kubernetes executor currently ignores the Docker image entrypoint value, so the start.sh script must
diff --git a/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml
index c8930bc6263..4993d22d400 100644
--- a/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml
@@ -4,6 +4,12 @@
# List of the variables: https://gitlab.com/gitlab-org/security-products/dependency-scanning#settings
# How to set: https://docs.gitlab.com/ee/ci/yaml/#variables
+variables:
+ DS_ANALYZER_IMAGE_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
+ DS_DEFAULT_ANALYZERS: "gemnasium, retire.js, gemnasium-python, gemnasium-maven, bundler-audit"
+ DS_MAJOR_VERSION: 2
+ DS_DISABLE_DIND: "false"
+
dependency_scanning:
stage: test
image: docker:stable
@@ -45,6 +51,7 @@ dependency_scanning:
DS_PIP_DEPENDENCY_PATH \
PIP_INDEX_URL \
PIP_EXTRA_INDEX_URL \
+ MAVEN_CLI_OPTS \
) \
--volume "$PWD:/code" \
--volume /var/run/docker.sock:/var/run/docker.sock \
@@ -61,3 +68,63 @@ dependency_scanning:
except:
variables:
- $DEPENDENCY_SCANNING_DISABLED
+ - $DS_DISABLE_DIND == 'true'
+
+.analyzer:
+ extends: dependency_scanning
+ services: []
+ except:
+ variables:
+ - $DS_DISABLE_DIND == 'false'
+ script:
+ - /analyzer run
+
+gemnasium-dependency_scanning:
+ extends: .analyzer
+ image:
+ name: "$DS_ANALYZER_IMAGE_PREFIX/gemnasium:$DS_MAJOR_VERSION"
+ only:
+ variables:
+ - $GITLAB_FEATURES =~ /\bdependency_scanning\b/ &&
+ $DS_DEFAULT_ANALYZERS =~ /gemnasium/ &&
+ $CI_PROJECT_REPOSITORY_LANGUAGES =~ /ruby|javascript|php/
+
+gemnasium-maven-dependency_scanning:
+ extends: .analyzer
+ image:
+ name: "$DS_ANALYZER_IMAGE_PREFIX/gemnasium-maven:$DS_MAJOR_VERSION"
+ only:
+ variables:
+ - $GITLAB_FEATURES =~ /\bdependency_scanning\b/ &&
+ $DS_DEFAULT_ANALYZERS =~ /gemnasium-maven/ &&
+ $CI_PROJECT_REPOSITORY_LANGUAGES =~ /\bjava\b/
+
+gemnasium-python-dependency_scanning:
+ extends: .analyzer
+ image:
+ name: "$DS_ANALYZER_IMAGE_PREFIX/gemnasium-python:$DS_MAJOR_VERSION"
+ only:
+ variables:
+ - $GITLAB_FEATURES =~ /\bdependency_scanning\b/ &&
+ $DS_DEFAULT_ANALYZERS =~ /gemnasium-python/ &&
+ $CI_PROJECT_REPOSITORY_LANGUAGES =~ /python/
+
+bundler-audit-dependency_scanning:
+ extends: .analyzer
+ image:
+ name: "$DS_ANALYZER_IMAGE_PREFIX/bundler-audit:$DS_MAJOR_VERSION"
+ only:
+ variables:
+ - $GITLAB_FEATURES =~ /\bdependency_scanning\b/ &&
+ $DS_DEFAULT_ANALYZERS =~ /bundler-audit/ &&
+ $CI_PROJECT_REPOSITORY_LANGUAGES =~ /ruby/
+
+retire-js-dependency_scanning:
+ extends: .analyzer
+ image:
+ name: "$DS_ANALYZER_IMAGE_PREFIX/retire.js:$DS_MAJOR_VERSION"
+ only:
+ variables:
+ - $GITLAB_FEATURES =~ /\bdependency_scanning\b/ &&
+ $DS_DEFAULT_ANALYZERS =~ /retire.js/ &&
+ $CI_PROJECT_REPOSITORY_LANGUAGES =~ /javascript/
diff --git a/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
index a0c2ab3aa26..c81b4efddbc 100644
--- a/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
@@ -7,7 +7,7 @@
variables:
SAST_ANALYZER_IMAGE_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
SAST_DEFAULT_ANALYZERS: "bandit, brakeman, gosec, spotbugs, flawfinder, phpcs-security-audit, security-code-scan, nodejs-scan, eslint, tslint, secrets, sobelow, pmd-apex"
- SAST_MAJOR_VERSION: 2
+ SAST_ANALYZER_IMAGE_TAG: 2
SAST_DISABLE_DIND: "false"
sast:
@@ -35,45 +35,12 @@ sast:
export DOCKER_HOST='tcp://localhost:2375'
fi
fi
- - | # this is required to avoid undesirable reset of Docker image ENV variables being set on build stage
- function propagate_env_vars() {
- CURRENT_ENV=$(printenv)
-
- for VAR_NAME; do
- echo $CURRENT_ENV | grep "${VAR_NAME}=" > /dev/null && echo "--env $VAR_NAME "
- done
- }
+ - |
+ printenv | grep -E '^(DOCKER_|CI|GITLAB_|FF_|HOME|PWD|OLDPWD|PATH|SHLVL|HOSTNAME)' | cut -d'=' -f1 | \
+ (while IFS='\\n' read -r VAR; do unset -v "$VAR"; done; /bin/printenv > .env)
- |
docker run \
- $(propagate_env_vars \
- SAST_BANDIT_EXCLUDED_PATHS \
- SAST_ANALYZER_IMAGES \
- SAST_ANALYZER_IMAGE_PREFIX \
- SAST_ANALYZER_IMAGE_TAG \
- SAST_DEFAULT_ANALYZERS \
- SAST_PULL_ANALYZER_IMAGES \
- SAST_BRAKEMAN_LEVEL \
- SAST_FLAWFINDER_LEVEL \
- SAST_GITLEAKS_ENTROPY_LEVEL \
- SAST_GOSEC_LEVEL \
- SAST_EXCLUDED_PATHS \
- SAST_DOCKER_CLIENT_NEGOTIATION_TIMEOUT \
- SAST_PULL_ANALYZER_IMAGE_TIMEOUT \
- SAST_RUN_ANALYZER_TIMEOUT \
- SAST_JAVA_VERSION \
- ANT_HOME \
- ANT_PATH \
- GRADLE_PATH \
- JAVA_OPTS \
- JAVA_PATH \
- JAVA_8_VERSION \
- JAVA_11_VERSION \
- MAVEN_CLI_OPTS \
- MAVEN_PATH \
- MAVEN_REPO_PATH \
- SBT_PATH \
- FAIL_NEVER \
- ) \
+ --env-file .env \
--volume "$PWD:/code" \
--volume /var/run/docker.sock:/var/run/docker.sock \
"registry.gitlab.com/gitlab-org/security-products/sast:$SAST_VERSION" /app/bin/run /code
@@ -94,7 +61,7 @@ sast:
bandit-sast:
extends: .analyzer
image:
- name: "$SAST_ANALYZER_IMAGE_PREFIX/bandit:$SAST_MAJOR_VERSION"
+ name: "$SAST_ANALYZER_IMAGE_PREFIX/bandit:$SAST_ANALYZER_IMAGE_TAG"
only:
variables:
- $GITLAB_FEATURES =~ /\bsast\b/ &&
@@ -104,7 +71,7 @@ bandit-sast:
brakeman-sast:
extends: .analyzer
image:
- name: "$SAST_ANALYZER_IMAGE_PREFIX/brakeman:$SAST_MAJOR_VERSION"
+ name: "$SAST_ANALYZER_IMAGE_PREFIX/brakeman:$SAST_ANALYZER_IMAGE_TAG"
only:
variables:
- $GITLAB_FEATURES =~ /\bsast\b/ &&
@@ -114,7 +81,7 @@ brakeman-sast:
eslint-sast:
extends: .analyzer
image:
- name: "$SAST_ANALYZER_IMAGE_PREFIX/eslint:$SAST_MAJOR_VERSION"
+ name: "$SAST_ANALYZER_IMAGE_PREFIX/eslint:$SAST_ANALYZER_IMAGE_TAG"
only:
variables:
- $GITLAB_FEATURES =~ /\bsast\b/ &&
@@ -124,7 +91,7 @@ eslint-sast:
flawfinder-sast:
extends: .analyzer
image:
- name: "$SAST_ANALYZER_IMAGE_PREFIX/flawfinder:$SAST_MAJOR_VERSION"
+ name: "$SAST_ANALYZER_IMAGE_PREFIX/flawfinder:$SAST_ANALYZER_IMAGE_TAG"
only:
variables:
- $GITLAB_FEATURES =~ /\bsast\b/ &&
@@ -134,7 +101,7 @@ flawfinder-sast:
gosec-sast:
extends: .analyzer
image:
- name: "$SAST_ANALYZER_IMAGE_PREFIX/gosec:$SAST_MAJOR_VERSION"
+ name: "$SAST_ANALYZER_IMAGE_PREFIX/gosec:$SAST_ANALYZER_IMAGE_TAG"
only:
variables:
- $GITLAB_FEATURES =~ /\bsast\b/ &&
@@ -144,7 +111,7 @@ gosec-sast:
nodejs-scan-sast:
extends: .analyzer
image:
- name: "$SAST_ANALYZER_IMAGE_PREFIX/nodejs-scan:$SAST_MAJOR_VERSION"
+ name: "$SAST_ANALYZER_IMAGE_PREFIX/nodejs-scan:$SAST_ANALYZER_IMAGE_TAG"
only:
variables:
- $GITLAB_FEATURES =~ /\bsast\b/ &&
@@ -154,7 +121,7 @@ nodejs-scan-sast:
phpcs-security-audit-sast:
extends: .analyzer
image:
- name: "$SAST_ANALYZER_IMAGE_PREFIX/phpcs-security-audit:$SAST_MAJOR_VERSION"
+ name: "$SAST_ANALYZER_IMAGE_PREFIX/phpcs-security-audit:$SAST_ANALYZER_IMAGE_TAG"
only:
variables:
- $GITLAB_FEATURES =~ /\bsast\b/ &&
@@ -164,7 +131,7 @@ phpcs-security-audit-sast:
pmd-apex-sast:
extends: .analyzer
image:
- name: "$SAST_ANALYZER_IMAGE_PREFIX/pmd-apex:$SAST_MAJOR_VERSION"
+ name: "$SAST_ANALYZER_IMAGE_PREFIX/pmd-apex:$SAST_ANALYZER_IMAGE_TAG"
only:
variables:
- $GITLAB_FEATURES =~ /\bsast\b/ &&
@@ -174,7 +141,7 @@ pmd-apex-sast:
secrets-sast:
extends: .analyzer
image:
- name: "$SAST_ANALYZER_IMAGE_PREFIX/secrets:$SAST_MAJOR_VERSION"
+ name: "$SAST_ANALYZER_IMAGE_PREFIX/secrets:$SAST_ANALYZER_IMAGE_TAG"
only:
variables:
- $GITLAB_FEATURES =~ /\bsast\b/ &&
@@ -183,7 +150,7 @@ secrets-sast:
security-code-scan-sast:
extends: .analyzer
image:
- name: "$SAST_ANALYZER_IMAGE_PREFIX/security-code-scan:$SAST_MAJOR_VERSION"
+ name: "$SAST_ANALYZER_IMAGE_PREFIX/security-code-scan:$SAST_ANALYZER_IMAGE_TAG"
only:
variables:
- $GITLAB_FEATURES =~ /\bsast\b/ &&
@@ -193,7 +160,7 @@ security-code-scan-sast:
sobelow-sast:
extends: .analyzer
image:
- name: "$SAST_ANALYZER_IMAGE_PREFIX/sobelow:$SAST_MAJOR_VERSION"
+ name: "$SAST_ANALYZER_IMAGE_PREFIX/sobelow:$SAST_ANALYZER_IMAGE_TAG"
only:
variables:
- $GITLAB_FEATURES =~ /\bsast\b/ &&
@@ -203,7 +170,7 @@ sobelow-sast:
spotbugs-sast:
extends: .analyzer
image:
- name: "$SAST_ANALYZER_IMAGE_PREFIX/spotbugs:$SAST_MAJOR_VERSION"
+ name: "$SAST_ANALYZER_IMAGE_PREFIX/spotbugs:$SAST_ANALYZER_IMAGE_TAG"
only:
variables:
- $GITLAB_FEATURES =~ /\bsast\b/ &&
@@ -213,7 +180,7 @@ spotbugs-sast:
tslint-sast:
extends: .analyzer
image:
- name: "$SAST_ANALYZER_IMAGE_PREFIX/tslint:$SAST_MAJOR_VERSION"
+ name: "$SAST_ANALYZER_IMAGE_PREFIX/tslint:$SAST_ANALYZER_IMAGE_TAG"
only:
variables:
- $GITLAB_FEATURES =~ /\bsast\b/ &&
diff --git a/lib/gitlab/ci/yaml_processor.rb b/lib/gitlab/ci/yaml_processor.rb
index f6a3abefcfb..833c545fc5b 100644
--- a/lib/gitlab/ci/yaml_processor.rb
+++ b/lib/gitlab/ci/yaml_processor.rb
@@ -39,15 +39,15 @@ module Gitlab
when: job[:when] || 'on_success',
environment: job[:environment_name],
coverage_regex: job[:coverage],
- yaml_variables: yaml_variables(name),
- needs_attributes: job[:needs]&.map { |need| { name: need } },
+ yaml_variables: transform_to_yaml_variables(job_variables(name)),
+ needs_attributes: job.dig(:needs, :job),
interruptible: job[:interruptible],
rules: job[:rules],
+ cache: job[:cache],
options: {
image: job[:image],
services: job[:services],
artifacts: job[:artifacts],
- cache: job[:cache],
dependencies: job[:dependencies],
job_timeout: job[:timeout],
before_script: job[:before_script],
@@ -59,7 +59,7 @@ module Gitlab
instance: job[:instance],
start_in: job[:start_in],
trigger: job[:trigger],
- bridge_needs: job[:needs]
+ bridge_needs: job.dig(:needs, :bridge)&.first
}.compact }.compact
end
@@ -83,6 +83,13 @@ module Gitlab
end
end
+ def workflow_attributes
+ {
+ rules: @config.dig(:workflow, :rules),
+ yaml_variables: transform_to_yaml_variables(@variables)
+ }
+ end
+
def self.validation_message(content, opts = {})
return 'Please provide content of .gitlab-ci.yml' if content.blank?
@@ -118,20 +125,17 @@ module Gitlab
end
end
- def yaml_variables(name)
- variables = (@variables || {})
- .merge(job_variables(name))
+ def job_variables(name)
+ job_variables = @jobs.dig(name.to_sym, :variables)
- variables.map do |key, value|
- { key: key.to_s, value: value, public: true }
- end
+ @variables.to_h
+ .merge(job_variables.to_h)
end
- def job_variables(name)
- job = @jobs[name.to_sym]
- return {} unless job
-
- job[:variables] || {}
+ def transform_to_yaml_variables(variables)
+ variables.to_h.map do |key, value|
+ { key: key.to_s, value: value, public: true }
+ end
end
def validate_job_stage!(name, job)
@@ -159,17 +163,19 @@ module Gitlab
end
def validate_job_needs!(name, job)
- return unless job[:needs]
+ return unless job.dig(:needs, :job)
stage_index = @stages.index(job[:stage])
- job[:needs].each do |need|
- raise ValidationError, "#{name} job: undefined need: #{need}" unless @jobs[need.to_sym]
+ job.dig(:needs, :job).each do |need|
+ need_job_name = need[:name]
+
+ raise ValidationError, "#{name} job: undefined need: #{need_job_name}" unless @jobs[need_job_name.to_sym]
- needs_stage_index = @stages.index(@jobs[need.to_sym][:stage])
+ needs_stage_index = @stages.index(@jobs[need_job_name.to_sym][:stage])
unless needs_stage_index.present? && needs_stage_index < stage_index
- raise ValidationError, "#{name} job: need #{need} is not defined in prior stages"
+ raise ValidationError, "#{name} job: need #{need_job_name} is not defined in prior stages"
end
end
end