Commit 34bf6b59 authored by Matija Čupić's avatar Matija Čupić

Implement Ci::Sources::Project for subscriptions

Implements Ci::Sources::Project for tracking pipelines triggered by a
subscription in an upstream project.
parent 883e5e0d
# frozen_string_literal: true
class CreateCiSourcesProjects < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
create_table :ci_sources_projects do |t|
t.bigint :pipeline_id, null: false
t.bigint :source_project_id, null: false
t.index [:source_project_id, :pipeline_id], unique: true
t.index :pipeline_id
end
end
end
# frozen_string_literal: true
class AddCiSourcesProjectPipelineForeignKey < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
with_lock_retries do
add_foreign_key :ci_sources_projects, :ci_pipelines, column: :pipeline_id, on_delete: :cascade # rubocop:disable Migration/AddConcurrentForeignKey
end
end
end
# frozen_string_literal: true
class AddCiSourcesProjectSourceProjectForeignKey < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
with_lock_retries do
add_foreign_key :ci_sources_projects, :projects, column: :source_project_id, on_delete: :cascade # rubocop:disable Migration/AddConcurrentForeignKey
end
end
end
...@@ -966,6 +966,13 @@ ActiveRecord::Schema.define(version: 2020_03_04_160823) do ...@@ -966,6 +966,13 @@ ActiveRecord::Schema.define(version: 2020_03_04_160823) do
t.index ["source_project_id"], name: "index_ci_sources_pipelines_on_source_project_id" t.index ["source_project_id"], name: "index_ci_sources_pipelines_on_source_project_id"
end end
create_table "ci_sources_projects", force: :cascade do |t|
t.bigint "pipeline_id", null: false
t.bigint "source_project_id", null: false
t.index ["pipeline_id"], name: "index_ci_sources_projects_on_pipeline_id"
t.index ["source_project_id", "pipeline_id"], name: "index_ci_sources_projects_on_source_project_id_and_pipeline_id", unique: true
end
create_table "ci_stages", id: :serial, force: :cascade do |t| create_table "ci_stages", id: :serial, force: :cascade do |t|
t.integer "project_id" t.integer "project_id"
t.integer "pipeline_id" t.integer "pipeline_id"
...@@ -4724,6 +4731,8 @@ ActiveRecord::Schema.define(version: 2020_03_04_160823) do ...@@ -4724,6 +4731,8 @@ ActiveRecord::Schema.define(version: 2020_03_04_160823) do
add_foreign_key "ci_sources_pipelines", "ci_pipelines", column: "source_pipeline_id", name: "fk_d4e29af7d7", on_delete: :cascade add_foreign_key "ci_sources_pipelines", "ci_pipelines", column: "source_pipeline_id", name: "fk_d4e29af7d7", on_delete: :cascade
add_foreign_key "ci_sources_pipelines", "projects", column: "source_project_id", name: "fk_acd9737679", on_delete: :cascade add_foreign_key "ci_sources_pipelines", "projects", column: "source_project_id", name: "fk_acd9737679", on_delete: :cascade
add_foreign_key "ci_sources_pipelines", "projects", name: "fk_1e53c97c0a", on_delete: :cascade add_foreign_key "ci_sources_pipelines", "projects", name: "fk_1e53c97c0a", on_delete: :cascade
add_foreign_key "ci_sources_projects", "ci_pipelines", column: "pipeline_id", on_delete: :cascade
add_foreign_key "ci_sources_projects", "projects", column: "source_project_id", on_delete: :cascade
add_foreign_key "ci_stages", "ci_pipelines", column: "pipeline_id", name: "fk_fb57e6cc56", on_delete: :cascade add_foreign_key "ci_stages", "ci_pipelines", column: "pipeline_id", name: "fk_fb57e6cc56", on_delete: :cascade
add_foreign_key "ci_stages", "projects", name: "fk_2360681d1d", on_delete: :cascade add_foreign_key "ci_stages", "projects", name: "fk_2360681d1d", on_delete: :cascade
add_foreign_key "ci_subscriptions_projects", "projects", column: "downstream_project_id", on_delete: :cascade add_foreign_key "ci_subscriptions_projects", "projects", column: "downstream_project_id", on_delete: :cascade
......
# frozen_string_literal: true
module Ci
module Sources
class Project < ApplicationRecord
self.table_name = "ci_sources_projects"
belongs_to :pipeline, class_name: "Ci::Pipeline", optional: false
belongs_to :source_project, class_name: "Project", foreign_key: :source_project_id, optional: false
validates :pipeline_id, uniqueness: { scope: :source_project_id }
end
end
end
...@@ -25,6 +25,8 @@ module EE ...@@ -25,6 +25,8 @@ module EE
has_many :downstream_bridges, class_name: '::Ci::Bridge', foreign_key: :upstream_pipeline_id has_many :downstream_bridges, class_name: '::Ci::Bridge', foreign_key: :upstream_pipeline_id
has_many :security_scans, class_name: 'Security::Scan', through: :builds has_many :security_scans, class_name: 'Security::Scan', through: :builds
has_one :source_project, class_name: 'Ci::Sources::Project', foreign_key: :pipeline_id
# Legacy way to fetch security reports based on job name. This has been replaced by the reports feature. # Legacy way to fetch security reports based on job name. This has been replaced by the reports feature.
scope :with_legacy_security_reports, -> do scope :with_legacy_security_reports, -> do
joins(:artifacts).where(ci_builds: { name: %w[sast dependency_scanning sast:container container_scanning dast] }) joins(:artifacts).where(ci_builds: { name: %w[sast dependency_scanning sast:container container_scanning dast] })
......
...@@ -95,6 +95,8 @@ module EE ...@@ -95,6 +95,8 @@ module EE
has_many :downstream_project_subscriptions, class_name: 'Ci::Subscriptions::Project', foreign_key: :upstream_project_id, inverse_of: :upstream_project has_many :downstream_project_subscriptions, class_name: 'Ci::Subscriptions::Project', foreign_key: :upstream_project_id, inverse_of: :upstream_project
has_many :downstream_projects, class_name: 'Project', through: :downstream_project_subscriptions, source: :downstream_project has_many :downstream_projects, class_name: 'Project', through: :downstream_project_subscriptions, source: :downstream_project
has_many :sourced_pipelines, class_name: 'Ci::Sources::Project', foreign_key: :source_project_id
scope :with_shared_runners_limit_enabled, -> { with_shared_runners.non_public_only } scope :with_shared_runners_limit_enabled, -> { with_shared_runners.non_public_only }
scope :mirror, -> { where(mirror: true) } scope :mirror, -> { where(mirror: true) }
......
...@@ -4,7 +4,10 @@ module Ci ...@@ -4,7 +4,10 @@ module Ci
class TriggerDownstreamSubscriptionService < ::BaseService class TriggerDownstreamSubscriptionService < ::BaseService
def execute(pipeline) def execute(pipeline)
pipeline.project.downstream_projects.each do |downstream_project| pipeline.project.downstream_projects.each do |downstream_project|
::Ci::CreatePipelineService.new(downstream_project, pipeline.user, ref: downstream_project.default_branch).execute(:pipeline) ::Ci::CreatePipelineService.new(downstream_project, pipeline.user, ref: downstream_project.default_branch)
.execute(:pipeline) do |downstream_pipeline|
downstream_pipeline.build_source_project(source_project: pipeline.project)
end
end end
end end
end end
......
# frozen_string_literal: true
FactoryBot.define do
factory :ci_sources_project, class: 'Ci::Sources::Project' do
pipeline factory: :ci_pipeline
source_project factory: :project
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Ci::Sources::Project do
describe 'Relations' do
it { is_expected.to belong_to(:pipeline).required }
it { is_expected.to belong_to(:source_project).required }
end
describe 'Validations' do
let!(:project_source) { create(:ci_sources_project) }
it { is_expected.to validate_uniqueness_of(:pipeline_id).scoped_to(:source_project_id) }
end
end
...@@ -9,19 +9,22 @@ describe Ci::TriggerDownstreamSubscriptionService do ...@@ -9,19 +9,22 @@ describe Ci::TriggerDownstreamSubscriptionService do
let(:upstream_project) { create(:project, :public) } let(:upstream_project) { create(:project, :public) }
let(:pipeline) { create(:ci_pipeline, project: upstream_project, user: create(:user)) } let(:pipeline) { create(:ci_pipeline, project: upstream_project, user: create(:user)) }
before do
stub_ci_pipeline_yaml_file(YAML.dump(job_name: { script: 'echo 1' }))
end
context 'when pipeline project has downstream projects' do context 'when pipeline project has downstream projects' do
before do before do
create(:project, :repository, upstream_projects: [upstream_project]) downstream_project = create(:project, :repository, upstream_projects: [upstream_project])
downstream_project.add_developer(pipeline.user)
end end
it 'calls the create pipeline service' do it 'creates a pipeline' do
service_double = instance_double(::Ci::CreatePipelineService) expect { execute }.to change { ::Ci::Pipeline.count }.from(1).to(2)
expect(service_double).to receive(:execute) end
expect(::Ci::CreatePipelineService).to receive(:new).with(
an_instance_of(Project), an_instance_of(User), ref: 'master'
).and_return(service_double)
execute it 'associates the downstream pipeline with the upstream project' do
expect { execute }.to change { pipeline.project.sourced_pipelines.count }.from(0).to(1)
end end
end end
......
...@@ -197,6 +197,7 @@ ci_pipelines: ...@@ -197,6 +197,7 @@ ci_pipelines:
- source_bridge - source_bridge
- source_job - source_job
- sourced_pipelines - sourced_pipelines
- source_project
- triggered_by_pipeline - triggered_by_pipeline
- triggered_pipelines - triggered_pipelines
- child_pipelines - child_pipelines
......
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