gitlab-ci-yaml_lint 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.travis.yml +5 -0
- data/Gemfile +8 -0
- data/LICENSE +20 -0
- data/README.md +50 -0
- data/Rakefile +6 -0
- data/bin/gitlab-ci-yaml_lint +13 -0
- data/gitlab-ci-yaml_lint.gemspec +31 -0
- data/gitlab_lib/LICENSE +27 -0
- data/gitlab_lib/gitlab/ci/config.rb +62 -0
- data/gitlab_lib/gitlab/ci/config/entry/artifacts.rb +35 -0
- data/gitlab_lib/gitlab/ci/config/entry/attributable.rb +27 -0
- data/gitlab_lib/gitlab/ci/config/entry/boolean.rb +18 -0
- data/gitlab_lib/gitlab/ci/config/entry/cache.rb +45 -0
- data/gitlab_lib/gitlab/ci/config/entry/commands.rb +33 -0
- data/gitlab_lib/gitlab/ci/config/entry/configurable.rb +77 -0
- data/gitlab_lib/gitlab/ci/config/entry/coverage.rb +22 -0
- data/gitlab_lib/gitlab/ci/config/entry/environment.rb +83 -0
- data/gitlab_lib/gitlab/ci/config/entry/factory.rb +73 -0
- data/gitlab_lib/gitlab/ci/config/entry/global.rb +72 -0
- data/gitlab_lib/gitlab/ci/config/entry/hidden.rb +22 -0
- data/gitlab_lib/gitlab/ci/config/entry/image.rb +47 -0
- data/gitlab_lib/gitlab/ci/config/entry/job.rb +157 -0
- data/gitlab_lib/gitlab/ci/config/entry/jobs.rb +52 -0
- data/gitlab_lib/gitlab/ci/config/entry/key.rb +22 -0
- data/gitlab_lib/gitlab/ci/config/entry/legacy_validation_helpers.rb +61 -0
- data/gitlab_lib/gitlab/ci/config/entry/node.rb +101 -0
- data/gitlab_lib/gitlab/ci/config/entry/paths.rb +18 -0
- data/gitlab_lib/gitlab/ci/config/entry/policy.rb +53 -0
- data/gitlab_lib/gitlab/ci/config/entry/script.rb +18 -0
- data/gitlab_lib/gitlab/ci/config/entry/service.rb +34 -0
- data/gitlab_lib/gitlab/ci/config/entry/services.rb +41 -0
- data/gitlab_lib/gitlab/ci/config/entry/simplifiable.rb +43 -0
- data/gitlab_lib/gitlab/ci/config/entry/stage.rb +22 -0
- data/gitlab_lib/gitlab/ci/config/entry/stages.rb +22 -0
- data/gitlab_lib/gitlab/ci/config/entry/undefined.rb +40 -0
- data/gitlab_lib/gitlab/ci/config/entry/unspecified.rb +19 -0
- data/gitlab_lib/gitlab/ci/config/entry/validatable.rb +38 -0
- data/gitlab_lib/gitlab/ci/config/entry/validator.rb +26 -0
- data/gitlab_lib/gitlab/ci/config/entry/validators.rb +144 -0
- data/gitlab_lib/gitlab/ci/config/entry/variables.rb +26 -0
- data/gitlab_lib/gitlab/ci/config/loader.rb +25 -0
- data/gitlab_lib/gitlab/ci/yaml_processor.rb +189 -0
- data/lib/gitlab/ci/yaml_lint.rb +73 -0
- data/lib/gitlab/ci/yaml_lint/rake/tasks.rb +28 -0
- data/lib/gitlab/ci/yaml_lint/version.rb +7 -0
- metadata +203 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
module Gitlab
|
2
|
+
module Ci
|
3
|
+
class Config
|
4
|
+
module Entry
|
5
|
+
##
|
6
|
+
# Entry that represents Coverage settings.
|
7
|
+
#
|
8
|
+
class Coverage < Node
|
9
|
+
include Validatable
|
10
|
+
|
11
|
+
validations do
|
12
|
+
validates :config, regexp: true
|
13
|
+
end
|
14
|
+
|
15
|
+
def value
|
16
|
+
@config[1...-1]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Gitlab
|
2
|
+
module Ci
|
3
|
+
class Config
|
4
|
+
module Entry
|
5
|
+
##
|
6
|
+
# Entry that represents an environment.
|
7
|
+
#
|
8
|
+
class Environment < Node
|
9
|
+
include Validatable
|
10
|
+
|
11
|
+
ALLOWED_KEYS = %i[name url action on_stop].freeze
|
12
|
+
|
13
|
+
validations do
|
14
|
+
validate do
|
15
|
+
unless hash? || string?
|
16
|
+
errors.add(:config, 'should be a hash or a string')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
validates :name, presence: true
|
21
|
+
validates :name,
|
22
|
+
type: {
|
23
|
+
with: String,
|
24
|
+
message: Gitlab::Regex.environment_name_regex_message
|
25
|
+
}
|
26
|
+
|
27
|
+
validates :name,
|
28
|
+
format: {
|
29
|
+
with: Gitlab::Regex.environment_name_regex,
|
30
|
+
message: Gitlab::Regex.environment_name_regex_message
|
31
|
+
}
|
32
|
+
|
33
|
+
with_options if: :hash? do
|
34
|
+
validates :config, allowed_keys: ALLOWED_KEYS
|
35
|
+
|
36
|
+
validates :url,
|
37
|
+
length: { maximum: 255 },
|
38
|
+
allow_nil: true
|
39
|
+
|
40
|
+
validates :action,
|
41
|
+
inclusion: { in: %w[start stop], message: 'should be start or stop' },
|
42
|
+
allow_nil: true
|
43
|
+
|
44
|
+
validates :on_stop, type: String, allow_nil: true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def hash?
|
49
|
+
@config.is_a?(Hash)
|
50
|
+
end
|
51
|
+
|
52
|
+
def string?
|
53
|
+
@config.is_a?(String)
|
54
|
+
end
|
55
|
+
|
56
|
+
def name
|
57
|
+
value[:name]
|
58
|
+
end
|
59
|
+
|
60
|
+
def url
|
61
|
+
value[:url]
|
62
|
+
end
|
63
|
+
|
64
|
+
def action
|
65
|
+
value[:action] || 'start'
|
66
|
+
end
|
67
|
+
|
68
|
+
def on_stop
|
69
|
+
value[:on_stop]
|
70
|
+
end
|
71
|
+
|
72
|
+
def value
|
73
|
+
case @config
|
74
|
+
when String then { name: @config, action: 'start' }
|
75
|
+
when Hash then @config
|
76
|
+
else {}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Gitlab
|
2
|
+
module Ci
|
3
|
+
class Config
|
4
|
+
module Entry
|
5
|
+
##
|
6
|
+
# Factory class responsible for fabricating entry objects.
|
7
|
+
#
|
8
|
+
class Factory
|
9
|
+
InvalidFactory = Class.new(StandardError)
|
10
|
+
|
11
|
+
def initialize(entry)
|
12
|
+
@entry = entry
|
13
|
+
@metadata = {}
|
14
|
+
@attributes = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def value(value)
|
18
|
+
@value = value
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def metadata(metadata)
|
23
|
+
@metadata.merge!(metadata)
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def with(attributes)
|
28
|
+
@attributes.merge!(attributes)
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def create!
|
33
|
+
raise InvalidFactory unless defined?(@value)
|
34
|
+
|
35
|
+
##
|
36
|
+
# We assume that unspecified entry is undefined.
|
37
|
+
# See issue #18775.
|
38
|
+
#
|
39
|
+
if @value.nil?
|
40
|
+
Entry::Unspecified.new(
|
41
|
+
fabricate_unspecified
|
42
|
+
)
|
43
|
+
else
|
44
|
+
fabricate(@entry, @value)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def fabricate_unspecified
|
51
|
+
##
|
52
|
+
# If entry has a default value we fabricate concrete node
|
53
|
+
# with default value.
|
54
|
+
#
|
55
|
+
if @entry.default.nil?
|
56
|
+
fabricate(Entry::Undefined)
|
57
|
+
else
|
58
|
+
fabricate(@entry, @entry.default)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def fabricate(entry, value = nil)
|
63
|
+
entry.new(value, @metadata).tap do |node|
|
64
|
+
node.key = @attributes[:key]
|
65
|
+
node.parent = @attributes[:parent]
|
66
|
+
node.description = @attributes[:description]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Gitlab
|
2
|
+
module Ci
|
3
|
+
class Config
|
4
|
+
module Entry
|
5
|
+
##
|
6
|
+
# This class represents a global entry - root Entry for entire
|
7
|
+
# GitLab CI Configuration file.
|
8
|
+
#
|
9
|
+
class Global < Node
|
10
|
+
include Configurable
|
11
|
+
|
12
|
+
entry :before_script, Entry::Script,
|
13
|
+
description: 'Script that will be executed before each job.'
|
14
|
+
|
15
|
+
entry :image, Entry::Image,
|
16
|
+
description: 'Docker image that will be used to execute jobs.'
|
17
|
+
|
18
|
+
entry :services, Entry::Services,
|
19
|
+
description: 'Docker images that will be linked to the container.'
|
20
|
+
|
21
|
+
entry :after_script, Entry::Script,
|
22
|
+
description: 'Script that will be executed after each job.'
|
23
|
+
|
24
|
+
entry :variables, Entry::Variables,
|
25
|
+
description: 'Environment variables that will be used.'
|
26
|
+
|
27
|
+
entry :stages, Entry::Stages,
|
28
|
+
description: 'Configuration of stages for this pipeline.'
|
29
|
+
|
30
|
+
entry :types, Entry::Stages,
|
31
|
+
description: 'Deprecated: stages for this pipeline.'
|
32
|
+
|
33
|
+
entry :cache, Entry::Cache,
|
34
|
+
description: 'Configure caching between build jobs.'
|
35
|
+
|
36
|
+
helpers :before_script, :image, :services, :after_script,
|
37
|
+
:variables, :stages, :types, :cache, :jobs
|
38
|
+
|
39
|
+
def compose!(_deps = nil)
|
40
|
+
super(self) do
|
41
|
+
compose_jobs!
|
42
|
+
compose_deprecated_entries!
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def compose_jobs!
|
49
|
+
factory = Entry::Factory.new(Entry::Jobs)
|
50
|
+
.value(@config.except(*self.class.nodes.keys))
|
51
|
+
.with(key: :jobs, parent: self,
|
52
|
+
description: 'Jobs definition for this pipeline')
|
53
|
+
|
54
|
+
@entries[:jobs] = factory.create!
|
55
|
+
end
|
56
|
+
|
57
|
+
def compose_deprecated_entries!
|
58
|
+
##
|
59
|
+
# Deprecated `:types` key workaround - if types are defined and
|
60
|
+
# stages are not defined we use types definition as stages.
|
61
|
+
#
|
62
|
+
if types_defined? && !stages_defined?
|
63
|
+
@entries[:stages] = @entries[:types]
|
64
|
+
end
|
65
|
+
|
66
|
+
@entries.delete(:types)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Gitlab
|
2
|
+
module Ci
|
3
|
+
class Config
|
4
|
+
module Entry
|
5
|
+
##
|
6
|
+
# Entry that represents a hidden CI/CD key.
|
7
|
+
#
|
8
|
+
class Hidden < Node
|
9
|
+
include Validatable
|
10
|
+
|
11
|
+
validations do
|
12
|
+
validates :config, presence: true
|
13
|
+
end
|
14
|
+
|
15
|
+
def relevant?
|
16
|
+
false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Gitlab
|
2
|
+
module Ci
|
3
|
+
class Config
|
4
|
+
module Entry
|
5
|
+
##
|
6
|
+
# Entry that represents a Docker image.
|
7
|
+
#
|
8
|
+
class Image < Node
|
9
|
+
include Validatable
|
10
|
+
|
11
|
+
ALLOWED_KEYS = %i[name entrypoint].freeze
|
12
|
+
|
13
|
+
validations do
|
14
|
+
validates :config, hash_or_string: true
|
15
|
+
validates :config, allowed_keys: ALLOWED_KEYS
|
16
|
+
|
17
|
+
validates :name, type: String, presence: true
|
18
|
+
validates :entrypoint, array_of_strings: true, allow_nil: true
|
19
|
+
end
|
20
|
+
|
21
|
+
def hash?
|
22
|
+
@config.is_a?(Hash)
|
23
|
+
end
|
24
|
+
|
25
|
+
def string?
|
26
|
+
@config.is_a?(String)
|
27
|
+
end
|
28
|
+
|
29
|
+
def name
|
30
|
+
value[:name]
|
31
|
+
end
|
32
|
+
|
33
|
+
def entrypoint
|
34
|
+
value[:entrypoint]
|
35
|
+
end
|
36
|
+
|
37
|
+
def value
|
38
|
+
return { name: @config } if string?
|
39
|
+
return @config if hash?
|
40
|
+
|
41
|
+
{}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
module Gitlab
|
2
|
+
module Ci
|
3
|
+
class Config
|
4
|
+
module Entry
|
5
|
+
##
|
6
|
+
# Entry that represents a concrete CI/CD job.
|
7
|
+
#
|
8
|
+
class Job < Node
|
9
|
+
include Configurable
|
10
|
+
include Attributable
|
11
|
+
|
12
|
+
ALLOWED_KEYS = %i[tags script only except type image services allow_failure
|
13
|
+
type stage when artifacts cache dependencies before_script
|
14
|
+
after_script variables environment coverage retry].freeze
|
15
|
+
|
16
|
+
validations do
|
17
|
+
validates :config, allowed_keys: ALLOWED_KEYS
|
18
|
+
validates :config, presence: true
|
19
|
+
validates :script, presence: true
|
20
|
+
validates :name, presence: true
|
21
|
+
validates :name, type: Symbol
|
22
|
+
|
23
|
+
with_options allow_nil: true do
|
24
|
+
validates :tags, array_of_strings: true
|
25
|
+
validates :allow_failure, boolean: true
|
26
|
+
validates :retry, numericality: { only_integer: true,
|
27
|
+
greater_than_or_equal_to: 0,
|
28
|
+
less_than_or_equal_to: 2 }
|
29
|
+
validates :when,
|
30
|
+
inclusion: { in: %w[on_success on_failure always manual],
|
31
|
+
message: 'should be on_success, on_failure, ' \
|
32
|
+
'always or manual' }
|
33
|
+
|
34
|
+
validates :dependencies, array_of_strings: true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
entry :before_script, Entry::Script,
|
39
|
+
description: 'Global before script overridden in this job.'
|
40
|
+
|
41
|
+
entry :script, Entry::Commands,
|
42
|
+
description: 'Commands that will be executed in this job.'
|
43
|
+
|
44
|
+
entry :stage, Entry::Stage,
|
45
|
+
description: 'Pipeline stage this job will be executed into.'
|
46
|
+
|
47
|
+
entry :type, Entry::Stage,
|
48
|
+
description: 'Deprecated: stage this job will be executed into.'
|
49
|
+
|
50
|
+
entry :after_script, Entry::Script,
|
51
|
+
description: 'Commands that will be executed when finishing job.'
|
52
|
+
|
53
|
+
entry :cache, Entry::Cache,
|
54
|
+
description: 'Cache definition for this job.'
|
55
|
+
|
56
|
+
entry :image, Entry::Image,
|
57
|
+
description: 'Image that will be used to execute this job.'
|
58
|
+
|
59
|
+
entry :services, Entry::Services,
|
60
|
+
description: 'Services that will be used to execute this job.'
|
61
|
+
|
62
|
+
entry :only, Entry::Policy,
|
63
|
+
description: 'Refs policy this job will be executed for.'
|
64
|
+
|
65
|
+
entry :except, Entry::Policy,
|
66
|
+
description: 'Refs policy this job will be executed for.'
|
67
|
+
|
68
|
+
entry :variables, Entry::Variables,
|
69
|
+
description: 'Environment variables available for this job.'
|
70
|
+
|
71
|
+
entry :artifacts, Entry::Artifacts,
|
72
|
+
description: 'Artifacts configuration for this job.'
|
73
|
+
|
74
|
+
entry :environment, Entry::Environment,
|
75
|
+
description: 'Environment configuration for this job.'
|
76
|
+
|
77
|
+
entry :coverage, Entry::Coverage,
|
78
|
+
description: 'Coverage configuration for this job.'
|
79
|
+
|
80
|
+
helpers :before_script, :script, :stage, :type, :after_script,
|
81
|
+
:cache, :image, :services, :only, :except, :variables,
|
82
|
+
:artifacts, :commands, :environment, :coverage, :retry
|
83
|
+
|
84
|
+
attributes :script, :tags, :allow_failure, :when, :dependencies, :retry
|
85
|
+
|
86
|
+
def compose!(deps = nil)
|
87
|
+
super do
|
88
|
+
if type_defined? && !stage_defined?
|
89
|
+
@entries[:stage] = @entries[:type]
|
90
|
+
end
|
91
|
+
|
92
|
+
@entries.delete(:type)
|
93
|
+
end
|
94
|
+
|
95
|
+
inherit!(deps)
|
96
|
+
end
|
97
|
+
|
98
|
+
def name
|
99
|
+
@metadata[:name]
|
100
|
+
end
|
101
|
+
|
102
|
+
def value
|
103
|
+
@config.merge(to_hash.compact)
|
104
|
+
end
|
105
|
+
|
106
|
+
def commands
|
107
|
+
(before_script_value.to_a + script_value.to_a).join("\n")
|
108
|
+
end
|
109
|
+
|
110
|
+
def manual_action?
|
111
|
+
self.when == 'manual'
|
112
|
+
end
|
113
|
+
|
114
|
+
def ignored?
|
115
|
+
allow_failure.nil? ? manual_action? : allow_failure
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def inherit!(deps)
|
121
|
+
return unless deps
|
122
|
+
|
123
|
+
self.class.nodes.each_key do |key|
|
124
|
+
global_entry = deps[key]
|
125
|
+
job_entry = self[key]
|
126
|
+
|
127
|
+
if global_entry.specified? && !job_entry.specified?
|
128
|
+
@entries[key] = global_entry
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def to_hash
|
134
|
+
{ name: name,
|
135
|
+
before_script: before_script_value,
|
136
|
+
script: script_value,
|
137
|
+
commands: commands,
|
138
|
+
image: image_value,
|
139
|
+
services: services_value,
|
140
|
+
stage: stage_value,
|
141
|
+
cache: cache_value,
|
142
|
+
only: only_value,
|
143
|
+
except: except_value,
|
144
|
+
variables: variables_defined? ? variables_value : nil,
|
145
|
+
environment: environment_defined? ? environment_value : nil,
|
146
|
+
environment_name: environment_defined? ? environment_value[:name] : nil,
|
147
|
+
coverage: coverage_defined? ? coverage_value : nil,
|
148
|
+
retry: retry_defined? ? retry_value.to_i : nil,
|
149
|
+
artifacts: artifacts_value,
|
150
|
+
after_script: after_script_value,
|
151
|
+
ignore: ignored? }
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|