Commit aa4a3349 authored by Marius Bobin's avatar Marius Bobin Committed by Fabio Pitino

Allow includes:file with project in parent-child pipelines

Allow to include project files in parent-child pipelines

```yaml
my-job:
  trigger:
    strategy: depend
    include:
      - project: 'my-group/my-pipeline-library'
        ref: 'master'
        file: 'path/to/child-pipeline.yml'
```
parent b5379076
---
title: Allow to include project files in parent-child pipelines
merge_request: 43404
author:
type: fixed
...@@ -71,6 +71,18 @@ microservice_a: ...@@ -71,6 +71,18 @@ microservice_a:
- template: Security/SAST.gitlab-ci.yml - template: Security/SAST.gitlab-ci.yml
``` ```
In [GitLab 13.5](https://gitlab.com/gitlab-org/gitlab/-/issues/205157) and later,
you can use [`include:file`](yaml/README.md#includefile) to trigger child pipelines
with a configuration file in a different project:
```yaml
microservice_a:
trigger:
include:
- project: 'my-group/my-pipeline-library'
file: 'path/to/ci-config.yml'
```
NOTE: **Note:** NOTE: **Note:**
The max number of entries that are accepted for `trigger:include:` is three. The max number of entries that are accepted for `trigger:include:` is three.
......
...@@ -3733,6 +3733,22 @@ child-pipeline: ...@@ -3733,6 +3733,22 @@ child-pipeline:
The `generated-config.yml` is extracted from the artifacts and used as the configuration The `generated-config.yml` is extracted from the artifacts and used as the configuration
for triggering the child pipeline. for triggering the child pipeline.
##### Trigger child pipeline with files from another project
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/205157) in GitLab 13.5.
To trigger child pipelines with files from another private project under the same
GitLab instance, use [`include:file`](#includefile):
```yaml
child-pipeline:
trigger:
include:
- project: 'my-group/my-pipeline-library'
ref: 'master'
file: '/path/to/child-pipeline.yml'
```
#### Linking pipelines with `trigger:strategy` #### Linking pipelines with `trigger:strategy`
By default, the `trigger` job completes with the `success` status By default, the `trigger` job completes with the `success` status
......
...@@ -10,7 +10,7 @@ module Gitlab ...@@ -10,7 +10,7 @@ module Gitlab
class Include < ::Gitlab::Config::Entry::Node class Include < ::Gitlab::Config::Entry::Node
include ::Gitlab::Config::Entry::Validatable include ::Gitlab::Config::Entry::Validatable
ALLOWED_KEYS = %i[local file remote template artifact job].freeze ALLOWED_KEYS = %i[local file remote template artifact job project ref].freeze
validations do validations do
validates :config, hash_or_string: true validates :config, hash_or_string: true
...@@ -22,6 +22,10 @@ module Gitlab ...@@ -22,6 +22,10 @@ module Gitlab
if config[:artifact] && config[:job].blank? if config[:artifact] && config[:job].blank?
errors.add(:config, "must specify the job where to fetch the artifact from") errors.add(:config, "must specify the job where to fetch the artifact from")
end end
if config[:project] && config[:file].blank?
errors.add(:config, "must specify the file where to fetch the config from")
end
end end
end end
end end
......
...@@ -61,6 +61,31 @@ RSpec.describe ::Gitlab::Ci::Config::Entry::Include do ...@@ -61,6 +61,31 @@ RSpec.describe ::Gitlab::Ci::Config::Entry::Include do
end end
end end
end end
context 'when using "project"' do
context 'and specifying "ref" and "file"' do
let(:config) { { project: 'my-group/my-pipeline-library', ref: 'master', file: 'test.yml' } }
it { is_expected.to be_valid }
end
context 'without "ref"' do
let(:config) { { project: 'my-group/my-pipeline-library', file: 'test.yml' } }
it { is_expected.to be_valid }
end
context 'without "file"' do
let(:config) { { project: 'my-group/my-pipeline-library' } }
it { is_expected.not_to be_valid }
it 'has specific error' do
expect(include_entry.errors)
.to include('include config must specify the file where to fetch the config from')
end
end
end
end end
context 'when value is something else' do context 'when value is something else' do
......
...@@ -2240,47 +2240,49 @@ module Gitlab ...@@ -2240,47 +2240,49 @@ module Gitlab
end end
describe 'with parent-child pipeline' do describe 'with parent-child pipeline' do
let(:config) do
YAML.dump({
build1: { stage: 'build', script: 'test' },
test1: {
stage: 'test',
trigger: {
include: includes
}
}
})
end
context 'when artifact and job are specified' do context 'when artifact and job are specified' do
let(:config) do let(:includes) { [{ artifact: 'generated.yml', job: 'build1' }] }
YAML.dump({
build1: { stage: 'build', script: 'test' },
test1: { stage: 'test', trigger: {
include: [{ artifact: 'generated.yml', job: 'build1' }]
} }
})
end
it { is_expected.to be_valid } it { is_expected.to be_valid }
end end
context 'when job is not specified specified while artifact is' do context 'when job is not specified while artifact is' do
let(:config) do let(:includes) { [{ artifact: 'generated.yml' }] }
YAML.dump({
build1: { stage: 'build', script: 'test' },
test1: { stage: 'test', trigger: {
include: [{ artifact: 'generated.yml' }]
} }
})
end
it_behaves_like 'returns errors', /include config must specify the job where to fetch the artifact from/ it_behaves_like 'returns errors', /include config must specify the job where to fetch the artifact from/
end end
context 'when include is a string' do context 'when project and file are specified' do
let(:config) do let(:includes) do
YAML.dump({ [{ file: 'generated.yml', project: 'my-namespace/my-project' }]
build1: { stage: 'build', script: 'test' },
test1: {
stage: 'test',
trigger: {
include: 'generated.yml'
}
}
})
end end
it { is_expected.to be_valid } it { is_expected.to be_valid }
end end
context 'when file is not specified while project is' do
let(:includes) { [{ project: 'something' }] }
it_behaves_like 'returns errors', /include config must specify the file where to fetch the config from/
end
context 'when include is a string' do
let(:includes) { 'generated.yml' }
it { is_expected.to be_valid }
end
end end
describe "Error handling" do describe "Error handling" do
......
...@@ -22,7 +22,7 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do ...@@ -22,7 +22,7 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do
end end
shared_examples 'successful creation' do shared_examples 'successful creation' do
it 'creates bridge jobs correctly' do it 'creates bridge jobs correctly', :aggregate_failures do
pipeline = create_pipeline! pipeline = create_pipeline!
test = pipeline.statuses.find_by(name: 'test') test = pipeline.statuses.find_by(name: 'test')
...@@ -221,6 +221,65 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do ...@@ -221,6 +221,65 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do
end end
end end
end end
context 'when including configs from a project' do
context 'when specifying all attributes' do
let(:config) do
<<~YAML
test:
script: rspec
deploy:
variables:
CROSS: downstream
stage: deploy
trigger:
include:
- project: my-namespace/my-project
file: 'path/to/child.yml'
ref: 'master'
YAML
end
it_behaves_like 'successful creation' do
let(:expected_bridge_options) do
{
'trigger' => {
'include' => [
{
'file' => 'path/to/child.yml',
'project' => 'my-namespace/my-project',
'ref' => 'master'
}
]
}
}
end
end
end
context 'without specifying file' do
let(:config) do
<<~YAML
test:
script: rspec
deploy:
variables:
CROSS: downstream
stage: deploy
trigger:
include:
- project: my-namespace/my-project
ref: 'master'
YAML
end
it_behaves_like 'creation failure' do
let(:expected_error) do
/include config must specify the file where to fetch the config from/
end
end
end
end
end end
def create_pipeline! def create_pipeline!
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment