Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Léo-Paul Géneau
gitlab-ce
Commits
c45ace89
Commit
c45ace89
authored
Sep 06, 2017
by
Maxim Rydkin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
move `lib/ci/gitlab_ci_yaml_processor.rb` into `lib/gitlab/ci/yaml_processor.rb`
parent
c295d336
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
1960 additions
and
1956 deletions
+1960
-1956
app/controllers/ci/lints_controller.rb
app/controllers/ci/lints_controller.rb
+2
-2
app/models/blob_viewer/gitlab_ci_yml.rb
app/models/blob_viewer/gitlab_ci_yml.rb
+1
-1
app/models/ci/pipeline.rb
app/models/ci/pipeline.rb
+2
-2
lib/api/lint.rb
lib/api/lint.rb
+1
-1
lib/ci/gitlab_ci_yaml_processor.rb
lib/ci/gitlab_ci_yaml_processor.rb
+0
-251
lib/gitlab/ci/yaml_processor.rb
lib/gitlab/ci/yaml_processor.rb
+253
-0
spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+0
-1697
spec/lib/gitlab/ci/yaml_processor_spec.rb
spec/lib/gitlab/ci/yaml_processor_spec.rb
+1699
-0
spec/views/ci/lints/show.html.haml_spec.rb
spec/views/ci/lints/show.html.haml_spec.rb
+2
-2
No files found.
app/controllers/ci/lints_controller.rb
View file @
c45ace89
...
...
@@ -7,11 +7,11 @@ module Ci
def
create
@content
=
params
[
:content
]
@error
=
Ci
::
GitlabCi
YamlProcessor
.
validation_message
(
@content
)
@error
=
Gitlab
::
Ci
::
YamlProcessor
.
validation_message
(
@content
)
@status
=
@error
.
blank?
if
@error
.
blank?
@config_processor
=
Ci
::
GitlabCi
YamlProcessor
.
new
(
@content
)
@config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
@content
)
@stages
=
@config_processor
.
stages
@builds
=
@config_processor
.
builds
@jobs
=
@config_processor
.
jobs
...
...
app/models/blob_viewer/gitlab_ci_yml.rb
View file @
c45ace89
...
...
@@ -13,7 +13,7 @@ module BlobViewer
prepare!
@validation_message
=
Ci
::
GitlabCi
YamlProcessor
.
validation_message
(
blob
.
data
)
@validation_message
=
Gitlab
::
Ci
::
YamlProcessor
.
validation_message
(
blob
.
data
)
end
def
valid?
...
...
app/models/ci/pipeline.rb
View file @
c45ace89
...
...
@@ -336,8 +336,8 @@ module Ci
return
@config_processor
if
defined?
(
@config_processor
)
@config_processor
||=
begin
Ci
::
GitlabCi
YamlProcessor
.
new
(
ci_yaml_file
,
project
.
full_path
)
rescue
Ci
::
GitlabCi
YamlProcessor
::
ValidationError
,
Psych
::
SyntaxError
=>
e
Gitlab
::
Ci
::
YamlProcessor
.
new
(
ci_yaml_file
,
project
.
full_path
)
rescue
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
Psych
::
SyntaxError
=>
e
self
.
yaml_errors
=
e
.
message
nil
rescue
...
...
lib/api/lint.rb
View file @
c45ace89
...
...
@@ -6,7 +6,7 @@ module API
requires
:content
,
type:
String
,
desc:
'Content of .gitlab-ci.yml'
end
post
'/lint'
do
error
=
Ci
::
GitlabCi
YamlProcessor
.
validation_message
(
params
[
:content
])
error
=
Gitlab
::
Ci
::
YamlProcessor
.
validation_message
(
params
[
:content
])
status
200
...
...
lib/ci/gitlab_ci_yaml_processor.rb
deleted
100644 → 0
View file @
c295d336
module
Ci
class
GitlabCiYamlProcessor
ValidationError
=
Class
.
new
(
StandardError
)
include
Gitlab
::
Ci
::
Config
::
Entry
::
LegacyValidationHelpers
attr_reader
:path
,
:cache
,
:stages
,
:jobs
def
initialize
(
config
,
path
=
nil
)
@ci_config
=
Gitlab
::
Ci
::
Config
.
new
(
config
)
@config
=
@ci_config
.
to_hash
@path
=
path
unless
@ci_config
.
valid?
raise
ValidationError
,
@ci_config
.
errors
.
first
end
initial_parsing
rescue
Gitlab
::
Ci
::
Config
::
Loader
::
FormatError
=>
e
raise
ValidationError
,
e
.
message
end
def
builds_for_stage_and_ref
(
stage
,
ref
,
tag
=
false
,
source
=
nil
)
jobs_for_stage_and_ref
(
stage
,
ref
,
tag
,
source
).
map
do
|
name
,
_
|
build_attributes
(
name
)
end
end
def
builds
@jobs
.
map
do
|
name
,
_
|
build_attributes
(
name
)
end
end
def
stage_seeds
(
pipeline
)
seeds
=
@stages
.
uniq
.
map
do
|
stage
|
builds
=
pipeline_stage_builds
(
stage
,
pipeline
)
Gitlab
::
Ci
::
Stage
::
Seed
.
new
(
pipeline
,
stage
,
builds
)
if
builds
.
any?
end
seeds
.
compact
end
def
build_attributes
(
name
)
job
=
@jobs
[
name
.
to_sym
]
||
{}
{
stage_idx:
@stages
.
index
(
job
[
:stage
]),
stage:
job
[
:stage
],
commands:
job
[
:commands
],
tag_list:
job
[
:tags
]
||
[],
name:
job
[
:name
].
to_s
,
allow_failure:
job
[
:ignore
],
when:
job
[
:when
]
||
'on_success'
,
environment:
job
[
:environment_name
],
coverage_regex:
job
[
:coverage
],
yaml_variables:
yaml_variables
(
name
),
options:
{
image:
job
[
:image
],
services:
job
[
:services
],
artifacts:
job
[
:artifacts
],
cache:
job
[
:cache
],
dependencies:
job
[
:dependencies
],
before_script:
job
[
:before_script
],
script:
job
[
:script
],
after_script:
job
[
:after_script
],
environment:
job
[
:environment
],
retry:
job
[
:retry
]
}.
compact
}
end
def
self
.
validation_message
(
content
)
return
'Please provide content of .gitlab-ci.yml'
if
content
.
blank?
begin
Ci
::
GitlabCiYamlProcessor
.
new
(
content
)
nil
rescue
ValidationError
,
Psych
::
SyntaxError
=>
e
e
.
message
end
end
private
def
pipeline_stage_builds
(
stage
,
pipeline
)
builds
=
builds_for_stage_and_ref
(
stage
,
pipeline
.
ref
,
pipeline
.
tag?
,
pipeline
.
source
)
builds
.
select
do
|
build
|
job
=
@jobs
[
build
.
fetch
(
:name
).
to_sym
]
has_kubernetes
=
pipeline
.
has_kubernetes_active?
only_kubernetes
=
job
.
dig
(
:only
,
:kubernetes
)
except_kubernetes
=
job
.
dig
(
:except
,
:kubernetes
)
[
!
only_kubernetes
&&
!
except_kubernetes
,
only_kubernetes
&&
has_kubernetes
,
except_kubernetes
&&
!
has_kubernetes
].
any?
end
end
def
jobs_for_ref
(
ref
,
tag
=
false
,
source
=
nil
)
@jobs
.
select
do
|
_
,
job
|
process?
(
job
.
dig
(
:only
,
:refs
),
job
.
dig
(
:except
,
:refs
),
ref
,
tag
,
source
)
end
end
def
jobs_for_stage_and_ref
(
stage
,
ref
,
tag
=
false
,
source
=
nil
)
jobs_for_ref
(
ref
,
tag
,
source
).
select
do
|
_
,
job
|
job
[
:stage
]
==
stage
end
end
def
initial_parsing
##
# Global config
#
@before_script
=
@ci_config
.
before_script
@image
=
@ci_config
.
image
@after_script
=
@ci_config
.
after_script
@services
=
@ci_config
.
services
@variables
=
@ci_config
.
variables
@stages
=
@ci_config
.
stages
@cache
=
@ci_config
.
cache
##
# Jobs
#
@jobs
=
@ci_config
.
jobs
@jobs
.
each
do
|
name
,
job
|
# logical validation for job
validate_job_stage!
(
name
,
job
)
validate_job_dependencies!
(
name
,
job
)
validate_job_environment!
(
name
,
job
)
end
end
def
yaml_variables
(
name
)
variables
=
(
@variables
||
{})
.
merge
(
job_variables
(
name
))
variables
.
map
do
|
key
,
value
|
{
key:
key
.
to_s
,
value:
value
,
public:
true
}
end
end
def
job_variables
(
name
)
job
=
@jobs
[
name
.
to_sym
]
return
{}
unless
job
job
[
:variables
]
||
{}
end
def
validate_job_stage!
(
name
,
job
)
return
unless
job
[
:stage
]
unless
job
[
:stage
].
is_a?
(
String
)
&&
job
[
:stage
].
in?
(
@stages
)
raise
ValidationError
,
"
#{
name
}
job: stage parameter should be
#{
@stages
.
join
(
", "
)
}
"
end
end
def
validate_job_dependencies!
(
name
,
job
)
return
unless
job
[
:dependencies
]
stage_index
=
@stages
.
index
(
job
[
:stage
])
job
[
:dependencies
].
each
do
|
dependency
|
raise
ValidationError
,
"
#{
name
}
job: undefined dependency:
#{
dependency
}
"
unless
@jobs
[
dependency
.
to_sym
]
unless
@stages
.
index
(
@jobs
[
dependency
.
to_sym
][
:stage
])
<
stage_index
raise
ValidationError
,
"
#{
name
}
job: dependency
#{
dependency
}
is not defined in prior stages"
end
end
end
def
validate_job_environment!
(
name
,
job
)
return
unless
job
[
:environment
]
return
unless
job
[
:environment
].
is_a?
(
Hash
)
environment
=
job
[
:environment
]
validate_on_stop_job!
(
name
,
environment
,
environment
[
:on_stop
])
end
def
validate_on_stop_job!
(
name
,
environment
,
on_stop
)
return
unless
on_stop
on_stop_job
=
@jobs
[
on_stop
.
to_sym
]
unless
on_stop_job
raise
ValidationError
,
"
#{
name
}
job: on_stop job
#{
on_stop
}
is not defined"
end
unless
on_stop_job
[
:environment
]
raise
ValidationError
,
"
#{
name
}
job: on_stop job
#{
on_stop
}
does not have environment defined"
end
unless
on_stop_job
[
:environment
][
:name
]
==
environment
[
:name
]
raise
ValidationError
,
"
#{
name
}
job: on_stop job
#{
on_stop
}
have different environment name"
end
unless
on_stop_job
[
:environment
][
:action
]
==
'stop'
raise
ValidationError
,
"
#{
name
}
job: on_stop job
#{
on_stop
}
needs to have action stop defined"
end
end
def
process?
(
only_params
,
except_params
,
ref
,
tag
,
source
)
if
only_params
.
present?
return
false
unless
matching?
(
only_params
,
ref
,
tag
,
source
)
end
if
except_params
.
present?
return
false
if
matching?
(
except_params
,
ref
,
tag
,
source
)
end
true
end
def
matching?
(
patterns
,
ref
,
tag
,
source
)
patterns
.
any?
do
|
pattern
|
pattern
,
path
=
pattern
.
split
(
'@'
,
2
)
matches_path?
(
path
)
&&
matches_pattern?
(
pattern
,
ref
,
tag
,
source
)
end
end
def
matches_path?
(
path
)
return
true
unless
path
path
==
self
.
path
end
def
matches_pattern?
(
pattern
,
ref
,
tag
,
source
)
return
true
if
tag
&&
pattern
==
'tags'
return
true
if
!
tag
&&
pattern
==
'branches'
return
true
if
source_to_pattern
(
source
)
==
pattern
if
pattern
.
first
==
"/"
&&
pattern
.
last
==
"/"
Regexp
.
new
(
pattern
[
1
...-
1
])
=~
ref
else
pattern
==
ref
end
end
def
source_to_pattern
(
source
)
if
%w[api external web]
.
include?
(
source
)
source
else
source
&
.
pluralize
end
end
end
end
lib/gitlab/ci/yaml_processor.rb
0 → 100644
View file @
c45ace89
module
Gitlab
module
Ci
class
YamlProcessor
ValidationError
=
Class
.
new
(
StandardError
)
include
Gitlab
::
Ci
::
Config
::
Entry
::
LegacyValidationHelpers
attr_reader
:path
,
:cache
,
:stages
,
:jobs
def
initialize
(
config
,
path
=
nil
)
@ci_config
=
Gitlab
::
Ci
::
Config
.
new
(
config
)
@config
=
@ci_config
.
to_hash
@path
=
path
unless
@ci_config
.
valid?
raise
ValidationError
,
@ci_config
.
errors
.
first
end
initial_parsing
rescue
Gitlab
::
Ci
::
Config
::
Loader
::
FormatError
=>
e
raise
ValidationError
,
e
.
message
end
def
builds_for_stage_and_ref
(
stage
,
ref
,
tag
=
false
,
source
=
nil
)
jobs_for_stage_and_ref
(
stage
,
ref
,
tag
,
source
).
map
do
|
name
,
_
|
build_attributes
(
name
)
end
end
def
builds
@jobs
.
map
do
|
name
,
_
|
build_attributes
(
name
)
end
end
def
stage_seeds
(
pipeline
)
seeds
=
@stages
.
uniq
.
map
do
|
stage
|
builds
=
pipeline_stage_builds
(
stage
,
pipeline
)
Gitlab
::
Ci
::
Stage
::
Seed
.
new
(
pipeline
,
stage
,
builds
)
if
builds
.
any?
end
seeds
.
compact
end
def
build_attributes
(
name
)
job
=
@jobs
[
name
.
to_sym
]
||
{}
{
stage_idx:
@stages
.
index
(
job
[
:stage
]),
stage:
job
[
:stage
],
commands:
job
[
:commands
],
tag_list:
job
[
:tags
]
||
[],
name:
job
[
:name
].
to_s
,
allow_failure:
job
[
:ignore
],
when:
job
[
:when
]
||
'on_success'
,
environment:
job
[
:environment_name
],
coverage_regex:
job
[
:coverage
],
yaml_variables:
yaml_variables
(
name
),
options:
{
image:
job
[
:image
],
services:
job
[
:services
],
artifacts:
job
[
:artifacts
],
cache:
job
[
:cache
],
dependencies:
job
[
:dependencies
],
before_script:
job
[
:before_script
],
script:
job
[
:script
],
after_script:
job
[
:after_script
],
environment:
job
[
:environment
],
retry:
job
[
:retry
]
}.
compact
}
end
def
self
.
validation_message
(
content
)
return
'Please provide content of .gitlab-ci.yml'
if
content
.
blank?
begin
Gitlab
::
Ci
::
YamlProcessor
.
new
(
content
)
nil
rescue
ValidationError
,
Psych
::
SyntaxError
=>
e
e
.
message
end
end
private
def
pipeline_stage_builds
(
stage
,
pipeline
)
builds
=
builds_for_stage_and_ref
(
stage
,
pipeline
.
ref
,
pipeline
.
tag?
,
pipeline
.
source
)
builds
.
select
do
|
build
|
job
=
@jobs
[
build
.
fetch
(
:name
).
to_sym
]
has_kubernetes
=
pipeline
.
has_kubernetes_active?
only_kubernetes
=
job
.
dig
(
:only
,
:kubernetes
)
except_kubernetes
=
job
.
dig
(
:except
,
:kubernetes
)
[
!
only_kubernetes
&&
!
except_kubernetes
,
only_kubernetes
&&
has_kubernetes
,
except_kubernetes
&&
!
has_kubernetes
].
any?
end
end
def
jobs_for_ref
(
ref
,
tag
=
false
,
source
=
nil
)
@jobs
.
select
do
|
_
,
job
|
process?
(
job
.
dig
(
:only
,
:refs
),
job
.
dig
(
:except
,
:refs
),
ref
,
tag
,
source
)
end
end
def
jobs_for_stage_and_ref
(
stage
,
ref
,
tag
=
false
,
source
=
nil
)
jobs_for_ref
(
ref
,
tag
,
source
).
select
do
|
_
,
job
|
job
[
:stage
]
==
stage
end
end
def
initial_parsing
##
# Global config
#
@before_script
=
@ci_config
.
before_script
@image
=
@ci_config
.
image
@after_script
=
@ci_config
.
after_script
@services
=
@ci_config
.
services
@variables
=
@ci_config
.
variables
@stages
=
@ci_config
.
stages
@cache
=
@ci_config
.
cache
##
# Jobs
#
@jobs
=
@ci_config
.
jobs
@jobs
.
each
do
|
name
,
job
|
# logical validation for job
validate_job_stage!
(
name
,
job
)
validate_job_dependencies!
(
name
,
job
)
validate_job_environment!
(
name
,
job
)
end
end
def
yaml_variables
(
name
)
variables
=
(
@variables
||
{})
.
merge
(
job_variables
(
name
))
variables
.
map
do
|
key
,
value
|
{
key:
key
.
to_s
,
value:
value
,
public:
true
}
end
end
def
job_variables
(
name
)
job
=
@jobs
[
name
.
to_sym
]
return
{}
unless
job
job
[
:variables
]
||
{}
end
def
validate_job_stage!
(
name
,
job
)
return
unless
job
[
:stage
]
unless
job
[
:stage
].
is_a?
(
String
)
&&
job
[
:stage
].
in?
(
@stages
)
raise
ValidationError
,
"
#{
name
}
job: stage parameter should be
#{
@stages
.
join
(
", "
)
}
"
end
end
def
validate_job_dependencies!
(
name
,
job
)
return
unless
job
[
:dependencies
]
stage_index
=
@stages
.
index
(
job
[
:stage
])
job
[
:dependencies
].
each
do
|
dependency
|
raise
ValidationError
,
"
#{
name
}
job: undefined dependency:
#{
dependency
}
"
unless
@jobs
[
dependency
.
to_sym
]
unless
@stages
.
index
(
@jobs
[
dependency
.
to_sym
][
:stage
])
<
stage_index
raise
ValidationError
,
"
#{
name
}
job: dependency
#{
dependency
}
is not defined in prior stages"
end
end
end
def
validate_job_environment!
(
name
,
job
)
return
unless
job
[
:environment
]
return
unless
job
[
:environment
].
is_a?
(
Hash
)
environment
=
job
[
:environment
]
validate_on_stop_job!
(
name
,
environment
,
environment
[
:on_stop
])
end
def
validate_on_stop_job!
(
name
,
environment
,
on_stop
)
return
unless
on_stop
on_stop_job
=
@jobs
[
on_stop
.
to_sym
]
unless
on_stop_job
raise
ValidationError
,
"
#{
name
}
job: on_stop job
#{
on_stop
}
is not defined"
end
unless
on_stop_job
[
:environment
]
raise
ValidationError
,
"
#{
name
}
job: on_stop job
#{
on_stop
}
does not have environment defined"
end
unless
on_stop_job
[
:environment
][
:name
]
==
environment
[
:name
]
raise
ValidationError
,
"
#{
name
}
job: on_stop job
#{
on_stop
}
have different environment name"
end
unless
on_stop_job
[
:environment
][
:action
]
==
'stop'
raise
ValidationError
,
"
#{
name
}
job: on_stop job
#{
on_stop
}
needs to have action stop defined"
end
end
def
process?
(
only_params
,
except_params
,
ref
,
tag
,
source
)
if
only_params
.
present?
return
false
unless
matching?
(
only_params
,
ref
,
tag
,
source
)
end
if
except_params
.
present?
return
false
if
matching?
(
except_params
,
ref
,
tag
,
source
)
end
true
end
def
matching?
(
patterns
,
ref
,
tag
,
source
)
patterns
.
any?
do
|
pattern
|
pattern
,
path
=
pattern
.
split
(
'@'
,
2
)
matches_path?
(
path
)
&&
matches_pattern?
(
pattern
,
ref
,
tag
,
source
)
end
end
def
matches_path?
(
path
)
return
true
unless
path
path
==
self
.
path
end
def
matches_pattern?
(
pattern
,
ref
,
tag
,
source
)
return
true
if
tag
&&
pattern
==
'tags'
return
true
if
!
tag
&&
pattern
==
'branches'
return
true
if
source_to_pattern
(
source
)
==
pattern
if
pattern
.
first
==
"/"
&&
pattern
.
last
==
"/"
Regexp
.
new
(
pattern
[
1
...-
1
])
=~
ref
else
pattern
==
ref
end
end
def
source_to_pattern
(
source
)
if
%w[api external web]
.
include?
(
source
)
source
else
source
&
.
pluralize
end
end
end
end
end
spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
deleted
100644 → 0
View file @
c295d336
require
'spec_helper'
module
Ci
describe
GitlabCiYamlProcessor
,
:lib
do
subject
{
described_class
.
new
(
config
,
path
)
}
let
(
:path
)
{
'path'
}
describe
'our current .gitlab-ci.yml'
do
let
(
:config
)
{
File
.
read
(
"
#{
Rails
.
root
}
/.gitlab-ci.yml"
)
}
it
'is valid'
do
error_message
=
described_class
.
validation_message
(
config
)
expect
(
error_message
).
to
be_nil
end
end
describe
'#build_attributes'
do
subject
{
described_class
.
new
(
config
,
path
).
build_attributes
(
:rspec
)
}
describe
'coverage entry'
do
describe
'code coverage regexp'
do
let
(
:config
)
do
YAML
.
dump
(
rspec:
{
script:
'rspec'
,
coverage:
'/Code coverage: \d+\.\d+/'
})
end
it
'includes coverage regexp in build attributes'
do
expect
(
subject
)
.
to
include
(
coverage_regex:
'Code coverage: \d+\.\d+'
)
end
end
end
describe
'retry entry'
do
context
'when retry count is specified'
do
let
(
:config
)
do
YAML
.
dump
(
rspec:
{
script:
'rspec'
,
retry:
1
})
end
it
'includes retry count in build options attribute'
do
expect
(
subject
[
:options
]).
to
include
(
retry:
1
)
end
end
context
'when retry count is not specified'
do
let
(
:config
)
do
YAML
.
dump
(
rspec:
{
script:
'rspec'
})
end
it
'does not persist retry count in the database'
do
expect
(
subject
[
:options
]).
not_to
have_key
(
:retry
)
end
end
end
describe
'allow failure entry'
do
context
'when job is a manual action'
do
context
'when allow_failure is defined'
do
let
(
:config
)
do
YAML
.
dump
(
rspec:
{
script:
'rspec'
,
when:
'manual'
,
allow_failure:
false
})
end
it
'is not allowed to fail'
do
expect
(
subject
[
:allow_failure
]).
to
be
false
end
end
context
'when allow_failure is not defined'
do
let
(
:config
)
do
YAML
.
dump
(
rspec:
{
script:
'rspec'
,
when:
'manual'
})
end
it
'is allowed to fail'
do
expect
(
subject
[
:allow_failure
]).
to
be
true
end
end
end
context
'when job is not a manual action'
do
context
'when allow_failure is defined'
do
let
(
:config
)
do
YAML
.
dump
(
rspec:
{
script:
'rspec'
,
allow_failure:
false
})
end
it
'is not allowed to fail'
do
expect
(
subject
[
:allow_failure
]).
to
be
false
end
end
context
'when allow_failure is not defined'
do
let
(
:config
)
do
YAML
.
dump
(
rspec:
{
script:
'rspec'
})
end
it
'is not allowed to fail'
do
expect
(
subject
[
:allow_failure
]).
to
be
false
end
end
end
end
end
describe
'#stage_seeds'
do
context
'when no refs policy is specified'
do
let
(
:config
)
do
YAML
.
dump
(
production:
{
stage:
'deploy'
,
script:
'cap prod'
},
rspec:
{
stage:
'test'
,
script:
'rspec'
},
spinach:
{
stage:
'test'
,
script:
'spinach'
})
end
let
(
:pipeline
)
{
create
(
:ci_empty_pipeline
)
}
it
'correctly fabricates a stage seeds object'
do
seeds
=
subject
.
stage_seeds
(
pipeline
)
expect
(
seeds
.
size
).
to
eq
2
expect
(
seeds
.
first
.
stage
[
:name
]).
to
eq
'test'
expect
(
seeds
.
second
.
stage
[
:name
]).
to
eq
'deploy'
expect
(
seeds
.
first
.
builds
.
dig
(
0
,
:name
)).
to
eq
'rspec'
expect
(
seeds
.
first
.
builds
.
dig
(
1
,
:name
)).
to
eq
'spinach'
expect
(
seeds
.
second
.
builds
.
dig
(
0
,
:name
)).
to
eq
'production'
end
end
context
'when refs policy is specified'
do
let
(
:config
)
do
YAML
.
dump
(
production:
{
stage:
'deploy'
,
script:
'cap prod'
,
only:
[
'master'
]
},
spinach:
{
stage:
'test'
,
script:
'spinach'
,
only:
[
'tags'
]
})
end
let
(
:pipeline
)
do
create
(
:ci_empty_pipeline
,
ref:
'feature'
,
tag:
true
)
end
it
'returns stage seeds only assigned to master to master'
do
seeds
=
subject
.
stage_seeds
(
pipeline
)
expect
(
seeds
.
size
).
to
eq
1
expect
(
seeds
.
first
.
stage
[
:name
]).
to
eq
'test'
expect
(
seeds
.
first
.
builds
.
dig
(
0
,
:name
)).
to
eq
'spinach'
end
end
context
'when source policy is specified'
do
let
(
:config
)
do
YAML
.
dump
(
production:
{
stage:
'deploy'
,
script:
'cap prod'
,
only:
[
'triggers'
]
},
spinach:
{
stage:
'test'
,
script:
'spinach'
,
only:
[
'schedules'
]
})
end
let
(
:pipeline
)
do
create
(
:ci_empty_pipeline
,
source: :schedule
)
end
it
'returns stage seeds only assigned to schedules'
do
seeds
=
subject
.
stage_seeds
(
pipeline
)
expect
(
seeds
.
size
).
to
eq
1
expect
(
seeds
.
first
.
stage
[
:name
]).
to
eq
'test'
expect
(
seeds
.
first
.
builds
.
dig
(
0
,
:name
)).
to
eq
'spinach'
end
end
context
'when kubernetes policy is specified'
do
let
(
:pipeline
)
{
create
(
:ci_empty_pipeline
)
}
let
(
:config
)
do
YAML
.
dump
(
spinach:
{
stage:
'test'
,
script:
'spinach'
},
production:
{
stage:
'deploy'
,
script:
'cap'
,
only:
{
kubernetes:
'active'
}
}
)
end
context
'when kubernetes is active'
do
let
(
:project
)
{
create
(
:kubernetes_project
)
}
let
(
:pipeline
)
{
create
(
:ci_empty_pipeline
,
project:
project
)
}
it
'returns seeds for kubernetes dependent job'
do
seeds
=
subject
.
stage_seeds
(
pipeline
)
expect
(
seeds
.
size
).
to
eq
2
expect
(
seeds
.
first
.
builds
.
dig
(
0
,
:name
)).
to
eq
'spinach'
expect
(
seeds
.
second
.
builds
.
dig
(
0
,
:name
)).
to
eq
'production'
end
end
context
'when kubernetes is not active'
do
it
'does not return seeds for kubernetes dependent job'
do
seeds
=
subject
.
stage_seeds
(
pipeline
)
expect
(
seeds
.
size
).
to
eq
1
expect
(
seeds
.
first
.
builds
.
dig
(
0
,
:name
)).
to
eq
'spinach'
end
end
end
end
describe
"#builds_for_stage_and_ref"
do
let
(
:type
)
{
'test'
}
it
"returns builds if no branch specified"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"master"
).
size
).
to
eq
(
1
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"master"
).
first
).
to
eq
({
stage:
"test"
,
stage_idx:
1
,
name:
"rspec"
,
commands:
"pwd
\n
rspec"
,
coverage_regex:
nil
,
tag_list:
[],
options:
{
before_script:
[
"pwd"
],
script:
[
"rspec"
]
},
allow_failure:
false
,
when:
"on_success"
,
environment:
nil
,
yaml_variables:
[]
})
end
describe
'only'
do
it
"does not return builds if only has another branch"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
only:
[
"deploy"
]
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"master"
).
size
).
to
eq
(
0
)
end
it
"does not return builds if only has regexp with another branch"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
only:
[
"/^deploy$/"
]
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"master"
).
size
).
to
eq
(
0
)
end
it
"returns builds if only has specified this branch"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
only:
[
"master"
]
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"master"
).
size
).
to
eq
(
1
)
end
it
"returns builds if only has a list of branches including specified"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
only:
%w(master deploy)
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
).
size
).
to
eq
(
1
)
end
it
"returns builds if only has a branches keyword specified"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
only:
[
"branches"
]
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
).
size
).
to
eq
(
1
)
end
it
"does not return builds if only has a tags keyword"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
only:
[
"tags"
]
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
).
size
).
to
eq
(
0
)
end
it
"returns builds if only has special keywords specified and source matches"
do
possibilities
=
[{
keyword:
'pushes'
,
source:
'push'
},
{
keyword:
'web'
,
source:
'web'
},
{
keyword:
'triggers'
,
source:
'trigger'
},
{
keyword:
'schedules'
,
source:
'schedule'
},
{
keyword:
'api'
,
source:
'api'
},
{
keyword:
'external'
,
source:
'external'
}]
possibilities
.
each
do
|
possibility
|
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
only:
[
possibility
[
:keyword
]]
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
,
false
,
possibility
[
:source
]).
size
).
to
eq
(
1
)
end
end
it
"does not return builds if only has special keywords specified and source doesn't match"
do
possibilities
=
[{
keyword:
'pushes'
,
source:
'web'
},
{
keyword:
'web'
,
source:
'push'
},
{
keyword:
'triggers'
,
source:
'schedule'
},
{
keyword:
'schedules'
,
source:
'external'
},
{
keyword:
'api'
,
source:
'trigger'
},
{
keyword:
'external'
,
source:
'api'
}]
possibilities
.
each
do
|
possibility
|
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
only:
[
possibility
[
:keyword
]]
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
,
false
,
possibility
[
:source
]).
size
).
to
eq
(
0
)
end
end
it
"returns builds if only has current repository path"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
only:
[
"branches@path"
]
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
).
size
).
to
eq
(
1
)
end
it
"does not return builds if only has different repository path"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
only:
[
"branches@fork"
]
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
).
size
).
to
eq
(
0
)
end
it
"returns build only for specified type"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
"test"
,
only:
%w(master deploy)
},
staging:
{
script:
"deploy"
,
type:
"deploy"
,
only:
%w(master deploy)
},
production:
{
script:
"deploy"
,
type:
"deploy"
,
only:
[
"master@path"
,
"deploy"
]
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
'fork'
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"deploy"
,
"deploy"
).
size
).
to
eq
(
2
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"deploy"
).
size
).
to
eq
(
1
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"deploy"
,
"master"
).
size
).
to
eq
(
1
)
end
context
'for invalid value'
do
let
(
:config
)
{
{
rspec:
{
script:
"rspec"
,
type:
"test"
,
only:
only
}
}
}
let
(
:processor
)
{
GitlabCiYamlProcessor
.
new
(
YAML
.
dump
(
config
))
}
context
'when it is integer'
do
let
(
:only
)
{
1
}
it
do
expect
{
processor
}.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
'jobs:rspec:only has to be either an array of conditions or a hash'
)
end
end
context
'when it is an array of integers'
do
let
(
:only
)
{
[
1
,
1
]
}
it
do
expect
{
processor
}.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
'jobs:rspec:only config should be an array of strings or regexps'
)
end
end
context
'when it is invalid regex'
do
let
(
:only
)
{
[
"/*invalid/"
]
}
it
do
expect
{
processor
}.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
'jobs:rspec:only config should be an array of strings or regexps'
)
end
end
end
end
describe
'except'
do
it
"returns builds if except has another branch"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
except:
[
"deploy"
]
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"master"
).
size
).
to
eq
(
1
)
end
it
"returns builds if except has regexp with another branch"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
except:
[
"/^deploy$/"
]
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"master"
).
size
).
to
eq
(
1
)
end
it
"does not return builds if except has specified this branch"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
except:
[
"master"
]
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"master"
).
size
).
to
eq
(
0
)
end
it
"does not return builds if except has a list of branches including specified"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
except:
%w(master deploy)
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
).
size
).
to
eq
(
0
)
end
it
"does not return builds if except has a branches keyword specified"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
except:
[
"branches"
]
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
).
size
).
to
eq
(
0
)
end
it
"returns builds if except has a tags keyword"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
except:
[
"tags"
]
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
).
size
).
to
eq
(
1
)
end
it
"does not return builds if except has special keywords specified and source matches"
do
possibilities
=
[{
keyword:
'pushes'
,
source:
'push'
},
{
keyword:
'web'
,
source:
'web'
},
{
keyword:
'triggers'
,
source:
'trigger'
},
{
keyword:
'schedules'
,
source:
'schedule'
},
{
keyword:
'api'
,
source:
'api'
},
{
keyword:
'external'
,
source:
'external'
}]
possibilities
.
each
do
|
possibility
|
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
except:
[
possibility
[
:keyword
]]
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
,
false
,
possibility
[
:source
]).
size
).
to
eq
(
0
)
end
end
it
"returns builds if except has special keywords specified and source doesn't match"
do
possibilities
=
[{
keyword:
'pushes'
,
source:
'web'
},
{
keyword:
'web'
,
source:
'push'
},
{
keyword:
'triggers'
,
source:
'schedule'
},
{
keyword:
'schedules'
,
source:
'external'
},
{
keyword:
'api'
,
source:
'trigger'
},
{
keyword:
'external'
,
source:
'api'
}]
possibilities
.
each
do
|
possibility
|
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
except:
[
possibility
[
:keyword
]]
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
,
false
,
possibility
[
:source
]).
size
).
to
eq
(
1
)
end
end
it
"does not return builds if except has current repository path"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
except:
[
"branches@path"
]
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
).
size
).
to
eq
(
0
)
end
it
"returns builds if except has different repository path"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
except:
[
"branches@fork"
]
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
).
size
).
to
eq
(
1
)
end
it
"returns build except specified type"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
"test"
,
except:
[
"master"
,
"deploy"
,
"test@fork"
]
},
staging:
{
script:
"deploy"
,
type:
"deploy"
,
except:
[
"master"
]
},
production:
{
script:
"deploy"
,
type:
"deploy"
,
except:
[
"master@fork"
]
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
'fork'
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"deploy"
,
"deploy"
).
size
).
to
eq
(
2
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"test"
).
size
).
to
eq
(
0
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"deploy"
,
"master"
).
size
).
to
eq
(
0
)
end
context
'for invalid value'
do
let
(
:config
)
{
{
rspec:
{
script:
"rspec"
,
except:
except
}
}
}
let
(
:processor
)
{
GitlabCiYamlProcessor
.
new
(
YAML
.
dump
(
config
))
}
context
'when it is integer'
do
let
(
:except
)
{
1
}
it
do
expect
{
processor
}.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
'jobs:rspec:except has to be either an array of conditions or a hash'
)
end
end
context
'when it is an array of integers'
do
let
(
:except
)
{
[
1
,
1
]
}
it
do
expect
{
processor
}.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
'jobs:rspec:except config should be an array of strings or regexps'
)
end
end
context
'when it is invalid regex'
do
let
(
:except
)
{
[
"/*invalid/"
]
}
it
do
expect
{
processor
}.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
'jobs:rspec:except config should be an array of strings or regexps'
)
end
end
end
end
end
describe
"Scripts handling"
do
let
(
:config_data
)
{
YAML
.
dump
(
config
)
}
let
(
:config_processor
)
{
GitlabCiYamlProcessor
.
new
(
config_data
,
path
)
}
subject
{
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
first
}
describe
"before_script"
do
context
"in global context"
do
let
(
:config
)
do
{
before_script:
[
"global script"
],
test:
{
script:
[
"script"
]
}
}
end
it
"return commands with scripts concencaced"
do
expect
(
subject
[
:commands
]).
to
eq
(
"global script
\n
script"
)
end
end
context
"overwritten in local context"
do
let
(
:config
)
do
{
before_script:
[
"global script"
],
test:
{
before_script:
[
"local script"
],
script:
[
"script"
]
}
}
end
it
"return commands with scripts concencaced"
do
expect
(
subject
[
:commands
]).
to
eq
(
"local script
\n
script"
)
end
end
end
describe
"script"
do
let
(
:config
)
do
{
test:
{
script:
[
"script"
]
}
}
end
it
"return commands with scripts concencaced"
do
expect
(
subject
[
:commands
]).
to
eq
(
"script"
)
end
end
describe
"after_script"
do
context
"in global context"
do
let
(
:config
)
do
{
after_script:
[
"after_script"
],
test:
{
script:
[
"script"
]
}
}
end
it
"return after_script in options"
do
expect
(
subject
[
:options
][
:after_script
]).
to
eq
([
"after_script"
])
end
end
context
"overwritten in local context"
do
let
(
:config
)
do
{
after_script:
[
"local after_script"
],
test:
{
after_script:
[
"local after_script"
],
script:
[
"script"
]
}
}
end
it
"return after_script in options"
do
expect
(
subject
[
:options
][
:after_script
]).
to
eq
([
"local after_script"
])
end
end
end
end
describe
"Image and service handling"
do
context
"when extended docker configuration is used"
do
it
"returns image and service when defined"
do
config
=
YAML
.
dump
({
image:
{
name:
"ruby:2.1"
,
entrypoint:
[
"/usr/local/bin/init"
,
"run"
]
},
services:
[
"mysql"
,
{
name:
"docker:dind"
,
alias:
"docker"
,
entrypoint:
[
"/usr/local/bin/init"
,
"run"
],
command:
[
"/usr/local/bin/init"
,
"run"
]
}],
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
size
).
to
eq
(
1
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
first
).
to
eq
({
stage:
"test"
,
stage_idx:
1
,
name:
"rspec"
,
commands:
"pwd
\n
rspec"
,
coverage_regex:
nil
,
tag_list:
[],
options:
{
before_script:
[
"pwd"
],
script:
[
"rspec"
],
image:
{
name:
"ruby:2.1"
,
entrypoint:
[
"/usr/local/bin/init"
,
"run"
]
},
services:
[{
name:
"mysql"
},
{
name:
"docker:dind"
,
alias:
"docker"
,
entrypoint:
[
"/usr/local/bin/init"
,
"run"
],
command:
[
"/usr/local/bin/init"
,
"run"
]
}]
},
allow_failure:
false
,
when:
"on_success"
,
environment:
nil
,
yaml_variables:
[]
})
end
it
"returns image and service when overridden for job"
do
config
=
YAML
.
dump
({
image:
"ruby:2.1"
,
services:
[
"mysql"
],
before_script:
[
"pwd"
],
rspec:
{
image:
{
name:
"ruby:2.5"
,
entrypoint:
[
"/usr/local/bin/init"
,
"run"
]
},
services:
[{
name:
"postgresql"
,
alias:
"db-pg"
,
entrypoint:
[
"/usr/local/bin/init"
,
"run"
],
command:
[
"/usr/local/bin/init"
,
"run"
]
},
"docker:dind"
],
script:
"rspec"
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
size
).
to
eq
(
1
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
first
).
to
eq
({
stage:
"test"
,
stage_idx:
1
,
name:
"rspec"
,
commands:
"pwd
\n
rspec"
,
coverage_regex:
nil
,
tag_list:
[],
options:
{
before_script:
[
"pwd"
],
script:
[
"rspec"
],
image:
{
name:
"ruby:2.5"
,
entrypoint:
[
"/usr/local/bin/init"
,
"run"
]
},
services:
[{
name:
"postgresql"
,
alias:
"db-pg"
,
entrypoint:
[
"/usr/local/bin/init"
,
"run"
],
command:
[
"/usr/local/bin/init"
,
"run"
]
},
{
name:
"docker:dind"
}]
},
allow_failure:
false
,
when:
"on_success"
,
environment:
nil
,
yaml_variables:
[]
})
end
end
context
"when etended docker configuration is not used"
do
it
"returns image and service when defined"
do
config
=
YAML
.
dump
({
image:
"ruby:2.1"
,
services:
[
"mysql"
,
"docker:dind"
],
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
size
).
to
eq
(
1
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
first
).
to
eq
({
stage:
"test"
,
stage_idx:
1
,
name:
"rspec"
,
commands:
"pwd
\n
rspec"
,
coverage_regex:
nil
,
tag_list:
[],
options:
{
before_script:
[
"pwd"
],
script:
[
"rspec"
],
image:
{
name:
"ruby:2.1"
},
services:
[{
name:
"mysql"
},
{
name:
"docker:dind"
}]
},
allow_failure:
false
,
when:
"on_success"
,
environment:
nil
,
yaml_variables:
[]
})
end
it
"returns image and service when overridden for job"
do
config
=
YAML
.
dump
({
image:
"ruby:2.1"
,
services:
[
"mysql"
],
before_script:
[
"pwd"
],
rspec:
{
image:
"ruby:2.5"
,
services:
[
"postgresql"
,
"docker:dind"
],
script:
"rspec"
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
size
).
to
eq
(
1
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
first
).
to
eq
({
stage:
"test"
,
stage_idx:
1
,
name:
"rspec"
,
commands:
"pwd
\n
rspec"
,
coverage_regex:
nil
,
tag_list:
[],
options:
{
before_script:
[
"pwd"
],
script:
[
"rspec"
],
image:
{
name:
"ruby:2.5"
},
services:
[{
name:
"postgresql"
},
{
name:
"docker:dind"
}]
},
allow_failure:
false
,
when:
"on_success"
,
environment:
nil
,
yaml_variables:
[]
})
end
end
end
describe
'Variables'
do
let
(
:config_processor
)
{
GitlabCiYamlProcessor
.
new
(
YAML
.
dump
(
config
),
path
)
}
subject
{
config_processor
.
builds
.
first
[
:yaml_variables
]
}
context
'when global variables are defined'
do
let
(
:variables
)
do
{
'VAR1'
=>
'value1'
,
'VAR2'
=>
'value2'
}
end
let
(
:config
)
do
{
variables:
variables
,
before_script:
[
'pwd'
],
rspec:
{
script:
'rspec'
}
}
end
it
'returns global variables'
do
expect
(
subject
).
to
contain_exactly
(
{
key:
'VAR1'
,
value:
'value1'
,
public:
true
},
{
key:
'VAR2'
,
value:
'value2'
,
public:
true
}
)
end
end
context
'when job and global variables are defined'
do
let
(
:global_variables
)
do
{
'VAR1'
=>
'global1'
,
'VAR3'
=>
'global3'
}
end
let
(
:job_variables
)
do
{
'VAR1'
=>
'value1'
,
'VAR2'
=>
'value2'
}
end
let
(
:config
)
do
{
before_script:
[
'pwd'
],
variables:
global_variables
,
rspec:
{
script:
'rspec'
,
variables:
job_variables
}
}
end
it
'returns all unique variables'
do
expect
(
subject
).
to
contain_exactly
(
{
key:
'VAR3'
,
value:
'global3'
,
public:
true
},
{
key:
'VAR1'
,
value:
'value1'
,
public:
true
},
{
key:
'VAR2'
,
value:
'value2'
,
public:
true
}
)
end
end
context
'when job variables are defined'
do
let
(
:config
)
do
{
before_script:
[
'pwd'
],
rspec:
{
script:
'rspec'
,
variables:
variables
}
}
end
context
'when syntax is correct'
do
let
(
:variables
)
do
{
'VAR1'
=>
'value1'
,
'VAR2'
=>
'value2'
}
end
it
'returns job variables'
do
expect
(
subject
).
to
contain_exactly
(
{
key:
'VAR1'
,
value:
'value1'
,
public:
true
},
{
key:
'VAR2'
,
value:
'value2'
,
public:
true
}
)
end
end
context
'when syntax is incorrect'
do
context
'when variables defined but invalid'
do
let
(
:variables
)
do
%w(VAR1 value1 VAR2 value2)
end
it
'raises error'
do
expect
{
subject
}
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
/jobs:rspec:variables config should be a hash of key value pairs/
)
end
end
context
'when variables key defined but value not specified'
do
let
(
:variables
)
do
nil
end
it
'returns empty array'
do
##
# When variables config is empty, we assume this is a valid
# configuration, see issue #18775
#
expect
(
subject
).
to
be_an_instance_of
(
Array
)
expect
(
subject
).
to
be_empty
end
end
end
end
context
'when job variables are not defined'
do
let
(
:config
)
do
{
before_script:
[
'pwd'
],
rspec:
{
script:
'rspec'
}
}
end
it
'returns empty array'
do
expect
(
subject
).
to
be_an_instance_of
(
Array
)
expect
(
subject
).
to
be_empty
end
end
end
describe
"When"
do
%w(on_success on_failure always)
.
each
do
|
when_state
|
it
"returns
#{
when_state
}
when defined"
do
config
=
YAML
.
dump
({
rspec:
{
script:
"rspec"
,
when:
when_state
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
builds
=
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
)
expect
(
builds
.
size
).
to
eq
(
1
)
expect
(
builds
.
first
[
:when
]).
to
eq
(
when_state
)
end
end
end
describe
'cache'
do
context
'when cache definition has unknown keys'
do
it
'raises relevant validation error'
do
config
=
YAML
.
dump
(
{
cache:
{
untracked:
true
,
invalid:
'key'
},
rspec:
{
script:
'rspec'
}
})
expect
{
GitlabCiYamlProcessor
.
new
(
config
)
}.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
'cache config contains unknown keys: invalid'
)
end
end
it
"returns cache when defined globally"
do
config
=
YAML
.
dump
({
cache:
{
paths:
[
"logs/"
,
"binaries/"
],
untracked:
true
,
key:
'key'
},
rspec:
{
script:
"rspec"
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
size
).
to
eq
(
1
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
first
[
:options
][
:cache
]).
to
eq
(
paths:
[
"logs/"
,
"binaries/"
],
untracked:
true
,
key:
'key'
,
policy:
'pull-push'
)
end
it
"returns cache when defined in a job"
do
config
=
YAML
.
dump
({
rspec:
{
cache:
{
paths:
[
"logs/"
,
"binaries/"
],
untracked:
true
,
key:
'key'
},
script:
"rspec"
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
size
).
to
eq
(
1
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
first
[
:options
][
:cache
]).
to
eq
(
paths:
[
"logs/"
,
"binaries/"
],
untracked:
true
,
key:
'key'
,
policy:
'pull-push'
)
end
it
"overwrite cache when defined for a job and globally"
do
config
=
YAML
.
dump
({
cache:
{
paths:
[
"logs/"
,
"binaries/"
],
untracked:
true
,
key:
'global'
},
rspec:
{
script:
"rspec"
,
cache:
{
paths:
[
"test/"
],
untracked:
false
,
key:
'local'
}
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
size
).
to
eq
(
1
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
first
[
:options
][
:cache
]).
to
eq
(
paths:
[
"test/"
],
untracked:
false
,
key:
'local'
,
policy:
'pull-push'
)
end
end
describe
"Artifacts"
do
it
"returns artifacts when defined"
do
config
=
YAML
.
dump
({
image:
"ruby:2.1"
,
services:
[
"mysql"
],
before_script:
[
"pwd"
],
rspec:
{
artifacts:
{
paths:
[
"logs/"
,
"binaries/"
],
untracked:
true
,
name:
"custom_name"
,
expire_in:
"7d"
},
script:
"rspec"
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
size
).
to
eq
(
1
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
first
).
to
eq
({
stage:
"test"
,
stage_idx:
1
,
name:
"rspec"
,
commands:
"pwd
\n
rspec"
,
coverage_regex:
nil
,
tag_list:
[],
options:
{
before_script:
[
"pwd"
],
script:
[
"rspec"
],
image:
{
name:
"ruby:2.1"
},
services:
[{
name:
"mysql"
}],
artifacts:
{
name:
"custom_name"
,
paths:
[
"logs/"
,
"binaries/"
],
untracked:
true
,
expire_in:
"7d"
}
},
when:
"on_success"
,
allow_failure:
false
,
environment:
nil
,
yaml_variables:
[]
})
end
%w[on_success on_failure always]
.
each
do
|
when_state
|
it
"returns artifacts for when
#{
when_state
}
defined"
do
config
=
YAML
.
dump
({
rspec:
{
script:
"rspec"
,
artifacts:
{
paths:
[
"logs/"
,
"binaries/"
],
when:
when_state
}
}
})
config_processor
=
GitlabCiYamlProcessor
.
new
(
config
,
path
)
builds
=
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
)
expect
(
builds
.
size
).
to
eq
(
1
)
expect
(
builds
.
first
[
:options
][
:artifacts
][
:when
]).
to
eq
(
when_state
)
end
end
end
describe
'#environment'
do
let
(
:config
)
do
{
deploy_to_production:
{
stage:
'deploy'
,
script:
'test'
,
environment:
environment
}
}
end
let
(
:processor
)
{
GitlabCiYamlProcessor
.
new
(
YAML
.
dump
(
config
))
}
let
(
:builds
)
{
processor
.
builds_for_stage_and_ref
(
'deploy'
,
'master'
)
}
context
'when a production environment is specified'
do
let
(
:environment
)
{
'production'
}
it
'does return production'
do
expect
(
builds
.
size
).
to
eq
(
1
)
expect
(
builds
.
first
[
:environment
]).
to
eq
(
environment
)
expect
(
builds
.
first
[
:options
]).
to
include
(
environment:
{
name:
environment
,
action:
"start"
})
end
end
context
'when hash is specified'
do
let
(
:environment
)
do
{
name:
'production'
,
url:
'http://production.gitlab.com'
}
end
it
'does return production and URL'
do
expect
(
builds
.
size
).
to
eq
(
1
)
expect
(
builds
.
first
[
:environment
]).
to
eq
(
environment
[
:name
])
expect
(
builds
.
first
[
:options
]).
to
include
(
environment:
environment
)
end
context
'the url has a port as variable'
do
let
(
:environment
)
do
{
name:
'production'
,
url:
'http://production.gitlab.com:$PORT'
}
end
it
'allows a variable for the port'
do
expect
(
builds
.
size
).
to
eq
(
1
)
expect
(
builds
.
first
[
:environment
]).
to
eq
(
environment
[
:name
])
expect
(
builds
.
first
[
:options
]).
to
include
(
environment:
environment
)
end
end
end
context
'when no environment is specified'
do
let
(
:environment
)
{
nil
}
it
'does return nil environment'
do
expect
(
builds
.
size
).
to
eq
(
1
)
expect
(
builds
.
first
[
:environment
]).
to
be_nil
end
end
context
'is not a string'
do
let
(
:environment
)
{
1
}
it
'raises error'
do
expect
{
builds
}.
to
raise_error
(
'jobs:deploy_to_production:environment config should be a hash or a string'
)
end
end
context
'is not a valid string'
do
let
(
:environment
)
{
'production:staging'
}
it
'raises error'
do
expect
{
builds
}.
to
raise_error
(
"jobs:deploy_to_production:environment name
#{
Gitlab
::
Regex
.
environment_name_regex_message
}
"
)
end
end
context
'when on_stop is specified'
do
let
(
:review
)
{
{
stage:
'deploy'
,
script:
'test'
,
environment:
{
name:
'review'
,
on_stop:
'close_review'
}
}
}
let
(
:config
)
{
{
review:
review
,
close_review:
close_review
}.
compact
}
context
'with matching job'
do
let
(
:close_review
)
{
{
stage:
'deploy'
,
script:
'test'
,
environment:
{
name:
'review'
,
action:
'stop'
}
}
}
it
'does return a list of builds'
do
expect
(
builds
.
size
).
to
eq
(
2
)
expect
(
builds
.
first
[
:environment
]).
to
eq
(
'review'
)
end
end
context
'without matching job'
do
let
(
:close_review
)
{
nil
}
it
'raises error'
do
expect
{
builds
}.
to
raise_error
(
'review job: on_stop job close_review is not defined'
)
end
end
context
'with close job without environment'
do
let
(
:close_review
)
{
{
stage:
'deploy'
,
script:
'test'
}
}
it
'raises error'
do
expect
{
builds
}.
to
raise_error
(
'review job: on_stop job close_review does not have environment defined'
)
end
end
context
'with close job for different environment'
do
let
(
:close_review
)
{
{
stage:
'deploy'
,
script:
'test'
,
environment:
'production'
}
}
it
'raises error'
do
expect
{
builds
}.
to
raise_error
(
'review job: on_stop job close_review have different environment name'
)
end
end
context
'with close job without stop action'
do
let
(
:close_review
)
{
{
stage:
'deploy'
,
script:
'test'
,
environment:
{
name:
'review'
}
}
}
it
'raises error'
do
expect
{
builds
}.
to
raise_error
(
'review job: on_stop job close_review needs to have action stop defined'
)
end
end
end
end
describe
"Dependencies"
do
let
(
:config
)
do
{
build1:
{
stage:
'build'
,
script:
'test'
},
build2:
{
stage:
'build'
,
script:
'test'
},
test1:
{
stage:
'test'
,
script:
'test'
,
dependencies:
dependencies
},
test2:
{
stage:
'test'
,
script:
'test'
},
deploy:
{
stage:
'test'
,
script:
'test'
}
}
end
subject
{
GitlabCiYamlProcessor
.
new
(
YAML
.
dump
(
config
))
}
context
'no dependencies'
do
let
(
:dependencies
)
{
}
it
{
expect
{
subject
}.
not_to
raise_error
}
end
context
'dependencies to builds'
do
let
(
:dependencies
)
{
%w(build1 build2)
}
it
{
expect
{
subject
}.
not_to
raise_error
}
end
context
'dependencies to builds defined as symbols'
do
let
(
:dependencies
)
{
[
:build1
,
:build2
]
}
it
{
expect
{
subject
}.
not_to
raise_error
}
end
context
'undefined dependency'
do
let
(
:dependencies
)
{
[
'undefined'
]
}
it
{
expect
{
subject
}.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
'test1 job: undefined dependency: undefined'
)
}
end
context
'dependencies to deploy'
do
let
(
:dependencies
)
{
[
'deploy'
]
}
it
{
expect
{
subject
}.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
'test1 job: dependency deploy is not defined in prior stages'
)
}
end
end
describe
"Hidden jobs"
do
let
(
:config_processor
)
{
GitlabCiYamlProcessor
.
new
(
config
)
}
subject
{
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
)
}
shared_examples
'hidden_job_handling'
do
it
"doesn't create jobs that start with dot"
do
expect
(
subject
.
size
).
to
eq
(
1
)
expect
(
subject
.
first
).
to
eq
({
stage:
"test"
,
stage_idx:
1
,
name:
"normal_job"
,
commands:
"test"
,
coverage_regex:
nil
,
tag_list:
[],
options:
{
script:
[
"test"
]
},
when:
"on_success"
,
allow_failure:
false
,
environment:
nil
,
yaml_variables:
[]
})
end
end
context
'when hidden job have a script definition'
do
let
(
:config
)
do
YAML
.
dump
({
'.hidden_job'
=>
{
image:
'ruby:2.1'
,
script:
'test'
},
'normal_job'
=>
{
script:
'test'
}
})
end
it_behaves_like
'hidden_job_handling'
end
context
"when hidden job doesn't have a script definition"
do
let
(
:config
)
do
YAML
.
dump
({
'.hidden_job'
=>
{
image:
'ruby:2.1'
},
'normal_job'
=>
{
script:
'test'
}
})
end
it_behaves_like
'hidden_job_handling'
end
end
describe
"YAML Alias/Anchor"
do
let
(
:config_processor
)
{
GitlabCiYamlProcessor
.
new
(
config
)
}
subject
{
config_processor
.
builds_for_stage_and_ref
(
"build"
,
"master"
)
}
shared_examples
'job_templates_handling'
do
it
"is correctly supported for jobs"
do
expect
(
subject
.
size
).
to
eq
(
2
)
expect
(
subject
.
first
).
to
eq
({
stage:
"build"
,
stage_idx:
0
,
name:
"job1"
,
commands:
"execute-script-for-job"
,
coverage_regex:
nil
,
tag_list:
[],
options:
{
script:
[
"execute-script-for-job"
]
},
when:
"on_success"
,
allow_failure:
false
,
environment:
nil
,
yaml_variables:
[]
})
expect
(
subject
.
second
).
to
eq
({
stage:
"build"
,
stage_idx:
0
,
name:
"job2"
,
commands:
"execute-script-for-job"
,
coverage_regex:
nil
,
tag_list:
[],
options:
{
script:
[
"execute-script-for-job"
]
},
when:
"on_success"
,
allow_failure:
false
,
environment:
nil
,
yaml_variables:
[]
})
end
end
context
'when template is a job'
do
let
(
:config
)
do
<<
EOT
job1: &JOBTMPL
stage: build
script: execute-script-for-job
job2: *JOBTMPL
EOT
end
it_behaves_like
'job_templates_handling'
end
context
'when template is a hidden job'
do
let
(
:config
)
do
<<
EOT
.template: &JOBTMPL
stage: build
script: execute-script-for-job
job1: *JOBTMPL
job2: *JOBTMPL
EOT
end
it_behaves_like
'job_templates_handling'
end
context
'when job adds its own keys to a template definition'
do
let
(
:config
)
do
<<
EOT
.template: &JOBTMPL
stage: build
job1:
<<: *JOBTMPL
script: execute-script-for-job
job2:
<<: *JOBTMPL
script: execute-script-for-job
EOT
end
it_behaves_like
'job_templates_handling'
end
end
describe
"Error handling"
do
it
"fails to parse YAML"
do
expect
{
GitlabCiYamlProcessor
.
new
(
"invalid: yaml: test"
)}.
to
raise_error
(
Psych
::
SyntaxError
)
end
it
"indicates that object is invalid"
do
expect
{
GitlabCiYamlProcessor
.
new
(
"invalid_yaml"
)}.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
)
end
it
"returns errors if tags parameter is invalid"
do
config
=
YAML
.
dump
({
rspec:
{
script:
"test"
,
tags:
"mysql"
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"jobs:rspec tags should be an array of strings"
)
end
it
"returns errors if before_script parameter is invalid"
do
config
=
YAML
.
dump
({
before_script:
"bundle update"
,
rspec:
{
script:
"test"
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"before_script config should be an array of strings"
)
end
it
"returns errors if job before_script parameter is not an array of strings"
do
config
=
YAML
.
dump
({
rspec:
{
script:
"test"
,
before_script:
[
10
,
"test"
]
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"jobs:rspec:before_script config should be an array of strings"
)
end
it
"returns errors if after_script parameter is invalid"
do
config
=
YAML
.
dump
({
after_script:
"bundle update"
,
rspec:
{
script:
"test"
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"after_script config should be an array of strings"
)
end
it
"returns errors if job after_script parameter is not an array of strings"
do
config
=
YAML
.
dump
({
rspec:
{
script:
"test"
,
after_script:
[
10
,
"test"
]
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"jobs:rspec:after_script config should be an array of strings"
)
end
it
"returns errors if image parameter is invalid"
do
config
=
YAML
.
dump
({
image:
[
"test"
],
rspec:
{
script:
"test"
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"image config should be a hash or a string"
)
end
it
"returns errors if job name is blank"
do
config
=
YAML
.
dump
({
''
=>
{
script:
"test"
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"jobs:job name can't be blank"
)
end
it
"returns errors if job name is non-string"
do
config
=
YAML
.
dump
({
10
=>
{
script:
"test"
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"jobs:10 name should be a symbol"
)
end
it
"returns errors if job image parameter is invalid"
do
config
=
YAML
.
dump
({
rspec:
{
script:
"test"
,
image:
[
"test"
]
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"jobs:rspec:image config should be a hash or a string"
)
end
it
"returns errors if services parameter is not an array"
do
config
=
YAML
.
dump
({
services:
"test"
,
rspec:
{
script:
"test"
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"services config should be a array"
)
end
it
"returns errors if services parameter is not an array of strings"
do
config
=
YAML
.
dump
({
services:
[
10
,
"test"
],
rspec:
{
script:
"test"
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"service config should be a hash or a string"
)
end
it
"returns errors if job services parameter is not an array"
do
config
=
YAML
.
dump
({
rspec:
{
script:
"test"
,
services:
"test"
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"jobs:rspec:services config should be a array"
)
end
it
"returns errors if job services parameter is not an array of strings"
do
config
=
YAML
.
dump
({
rspec:
{
script:
"test"
,
services:
[
10
,
"test"
]
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"service config should be a hash or a string"
)
end
it
"returns error if job configuration is invalid"
do
config
=
YAML
.
dump
({
extra:
"bundle update"
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"jobs:extra config should be a hash"
)
end
it
"returns errors if services configuration is not correct"
do
config
=
YAML
.
dump
({
extra:
{
script:
'rspec'
,
services:
"test"
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"jobs:extra:services config should be a array"
)
end
it
"returns errors if there are no jobs defined"
do
config
=
YAML
.
dump
({
before_script:
[
"bundle update"
]
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"jobs config should contain at least one visible job"
)
end
it
"returns errors if there are no visible jobs defined"
do
config
=
YAML
.
dump
({
before_script:
[
"bundle update"
],
'.hidden'
.
to_sym
=>
{
script:
'ls'
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"jobs config should contain at least one visible job"
)
end
it
"returns errors if job allow_failure parameter is not an boolean"
do
config
=
YAML
.
dump
({
rspec:
{
script:
"test"
,
allow_failure:
"string"
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"jobs:rspec allow failure should be a boolean value"
)
end
it
"returns errors if job stage is not a string"
do
config
=
YAML
.
dump
({
rspec:
{
script:
"test"
,
type:
1
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"jobs:rspec:type config should be a string"
)
end
it
"returns errors if job stage is not a pre-defined stage"
do
config
=
YAML
.
dump
({
rspec:
{
script:
"test"
,
type:
"acceptance"
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"rspec job: stage parameter should be build, test, deploy"
)
end
it
"returns errors if job stage is not a defined stage"
do
config
=
YAML
.
dump
({
types:
%w(build test)
,
rspec:
{
script:
"test"
,
type:
"acceptance"
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"rspec job: stage parameter should be build, test"
)
end
it
"returns errors if stages is not an array"
do
config
=
YAML
.
dump
({
stages:
"test"
,
rspec:
{
script:
"test"
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"stages config should be an array of strings"
)
end
it
"returns errors if stages is not an array of strings"
do
config
=
YAML
.
dump
({
stages:
[
true
,
"test"
],
rspec:
{
script:
"test"
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"stages config should be an array of strings"
)
end
it
"returns errors if variables is not a map"
do
config
=
YAML
.
dump
({
variables:
"test"
,
rspec:
{
script:
"test"
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"variables config should be a hash of key value pairs"
)
end
it
"returns errors if variables is not a map of key-value strings"
do
config
=
YAML
.
dump
({
variables:
{
test:
false
},
rspec:
{
script:
"test"
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"variables config should be a hash of key value pairs"
)
end
it
"returns errors if job when is not on_success, on_failure or always"
do
config
=
YAML
.
dump
({
rspec:
{
script:
"test"
,
when:
1
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"jobs:rspec when should be on_success, on_failure, always or manual"
)
end
it
"returns errors if job artifacts:name is not an a string"
do
config
=
YAML
.
dump
({
types:
%w(build test)
,
rspec:
{
script:
"test"
,
artifacts:
{
name:
1
}
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"jobs:rspec:artifacts name should be a string"
)
end
it
"returns errors if job artifacts:when is not an a predefined value"
do
config
=
YAML
.
dump
({
types:
%w(build test)
,
rspec:
{
script:
"test"
,
artifacts:
{
when:
1
}
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"jobs:rspec:artifacts when should be on_success, on_failure or always"
)
end
it
"returns errors if job artifacts:expire_in is not an a string"
do
config
=
YAML
.
dump
({
types:
%w(build test)
,
rspec:
{
script:
"test"
,
artifacts:
{
expire_in:
1
}
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"jobs:rspec:artifacts expire in should be a duration"
)
end
it
"returns errors if job artifacts:expire_in is not an a valid duration"
do
config
=
YAML
.
dump
({
types:
%w(build test)
,
rspec:
{
script:
"test"
,
artifacts:
{
expire_in:
"7 elephants"
}
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"jobs:rspec:artifacts expire in should be a duration"
)
end
it
"returns errors if job artifacts:untracked is not an array of strings"
do
config
=
YAML
.
dump
({
types:
%w(build test)
,
rspec:
{
script:
"test"
,
artifacts:
{
untracked:
"string"
}
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"jobs:rspec:artifacts untracked should be a boolean value"
)
end
it
"returns errors if job artifacts:paths is not an array of strings"
do
config
=
YAML
.
dump
({
types:
%w(build test)
,
rspec:
{
script:
"test"
,
artifacts:
{
paths:
"string"
}
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"jobs:rspec:artifacts paths should be an array of strings"
)
end
it
"returns errors if cache:untracked is not an array of strings"
do
config
=
YAML
.
dump
({
cache:
{
untracked:
"string"
},
rspec:
{
script:
"test"
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"cache:untracked config should be a boolean value"
)
end
it
"returns errors if cache:paths is not an array of strings"
do
config
=
YAML
.
dump
({
cache:
{
paths:
"string"
},
rspec:
{
script:
"test"
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"cache:paths config should be an array of strings"
)
end
it
"returns errors if cache:key is not a string"
do
config
=
YAML
.
dump
({
cache:
{
key:
1
},
rspec:
{
script:
"test"
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"cache:key config should be a string or symbol"
)
end
it
"returns errors if job cache:key is not an a string"
do
config
=
YAML
.
dump
({
types:
%w(build test)
,
rspec:
{
script:
"test"
,
cache:
{
key:
1
}
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"jobs:rspec:cache:key config should be a string or symbol"
)
end
it
"returns errors if job cache:untracked is not an array of strings"
do
config
=
YAML
.
dump
({
types:
%w(build test)
,
rspec:
{
script:
"test"
,
cache:
{
untracked:
"string"
}
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"jobs:rspec:cache:untracked config should be a boolean value"
)
end
it
"returns errors if job cache:paths is not an array of strings"
do
config
=
YAML
.
dump
({
types:
%w(build test)
,
rspec:
{
script:
"test"
,
cache:
{
paths:
"string"
}
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"jobs:rspec:cache:paths config should be an array of strings"
)
end
it
"returns errors if job dependencies is not an array of strings"
do
config
=
YAML
.
dump
({
types:
%w(build test)
,
rspec:
{
script:
"test"
,
dependencies:
"string"
}
})
expect
do
GitlabCiYamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
GitlabCiYamlProcessor
::
ValidationError
,
"jobs:rspec dependencies should be an array of strings"
)
end
end
describe
"Validate configuration templates"
do
templates
=
Dir
.
glob
(
"
#{
Rails
.
root
.
join
(
'vendor/gitlab-ci-yml'
)
}
/**/*.gitlab-ci.yml"
)
templates
.
each
do
|
file
|
it
"does not return errors for
#{
file
}
"
do
file
=
File
.
read
(
file
)
expect
{
GitlabCiYamlProcessor
.
new
(
file
)
}.
not_to
raise_error
end
end
end
describe
"#validation_message"
do
context
"when the YAML could not be parsed"
do
it
"returns an error about invalid configutaion"
do
content
=
YAML
.
dump
(
"invalid: yaml: test"
)
expect
(
GitlabCiYamlProcessor
.
validation_message
(
content
))
.
to
eq
"Invalid configuration format"
end
end
context
"when the tags parameter is invalid"
do
it
"returns an error about invalid tags"
do
content
=
YAML
.
dump
({
rspec:
{
script:
"test"
,
tags:
"mysql"
}
})
expect
(
GitlabCiYamlProcessor
.
validation_message
(
content
))
.
to
eq
"jobs:rspec tags should be an array of strings"
end
end
context
"when YAML content is empty"
do
it
"returns an error about missing content"
do
expect
(
GitlabCiYamlProcessor
.
validation_message
(
''
))
.
to
eq
"Please provide content of .gitlab-ci.yml"
end
end
context
"when the YAML is valid"
do
it
"does not return any errors"
do
content
=
File
.
read
(
Rails
.
root
.
join
(
'spec/support/gitlab_stubs/gitlab_ci.yml'
))
expect
(
GitlabCiYamlProcessor
.
validation_message
(
content
)).
to
be_nil
end
end
end
end
end
spec/lib/gitlab/ci/yaml_processor_spec.rb
0 → 100644
View file @
c45ace89
require
'spec_helper'
module
Gitlab
module
Ci
describe
YamlProcessor
,
:lib
do
subject
{
described_class
.
new
(
config
,
path
)
}
let
(
:path
)
{
'path'
}
describe
'our current .gitlab-ci.yml'
do
let
(
:config
)
{
File
.
read
(
"
#{
Rails
.
root
}
/.gitlab-ci.yml"
)
}
it
'is valid'
do
error_message
=
described_class
.
validation_message
(
config
)
expect
(
error_message
).
to
be_nil
end
end
describe
'#build_attributes'
do
subject
{
described_class
.
new
(
config
,
path
).
build_attributes
(
:rspec
)
}
describe
'coverage entry'
do
describe
'code coverage regexp'
do
let
(
:config
)
do
YAML
.
dump
(
rspec:
{
script:
'rspec'
,
coverage:
'/Code coverage: \d+\.\d+/'
})
end
it
'includes coverage regexp in build attributes'
do
expect
(
subject
)
.
to
include
(
coverage_regex:
'Code coverage: \d+\.\d+'
)
end
end
end
describe
'retry entry'
do
context
'when retry count is specified'
do
let
(
:config
)
do
YAML
.
dump
(
rspec:
{
script:
'rspec'
,
retry:
1
})
end
it
'includes retry count in build options attribute'
do
expect
(
subject
[
:options
]).
to
include
(
retry:
1
)
end
end
context
'when retry count is not specified'
do
let
(
:config
)
do
YAML
.
dump
(
rspec:
{
script:
'rspec'
})
end
it
'does not persist retry count in the database'
do
expect
(
subject
[
:options
]).
not_to
have_key
(
:retry
)
end
end
end
describe
'allow failure entry'
do
context
'when job is a manual action'
do
context
'when allow_failure is defined'
do
let
(
:config
)
do
YAML
.
dump
(
rspec:
{
script:
'rspec'
,
when:
'manual'
,
allow_failure:
false
})
end
it
'is not allowed to fail'
do
expect
(
subject
[
:allow_failure
]).
to
be
false
end
end
context
'when allow_failure is not defined'
do
let
(
:config
)
do
YAML
.
dump
(
rspec:
{
script:
'rspec'
,
when:
'manual'
})
end
it
'is allowed to fail'
do
expect
(
subject
[
:allow_failure
]).
to
be
true
end
end
end
context
'when job is not a manual action'
do
context
'when allow_failure is defined'
do
let
(
:config
)
do
YAML
.
dump
(
rspec:
{
script:
'rspec'
,
allow_failure:
false
})
end
it
'is not allowed to fail'
do
expect
(
subject
[
:allow_failure
]).
to
be
false
end
end
context
'when allow_failure is not defined'
do
let
(
:config
)
do
YAML
.
dump
(
rspec:
{
script:
'rspec'
})
end
it
'is not allowed to fail'
do
expect
(
subject
[
:allow_failure
]).
to
be
false
end
end
end
end
end
describe
'#stage_seeds'
do
context
'when no refs policy is specified'
do
let
(
:config
)
do
YAML
.
dump
(
production:
{
stage:
'deploy'
,
script:
'cap prod'
},
rspec:
{
stage:
'test'
,
script:
'rspec'
},
spinach:
{
stage:
'test'
,
script:
'spinach'
})
end
let
(
:pipeline
)
{
create
(
:ci_empty_pipeline
)
}
it
'correctly fabricates a stage seeds object'
do
seeds
=
subject
.
stage_seeds
(
pipeline
)
expect
(
seeds
.
size
).
to
eq
2
expect
(
seeds
.
first
.
stage
[
:name
]).
to
eq
'test'
expect
(
seeds
.
second
.
stage
[
:name
]).
to
eq
'deploy'
expect
(
seeds
.
first
.
builds
.
dig
(
0
,
:name
)).
to
eq
'rspec'
expect
(
seeds
.
first
.
builds
.
dig
(
1
,
:name
)).
to
eq
'spinach'
expect
(
seeds
.
second
.
builds
.
dig
(
0
,
:name
)).
to
eq
'production'
end
end
context
'when refs policy is specified'
do
let
(
:config
)
do
YAML
.
dump
(
production:
{
stage:
'deploy'
,
script:
'cap prod'
,
only:
[
'master'
]
},
spinach:
{
stage:
'test'
,
script:
'spinach'
,
only:
[
'tags'
]
})
end
let
(
:pipeline
)
do
create
(
:ci_empty_pipeline
,
ref:
'feature'
,
tag:
true
)
end
it
'returns stage seeds only assigned to master to master'
do
seeds
=
subject
.
stage_seeds
(
pipeline
)
expect
(
seeds
.
size
).
to
eq
1
expect
(
seeds
.
first
.
stage
[
:name
]).
to
eq
'test'
expect
(
seeds
.
first
.
builds
.
dig
(
0
,
:name
)).
to
eq
'spinach'
end
end
context
'when source policy is specified'
do
let
(
:config
)
do
YAML
.
dump
(
production:
{
stage:
'deploy'
,
script:
'cap prod'
,
only:
[
'triggers'
]
},
spinach:
{
stage:
'test'
,
script:
'spinach'
,
only:
[
'schedules'
]
})
end
let
(
:pipeline
)
do
create
(
:ci_empty_pipeline
,
source: :schedule
)
end
it
'returns stage seeds only assigned to schedules'
do
seeds
=
subject
.
stage_seeds
(
pipeline
)
expect
(
seeds
.
size
).
to
eq
1
expect
(
seeds
.
first
.
stage
[
:name
]).
to
eq
'test'
expect
(
seeds
.
first
.
builds
.
dig
(
0
,
:name
)).
to
eq
'spinach'
end
end
context
'when kubernetes policy is specified'
do
let
(
:pipeline
)
{
create
(
:ci_empty_pipeline
)
}
let
(
:config
)
do
YAML
.
dump
(
spinach:
{
stage:
'test'
,
script:
'spinach'
},
production:
{
stage:
'deploy'
,
script:
'cap'
,
only:
{
kubernetes:
'active'
}
}
)
end
context
'when kubernetes is active'
do
let
(
:project
)
{
create
(
:kubernetes_project
)
}
let
(
:pipeline
)
{
create
(
:ci_empty_pipeline
,
project:
project
)
}
it
'returns seeds for kubernetes dependent job'
do
seeds
=
subject
.
stage_seeds
(
pipeline
)
expect
(
seeds
.
size
).
to
eq
2
expect
(
seeds
.
first
.
builds
.
dig
(
0
,
:name
)).
to
eq
'spinach'
expect
(
seeds
.
second
.
builds
.
dig
(
0
,
:name
)).
to
eq
'production'
end
end
context
'when kubernetes is not active'
do
it
'does not return seeds for kubernetes dependent job'
do
seeds
=
subject
.
stage_seeds
(
pipeline
)
expect
(
seeds
.
size
).
to
eq
1
expect
(
seeds
.
first
.
builds
.
dig
(
0
,
:name
)).
to
eq
'spinach'
end
end
end
end
describe
"#builds_for_stage_and_ref"
do
let
(
:type
)
{
'test'
}
it
"returns builds if no branch specified"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"master"
).
size
).
to
eq
(
1
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"master"
).
first
).
to
eq
({
stage:
"test"
,
stage_idx:
1
,
name:
"rspec"
,
commands:
"pwd
\n
rspec"
,
coverage_regex:
nil
,
tag_list:
[],
options:
{
before_script:
[
"pwd"
],
script:
[
"rspec"
]
},
allow_failure:
false
,
when:
"on_success"
,
environment:
nil
,
yaml_variables:
[]
})
end
describe
'only'
do
it
"does not return builds if only has another branch"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
only:
[
"deploy"
]
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"master"
).
size
).
to
eq
(
0
)
end
it
"does not return builds if only has regexp with another branch"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
only:
[
"/^deploy$/"
]
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"master"
).
size
).
to
eq
(
0
)
end
it
"returns builds if only has specified this branch"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
only:
[
"master"
]
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"master"
).
size
).
to
eq
(
1
)
end
it
"returns builds if only has a list of branches including specified"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
only:
%w(master deploy)
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
).
size
).
to
eq
(
1
)
end
it
"returns builds if only has a branches keyword specified"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
only:
[
"branches"
]
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
).
size
).
to
eq
(
1
)
end
it
"does not return builds if only has a tags keyword"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
only:
[
"tags"
]
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
).
size
).
to
eq
(
0
)
end
it
"returns builds if only has special keywords specified and source matches"
do
possibilities
=
[{
keyword:
'pushes'
,
source:
'push'
},
{
keyword:
'web'
,
source:
'web'
},
{
keyword:
'triggers'
,
source:
'trigger'
},
{
keyword:
'schedules'
,
source:
'schedule'
},
{
keyword:
'api'
,
source:
'api'
},
{
keyword:
'external'
,
source:
'external'
}]
possibilities
.
each
do
|
possibility
|
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
only:
[
possibility
[
:keyword
]]
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
,
false
,
possibility
[
:source
]).
size
).
to
eq
(
1
)
end
end
it
"does not return builds if only has special keywords specified and source doesn't match"
do
possibilities
=
[{
keyword:
'pushes'
,
source:
'web'
},
{
keyword:
'web'
,
source:
'push'
},
{
keyword:
'triggers'
,
source:
'schedule'
},
{
keyword:
'schedules'
,
source:
'external'
},
{
keyword:
'api'
,
source:
'trigger'
},
{
keyword:
'external'
,
source:
'api'
}]
possibilities
.
each
do
|
possibility
|
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
only:
[
possibility
[
:keyword
]]
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
,
false
,
possibility
[
:source
]).
size
).
to
eq
(
0
)
end
end
it
"returns builds if only has current repository path"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
only:
[
"branches@path"
]
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
).
size
).
to
eq
(
1
)
end
it
"does not return builds if only has different repository path"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
only:
[
"branches@fork"
]
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
).
size
).
to
eq
(
0
)
end
it
"returns build only for specified type"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
"test"
,
only:
%w(master deploy)
},
staging:
{
script:
"deploy"
,
type:
"deploy"
,
only:
%w(master deploy)
},
production:
{
script:
"deploy"
,
type:
"deploy"
,
only:
[
"master@path"
,
"deploy"
]
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
'fork'
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"deploy"
,
"deploy"
).
size
).
to
eq
(
2
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"deploy"
).
size
).
to
eq
(
1
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"deploy"
,
"master"
).
size
).
to
eq
(
1
)
end
context
'for invalid value'
do
let
(
:config
)
{
{
rspec:
{
script:
"rspec"
,
type:
"test"
,
only:
only
}
}
}
let
(
:processor
)
{
Gitlab
::
Ci
::
YamlProcessor
.
new
(
YAML
.
dump
(
config
))
}
context
'when it is integer'
do
let
(
:only
)
{
1
}
it
do
expect
{
processor
}.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
'jobs:rspec:only has to be either an array of conditions or a hash'
)
end
end
context
'when it is an array of integers'
do
let
(
:only
)
{
[
1
,
1
]
}
it
do
expect
{
processor
}.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
'jobs:rspec:only config should be an array of strings or regexps'
)
end
end
context
'when it is invalid regex'
do
let
(
:only
)
{
[
"/*invalid/"
]
}
it
do
expect
{
processor
}.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
'jobs:rspec:only config should be an array of strings or regexps'
)
end
end
end
end
describe
'except'
do
it
"returns builds if except has another branch"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
except:
[
"deploy"
]
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"master"
).
size
).
to
eq
(
1
)
end
it
"returns builds if except has regexp with another branch"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
except:
[
"/^deploy$/"
]
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"master"
).
size
).
to
eq
(
1
)
end
it
"does not return builds if except has specified this branch"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
except:
[
"master"
]
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"master"
).
size
).
to
eq
(
0
)
end
it
"does not return builds if except has a list of branches including specified"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
except:
%w(master deploy)
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
).
size
).
to
eq
(
0
)
end
it
"does not return builds if except has a branches keyword specified"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
except:
[
"branches"
]
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
).
size
).
to
eq
(
0
)
end
it
"returns builds if except has a tags keyword"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
except:
[
"tags"
]
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
).
size
).
to
eq
(
1
)
end
it
"does not return builds if except has special keywords specified and source matches"
do
possibilities
=
[{
keyword:
'pushes'
,
source:
'push'
},
{
keyword:
'web'
,
source:
'web'
},
{
keyword:
'triggers'
,
source:
'trigger'
},
{
keyword:
'schedules'
,
source:
'schedule'
},
{
keyword:
'api'
,
source:
'api'
},
{
keyword:
'external'
,
source:
'external'
}]
possibilities
.
each
do
|
possibility
|
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
except:
[
possibility
[
:keyword
]]
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
,
false
,
possibility
[
:source
]).
size
).
to
eq
(
0
)
end
end
it
"returns builds if except has special keywords specified and source doesn't match"
do
possibilities
=
[{
keyword:
'pushes'
,
source:
'web'
},
{
keyword:
'web'
,
source:
'push'
},
{
keyword:
'triggers'
,
source:
'schedule'
},
{
keyword:
'schedules'
,
source:
'external'
},
{
keyword:
'api'
,
source:
'trigger'
},
{
keyword:
'external'
,
source:
'api'
}]
possibilities
.
each
do
|
possibility
|
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
except:
[
possibility
[
:keyword
]]
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
,
false
,
possibility
[
:source
]).
size
).
to
eq
(
1
)
end
end
it
"does not return builds if except has current repository path"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
except:
[
"branches@path"
]
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
).
size
).
to
eq
(
0
)
end
it
"returns builds if except has different repository path"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
type
,
except:
[
"branches@fork"
]
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
type
,
"deploy"
).
size
).
to
eq
(
1
)
end
it
"returns build except specified type"
do
config
=
YAML
.
dump
({
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
,
type:
"test"
,
except:
[
"master"
,
"deploy"
,
"test@fork"
]
},
staging:
{
script:
"deploy"
,
type:
"deploy"
,
except:
[
"master"
]
},
production:
{
script:
"deploy"
,
type:
"deploy"
,
except:
[
"master@fork"
]
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
'fork'
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"deploy"
,
"deploy"
).
size
).
to
eq
(
2
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"test"
).
size
).
to
eq
(
0
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"deploy"
,
"master"
).
size
).
to
eq
(
0
)
end
context
'for invalid value'
do
let
(
:config
)
{
{
rspec:
{
script:
"rspec"
,
except:
except
}
}
}
let
(
:processor
)
{
Gitlab
::
Ci
::
YamlProcessor
.
new
(
YAML
.
dump
(
config
))
}
context
'when it is integer'
do
let
(
:except
)
{
1
}
it
do
expect
{
processor
}.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
'jobs:rspec:except has to be either an array of conditions or a hash'
)
end
end
context
'when it is an array of integers'
do
let
(
:except
)
{
[
1
,
1
]
}
it
do
expect
{
processor
}.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
'jobs:rspec:except config should be an array of strings or regexps'
)
end
end
context
'when it is invalid regex'
do
let
(
:except
)
{
[
"/*invalid/"
]
}
it
do
expect
{
processor
}.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
'jobs:rspec:except config should be an array of strings or regexps'
)
end
end
end
end
end
describe
"Scripts handling"
do
let
(
:config_data
)
{
YAML
.
dump
(
config
)
}
let
(
:config_processor
)
{
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config_data
,
path
)
}
subject
{
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
first
}
describe
"before_script"
do
context
"in global context"
do
let
(
:config
)
do
{
before_script:
[
"global script"
],
test:
{
script:
[
"script"
]
}
}
end
it
"return commands with scripts concencaced"
do
expect
(
subject
[
:commands
]).
to
eq
(
"global script
\n
script"
)
end
end
context
"overwritten in local context"
do
let
(
:config
)
do
{
before_script:
[
"global script"
],
test:
{
before_script:
[
"local script"
],
script:
[
"script"
]
}
}
end
it
"return commands with scripts concencaced"
do
expect
(
subject
[
:commands
]).
to
eq
(
"local script
\n
script"
)
end
end
end
describe
"script"
do
let
(
:config
)
do
{
test:
{
script:
[
"script"
]
}
}
end
it
"return commands with scripts concencaced"
do
expect
(
subject
[
:commands
]).
to
eq
(
"script"
)
end
end
describe
"after_script"
do
context
"in global context"
do
let
(
:config
)
do
{
after_script:
[
"after_script"
],
test:
{
script:
[
"script"
]
}
}
end
it
"return after_script in options"
do
expect
(
subject
[
:options
][
:after_script
]).
to
eq
([
"after_script"
])
end
end
context
"overwritten in local context"
do
let
(
:config
)
do
{
after_script:
[
"local after_script"
],
test:
{
after_script:
[
"local after_script"
],
script:
[
"script"
]
}
}
end
it
"return after_script in options"
do
expect
(
subject
[
:options
][
:after_script
]).
to
eq
([
"local after_script"
])
end
end
end
end
describe
"Image and service handling"
do
context
"when extended docker configuration is used"
do
it
"returns image and service when defined"
do
config
=
YAML
.
dump
({
image:
{
name:
"ruby:2.1"
,
entrypoint:
[
"/usr/local/bin/init"
,
"run"
]
},
services:
[
"mysql"
,
{
name:
"docker:dind"
,
alias:
"docker"
,
entrypoint:
[
"/usr/local/bin/init"
,
"run"
],
command:
[
"/usr/local/bin/init"
,
"run"
]
}],
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
size
).
to
eq
(
1
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
first
).
to
eq
({
stage:
"test"
,
stage_idx:
1
,
name:
"rspec"
,
commands:
"pwd
\n
rspec"
,
coverage_regex:
nil
,
tag_list:
[],
options:
{
before_script:
[
"pwd"
],
script:
[
"rspec"
],
image:
{
name:
"ruby:2.1"
,
entrypoint:
[
"/usr/local/bin/init"
,
"run"
]
},
services:
[{
name:
"mysql"
},
{
name:
"docker:dind"
,
alias:
"docker"
,
entrypoint:
[
"/usr/local/bin/init"
,
"run"
],
command:
[
"/usr/local/bin/init"
,
"run"
]
}]
},
allow_failure:
false
,
when:
"on_success"
,
environment:
nil
,
yaml_variables:
[]
})
end
it
"returns image and service when overridden for job"
do
config
=
YAML
.
dump
({
image:
"ruby:2.1"
,
services:
[
"mysql"
],
before_script:
[
"pwd"
],
rspec:
{
image:
{
name:
"ruby:2.5"
,
entrypoint:
[
"/usr/local/bin/init"
,
"run"
]
},
services:
[{
name:
"postgresql"
,
alias:
"db-pg"
,
entrypoint:
[
"/usr/local/bin/init"
,
"run"
],
command:
[
"/usr/local/bin/init"
,
"run"
]
},
"docker:dind"
],
script:
"rspec"
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
size
).
to
eq
(
1
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
first
).
to
eq
({
stage:
"test"
,
stage_idx:
1
,
name:
"rspec"
,
commands:
"pwd
\n
rspec"
,
coverage_regex:
nil
,
tag_list:
[],
options:
{
before_script:
[
"pwd"
],
script:
[
"rspec"
],
image:
{
name:
"ruby:2.5"
,
entrypoint:
[
"/usr/local/bin/init"
,
"run"
]
},
services:
[{
name:
"postgresql"
,
alias:
"db-pg"
,
entrypoint:
[
"/usr/local/bin/init"
,
"run"
],
command:
[
"/usr/local/bin/init"
,
"run"
]
},
{
name:
"docker:dind"
}]
},
allow_failure:
false
,
when:
"on_success"
,
environment:
nil
,
yaml_variables:
[]
})
end
end
context
"when etended docker configuration is not used"
do
it
"returns image and service when defined"
do
config
=
YAML
.
dump
({
image:
"ruby:2.1"
,
services:
[
"mysql"
,
"docker:dind"
],
before_script:
[
"pwd"
],
rspec:
{
script:
"rspec"
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
size
).
to
eq
(
1
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
first
).
to
eq
({
stage:
"test"
,
stage_idx:
1
,
name:
"rspec"
,
commands:
"pwd
\n
rspec"
,
coverage_regex:
nil
,
tag_list:
[],
options:
{
before_script:
[
"pwd"
],
script:
[
"rspec"
],
image:
{
name:
"ruby:2.1"
},
services:
[{
name:
"mysql"
},
{
name:
"docker:dind"
}]
},
allow_failure:
false
,
when:
"on_success"
,
environment:
nil
,
yaml_variables:
[]
})
end
it
"returns image and service when overridden for job"
do
config
=
YAML
.
dump
({
image:
"ruby:2.1"
,
services:
[
"mysql"
],
before_script:
[
"pwd"
],
rspec:
{
image:
"ruby:2.5"
,
services:
[
"postgresql"
,
"docker:dind"
],
script:
"rspec"
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
size
).
to
eq
(
1
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
first
).
to
eq
({
stage:
"test"
,
stage_idx:
1
,
name:
"rspec"
,
commands:
"pwd
\n
rspec"
,
coverage_regex:
nil
,
tag_list:
[],
options:
{
before_script:
[
"pwd"
],
script:
[
"rspec"
],
image:
{
name:
"ruby:2.5"
},
services:
[{
name:
"postgresql"
},
{
name:
"docker:dind"
}]
},
allow_failure:
false
,
when:
"on_success"
,
environment:
nil
,
yaml_variables:
[]
})
end
end
end
describe
'Variables'
do
let
(
:config_processor
)
{
Gitlab
::
Ci
::
YamlProcessor
.
new
(
YAML
.
dump
(
config
),
path
)
}
subject
{
config_processor
.
builds
.
first
[
:yaml_variables
]
}
context
'when global variables are defined'
do
let
(
:variables
)
do
{
'VAR1'
=>
'value1'
,
'VAR2'
=>
'value2'
}
end
let
(
:config
)
do
{
variables:
variables
,
before_script:
[
'pwd'
],
rspec:
{
script:
'rspec'
}
}
end
it
'returns global variables'
do
expect
(
subject
).
to
contain_exactly
(
{
key:
'VAR1'
,
value:
'value1'
,
public:
true
},
{
key:
'VAR2'
,
value:
'value2'
,
public:
true
}
)
end
end
context
'when job and global variables are defined'
do
let
(
:global_variables
)
do
{
'VAR1'
=>
'global1'
,
'VAR3'
=>
'global3'
}
end
let
(
:job_variables
)
do
{
'VAR1'
=>
'value1'
,
'VAR2'
=>
'value2'
}
end
let
(
:config
)
do
{
before_script:
[
'pwd'
],
variables:
global_variables
,
rspec:
{
script:
'rspec'
,
variables:
job_variables
}
}
end
it
'returns all unique variables'
do
expect
(
subject
).
to
contain_exactly
(
{
key:
'VAR3'
,
value:
'global3'
,
public:
true
},
{
key:
'VAR1'
,
value:
'value1'
,
public:
true
},
{
key:
'VAR2'
,
value:
'value2'
,
public:
true
}
)
end
end
context
'when job variables are defined'
do
let
(
:config
)
do
{
before_script:
[
'pwd'
],
rspec:
{
script:
'rspec'
,
variables:
variables
}
}
end
context
'when syntax is correct'
do
let
(
:variables
)
do
{
'VAR1'
=>
'value1'
,
'VAR2'
=>
'value2'
}
end
it
'returns job variables'
do
expect
(
subject
).
to
contain_exactly
(
{
key:
'VAR1'
,
value:
'value1'
,
public:
true
},
{
key:
'VAR2'
,
value:
'value2'
,
public:
true
}
)
end
end
context
'when syntax is incorrect'
do
context
'when variables defined but invalid'
do
let
(
:variables
)
do
%w(VAR1 value1 VAR2 value2)
end
it
'raises error'
do
expect
{
subject
}
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
/jobs:rspec:variables config should be a hash of key value pairs/
)
end
end
context
'when variables key defined but value not specified'
do
let
(
:variables
)
do
nil
end
it
'returns empty array'
do
##
# When variables config is empty, we assume this is a valid
# configuration, see issue #18775
#
expect
(
subject
).
to
be_an_instance_of
(
Array
)
expect
(
subject
).
to
be_empty
end
end
end
end
context
'when job variables are not defined'
do
let
(
:config
)
do
{
before_script:
[
'pwd'
],
rspec:
{
script:
'rspec'
}
}
end
it
'returns empty array'
do
expect
(
subject
).
to
be_an_instance_of
(
Array
)
expect
(
subject
).
to
be_empty
end
end
end
describe
"When"
do
%w(on_success on_failure always)
.
each
do
|
when_state
|
it
"returns
#{
when_state
}
when defined"
do
config
=
YAML
.
dump
({
rspec:
{
script:
"rspec"
,
when:
when_state
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
builds
=
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
)
expect
(
builds
.
size
).
to
eq
(
1
)
expect
(
builds
.
first
[
:when
]).
to
eq
(
when_state
)
end
end
end
describe
'cache'
do
context
'when cache definition has unknown keys'
do
it
'raises relevant validation error'
do
config
=
YAML
.
dump
(
{
cache:
{
untracked:
true
,
invalid:
'key'
},
rspec:
{
script:
'rspec'
}
})
expect
{
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
)
}.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
'cache config contains unknown keys: invalid'
)
end
end
it
"returns cache when defined globally"
do
config
=
YAML
.
dump
({
cache:
{
paths:
[
"logs/"
,
"binaries/"
],
untracked:
true
,
key:
'key'
},
rspec:
{
script:
"rspec"
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
size
).
to
eq
(
1
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
first
[
:options
][
:cache
]).
to
eq
(
paths:
[
"logs/"
,
"binaries/"
],
untracked:
true
,
key:
'key'
,
policy:
'pull-push'
)
end
it
"returns cache when defined in a job"
do
config
=
YAML
.
dump
({
rspec:
{
cache:
{
paths:
[
"logs/"
,
"binaries/"
],
untracked:
true
,
key:
'key'
},
script:
"rspec"
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
size
).
to
eq
(
1
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
first
[
:options
][
:cache
]).
to
eq
(
paths:
[
"logs/"
,
"binaries/"
],
untracked:
true
,
key:
'key'
,
policy:
'pull-push'
)
end
it
"overwrite cache when defined for a job and globally"
do
config
=
YAML
.
dump
({
cache:
{
paths:
[
"logs/"
,
"binaries/"
],
untracked:
true
,
key:
'global'
},
rspec:
{
script:
"rspec"
,
cache:
{
paths:
[
"test/"
],
untracked:
false
,
key:
'local'
}
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
size
).
to
eq
(
1
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
first
[
:options
][
:cache
]).
to
eq
(
paths:
[
"test/"
],
untracked:
false
,
key:
'local'
,
policy:
'pull-push'
)
end
end
describe
"Artifacts"
do
it
"returns artifacts when defined"
do
config
=
YAML
.
dump
({
image:
"ruby:2.1"
,
services:
[
"mysql"
],
before_script:
[
"pwd"
],
rspec:
{
artifacts:
{
paths:
[
"logs/"
,
"binaries/"
],
untracked:
true
,
name:
"custom_name"
,
expire_in:
"7d"
},
script:
"rspec"
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
size
).
to
eq
(
1
)
expect
(
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
).
first
).
to
eq
({
stage:
"test"
,
stage_idx:
1
,
name:
"rspec"
,
commands:
"pwd
\n
rspec"
,
coverage_regex:
nil
,
tag_list:
[],
options:
{
before_script:
[
"pwd"
],
script:
[
"rspec"
],
image:
{
name:
"ruby:2.1"
},
services:
[{
name:
"mysql"
}],
artifacts:
{
name:
"custom_name"
,
paths:
[
"logs/"
,
"binaries/"
],
untracked:
true
,
expire_in:
"7d"
}
},
when:
"on_success"
,
allow_failure:
false
,
environment:
nil
,
yaml_variables:
[]
})
end
%w[on_success on_failure always]
.
each
do
|
when_state
|
it
"returns artifacts for when
#{
when_state
}
defined"
do
config
=
YAML
.
dump
({
rspec:
{
script:
"rspec"
,
artifacts:
{
paths:
[
"logs/"
,
"binaries/"
],
when:
when_state
}
}
})
config_processor
=
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
builds
=
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
)
expect
(
builds
.
size
).
to
eq
(
1
)
expect
(
builds
.
first
[
:options
][
:artifacts
][
:when
]).
to
eq
(
when_state
)
end
end
end
describe
'#environment'
do
let
(
:config
)
do
{
deploy_to_production:
{
stage:
'deploy'
,
script:
'test'
,
environment:
environment
}
}
end
let
(
:processor
)
{
Gitlab
::
Ci
::
YamlProcessor
.
new
(
YAML
.
dump
(
config
))
}
let
(
:builds
)
{
processor
.
builds_for_stage_and_ref
(
'deploy'
,
'master'
)
}
context
'when a production environment is specified'
do
let
(
:environment
)
{
'production'
}
it
'does return production'
do
expect
(
builds
.
size
).
to
eq
(
1
)
expect
(
builds
.
first
[
:environment
]).
to
eq
(
environment
)
expect
(
builds
.
first
[
:options
]).
to
include
(
environment:
{
name:
environment
,
action:
"start"
})
end
end
context
'when hash is specified'
do
let
(
:environment
)
do
{
name:
'production'
,
url:
'http://production.gitlab.com'
}
end
it
'does return production and URL'
do
expect
(
builds
.
size
).
to
eq
(
1
)
expect
(
builds
.
first
[
:environment
]).
to
eq
(
environment
[
:name
])
expect
(
builds
.
first
[
:options
]).
to
include
(
environment:
environment
)
end
context
'the url has a port as variable'
do
let
(
:environment
)
do
{
name:
'production'
,
url:
'http://production.gitlab.com:$PORT'
}
end
it
'allows a variable for the port'
do
expect
(
builds
.
size
).
to
eq
(
1
)
expect
(
builds
.
first
[
:environment
]).
to
eq
(
environment
[
:name
])
expect
(
builds
.
first
[
:options
]).
to
include
(
environment:
environment
)
end
end
end
context
'when no environment is specified'
do
let
(
:environment
)
{
nil
}
it
'does return nil environment'
do
expect
(
builds
.
size
).
to
eq
(
1
)
expect
(
builds
.
first
[
:environment
]).
to
be_nil
end
end
context
'is not a string'
do
let
(
:environment
)
{
1
}
it
'raises error'
do
expect
{
builds
}.
to
raise_error
(
'jobs:deploy_to_production:environment config should be a hash or a string'
)
end
end
context
'is not a valid string'
do
let
(
:environment
)
{
'production:staging'
}
it
'raises error'
do
expect
{
builds
}.
to
raise_error
(
"jobs:deploy_to_production:environment name
#{
Gitlab
::
Regex
.
environment_name_regex_message
}
"
)
end
end
context
'when on_stop is specified'
do
let
(
:review
)
{
{
stage:
'deploy'
,
script:
'test'
,
environment:
{
name:
'review'
,
on_stop:
'close_review'
}
}
}
let
(
:config
)
{
{
review:
review
,
close_review:
close_review
}.
compact
}
context
'with matching job'
do
let
(
:close_review
)
{
{
stage:
'deploy'
,
script:
'test'
,
environment:
{
name:
'review'
,
action:
'stop'
}
}
}
it
'does return a list of builds'
do
expect
(
builds
.
size
).
to
eq
(
2
)
expect
(
builds
.
first
[
:environment
]).
to
eq
(
'review'
)
end
end
context
'without matching job'
do
let
(
:close_review
)
{
nil
}
it
'raises error'
do
expect
{
builds
}.
to
raise_error
(
'review job: on_stop job close_review is not defined'
)
end
end
context
'with close job without environment'
do
let
(
:close_review
)
{
{
stage:
'deploy'
,
script:
'test'
}
}
it
'raises error'
do
expect
{
builds
}.
to
raise_error
(
'review job: on_stop job close_review does not have environment defined'
)
end
end
context
'with close job for different environment'
do
let
(
:close_review
)
{
{
stage:
'deploy'
,
script:
'test'
,
environment:
'production'
}
}
it
'raises error'
do
expect
{
builds
}.
to
raise_error
(
'review job: on_stop job close_review have different environment name'
)
end
end
context
'with close job without stop action'
do
let
(
:close_review
)
{
{
stage:
'deploy'
,
script:
'test'
,
environment:
{
name:
'review'
}
}
}
it
'raises error'
do
expect
{
builds
}.
to
raise_error
(
'review job: on_stop job close_review needs to have action stop defined'
)
end
end
end
end
describe
"Dependencies"
do
let
(
:config
)
do
{
build1:
{
stage:
'build'
,
script:
'test'
},
build2:
{
stage:
'build'
,
script:
'test'
},
test1:
{
stage:
'test'
,
script:
'test'
,
dependencies:
dependencies
},
test2:
{
stage:
'test'
,
script:
'test'
},
deploy:
{
stage:
'test'
,
script:
'test'
}
}
end
subject
{
Gitlab
::
Ci
::
YamlProcessor
.
new
(
YAML
.
dump
(
config
))
}
context
'no dependencies'
do
let
(
:dependencies
)
{
}
it
{
expect
{
subject
}.
not_to
raise_error
}
end
context
'dependencies to builds'
do
let
(
:dependencies
)
{
%w(build1 build2)
}
it
{
expect
{
subject
}.
not_to
raise_error
}
end
context
'dependencies to builds defined as symbols'
do
let
(
:dependencies
)
{
[
:build1
,
:build2
]
}
it
{
expect
{
subject
}.
not_to
raise_error
}
end
context
'undefined dependency'
do
let
(
:dependencies
)
{
[
'undefined'
]
}
it
{
expect
{
subject
}.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
'test1 job: undefined dependency: undefined'
)
}
end
context
'dependencies to deploy'
do
let
(
:dependencies
)
{
[
'deploy'
]
}
it
{
expect
{
subject
}.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
'test1 job: dependency deploy is not defined in prior stages'
)
}
end
end
describe
"Hidden jobs"
do
let
(
:config_processor
)
{
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
)
}
subject
{
config_processor
.
builds_for_stage_and_ref
(
"test"
,
"master"
)
}
shared_examples
'hidden_job_handling'
do
it
"doesn't create jobs that start with dot"
do
expect
(
subject
.
size
).
to
eq
(
1
)
expect
(
subject
.
first
).
to
eq
({
stage:
"test"
,
stage_idx:
1
,
name:
"normal_job"
,
commands:
"test"
,
coverage_regex:
nil
,
tag_list:
[],
options:
{
script:
[
"test"
]
},
when:
"on_success"
,
allow_failure:
false
,
environment:
nil
,
yaml_variables:
[]
})
end
end
context
'when hidden job have a script definition'
do
let
(
:config
)
do
YAML
.
dump
({
'.hidden_job'
=>
{
image:
'ruby:2.1'
,
script:
'test'
},
'normal_job'
=>
{
script:
'test'
}
})
end
it_behaves_like
'hidden_job_handling'
end
context
"when hidden job doesn't have a script definition"
do
let
(
:config
)
do
YAML
.
dump
({
'.hidden_job'
=>
{
image:
'ruby:2.1'
},
'normal_job'
=>
{
script:
'test'
}
})
end
it_behaves_like
'hidden_job_handling'
end
end
describe
"YAML Alias/Anchor"
do
let
(
:config_processor
)
{
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
)
}
subject
{
config_processor
.
builds_for_stage_and_ref
(
"build"
,
"master"
)
}
shared_examples
'job_templates_handling'
do
it
"is correctly supported for jobs"
do
expect
(
subject
.
size
).
to
eq
(
2
)
expect
(
subject
.
first
).
to
eq
({
stage:
"build"
,
stage_idx:
0
,
name:
"job1"
,
commands:
"execute-script-for-job"
,
coverage_regex:
nil
,
tag_list:
[],
options:
{
script:
[
"execute-script-for-job"
]
},
when:
"on_success"
,
allow_failure:
false
,
environment:
nil
,
yaml_variables:
[]
})
expect
(
subject
.
second
).
to
eq
({
stage:
"build"
,
stage_idx:
0
,
name:
"job2"
,
commands:
"execute-script-for-job"
,
coverage_regex:
nil
,
tag_list:
[],
options:
{
script:
[
"execute-script-for-job"
]
},
when:
"on_success"
,
allow_failure:
false
,
environment:
nil
,
yaml_variables:
[]
})
end
end
context
'when template is a job'
do
let
(
:config
)
do
<<
EOT
job1: &JOBTMPL
stage: build
script: execute-script-for-job
job2: *JOBTMPL
EOT
end
it_behaves_like
'job_templates_handling'
end
context
'when template is a hidden job'
do
let
(
:config
)
do
<<
EOT
.template: &JOBTMPL
stage: build
script: execute-script-for-job
job1: *JOBTMPL
job2: *JOBTMPL
EOT
end
it_behaves_like
'job_templates_handling'
end
context
'when job adds its own keys to a template definition'
do
let
(
:config
)
do
<<
EOT
.template: &JOBTMPL
stage: build
job1:
<<: *JOBTMPL
script: execute-script-for-job
job2:
<<: *JOBTMPL
script: execute-script-for-job
EOT
end
it_behaves_like
'job_templates_handling'
end
end
describe
"Error handling"
do
it
"fails to parse YAML"
do
expect
{
Gitlab
::
Ci
::
YamlProcessor
.
new
(
"invalid: yaml: test"
)}.
to
raise_error
(
Psych
::
SyntaxError
)
end
it
"indicates that object is invalid"
do
expect
{
Gitlab
::
Ci
::
YamlProcessor
.
new
(
"invalid_yaml"
)}.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
)
end
it
"returns errors if tags parameter is invalid"
do
config
=
YAML
.
dump
({
rspec:
{
script:
"test"
,
tags:
"mysql"
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"jobs:rspec tags should be an array of strings"
)
end
it
"returns errors if before_script parameter is invalid"
do
config
=
YAML
.
dump
({
before_script:
"bundle update"
,
rspec:
{
script:
"test"
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"before_script config should be an array of strings"
)
end
it
"returns errors if job before_script parameter is not an array of strings"
do
config
=
YAML
.
dump
({
rspec:
{
script:
"test"
,
before_script:
[
10
,
"test"
]
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"jobs:rspec:before_script config should be an array of strings"
)
end
it
"returns errors if after_script parameter is invalid"
do
config
=
YAML
.
dump
({
after_script:
"bundle update"
,
rspec:
{
script:
"test"
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"after_script config should be an array of strings"
)
end
it
"returns errors if job after_script parameter is not an array of strings"
do
config
=
YAML
.
dump
({
rspec:
{
script:
"test"
,
after_script:
[
10
,
"test"
]
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"jobs:rspec:after_script config should be an array of strings"
)
end
it
"returns errors if image parameter is invalid"
do
config
=
YAML
.
dump
({
image:
[
"test"
],
rspec:
{
script:
"test"
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"image config should be a hash or a string"
)
end
it
"returns errors if job name is blank"
do
config
=
YAML
.
dump
({
''
=>
{
script:
"test"
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"jobs:job name can't be blank"
)
end
it
"returns errors if job name is non-string"
do
config
=
YAML
.
dump
({
10
=>
{
script:
"test"
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"jobs:10 name should be a symbol"
)
end
it
"returns errors if job image parameter is invalid"
do
config
=
YAML
.
dump
({
rspec:
{
script:
"test"
,
image:
[
"test"
]
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"jobs:rspec:image config should be a hash or a string"
)
end
it
"returns errors if services parameter is not an array"
do
config
=
YAML
.
dump
({
services:
"test"
,
rspec:
{
script:
"test"
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"services config should be a array"
)
end
it
"returns errors if services parameter is not an array of strings"
do
config
=
YAML
.
dump
({
services:
[
10
,
"test"
],
rspec:
{
script:
"test"
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"service config should be a hash or a string"
)
end
it
"returns errors if job services parameter is not an array"
do
config
=
YAML
.
dump
({
rspec:
{
script:
"test"
,
services:
"test"
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"jobs:rspec:services config should be a array"
)
end
it
"returns errors if job services parameter is not an array of strings"
do
config
=
YAML
.
dump
({
rspec:
{
script:
"test"
,
services:
[
10
,
"test"
]
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"service config should be a hash or a string"
)
end
it
"returns error if job configuration is invalid"
do
config
=
YAML
.
dump
({
extra:
"bundle update"
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"jobs:extra config should be a hash"
)
end
it
"returns errors if services configuration is not correct"
do
config
=
YAML
.
dump
({
extra:
{
script:
'rspec'
,
services:
"test"
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"jobs:extra:services config should be a array"
)
end
it
"returns errors if there are no jobs defined"
do
config
=
YAML
.
dump
({
before_script:
[
"bundle update"
]
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"jobs config should contain at least one visible job"
)
end
it
"returns errors if there are no visible jobs defined"
do
config
=
YAML
.
dump
({
before_script:
[
"bundle update"
],
'.hidden'
.
to_sym
=>
{
script:
'ls'
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"jobs config should contain at least one visible job"
)
end
it
"returns errors if job allow_failure parameter is not an boolean"
do
config
=
YAML
.
dump
({
rspec:
{
script:
"test"
,
allow_failure:
"string"
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"jobs:rspec allow failure should be a boolean value"
)
end
it
"returns errors if job stage is not a string"
do
config
=
YAML
.
dump
({
rspec:
{
script:
"test"
,
type:
1
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"jobs:rspec:type config should be a string"
)
end
it
"returns errors if job stage is not a pre-defined stage"
do
config
=
YAML
.
dump
({
rspec:
{
script:
"test"
,
type:
"acceptance"
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"rspec job: stage parameter should be build, test, deploy"
)
end
it
"returns errors if job stage is not a defined stage"
do
config
=
YAML
.
dump
({
types:
%w(build test)
,
rspec:
{
script:
"test"
,
type:
"acceptance"
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"rspec job: stage parameter should be build, test"
)
end
it
"returns errors if stages is not an array"
do
config
=
YAML
.
dump
({
stages:
"test"
,
rspec:
{
script:
"test"
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"stages config should be an array of strings"
)
end
it
"returns errors if stages is not an array of strings"
do
config
=
YAML
.
dump
({
stages:
[
true
,
"test"
],
rspec:
{
script:
"test"
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"stages config should be an array of strings"
)
end
it
"returns errors if variables is not a map"
do
config
=
YAML
.
dump
({
variables:
"test"
,
rspec:
{
script:
"test"
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"variables config should be a hash of key value pairs"
)
end
it
"returns errors if variables is not a map of key-value strings"
do
config
=
YAML
.
dump
({
variables:
{
test:
false
},
rspec:
{
script:
"test"
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"variables config should be a hash of key value pairs"
)
end
it
"returns errors if job when is not on_success, on_failure or always"
do
config
=
YAML
.
dump
({
rspec:
{
script:
"test"
,
when:
1
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
,
path
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"jobs:rspec when should be on_success, on_failure, always or manual"
)
end
it
"returns errors if job artifacts:name is not an a string"
do
config
=
YAML
.
dump
({
types:
%w(build test)
,
rspec:
{
script:
"test"
,
artifacts:
{
name:
1
}
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"jobs:rspec:artifacts name should be a string"
)
end
it
"returns errors if job artifacts:when is not an a predefined value"
do
config
=
YAML
.
dump
({
types:
%w(build test)
,
rspec:
{
script:
"test"
,
artifacts:
{
when:
1
}
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"jobs:rspec:artifacts when should be on_success, on_failure or always"
)
end
it
"returns errors if job artifacts:expire_in is not an a string"
do
config
=
YAML
.
dump
({
types:
%w(build test)
,
rspec:
{
script:
"test"
,
artifacts:
{
expire_in:
1
}
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"jobs:rspec:artifacts expire in should be a duration"
)
end
it
"returns errors if job artifacts:expire_in is not an a valid duration"
do
config
=
YAML
.
dump
({
types:
%w(build test)
,
rspec:
{
script:
"test"
,
artifacts:
{
expire_in:
"7 elephants"
}
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"jobs:rspec:artifacts expire in should be a duration"
)
end
it
"returns errors if job artifacts:untracked is not an array of strings"
do
config
=
YAML
.
dump
({
types:
%w(build test)
,
rspec:
{
script:
"test"
,
artifacts:
{
untracked:
"string"
}
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"jobs:rspec:artifacts untracked should be a boolean value"
)
end
it
"returns errors if job artifacts:paths is not an array of strings"
do
config
=
YAML
.
dump
({
types:
%w(build test)
,
rspec:
{
script:
"test"
,
artifacts:
{
paths:
"string"
}
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"jobs:rspec:artifacts paths should be an array of strings"
)
end
it
"returns errors if cache:untracked is not an array of strings"
do
config
=
YAML
.
dump
({
cache:
{
untracked:
"string"
},
rspec:
{
script:
"test"
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"cache:untracked config should be a boolean value"
)
end
it
"returns errors if cache:paths is not an array of strings"
do
config
=
YAML
.
dump
({
cache:
{
paths:
"string"
},
rspec:
{
script:
"test"
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"cache:paths config should be an array of strings"
)
end
it
"returns errors if cache:key is not a string"
do
config
=
YAML
.
dump
({
cache:
{
key:
1
},
rspec:
{
script:
"test"
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"cache:key config should be a string or symbol"
)
end
it
"returns errors if job cache:key is not an a string"
do
config
=
YAML
.
dump
({
types:
%w(build test)
,
rspec:
{
script:
"test"
,
cache:
{
key:
1
}
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"jobs:rspec:cache:key config should be a string or symbol"
)
end
it
"returns errors if job cache:untracked is not an array of strings"
do
config
=
YAML
.
dump
({
types:
%w(build test)
,
rspec:
{
script:
"test"
,
cache:
{
untracked:
"string"
}
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"jobs:rspec:cache:untracked config should be a boolean value"
)
end
it
"returns errors if job cache:paths is not an array of strings"
do
config
=
YAML
.
dump
({
types:
%w(build test)
,
rspec:
{
script:
"test"
,
cache:
{
paths:
"string"
}
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"jobs:rspec:cache:paths config should be an array of strings"
)
end
it
"returns errors if job dependencies is not an array of strings"
do
config
=
YAML
.
dump
({
types:
%w(build test)
,
rspec:
{
script:
"test"
,
dependencies:
"string"
}
})
expect
do
Gitlab
::
Ci
::
YamlProcessor
.
new
(
config
)
end
.
to
raise_error
(
Gitlab
::
Ci
::
YamlProcessor
::
ValidationError
,
"jobs:rspec dependencies should be an array of strings"
)
end
end
describe
"Validate configuration templates"
do
templates
=
Dir
.
glob
(
"
#{
Rails
.
root
.
join
(
'vendor/gitlab-ci-yml'
)
}
/**/*.gitlab-ci.yml"
)
templates
.
each
do
|
file
|
it
"does not return errors for
#{
file
}
"
do
file
=
File
.
read
(
file
)
expect
{
Gitlab
::
Ci
::
YamlProcessor
.
new
(
file
)
}.
not_to
raise_error
end
end
end
describe
"#validation_message"
do
context
"when the YAML could not be parsed"
do
it
"returns an error about invalid configutaion"
do
content
=
YAML
.
dump
(
"invalid: yaml: test"
)
expect
(
Gitlab
::
Ci
::
YamlProcessor
.
validation_message
(
content
))
.
to
eq
"Invalid configuration format"
end
end
context
"when the tags parameter is invalid"
do
it
"returns an error about invalid tags"
do
content
=
YAML
.
dump
({
rspec:
{
script:
"test"
,
tags:
"mysql"
}
})
expect
(
Gitlab
::
Ci
::
YamlProcessor
.
validation_message
(
content
))
.
to
eq
"jobs:rspec tags should be an array of strings"
end
end
context
"when YAML content is empty"
do
it
"returns an error about missing content"
do
expect
(
Gitlab
::
Ci
::
YamlProcessor
.
validation_message
(
''
))
.
to
eq
"Please provide content of .gitlab-ci.yml"
end
end
context
"when the YAML is valid"
do
it
"does not return any errors"
do
content
=
File
.
read
(
Rails
.
root
.
join
(
'spec/support/gitlab_stubs/gitlab_ci.yml'
))
expect
(
Gitlab
::
Ci
::
YamlProcessor
.
validation_message
(
content
)).
to
be_nil
end
end
end
end
end
end
spec/views/ci/lints/show.html.haml_spec.rb
View file @
c45ace89
...
...
@@ -4,7 +4,7 @@ describe 'ci/lints/show' do
include
Devise
::
Test
::
ControllerHelpers
describe
'XSS protection'
do
let
(
:config_processor
)
{
Ci
::
GitlabCi
YamlProcessor
.
new
(
YAML
.
dump
(
content
))
}
let
(
:config_processor
)
{
Gitlab
::
Ci
::
YamlProcessor
.
new
(
YAML
.
dump
(
content
))
}
before
do
assign
(
:status
,
true
)
assign
(
:builds
,
config_processor
.
builds
)
...
...
@@ -59,7 +59,7 @@ describe 'ci/lints/show' do
}
end
let
(
:config_processor
)
{
Ci
::
GitlabCi
YamlProcessor
.
new
(
YAML
.
dump
(
content
))
}
let
(
:config_processor
)
{
Gitlab
::
Ci
::
YamlProcessor
.
new
(
YAML
.
dump
(
content
))
}
context
'when the content is valid'
do
before
do
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment