From 16084ee9d3fdfc333a113e4cbcd3f6d2fc402628 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= <matteeyah@gmail.com>
Date: Wed, 31 Jul 2019 17:36:57 +0200
Subject: [PATCH] Port changes from EE

Ports changes from
https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/12343
---
 app/models/ci/pipeline.rb                     |  4 ++++
 doc/ci/multi_project_pipelines.md             | 15 ++++++++++++
 lib/gitlab/ci/pipeline/seed/build.rb          |  5 +++-
 lib/gitlab/ci/yaml_processor.rb               |  3 ++-
 spec/factories/ci/bridge.rb                   |  9 ++++++-
 .../lib/gitlab/ci/pipeline/seed/build_spec.rb | 24 +++++++++++++++----
 spec/lib/gitlab/ci/yaml_processor_spec.rb     |  5 +++-
 spec/models/ci/bridge_spec.rb                 |  2 +-
 spec/models/ci/pipeline_spec.rb               |  7 ++++++
 9 files changed, 65 insertions(+), 9 deletions(-)

diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index ffab4e82f90..754c7a5985f 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -328,6 +328,10 @@ module Ci
       config_sources.values_at(:repository_source, :auto_devops_source, :unknown_source)
     end
 
+    def self.bridgeable_statuses
+      ::Ci::Pipeline::AVAILABLE_STATUSES - %w[created preparing pending]
+    end
+
     def stages_count
       statuses.select(:stage).distinct.count
     end
diff --git a/doc/ci/multi_project_pipelines.md b/doc/ci/multi_project_pipelines.md
index ced4344a0b0..cb8d383f7d9 100644
--- a/doc/ci/multi_project_pipelines.md
+++ b/doc/ci/multi_project_pipelines.md
@@ -176,6 +176,21 @@ Upstream pipelines take precedence over downstream ones. If there are two
 variables with the same name defined in both upstream and downstream projects,
 the ones defined in the upstream project will take precedence.
 
+### Mirroring status from upstream pipeline
+
+You can mirror the pipeline status from an upstream pipeline to a bridge job by
+using the `needs:pipeline` keyword. The latest pipeline status from master is
+replicated to the bridge job.
+
+Example:
+
+```yaml
+upstream_bridge:
+  stage: test
+  needs:
+    pipeline: other/project
+```
+
 ### Limitations
 
 Because bridge jobs are a little different to regular jobs, it is not
diff --git a/lib/gitlab/ci/pipeline/seed/build.rb b/lib/gitlab/ci/pipeline/seed/build.rb
index ab0d4c38ab6..175f146c5b3 100644
--- a/lib/gitlab/ci/pipeline/seed/build.rb
+++ b/lib/gitlab/ci/pipeline/seed/build.rb
@@ -45,7 +45,10 @@ module Gitlab
           end
 
           def bridge?
-            @attributes.to_h.dig(:options, :trigger).present?
+            attributes_hash = @attributes.to_h
+            attributes_hash.dig(:options, :trigger).present? ||
+              (attributes_hash.dig(:options, :bridge_needs).instance_of?(Hash) &&
+               attributes_hash.dig(:options, :bridge_needs, :pipeline).present?)
           end
 
           def all_of_only?
diff --git a/lib/gitlab/ci/yaml_processor.rb b/lib/gitlab/ci/yaml_processor.rb
index 998130e5bd0..2e1eab270ff 100644
--- a/lib/gitlab/ci/yaml_processor.rb
+++ b/lib/gitlab/ci/yaml_processor.rb
@@ -55,7 +55,8 @@ module Gitlab
             parallel: job[:parallel],
             instance: job[:instance],
             start_in: job[:start_in],
-            trigger: job[:trigger]
+            trigger: job[:trigger],
+            bridge_needs: job[:needs]
           }.compact }.compact
       end
 
diff --git a/spec/factories/ci/bridge.rb b/spec/factories/ci/bridge.rb
index 6491b9dca19..b1b714277e4 100644
--- a/spec/factories/ci/bridge.rb
+++ b/spec/factories/ci/bridge.rb
@@ -8,7 +8,7 @@ FactoryBot.define do
     ref 'master'
     tag false
     created_at 'Di 29. Okt 09:50:00 CET 2013'
-    status :success
+    status :created
 
     pipeline factory: :ci_pipeline
 
@@ -17,6 +17,7 @@ FactoryBot.define do
     end
 
     transient { downstream nil }
+    transient { upstream nil }
 
     after(:build) do |bridge, evaluator|
       bridge.project ||= bridge.pipeline.project
@@ -26,6 +27,12 @@ FactoryBot.define do
           trigger: { project: evaluator.downstream.full_path }
         )
       end
+
+      if evaluator.upstream.present?
+        bridge.options = bridge.options.to_h.merge(
+          bridge_needs: { pipeline: evaluator.upstream.full_path }
+        )
+      end
     end
   end
 end
diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
index 762025f9bd9..1b8e86b1f18 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
@@ -20,20 +20,36 @@ describe Gitlab::Ci::Pipeline::Seed::Build do
   describe '#bridge?' do
     subject { seed_build.bridge? }
 
-    context 'when job is a bridge' do
+    context 'when job is a downstream bridge' do
       let(:attributes) do
         { name: 'rspec', ref: 'master', options: { trigger: 'my/project' } }
       end
 
       it { is_expected.to be_truthy }
+
+      context 'when trigger definition is empty' do
+        let(:attributes) do
+          { name: 'rspec', ref: 'master', options: { trigger: '' } }
+        end
+
+        it { is_expected.to be_falsey }
+      end
     end
 
-    context 'when trigger definition is empty' do
+    context 'when job is an upstream bridge' do
       let(:attributes) do
-        { name: 'rspec', ref: 'master', options: { trigger: '' } }
+        { name: 'rspec', ref: 'master', options: { bridge_needs: { pipeline: 'my/project' } } }
       end
 
-      it { is_expected.to be_falsey }
+      it { is_expected.to be_truthy }
+
+      context 'when upstream definition is empty' do
+        let(:attributes) do
+          { name: 'rspec', ref: 'master', options: { bridge_needs: { pipeline: '' } } }
+        end
+
+        it { is_expected.to be_falsey }
+      end
     end
 
     context 'when job is not a bridge' do
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index 4ffa1fc9fd8..d5567b4f166 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -1153,7 +1153,10 @@ module Gitlab
               stage_idx: 1,
               name: "test1",
               options: {
-                script: ["test"]
+                script: ["test"],
+                # This does not make sense, there is a follow-up:
+                # https://gitlab.com/gitlab-org/gitlab-ce/issues/65569
+                bridge_needs: %w[build1 build2]
               },
               needs_attributes: [
                 { name: "build1" },
diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb
index eb32198265b..a871f9b3fe6 100644
--- a/spec/models/ci/bridge_spec.rb
+++ b/spec/models/ci/bridge_spec.rb
@@ -23,7 +23,7 @@ describe Ci::Bridge do
     let(:status) { bridge.detailed_status(user) }
 
     it 'returns detailed status object' do
-      expect(status).to be_a Gitlab::Ci::Status::Success
+      expect(status).to be_a Gitlab::Ci::Status::Created
     end
   end
 
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 1fb83fbb088..78be4a8131a 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -1929,6 +1929,13 @@ describe Ci::Pipeline, :mailer do
     it { is_expected.to be_an(Array) }
   end
 
+  describe '.bridgeable_statuses' do
+    subject { described_class.bridgeable_statuses }
+
+    it { is_expected.to be_an(Array) }
+    it { is_expected.not_to include('created', 'preparing', 'pending') }
+  end
+
   describe '#status' do
     let(:build) do
       create(:ci_build, :created, pipeline: pipeline, name: 'test')
-- 
2.30.9