Commit c935a902 authored by Furkan Ayhan's avatar Furkan Ayhan Committed by Shinya Maeda

Implement passing dotenv variables to bridge jobs

It is behind a FF ci_bridge_dependency_variables
parent 4c839b62
......@@ -7,6 +7,7 @@ module Ci
include Importable
include AfterCommitQueue
include Ci::HasRef
extend ::Gitlab::Utils::Override
InvalidBridgeTypeError = Class.new(StandardError)
InvalidTransitionError = Class.new(StandardError)
......@@ -203,8 +204,11 @@ module Ci
end
end
override :dependency_variables
def dependency_variables
[]
return [] unless ::Feature.enabled?(:ci_bridge_dependency_variables, project)
super
end
def target_revision_ref
......
......@@ -571,14 +571,6 @@ module Ci
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
{ trace_sections: true }
end
......@@ -828,10 +820,6 @@ module Ci
Gitlab::Ci::Build::Credentials::Factory.new(self).create!
end
def all_dependencies
dependencies.all
end
def has_valid_build_dependencies?
dependencies.valid?
end
......@@ -994,12 +982,6 @@ module Ci
end
end
def dependencies
strong_memoize(:dependencies) do
Ci::BuildDependencies.new(self)
end
end
def build_data
@build_data ||= Gitlab::DataBuilder::Build.build(self)
end
......
......@@ -103,5 +103,25 @@ module Ci
pipeline.ensure_scheduling_type!
reset
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 all_dependencies
dependencies.all
end
private
def dependencies
strong_memoize(:dependencies) do
Ci::BuildDependencies.new(self)
end
end
end
end
---
name: ci_bridge_dependency_variables
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46530
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/273734
type: development
group: group::pipeline authoring
default_enabled: false
# frozen_string_literal: true
require 'faker'
module QA
RSpec.describe 'Verify', :runner, :requires_admin do
describe "Pass dotenv variables to downstream via bridge" do
let(:feature_flag) { :ci_bridge_dependency_variables }
let(:executor_1) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(8)}" }
let(:executor_2) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(8)}" }
let(:upstream_project) do
Resource::Project.fabricate_via_api! do |project|
project.name = 'project-with-pipeline-1'
end
end
let(:downstream_project) do
Resource::Project.fabricate_via_api! do |project|
project.name = 'project-with-pipeline-2'
end
end
let!(:runner_1) do
Resource::Runner.fabricate! do |runner|
runner.project = upstream_project
runner.name = executor_1
runner.tags = [executor_1]
end
end
let!(:runner_2) do
Resource::Runner.fabricate! do |runner|
runner.project = downstream_project
runner.name = executor_2
runner.tags = [executor_2]
end
end
before do
Runtime::Feature.enable(feature_flag)
Flow::Login.sign_in
add_ci_file(downstream_project, downstream_ci_file)
add_ci_file(upstream_project, upstream_ci_file)
upstream_project.visit!
Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'success')
end
after do
Runtime::Feature.disable(feature_flag)
runner_1.remove_via_api!
runner_2.remove_via_api!
end
it 'runs the pipeline with composed config', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1086' do
Page::Project::Pipeline::Show.perform do |parent_pipeline|
Support::Waiter.wait_until { parent_pipeline.has_child_pipeline? }
parent_pipeline.expand_child_pipeline
parent_pipeline.click_job('downstream_test')
end
Page::Project::Job::Show.perform do |show|
expect(show).to have_passed(timeout: 360)
end
end
private
def add_ci_file(project, file)
Resource::Repository::Commit.fabricate_via_api! do |commit|
commit.project = project
commit.commit_message = 'Add config file'
commit.add_files([file])
end
end
def upstream_ci_file
{
file_path: '.gitlab-ci.yml',
content: <<~YAML
build:
stage: build
tags: ["#{executor_1}"]
script: echo "MY_VAR=hello" >> variables.env
artifacts:
reports:
dotenv: variables.env
trigger:
stage: deploy
variables:
PASSED_MY_VAR: $MY_VAR
trigger: #{downstream_project.full_path}
YAML
}
end
def downstream_ci_file
{
file_path: '.gitlab-ci.yml',
content: <<~YAML
downstream_test:
stage: test
tags: ["#{executor_2}"]
script: '[ "$PASSED_MY_VAR" = hello ]; exit "$?"'
YAML
}
end
end
end
end
......@@ -55,6 +55,17 @@ RSpec.describe Ci::Bridge do
expect(bridge.scoped_variables_hash.keys).to include(*variables)
end
context 'when bridge has dependency which has dotenv variable' do
let(:test) { create(:ci_build, pipeline: pipeline, stage_idx: 0) }
let(:bridge) { create(:ci_bridge, pipeline: pipeline, stage_idx: 1, options: { dependencies: [test.name] }) }
let!(:job_variable) { create(:ci_job_variable, :dotenv_source, job: test) }
it 'includes inherited variable' do
expect(bridge.scoped_variables_hash).to include(job_variable.key => job_variable.value)
end
end
end
describe 'state machine transitions' do
......@@ -357,4 +368,53 @@ RSpec.describe Ci::Bridge do
it { is_expected.to be_falsey }
end
end
describe '#dependency_variables' do
subject { bridge.dependency_variables }
shared_context 'when ci_bridge_dependency_variables is disabled' do
before do
stub_feature_flags(ci_bridge_dependency_variables: false)
end
it { is_expected.to be_empty }
end
context 'when downloading from previous stages' do
let!(:prepare1) { create(:ci_build, name: 'prepare1', pipeline: pipeline, stage_idx: 0) }
let!(:bridge) { create(:ci_bridge, pipeline: pipeline, stage_idx: 1) }
let!(:job_variable_1) { create(:ci_job_variable, :dotenv_source, job: prepare1) }
let!(:job_variable_2) { create(:ci_job_variable, job: prepare1) }
it 'inherits only dependent variables' do
expect(subject.to_hash).to eq(job_variable_1.key => job_variable_1.value)
end
it_behaves_like 'when ci_bridge_dependency_variables is disabled'
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!(:bridge) do
create(:ci_bridge, pipeline: pipeline,
stage_idx: 1,
scheduling_type: 'dag',
needs_attributes: [{ name: 'prepare1', artifacts: true },
{ name: 'prepare2', artifacts: false }])
end
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
it_behaves_like 'when ci_bridge_dependency_variables is disabled'
end
end
end
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