From 9a271d80128451eecc9a301d5e9924c09740be07 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 11 Mar 2016 14:15:13 +0100 Subject: [PATCH] Allow to define on which builds the current one depends on --- CHANGELOG | 1 + doc/ci/yaml/README.md | 55 ++++++++++++++++++++ lib/ci/gitlab_ci_yaml_processor.rb | 21 +++++++- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 45 ++++++++++++++++ 4 files changed, 121 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index c171d662b8d..fa634cc20db 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -22,6 +22,7 @@ v 8.6.0 (unreleased) - Add support for cross-project label references - Update documentation to reflect Guest role not being enforced on internal projects - Allow search for logged out users + - Allow to define on which builds the current one depends on - Fix bug where Bitbucket `closed` issues were imported as `opened` (Iuri de Silvio) - Don't show Issues/MRs from archived projects in Groups view - Increase the notes polling timeout over time (Roberto Dip) diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 9a1f86cec45..fb62ed25d64 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -241,6 +241,7 @@ job_name: | tags | no | Defines a list of tags which are used to select runner | | allow_failure | no | Allow build to fail. Failed build doesn't contribute to commit status | | when | no | Define when to run build. Can be `on_success`, `on_failure` or `always` | +| dependencies | no | Define a builds that this build depends on | | artifacts | no | Define list build artifacts | | cache | no | Define list of files that should be cached between subsequent runs | @@ -514,6 +515,60 @@ job: untracked: true ``` +### dependencies + +_**Note:** Introduced in GitLab 8.6 and GitLab Runner v1.1.1._ + +This feature should be used with `artifacts` and allows to define artifacts passing between different builds. + +`artifacts` from previous stages are passed by default. + +To use a feature define `dependencies` in context of the build and pass +a list of all previous builds from which the artifacts should be downloaded. +You can only define a builds from stages that are executed before this one. +Error will be shown if you define builds from current stage or next stages. + +How to use artifacts passing between stages: + +``` +build:osx: + stage: build + script: ... + artifacts: + paths: + - binaries/ + +build:linux: + stage: build + script: ... + artifacts: + paths: + - binaries/ + +test:osx: + stage: test + script: ... + dependencies: + - build:osx + +test:linux: + stage: test + script: ... + dependencies: + - build:linux + +deploy: + stage: deploy + script: ... +``` + +The above will create a build artifacts for two jobs: `build:osx` and `build:linux`. +When executing the `test:osx` the artifacts for `build:osx` will be downloaded and extracted in context of the build. +The same happens for `test:linux` and artifacts from `build:linux`. + +The job `deploy` will download artifacts from all previous builds. +However, only the `build:osx` and `build:linux` exports artifacts so only these will be downloaded. + ### cache _**Note:** Introduced in GitLab Runner v0.7.0._ diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index ce3d0138268..04b58cf1cd3 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -5,7 +5,9 @@ module Ci DEFAULT_STAGES = %w(build test deploy) DEFAULT_STAGE = 'test' ALLOWED_YAML_KEYS = [:before_script, :image, :services, :types, :stages, :variables, :cache] - ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services, :allow_failure, :type, :stage, :when, :artifacts, :cache] + ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services, + :allow_failure, :type, :stage, :when, :artifacts, :cache, + :dependencies] attr_reader :before_script, :image, :services, :variables, :path, :cache @@ -145,6 +147,7 @@ module Ci validate_job_stage!(name, job) if job[:stage] validate_job_cache!(name, job) if job[:cache] validate_job_artifacts!(name, job) if job[:artifacts] + validate_job_dependencies!(name, job) if job[:dependencies] end private @@ -231,6 +234,22 @@ module Ci end end + def validate_job_dependencies!(name, job) + if !validate_array_of_strings(job[:dependencies]) + raise ValidationError, "#{name} job: dependencies parameter should be an array of strings" + end + + stage_index = stages.index(job[:stage]) + + job[:dependencies].each do |dependency| + raise ValidationError, "#{name} job: undefined dependency: #{dependency}" unless @jobs[dependency] + + unless stages.index(@jobs[dependency][:stage]) < stage_index + raise ValidationError, "#{name} job: dependency #{dependency} is not defined in prior stages" + end + end + end + def validate_array_of_strings(values) values.is_a?(Array) && values.all? { |value| validate_string(value) } end diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index 44d2b9eb1f7..fe5096989b2 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -428,6 +428,44 @@ module Ci 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 }.to_not raise_error } + end + + context 'dependencies to builds' do + let(:dependencies) { [:build1, :build2] } + + it { expect { subject }.to_not 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) do YAML.dump({ @@ -682,6 +720,13 @@ module Ci GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: cache:paths parameter should be an array of strings") end + + it "returns errors if job dependencies is not an array of strings" do + config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", dependencies: "string" } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: dependencies parameter should be an array of strings") + end end end end