Commit f5d36a61 authored by Mayra Cabrera's avatar Mayra Cabrera

Merge branch '22638-inherit-environment-variables-from-dependent-jobs' into 'master'

Implement passing ENV variables from dependent jobs

See merge request gitlab-org/gitlab!30509
parents b7eb9ddc cddcdc46
...@@ -166,6 +166,10 @@ module Ci ...@@ -166,6 +166,10 @@ module Ci
end end
end end
def dependency_variables
[]
end
private private
def cross_project_params def cross_project_params
......
...@@ -604,6 +604,14 @@ module Ci ...@@ -604,6 +604,14 @@ module Ci
Ci::FreezePeriodStatus.new(project: project).execute Ci::FreezePeriodStatus.new(project: project).execute
end end
def dependency_variables
return [] if all_dependencies.empty?
Gitlab::Ci::Variables::Collection.new.concat(
Ci::JobVariable.where(job: all_dependencies).dotenv_source
)
end
def features def features
{ trace_sections: true } { trace_sections: true }
end end
......
...@@ -18,6 +18,7 @@ module Ci ...@@ -18,6 +18,7 @@ module Ci
variables.concat(deployment_variables(environment: environment)) variables.concat(deployment_variables(environment: environment))
variables.concat(yaml_variables) variables.concat(yaml_variables)
variables.concat(user_variables) variables.concat(user_variables)
variables.concat(dependency_variables) if Feature.enabled?(:ci_dependency_variables, project)
variables.concat(secret_group_variables) variables.concat(secret_group_variables)
variables.concat(secret_project_variables(environment: environment)) variables.concat(secret_project_variables(environment: environment))
variables.concat(trigger_request.user_variables) if trigger_request variables.concat(trigger_request.user_variables) if trigger_request
......
...@@ -394,6 +394,73 @@ Once you set them, they will be available for all subsequent pipelines. Any grou ...@@ -394,6 +394,73 @@ Once you set them, they will be available for all subsequent pipelines. Any grou
![CI/CD settings - inherited variables](img/inherited_group_variables_v12_5.png) ![CI/CD settings - inherited variables](img/inherited_group_variables_v12_5.png)
### Inherit environment variables
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22638) in GitLab 13.0.
> - It's deployed behind a feature flag (`ci_dependency_variables`), disabled by default.
You can inherit environment variables from dependent jobs.
This feature makes use of the [`artifacts:reports:dotenv`](../pipelines/job_artifacts.md#artifactsreportsdotenv) report feature.
Example with [`dependencies`](../yaml/README.md#dependencies) keyword.
```yaml
build:
stage: build
script:
- echo "BUILD_VERSION=hello" >> build.env
artifacts:
reports:
dotenv: build.env
deploy:
stage: deploy
script:
- echo $BUILD_VERSION # => hello
dependencies:
- build
```
Example with the [`needs`](../yaml/README.md#artifact-downloads-with-needs) keyword:
```yaml
build:
stage: build
script:
- echo "BUILD_VERSION=hello" >> build.env
artifacts:
reports:
dotenv: build.env
deploy:
stage: deploy
script:
- echo $BUILD_VERSION # => hello
needs:
- job: build
artifacts: true
```
### Enable inherited environment variables **(CORE ONLY)**
The Inherited Environment Variables feature is under development and not ready for production use. It is
deployed behind a feature flag that is **disabled by default**.
[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
can enable it for your instance.
To enable it:
```ruby
Feature.enable(:ci_dependency_variables)
```
To disable it:
```ruby
Feature.disable(:ci_dependency_variables)
```
## Priority of environment variables ## Priority of environment variables
Variables of different types can take precedence over other Variables of different types can take precedence over other
...@@ -404,6 +471,7 @@ The order of precedence for variables is (from highest to lowest): ...@@ -404,6 +471,7 @@ The order of precedence for variables is (from highest to lowest):
1. [Trigger variables](../triggers/README.md#making-use-of-trigger-variables) or [scheduled pipeline variables](../pipelines/schedules.md#using-variables). 1. [Trigger variables](../triggers/README.md#making-use-of-trigger-variables) or [scheduled pipeline variables](../pipelines/schedules.md#using-variables).
1. Project-level [variables](#custom-environment-variables) or [protected variables](#protect-a-custom-variable). 1. Project-level [variables](#custom-environment-variables) or [protected variables](#protect-a-custom-variable).
1. Group-level [variables](#group-level-environment-variables) or [protected variables](#protect-a-custom-variable). 1. Group-level [variables](#group-level-environment-variables) or [protected variables](#protect-a-custom-variable).
1. [Inherited environment variables](#inherit-environment-variables).
1. YAML-defined [job-level variables](../yaml/README.md#variables). 1. YAML-defined [job-level variables](../yaml/README.md#variables).
1. YAML-defined [global variables](../yaml/README.md#variables). 1. YAML-defined [global variables](../yaml/README.md#variables).
1. [Deployment variables](#deployment-environment-variables). 1. [Deployment variables](#deployment-environment-variables).
......
...@@ -2375,12 +2375,14 @@ describe Ci::Build do ...@@ -2375,12 +2375,14 @@ describe Ci::Build do
let(:pipeline_pre_var) { { key: 'pipeline', value: 'value', public: true, masked: false } } let(:pipeline_pre_var) { { key: 'pipeline', value: 'value', public: true, masked: false } }
let(:build_yaml_var) { { key: 'yaml', value: 'value', public: true, masked: false } } let(:build_yaml_var) { { key: 'yaml', value: 'value', public: true, masked: false } }
let(:job_jwt_var) { { key: 'CI_JOB_JWT', value: 'ci.job.jwt', public: false, masked: true } } let(:job_jwt_var) { { key: 'CI_JOB_JWT', value: 'ci.job.jwt', public: false, masked: true } }
let(:job_dependency_var) { { key: 'job_dependency', value: 'value', public: true, masked: false } }
before do before do
allow(build).to receive(:predefined_variables) { [build_pre_var] } allow(build).to receive(:predefined_variables) { [build_pre_var] }
allow(build).to receive(:yaml_variables) { [build_yaml_var] } allow(build).to receive(:yaml_variables) { [build_yaml_var] }
allow(build).to receive(:persisted_variables) { [] } allow(build).to receive(:persisted_variables) { [] }
allow(build).to receive(:job_jwt_variables) { [job_jwt_var] } allow(build).to receive(:job_jwt_variables) { [job_jwt_var] }
allow(build).to receive(:dependency_variables) { [job_dependency_var] }
allow_any_instance_of(Project) allow_any_instance_of(Project)
.to receive(:predefined_variables) { [project_pre_var] } .to receive(:predefined_variables) { [project_pre_var] }
...@@ -2398,6 +2400,7 @@ describe Ci::Build do ...@@ -2398,6 +2400,7 @@ describe Ci::Build do
project_pre_var, project_pre_var,
pipeline_pre_var, pipeline_pre_var,
build_yaml_var, build_yaml_var,
job_dependency_var,
{ key: 'secret', value: 'value', public: false, masked: false }]) { key: 'secret', value: 'value', public: false, masked: false }])
end end
end end
...@@ -3008,6 +3011,15 @@ describe Ci::Build do ...@@ -3008,6 +3011,15 @@ describe Ci::Build do
end end
end end
end end
context 'when build has dependency which has dotenv variable' do
let!(:prepare) { create(:ci_build, pipeline: pipeline, stage_idx: 0) }
let!(:build) { create(:ci_build, pipeline: pipeline, stage_idx: 1, options: { dependencies: [prepare.name] }) }
let!(:job_variable) { create(:ci_job_variable, :dotenv_source, job: prepare) }
it { is_expected.to include(key: job_variable.key, value: job_variable.value, public: false, masked: false) }
end
end end
describe '#scoped_variables' do describe '#scoped_variables' do
...@@ -3070,6 +3082,33 @@ describe Ci::Build do ...@@ -3070,6 +3082,33 @@ describe Ci::Build do
end end
end end
end end
context 'with dependency variables' do
let!(:prepare) { create(:ci_build, name: 'prepare', pipeline: pipeline, stage_idx: 0) }
let!(:build) { create(:ci_build, pipeline: pipeline, stage_idx: 1, options: { dependencies: ['prepare'] }) }
let!(:job_variable) { create(:ci_job_variable, :dotenv_source, job: prepare) }
context 'FF ci_dependency_variables is enabled' do
before do
stub_feature_flags(ci_dependency_variables: true)
end
it 'inherits dependent variables' do
expect(build.scoped_variables.to_hash).to include(job_variable.key => job_variable.value)
end
end
context 'FF ci_dependency_variables is disabled' do
before do
stub_feature_flags(ci_dependency_variables: false)
end
it 'does not inherit dependent variables' do
expect(build.scoped_variables.to_hash).not_to include(job_variable.key => job_variable.value)
end
end
end
end end
describe '#secret_group_variables' do describe '#secret_group_variables' do
...@@ -3314,6 +3353,41 @@ describe Ci::Build do ...@@ -3314,6 +3353,41 @@ describe Ci::Build do
end end
end end
describe '#dependency_variables' do
subject { build.dependency_variables }
context 'when using dependencies' do
let!(:prepare1) { create(:ci_build, name: 'prepare1', pipeline: pipeline, stage_idx: 0) }
let!(:prepare2) { create(:ci_build, name: 'prepare2', pipeline: pipeline, stage_idx: 0) }
let!(:build) { create(:ci_build, pipeline: pipeline, stage_idx: 1, options: { dependencies: ['prepare1'] }) }
let!(:job_variable_1) { create(:ci_job_variable, :dotenv_source, job: prepare1) }
let!(:job_variable_2) { create(:ci_job_variable, job: prepare1) }
let!(:job_variable_3) { create(:ci_job_variable, :dotenv_source, job: prepare2) }
it 'inherits only dependent variables' do
expect(subject.to_hash).to eq(job_variable_1.key => job_variable_1.value)
end
end
context 'when using needs' do
let!(:prepare1) { create(:ci_build, name: 'prepare1', pipeline: pipeline, stage_idx: 0) }
let!(:prepare2) { create(:ci_build, name: 'prepare2', pipeline: pipeline, stage_idx: 0) }
let!(:prepare3) { create(:ci_build, name: 'prepare3', pipeline: pipeline, stage_idx: 0) }
let!(:build) { create(:ci_build, pipeline: pipeline, stage_idx: 1, scheduling_type: 'dag') }
let!(:build_needs_prepare1) { create(:ci_build_need, build: build, name: 'prepare1', artifacts: true) }
let!(:build_needs_prepare2) { create(:ci_build_need, build: build, name: 'prepare2', artifacts: false) }
let!(:job_variable_1) { create(:ci_job_variable, :dotenv_source, job: prepare1) }
let!(:job_variable_2) { create(:ci_job_variable, :dotenv_source, job: prepare2) }
let!(:job_variable_3) { create(:ci_job_variable, :dotenv_source, job: prepare3) }
it 'inherits only needs with artifacts variables' do
expect(subject.to_hash).to eq(job_variable_1.key => job_variable_1.value)
end
end
end
describe 'state transition: any => [:preparing]' do describe 'state transition: any => [:preparing]' do
let(:build) { create(:ci_build, :created) } let(:build) { create(:ci_build, :created) }
......
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