Merge branch 'mr-pipelines-2' into 'master'
Merge request pipelines See merge request gitlab-org/gitlab-ce!23217
This commit is contained in:
commit
fb048d2cf5
26 changed files with 1248 additions and 28 deletions
|
@ -16,6 +16,7 @@ module Ci
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
belongs_to :auto_canceled_by, class_name: 'Ci::Pipeline'
|
belongs_to :auto_canceled_by, class_name: 'Ci::Pipeline'
|
||||||
belongs_to :pipeline_schedule, class_name: 'Ci::PipelineSchedule'
|
belongs_to :pipeline_schedule, class_name: 'Ci::PipelineSchedule'
|
||||||
|
belongs_to :merge_request, class_name: 'MergeRequest'
|
||||||
|
|
||||||
has_internal_id :iid, scope: :project, presence: false, init: ->(s) do
|
has_internal_id :iid, scope: :project, presence: false, init: ->(s) do
|
||||||
s&.project&.pipelines&.maximum(:iid) || s&.project&.pipelines&.count
|
s&.project&.pipelines&.maximum(:iid) || s&.project&.pipelines&.count
|
||||||
|
@ -50,6 +51,9 @@ module Ci
|
||||||
|
|
||||||
validates :sha, presence: { unless: :importing? }
|
validates :sha, presence: { unless: :importing? }
|
||||||
validates :ref, presence: { unless: :importing? }
|
validates :ref, presence: { unless: :importing? }
|
||||||
|
validates :merge_request, presence: { if: :merge_request? }
|
||||||
|
validates :merge_request, absence: { unless: :merge_request? }
|
||||||
|
validates :tag, inclusion: { in: [false], if: :merge_request? }
|
||||||
validates :status, presence: { unless: :importing? }
|
validates :status, presence: { unless: :importing? }
|
||||||
validate :valid_commit_sha, unless: :importing?
|
validate :valid_commit_sha, unless: :importing?
|
||||||
|
|
||||||
|
@ -171,6 +175,13 @@ module Ci
|
||||||
|
|
||||||
scope :internal, -> { where(source: internal_sources) }
|
scope :internal, -> { where(source: internal_sources) }
|
||||||
|
|
||||||
|
scope :sort_by_merge_request_pipelines, -> do
|
||||||
|
sql = 'CASE ci_pipelines.source WHEN (?) THEN 0 ELSE 1 END, ci_pipelines.id DESC'
|
||||||
|
query = ActiveRecord::Base.send(:sanitize_sql_array, [sql, sources[:merge_request]]) # rubocop:disable GitlabSecurity/PublicSend
|
||||||
|
|
||||||
|
order(query)
|
||||||
|
end
|
||||||
|
|
||||||
scope :for_user, -> (user) { where(user: user) }
|
scope :for_user, -> (user) { where(user: user) }
|
||||||
|
|
||||||
# Returns the pipelines in descending order (= newest first), optionally
|
# Returns the pipelines in descending order (= newest first), optionally
|
||||||
|
@ -372,7 +383,7 @@ module Ci
|
||||||
end
|
end
|
||||||
|
|
||||||
def branch?
|
def branch?
|
||||||
!tag?
|
!tag? && !merge_request?
|
||||||
end
|
end
|
||||||
|
|
||||||
def stuck?
|
def stuck?
|
||||||
|
@ -619,7 +630,12 @@ module Ci
|
||||||
|
|
||||||
# All the merge requests for which the current pipeline runs/ran against
|
# All the merge requests for which the current pipeline runs/ran against
|
||||||
def all_merge_requests
|
def all_merge_requests
|
||||||
@all_merge_requests ||= project.merge_requests.where(source_branch: ref)
|
@all_merge_requests ||=
|
||||||
|
if merge_request?
|
||||||
|
project.merge_requests.where(id: merge_request.id)
|
||||||
|
else
|
||||||
|
project.merge_requests.where(source_branch: ref)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def detailed_status(current_user)
|
def detailed_status(current_user)
|
||||||
|
@ -696,6 +712,8 @@ module Ci
|
||||||
def git_ref
|
def git_ref
|
||||||
if branch?
|
if branch?
|
||||||
Gitlab::Git::BRANCH_REF_PREFIX + ref.to_s
|
Gitlab::Git::BRANCH_REF_PREFIX + ref.to_s
|
||||||
|
elsif merge_request?
|
||||||
|
Gitlab::Git::BRANCH_REF_PREFIX + ref.to_s
|
||||||
elsif tag?
|
elsif tag?
|
||||||
Gitlab::Git::TAG_REF_PREFIX + ref.to_s
|
Gitlab::Git::TAG_REF_PREFIX + ref.to_s
|
||||||
else
|
else
|
||||||
|
|
|
@ -21,7 +21,8 @@ module Ci
|
||||||
trigger: 3,
|
trigger: 3,
|
||||||
schedule: 4,
|
schedule: 4,
|
||||||
api: 5,
|
api: 5,
|
||||||
external: 6
|
external: 6,
|
||||||
|
merge_request: 10
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -63,6 +63,7 @@ class MergeRequest < ActiveRecord::Base
|
||||||
dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
|
dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
|
||||||
|
|
||||||
has_many :cached_closes_issues, through: :merge_requests_closing_issues, source: :issue
|
has_many :cached_closes_issues, through: :merge_requests_closing_issues, source: :issue
|
||||||
|
has_many :merge_request_pipelines, foreign_key: 'merge_request_id', class_name: 'Ci::Pipeline'
|
||||||
|
|
||||||
belongs_to :assignee, class_name: "User"
|
belongs_to :assignee, class_name: "User"
|
||||||
|
|
||||||
|
@ -1052,12 +1053,17 @@ class MergeRequest < ActiveRecord::Base
|
||||||
diverged_commits_count > 0
|
diverged_commits_count > 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def all_pipelines
|
def all_pipelines(shas: all_commit_shas)
|
||||||
return Ci::Pipeline.none unless source_project
|
return Ci::Pipeline.none unless source_project
|
||||||
|
|
||||||
@all_pipelines ||= source_project.pipelines
|
@all_pipelines ||= source_project.pipelines
|
||||||
.where(sha: all_commit_shas, ref: source_branch)
|
.where(sha: shas, ref: source_branch)
|
||||||
.order(id: :desc)
|
.where(merge_request: [nil, self])
|
||||||
|
.sort_by_merge_request_pipelines
|
||||||
|
end
|
||||||
|
|
||||||
|
def merge_request_pipeline_exists?
|
||||||
|
merge_request_pipelines.exists?(sha: diff_head_sha)
|
||||||
end
|
end
|
||||||
|
|
||||||
def has_test_reports?
|
def has_test_reports?
|
||||||
|
|
|
@ -48,6 +48,7 @@ class PipelineEntity < Grape::Entity
|
||||||
|
|
||||||
expose :tag?, as: :tag
|
expose :tag?, as: :tag
|
||||||
expose :branch?, as: :branch
|
expose :branch?, as: :branch
|
||||||
|
expose :merge_request?, as: :merge_request
|
||||||
end
|
end
|
||||||
|
|
||||||
expose :commit, using: CommitEntity
|
expose :commit, using: CommitEntity
|
||||||
|
|
|
@ -14,7 +14,7 @@ module Ci
|
||||||
Gitlab::Ci::Pipeline::Chain::Populate,
|
Gitlab::Ci::Pipeline::Chain::Populate,
|
||||||
Gitlab::Ci::Pipeline::Chain::Create].freeze
|
Gitlab::Ci::Pipeline::Chain::Create].freeze
|
||||||
|
|
||||||
def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil, &block)
|
def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil, merge_request: nil, &block)
|
||||||
@pipeline = Ci::Pipeline.new
|
@pipeline = Ci::Pipeline.new
|
||||||
|
|
||||||
command = Gitlab::Ci::Pipeline::Chain::Command.new(
|
command = Gitlab::Ci::Pipeline::Chain::Command.new(
|
||||||
|
@ -25,6 +25,7 @@ module Ci
|
||||||
before_sha: params[:before],
|
before_sha: params[:before],
|
||||||
trigger_request: trigger_request,
|
trigger_request: trigger_request,
|
||||||
schedule: schedule,
|
schedule: schedule,
|
||||||
|
merge_request: merge_request,
|
||||||
ignore_skip_ci: ignore_skip_ci,
|
ignore_skip_ci: ignore_skip_ci,
|
||||||
save_incompleted: save_on_errors,
|
save_incompleted: save_on_errors,
|
||||||
seeds_block: block,
|
seeds_block: block,
|
||||||
|
|
|
@ -54,6 +54,24 @@ module MergeRequests
|
||||||
merge_request, merge_request.project, current_user, merge_request.assignee)
|
merge_request, merge_request.project, current_user, merge_request.assignee)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def create_merge_request_pipeline(merge_request, user)
|
||||||
|
return unless Feature.enabled?(:ci_merge_request_pipeline,
|
||||||
|
merge_request.source_project,
|
||||||
|
default_enabled: true)
|
||||||
|
|
||||||
|
##
|
||||||
|
# UpdateMergeRequestsWorker could be retried by an exception.
|
||||||
|
# MR pipelines should not be recreated in such case.
|
||||||
|
return if merge_request.merge_request_pipeline_exists?
|
||||||
|
|
||||||
|
Ci::CreatePipelineService
|
||||||
|
.new(merge_request.source_project, user, ref: merge_request.source_branch)
|
||||||
|
.execute(:merge_request,
|
||||||
|
ignore_skip_ci: true,
|
||||||
|
save_on_errors: false,
|
||||||
|
merge_request: merge_request)
|
||||||
|
end
|
||||||
|
|
||||||
# Returns all origin and fork merge requests from `@project` satisfying passed arguments.
|
# Returns all origin and fork merge requests from `@project` satisfying passed arguments.
|
||||||
# rubocop: disable CodeReuse/ActiveRecord
|
# rubocop: disable CodeReuse/ActiveRecord
|
||||||
def merge_requests_for(source_branch, mr_states: [:opened])
|
def merge_requests_for(source_branch, mr_states: [:opened])
|
||||||
|
|
|
@ -25,6 +25,7 @@ module MergeRequests
|
||||||
def after_create(issuable)
|
def after_create(issuable)
|
||||||
todo_service.new_merge_request(issuable, current_user)
|
todo_service.new_merge_request(issuable, current_user)
|
||||||
issuable.cache_merge_request_closes_issues!(current_user)
|
issuable.cache_merge_request_closes_issues!(current_user)
|
||||||
|
create_merge_request_pipeline(issuable, current_user)
|
||||||
update_merge_requests_head_pipeline(issuable)
|
update_merge_requests_head_pipeline(issuable)
|
||||||
|
|
||||||
super
|
super
|
||||||
|
@ -49,18 +50,14 @@ module MergeRequests
|
||||||
merge_request.update(head_pipeline_id: pipeline.id) if pipeline
|
merge_request.update(head_pipeline_id: pipeline.id) if pipeline
|
||||||
end
|
end
|
||||||
|
|
||||||
# rubocop: disable CodeReuse/ActiveRecord
|
|
||||||
def head_pipeline_for(merge_request)
|
def head_pipeline_for(merge_request)
|
||||||
return unless merge_request.source_project
|
return unless merge_request.source_project
|
||||||
|
|
||||||
sha = merge_request.source_branch_sha
|
sha = merge_request.source_branch_sha
|
||||||
return unless sha
|
return unless sha
|
||||||
|
|
||||||
pipelines = merge_request.source_project.pipelines.where(ref: merge_request.source_branch, sha: sha)
|
merge_request.all_pipelines(shas: sha).first
|
||||||
|
|
||||||
pipelines.order(id: :desc).first
|
|
||||||
end
|
end
|
||||||
# rubocop: enable CodeReuse/ActiveRecord
|
|
||||||
|
|
||||||
def set_projects!
|
def set_projects!
|
||||||
# @project is used to determine whether the user can set the merge request's
|
# @project is used to determine whether the user can set the merge request's
|
||||||
|
|
|
@ -92,6 +92,7 @@ module MergeRequests
|
||||||
end
|
end
|
||||||
|
|
||||||
merge_request.mark_as_unchecked
|
merge_request.mark_as_unchecked
|
||||||
|
create_merge_request_pipeline(merge_request, current_user)
|
||||||
UpdateHeadPipelineForMergeRequestWorker.perform_async(merge_request.id)
|
UpdateHeadPipelineForMergeRequestWorker.perform_async(merge_request.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,11 @@ class UpdateHeadPipelineForMergeRequestWorker
|
||||||
|
|
||||||
queue_namespace :pipeline_processing
|
queue_namespace :pipeline_processing
|
||||||
|
|
||||||
# rubocop: disable CodeReuse/ActiveRecord
|
|
||||||
def perform(merge_request_id)
|
def perform(merge_request_id)
|
||||||
merge_request = MergeRequest.find(merge_request_id)
|
merge_request = MergeRequest.find(merge_request_id)
|
||||||
pipeline = Ci::Pipeline.where(project: merge_request.source_project, ref: merge_request.source_branch).last
|
|
||||||
|
sha = merge_request.diff_head_sha
|
||||||
|
pipeline = merge_request.all_pipelines(shas: sha).first
|
||||||
|
|
||||||
return unless pipeline && pipeline.latest?
|
return unless pipeline && pipeline.latest?
|
||||||
|
|
||||||
|
@ -21,7 +22,6 @@ class UpdateHeadPipelineForMergeRequestWorker
|
||||||
|
|
||||||
merge_request.update_attribute(:head_pipeline_id, pipeline.id)
|
merge_request.update_attribute(:head_pipeline_id, pipeline.id)
|
||||||
end
|
end
|
||||||
# rubocop: enable CodeReuse/ActiveRecord
|
|
||||||
|
|
||||||
def log_error_message_for(merge_request)
|
def log_error_message_for(merge_request)
|
||||||
Rails.logger.error(
|
Rails.logger.error(
|
||||||
|
|
5
changelogs/unreleased/mr-pipelines-2.yml
Normal file
5
changelogs/unreleased/mr-pipelines-2.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Merge request pipelines
|
||||||
|
merge_request: 23217
|
||||||
|
author:
|
||||||
|
type: added
|
|
@ -0,0 +1,13 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddMergeRequestIdToCiPipelines < ActiveRecord::Migration
|
||||||
|
DOWNTIME = false
|
||||||
|
|
||||||
|
def up
|
||||||
|
add_column :ci_pipelines, :merge_request_id, :integer
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
remove_column :ci_pipelines, :merge_request_id, :integer
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,22 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddForeignKeyToCiPipelinesMergeRequests < ActiveRecord::Migration
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
|
||||||
|
DOWNTIME = false
|
||||||
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
add_concurrent_index :ci_pipelines, :merge_request_id
|
||||||
|
add_concurrent_foreign_key :ci_pipelines, :merge_requests, column: :merge_request_id, on_delete: :cascade
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
if foreign_key_exists?(:ci_pipelines, :merge_requests, column: :merge_request_id)
|
||||||
|
remove_foreign_key :ci_pipelines, :merge_requests
|
||||||
|
end
|
||||||
|
|
||||||
|
remove_concurrent_index :ci_pipelines, :merge_request_id
|
||||||
|
end
|
||||||
|
end
|
|
@ -474,7 +474,9 @@ ActiveRecord::Schema.define(version: 20181126153547) do
|
||||||
t.boolean "protected"
|
t.boolean "protected"
|
||||||
t.integer "failure_reason"
|
t.integer "failure_reason"
|
||||||
t.integer "iid"
|
t.integer "iid"
|
||||||
|
t.integer "merge_request_id"
|
||||||
t.index ["auto_canceled_by_id"], name: "index_ci_pipelines_on_auto_canceled_by_id", using: :btree
|
t.index ["auto_canceled_by_id"], name: "index_ci_pipelines_on_auto_canceled_by_id", using: :btree
|
||||||
|
t.index ["merge_request_id"], name: "index_ci_pipelines_on_merge_request_id", using: :btree
|
||||||
t.index ["pipeline_schedule_id"], name: "index_ci_pipelines_on_pipeline_schedule_id", using: :btree
|
t.index ["pipeline_schedule_id"], name: "index_ci_pipelines_on_pipeline_schedule_id", using: :btree
|
||||||
t.index ["project_id", "iid"], name: "index_ci_pipelines_on_project_id_and_iid", unique: true, where: "(iid IS NOT NULL)", using: :btree
|
t.index ["project_id", "iid"], name: "index_ci_pipelines_on_project_id_and_iid", unique: true, where: "(iid IS NOT NULL)", using: :btree
|
||||||
t.index ["project_id", "ref", "status", "id"], name: "index_ci_pipelines_on_project_id_and_ref_and_status_and_id", using: :btree
|
t.index ["project_id", "ref", "status", "id"], name: "index_ci_pipelines_on_project_id_and_ref_and_status_and_id", using: :btree
|
||||||
|
@ -2292,6 +2294,7 @@ ActiveRecord::Schema.define(version: 20181126153547) do
|
||||||
add_foreign_key "ci_pipeline_variables", "ci_pipelines", column: "pipeline_id", name: "fk_f29c5f4380", on_delete: :cascade
|
add_foreign_key "ci_pipeline_variables", "ci_pipelines", column: "pipeline_id", name: "fk_f29c5f4380", on_delete: :cascade
|
||||||
add_foreign_key "ci_pipelines", "ci_pipeline_schedules", column: "pipeline_schedule_id", name: "fk_3d34ab2e06", on_delete: :nullify
|
add_foreign_key "ci_pipelines", "ci_pipeline_schedules", column: "pipeline_schedule_id", name: "fk_3d34ab2e06", on_delete: :nullify
|
||||||
add_foreign_key "ci_pipelines", "ci_pipelines", column: "auto_canceled_by_id", name: "fk_262d4c2d19", on_delete: :nullify
|
add_foreign_key "ci_pipelines", "ci_pipelines", column: "auto_canceled_by_id", name: "fk_262d4c2d19", on_delete: :nullify
|
||||||
|
add_foreign_key "ci_pipelines", "merge_requests", name: "fk_a23be95014", on_delete: :cascade
|
||||||
add_foreign_key "ci_pipelines", "projects", name: "fk_86635dbd80", on_delete: :cascade
|
add_foreign_key "ci_pipelines", "projects", name: "fk_86635dbd80", on_delete: :cascade
|
||||||
add_foreign_key "ci_runner_namespaces", "ci_runners", column: "runner_id", on_delete: :cascade
|
add_foreign_key "ci_runner_namespaces", "ci_runners", column: "runner_id", on_delete: :cascade
|
||||||
add_foreign_key "ci_runner_namespaces", "namespaces", on_delete: :cascade
|
add_foreign_key "ci_runner_namespaces", "namespaces", on_delete: :cascade
|
||||||
|
|
|
@ -16,6 +16,7 @@ module Gitlab
|
||||||
trigger_requests: Array(@command.trigger_request),
|
trigger_requests: Array(@command.trigger_request),
|
||||||
user: @command.current_user,
|
user: @command.current_user,
|
||||||
pipeline_schedule: @command.schedule,
|
pipeline_schedule: @command.schedule,
|
||||||
|
merge_request: @command.merge_request,
|
||||||
protected: @command.protected_ref?,
|
protected: @command.protected_ref?,
|
||||||
variables_attributes: Array(@command.variables_attributes)
|
variables_attributes: Array(@command.variables_attributes)
|
||||||
)
|
)
|
||||||
|
|
|
@ -8,7 +8,7 @@ module Gitlab
|
||||||
Command = Struct.new(
|
Command = Struct.new(
|
||||||
:source, :project, :current_user,
|
:source, :project, :current_user,
|
||||||
:origin_ref, :checkout_sha, :after_sha, :before_sha,
|
:origin_ref, :checkout_sha, :after_sha, :before_sha,
|
||||||
:trigger_request, :schedule,
|
:trigger_request, :schedule, :merge_request,
|
||||||
:ignore_skip_ci, :save_incompleted,
|
:ignore_skip_ci, :save_incompleted,
|
||||||
:seeds_block, :variables_attributes
|
:seeds_block, :variables_attributes
|
||||||
) do
|
) do
|
||||||
|
|
|
@ -0,0 +1,333 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe 'Merge request > User sees merge request pipelines', :js do
|
||||||
|
include ProjectForksHelper
|
||||||
|
include TestReportsHelper
|
||||||
|
|
||||||
|
let(:project) { create(:project, :public, :repository) }
|
||||||
|
let(:user) { project.creator }
|
||||||
|
|
||||||
|
let(:config) do
|
||||||
|
{
|
||||||
|
build: {
|
||||||
|
script: 'build'
|
||||||
|
},
|
||||||
|
test: {
|
||||||
|
script: 'test',
|
||||||
|
only: ['merge_requests']
|
||||||
|
},
|
||||||
|
deploy: {
|
||||||
|
script: 'deploy',
|
||||||
|
except: ['merge_requests']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_application_setting(auto_devops_enabled: false)
|
||||||
|
stub_feature_flags(ci_merge_request_pipeline: true)
|
||||||
|
stub_ci_pipeline_yaml_file(YAML.dump(config))
|
||||||
|
project.add_maintainer(user)
|
||||||
|
sign_in(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a user created a merge request in the parent project' do
|
||||||
|
let(:merge_request) do
|
||||||
|
create(:merge_request,
|
||||||
|
source_project: project,
|
||||||
|
target_project: project,
|
||||||
|
source_branch: 'feature',
|
||||||
|
target_branch: 'master')
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:push_pipeline) do
|
||||||
|
Ci::CreatePipelineService.new(project, user, ref: 'feature')
|
||||||
|
.execute(:push)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:merge_request_pipeline) do
|
||||||
|
Ci::CreatePipelineService.new(project, user, ref: 'feature')
|
||||||
|
.execute(:merge_request, merge_request: merge_request)
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
visit project_merge_request_path(project, merge_request)
|
||||||
|
|
||||||
|
page.within('.merge-request-tabs') do
|
||||||
|
click_link('Pipelines')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sees branch pipelines and merge request pipelines in correct order' do
|
||||||
|
page.within('.ci-table') do
|
||||||
|
expect(page).to have_selector('.ci-pending', count: 2)
|
||||||
|
expect(first('.js-pipeline-url-link')).to have_content("##{merge_request_pipeline.id}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sees the latest merge request pipeline as the head pipeline' do
|
||||||
|
page.within('.ci-widget-content') do
|
||||||
|
expect(page).to have_content("##{merge_request_pipeline.id}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a user updated a merge request in the parent project' do
|
||||||
|
let!(:push_pipeline_2) do
|
||||||
|
Ci::CreatePipelineService.new(project, user, ref: 'feature')
|
||||||
|
.execute(:push)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:merge_request_pipeline_2) do
|
||||||
|
Ci::CreatePipelineService.new(project, user, ref: 'feature')
|
||||||
|
.execute(:merge_request, merge_request: merge_request)
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
visit project_merge_request_path(project, merge_request)
|
||||||
|
|
||||||
|
page.within('.merge-request-tabs') do
|
||||||
|
click_link('Pipelines')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sees branch pipelines and merge request pipelines in correct order' do
|
||||||
|
page.within('.ci-table') do
|
||||||
|
expect(page).to have_selector('.ci-pending', count: 4)
|
||||||
|
|
||||||
|
expect(all('.js-pipeline-url-link')[0])
|
||||||
|
.to have_content("##{merge_request_pipeline_2.id}")
|
||||||
|
|
||||||
|
expect(all('.js-pipeline-url-link')[1])
|
||||||
|
.to have_content("##{merge_request_pipeline.id}")
|
||||||
|
|
||||||
|
expect(all('.js-pipeline-url-link')[2])
|
||||||
|
.to have_content("##{push_pipeline_2.id}")
|
||||||
|
|
||||||
|
expect(all('.js-pipeline-url-link')[3])
|
||||||
|
.to have_content("##{push_pipeline.id}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sees the latest merge request pipeline as the head pipeline' do
|
||||||
|
page.within('.ci-widget-content') do
|
||||||
|
expect(page).to have_content("##{merge_request_pipeline_2.id}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a user merges a merge request in the parent project' do
|
||||||
|
before do
|
||||||
|
click_button 'Merge when pipeline succeeds'
|
||||||
|
|
||||||
|
wait_for_requests
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when merge request pipeline is pending' do
|
||||||
|
it 'waits the head pipeline' do
|
||||||
|
expect(page).to have_content('to be merged automatically when the pipeline succeeds')
|
||||||
|
expect(page).to have_link('Cancel automatic merge')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when merge request pipeline succeeds' do
|
||||||
|
before do
|
||||||
|
merge_request_pipeline.succeed!
|
||||||
|
|
||||||
|
wait_for_requests
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'merges the merge request' do
|
||||||
|
expect(page).to have_content('Merged by')
|
||||||
|
expect(page).to have_link('Revert')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when branch pipeline succeeds' do
|
||||||
|
before do
|
||||||
|
push_pipeline.succeed!
|
||||||
|
|
||||||
|
wait_for_requests
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'waits the head pipeline' do
|
||||||
|
expect(page).to have_content('to be merged automatically when the pipeline succeeds')
|
||||||
|
expect(page).to have_link('Cancel automatic merge')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when there are no `merge_requests` keyword in .gitlab-ci.yml' do
|
||||||
|
let(:config) do
|
||||||
|
{
|
||||||
|
build: {
|
||||||
|
script: 'build'
|
||||||
|
},
|
||||||
|
test: {
|
||||||
|
script: 'test'
|
||||||
|
},
|
||||||
|
deploy: {
|
||||||
|
script: 'deploy'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sees a branch pipeline in pipeline tab' do
|
||||||
|
page.within('.ci-table') do
|
||||||
|
expect(page).to have_selector('.ci-pending', count: 1)
|
||||||
|
expect(first('.js-pipeline-url-link')).to have_content("##{push_pipeline.id}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sees the latest branch pipeline as the head pipeline' do
|
||||||
|
page.within('.ci-widget-content') do
|
||||||
|
expect(page).to have_content("##{push_pipeline.id}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a user created a merge request from a forked project to the parent project' do
|
||||||
|
let(:merge_request) do
|
||||||
|
create(:merge_request,
|
||||||
|
source_project: forked_project,
|
||||||
|
target_project: project,
|
||||||
|
source_branch: 'feature',
|
||||||
|
target_branch: 'master')
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:push_pipeline) do
|
||||||
|
Ci::CreatePipelineService.new(forked_project, user2, ref: 'feature')
|
||||||
|
.execute(:push)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:merge_request_pipeline) do
|
||||||
|
Ci::CreatePipelineService.new(forked_project, user2, ref: 'feature')
|
||||||
|
.execute(:merge_request, merge_request: merge_request)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:forked_project) { fork_project(project, user2, repository: true) }
|
||||||
|
let(:user2) { create(:user) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
forked_project.add_maintainer(user2)
|
||||||
|
|
||||||
|
visit project_merge_request_path(project, merge_request)
|
||||||
|
|
||||||
|
page.within('.merge-request-tabs') do
|
||||||
|
click_link('Pipelines')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sees branch pipelines and merge request pipelines in correct order' do
|
||||||
|
page.within('.ci-table') do
|
||||||
|
expect(page).to have_selector('.ci-pending', count: 2)
|
||||||
|
expect(first('.js-pipeline-url-link')).to have_content("##{merge_request_pipeline.id}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sees the latest merge request pipeline as the head pipeline' do
|
||||||
|
page.within('.ci-widget-content') do
|
||||||
|
expect(page).to have_content("##{merge_request_pipeline.id}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sees pipeline list in forked project' do
|
||||||
|
visit project_pipelines_path(forked_project)
|
||||||
|
|
||||||
|
expect(page).to have_selector('.ci-pending', count: 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a user updated a merge request from a forked project to the parent project' do
|
||||||
|
let!(:push_pipeline_2) do
|
||||||
|
Ci::CreatePipelineService.new(forked_project, user2, ref: 'feature')
|
||||||
|
.execute(:push)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:merge_request_pipeline_2) do
|
||||||
|
Ci::CreatePipelineService.new(forked_project, user2, ref: 'feature')
|
||||||
|
.execute(:merge_request, merge_request: merge_request)
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
visit project_merge_request_path(project, merge_request)
|
||||||
|
|
||||||
|
page.within('.merge-request-tabs') do
|
||||||
|
click_link('Pipelines')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sees branch pipelines and merge request pipelines in correct order' do
|
||||||
|
page.within('.ci-table') do
|
||||||
|
expect(page).to have_selector('.ci-pending', count: 4)
|
||||||
|
|
||||||
|
expect(all('.js-pipeline-url-link')[0])
|
||||||
|
.to have_content("##{merge_request_pipeline_2.id}")
|
||||||
|
|
||||||
|
expect(all('.js-pipeline-url-link')[1])
|
||||||
|
.to have_content("##{merge_request_pipeline.id}")
|
||||||
|
|
||||||
|
expect(all('.js-pipeline-url-link')[2])
|
||||||
|
.to have_content("##{push_pipeline_2.id}")
|
||||||
|
|
||||||
|
expect(all('.js-pipeline-url-link')[3])
|
||||||
|
.to have_content("##{push_pipeline.id}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sees the latest merge request pipeline as the head pipeline' do
|
||||||
|
page.within('.ci-widget-content') do
|
||||||
|
expect(page).to have_content("##{merge_request_pipeline_2.id}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sees pipeline list in forked project' do
|
||||||
|
visit project_pipelines_path(forked_project)
|
||||||
|
|
||||||
|
expect(page).to have_selector('.ci-pending', count: 4)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a user merges a merge request from a forked project to the parent project' do
|
||||||
|
before do
|
||||||
|
click_button 'Merge when pipeline succeeds'
|
||||||
|
|
||||||
|
wait_for_requests
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when merge request pipeline is pending' do
|
||||||
|
it 'waits the head pipeline' do
|
||||||
|
expect(page).to have_content('to be merged automatically when the pipeline succeeds')
|
||||||
|
expect(page).to have_link('Cancel automatic merge')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when merge request pipeline succeeds' do
|
||||||
|
before do
|
||||||
|
merge_request_pipeline.succeed!
|
||||||
|
|
||||||
|
wait_for_requests
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'merges the merge request' do
|
||||||
|
expect(page).to have_content('Merged by')
|
||||||
|
expect(page).to have_link('Revert')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when branch pipeline succeeds' do
|
||||||
|
before do
|
||||||
|
push_pipeline.succeed!
|
||||||
|
|
||||||
|
wait_for_requests
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'waits the head pipeline' do
|
||||||
|
expect(page).to have_content('to be merged automatically when the pipeline succeeds')
|
||||||
|
expect(page).to have_link('Cancel automatic merge')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -18,6 +18,7 @@ describe Gitlab::Ci::Pipeline::Chain::Build do
|
||||||
before_sha: nil,
|
before_sha: nil,
|
||||||
trigger_request: nil,
|
trigger_request: nil,
|
||||||
schedule: nil,
|
schedule: nil,
|
||||||
|
merge_request: nil,
|
||||||
project: project,
|
project: project,
|
||||||
current_user: user,
|
current_user: user,
|
||||||
variables_attributes: variables_attributes)
|
variables_attributes: variables_attributes)
|
||||||
|
@ -76,6 +77,7 @@ describe Gitlab::Ci::Pipeline::Chain::Build do
|
||||||
before_sha: nil,
|
before_sha: nil,
|
||||||
trigger_request: nil,
|
trigger_request: nil,
|
||||||
schedule: nil,
|
schedule: nil,
|
||||||
|
merge_request: nil,
|
||||||
project: project,
|
project: project,
|
||||||
current_user: user)
|
current_user: user)
|
||||||
end
|
end
|
||||||
|
@ -90,4 +92,31 @@ describe Gitlab::Ci::Pipeline::Chain::Build do
|
||||||
expect(pipeline).to be_tag
|
expect(pipeline).to be_tag
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when pipeline is running for a merge request' do
|
||||||
|
let(:command) do
|
||||||
|
Gitlab::Ci::Pipeline::Chain::Command.new(
|
||||||
|
source: :merge_request,
|
||||||
|
origin_ref: 'feature',
|
||||||
|
checkout_sha: project.commit.id,
|
||||||
|
after_sha: nil,
|
||||||
|
before_sha: nil,
|
||||||
|
trigger_request: nil,
|
||||||
|
schedule: nil,
|
||||||
|
merge_request: merge_request,
|
||||||
|
project: project,
|
||||||
|
current_user: user)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:merge_request) { build(:merge_request, target_project: project) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
step.perform!
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'correctly indicated that this is a merge request pipeline' do
|
||||||
|
expect(pipeline).to be_merge_request
|
||||||
|
expect(pipeline.merge_request).to eq(merge_request)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -106,4 +106,34 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Config do
|
||||||
expect(step.break?).to be false
|
expect(step.break?).to be false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when pipeline source is merge request' do
|
||||||
|
before do
|
||||||
|
stub_ci_pipeline_yaml_file(YAML.dump(config))
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:pipeline) { build_stubbed(:ci_pipeline, project: project) }
|
||||||
|
|
||||||
|
let(:merge_request_pipeline) do
|
||||||
|
build(:ci_pipeline, source: :merge_request, project: project)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:chain) { described_class.new(merge_request_pipeline, command).tap(&:perform!) }
|
||||||
|
|
||||||
|
context "when config contains 'merge_requests' keyword" do
|
||||||
|
let(:config) { { rspec: { script: 'echo', only: ['merge_requests'] } } }
|
||||||
|
|
||||||
|
it 'does not break the chain' do
|
||||||
|
expect(chain).not_to be_break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when config contains 'merge_request' keyword" do
|
||||||
|
let(:config) { { rspec: { script: 'echo', only: ['merge_request'] } } }
|
||||||
|
|
||||||
|
it 'does not break the chain' do
|
||||||
|
expect(chain).not_to be_break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -94,6 +94,7 @@ merge_requests:
|
||||||
- timelogs
|
- timelogs
|
||||||
- head_pipeline
|
- head_pipeline
|
||||||
- latest_merge_request_diff
|
- latest_merge_request_diff
|
||||||
|
- merge_request_pipelines
|
||||||
merge_request_diff:
|
merge_request_diff:
|
||||||
- merge_request
|
- merge_request
|
||||||
- merge_request_diff_commits
|
- merge_request_diff_commits
|
||||||
|
@ -121,6 +122,7 @@ pipelines:
|
||||||
- artifacts
|
- artifacts
|
||||||
- pipeline_schedule
|
- pipeline_schedule
|
||||||
- merge_requests
|
- merge_requests
|
||||||
|
- merge_request
|
||||||
- deployments
|
- deployments
|
||||||
- environments
|
- environments
|
||||||
pipeline_variables:
|
pipeline_variables:
|
||||||
|
|
|
@ -243,6 +243,7 @@ Ci::Pipeline:
|
||||||
- failure_reason
|
- failure_reason
|
||||||
- protected
|
- protected
|
||||||
- iid
|
- iid
|
||||||
|
- merge_request_id
|
||||||
Ci::Stage:
|
Ci::Stage:
|
||||||
- id
|
- id
|
||||||
- name
|
- name
|
||||||
|
|
|
@ -14,6 +14,7 @@ describe Ci::Pipeline, :mailer do
|
||||||
it { is_expected.to belong_to(:user) }
|
it { is_expected.to belong_to(:user) }
|
||||||
it { is_expected.to belong_to(:auto_canceled_by) }
|
it { is_expected.to belong_to(:auto_canceled_by) }
|
||||||
it { is_expected.to belong_to(:pipeline_schedule) }
|
it { is_expected.to belong_to(:pipeline_schedule) }
|
||||||
|
it { is_expected.to belong_to(:merge_request) }
|
||||||
|
|
||||||
it { is_expected.to have_many(:statuses) }
|
it { is_expected.to have_many(:statuses) }
|
||||||
it { is_expected.to have_many(:trigger_requests) }
|
it { is_expected.to have_many(:trigger_requests) }
|
||||||
|
@ -37,6 +38,128 @@ describe Ci::Pipeline, :mailer do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '.sort_by_merge_request_pipelines' do
|
||||||
|
subject { described_class.sort_by_merge_request_pipelines }
|
||||||
|
|
||||||
|
context 'when branch pipelines exist' do
|
||||||
|
let!(:branch_pipeline_1) { create(:ci_pipeline, source: :push) }
|
||||||
|
let!(:branch_pipeline_2) { create(:ci_pipeline, source: :push) }
|
||||||
|
|
||||||
|
it 'returns pipelines order by id' do
|
||||||
|
expect(subject).to eq([branch_pipeline_2,
|
||||||
|
branch_pipeline_1])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when merge request pipelines exist' do
|
||||||
|
let!(:merge_request_pipeline_1) do
|
||||||
|
create(:ci_pipeline, source: :merge_request, merge_request: merge_request)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:merge_request_pipeline_2) do
|
||||||
|
create(:ci_pipeline, source: :merge_request, merge_request: merge_request)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:merge_request) do
|
||||||
|
create(:merge_request,
|
||||||
|
source_project: project,
|
||||||
|
source_branch: 'feature',
|
||||||
|
target_project: project,
|
||||||
|
target_branch: 'master')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns pipelines order by id' do
|
||||||
|
expect(subject).to eq([merge_request_pipeline_2,
|
||||||
|
merge_request_pipeline_1])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when both branch pipeline and merge request pipeline exist' do
|
||||||
|
let!(:branch_pipeline_1) { create(:ci_pipeline, source: :push) }
|
||||||
|
let!(:branch_pipeline_2) { create(:ci_pipeline, source: :push) }
|
||||||
|
|
||||||
|
let!(:merge_request_pipeline_1) do
|
||||||
|
create(:ci_pipeline, source: :merge_request, merge_request: merge_request)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:merge_request_pipeline_2) do
|
||||||
|
create(:ci_pipeline, source: :merge_request, merge_request: merge_request)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:merge_request) do
|
||||||
|
create(:merge_request,
|
||||||
|
source_project: project,
|
||||||
|
source_branch: 'feature',
|
||||||
|
target_project: project,
|
||||||
|
target_branch: 'master')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns merge request pipeline first' do
|
||||||
|
expect(subject).to eq([merge_request_pipeline_2,
|
||||||
|
merge_request_pipeline_1,
|
||||||
|
branch_pipeline_2,
|
||||||
|
branch_pipeline_1])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.merge_request' do
|
||||||
|
subject { described_class.merge_request }
|
||||||
|
|
||||||
|
context 'when there is a merge request pipeline' do
|
||||||
|
let!(:pipeline) { create(:ci_pipeline, source: :merge_request, merge_request: merge_request) }
|
||||||
|
let(:merge_request) { create(:merge_request) }
|
||||||
|
|
||||||
|
it 'returns merge request pipeline first' do
|
||||||
|
expect(subject).to eq([pipeline])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when there are no merge request pipelines' do
|
||||||
|
let!(:pipeline) { create(:ci_pipeline, source: :push) }
|
||||||
|
|
||||||
|
it 'returns empty array' do
|
||||||
|
expect(subject).to be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'Validations for merge request pipelines' do
|
||||||
|
let(:pipeline) { build(:ci_pipeline, source: source, merge_request: merge_request) }
|
||||||
|
|
||||||
|
context 'when source is merge request' do
|
||||||
|
let(:source) { :merge_request }
|
||||||
|
|
||||||
|
context 'when merge request is specified' do
|
||||||
|
let(:merge_request) { create(:merge_request, source_project: project, source_branch: 'feature', target_project: project, target_branch: 'master') }
|
||||||
|
|
||||||
|
it { expect(pipeline).to be_valid }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when merge request is empty' do
|
||||||
|
let(:merge_request) { nil }
|
||||||
|
|
||||||
|
it { expect(pipeline).not_to be_valid }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when source is web' do
|
||||||
|
let(:source) { :web }
|
||||||
|
|
||||||
|
context 'when merge request is specified' do
|
||||||
|
let(:merge_request) { create(:merge_request, source_project: project, source_branch: 'feature', target_project: project, target_branch: 'master') }
|
||||||
|
|
||||||
|
it { expect(pipeline).not_to be_valid }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when merge request is empty' do
|
||||||
|
let(:merge_request) { nil }
|
||||||
|
|
||||||
|
it { expect(pipeline).to be_valid }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'modules' do
|
describe 'modules' do
|
||||||
it_behaves_like 'AtomicInternalId', validate_presence: false do
|
it_behaves_like 'AtomicInternalId', validate_presence: false do
|
||||||
let(:internal_id_attribute) { :iid }
|
let(:internal_id_attribute) { :iid }
|
||||||
|
@ -760,27 +883,85 @@ describe Ci::Pipeline, :mailer do
|
||||||
describe '#branch?' do
|
describe '#branch?' do
|
||||||
subject { pipeline.branch? }
|
subject { pipeline.branch? }
|
||||||
|
|
||||||
context 'is not a tag' do
|
context 'when ref is not a tag' do
|
||||||
before do
|
before do
|
||||||
pipeline.tag = false
|
pipeline.tag = false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'return true when tag is set to false' do
|
it 'return true' do
|
||||||
is_expected.to be_truthy
|
is_expected.to be_truthy
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when source is merge request' do
|
||||||
|
let(:pipeline) do
|
||||||
|
create(:ci_pipeline, source: :merge_request, merge_request: merge_request)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:merge_request) do
|
||||||
|
create(:merge_request,
|
||||||
|
source_project: project,
|
||||||
|
source_branch: 'feature',
|
||||||
|
target_project: project,
|
||||||
|
target_branch: 'master')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns false' do
|
||||||
|
is_expected.to be_falsey
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'is not a tag' do
|
context 'when ref is a tag' do
|
||||||
before do
|
before do
|
||||||
pipeline.tag = true
|
pipeline.tag = true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'return false when tag is set to true' do
|
it 'return false' do
|
||||||
is_expected.to be_falsey
|
is_expected.to be_falsey
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#git_ref' do
|
||||||
|
subject { pipeline.send(:git_ref) }
|
||||||
|
|
||||||
|
context 'when ref is branch' do
|
||||||
|
let(:pipeline) { create(:ci_pipeline, tag: false) }
|
||||||
|
|
||||||
|
it 'returns branch ref' do
|
||||||
|
is_expected.to eq(Gitlab::Git::BRANCH_REF_PREFIX + pipeline.ref.to_s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when ref is tag' do
|
||||||
|
let(:pipeline) { create(:ci_pipeline, tag: true) }
|
||||||
|
|
||||||
|
it 'returns branch ref' do
|
||||||
|
is_expected.to eq(Gitlab::Git::TAG_REF_PREFIX + pipeline.ref.to_s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when ref is merge request' do
|
||||||
|
let(:pipeline) do
|
||||||
|
create(:ci_pipeline,
|
||||||
|
source: :merge_request,
|
||||||
|
merge_request: merge_request)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:merge_request) do
|
||||||
|
create(:merge_request,
|
||||||
|
source_project: project,
|
||||||
|
source_branch: 'feature',
|
||||||
|
target_project: project,
|
||||||
|
target_branch: 'master')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns branch ref' do
|
||||||
|
is_expected.to eq(Gitlab::Git::BRANCH_REF_PREFIX + pipeline.ref.to_s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'ref_exists?' do
|
describe 'ref_exists?' do
|
||||||
context 'when repository exists' do
|
context 'when repository exists' do
|
||||||
using RSpec::Parameterized::TableSyntax
|
using RSpec::Parameterized::TableSyntax
|
||||||
|
@ -1855,6 +2036,55 @@ describe Ci::Pipeline, :mailer do
|
||||||
|
|
||||||
expect(pipeline.all_merge_requests).to be_empty
|
expect(pipeline.all_merge_requests).to be_empty
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when there is a merge request pipeline' do
|
||||||
|
let(:source_branch) { 'feature' }
|
||||||
|
let(:target_branch) { 'master' }
|
||||||
|
|
||||||
|
let!(:pipeline) do
|
||||||
|
create(:ci_pipeline,
|
||||||
|
source: :merge_request,
|
||||||
|
project: project,
|
||||||
|
ref: source_branch,
|
||||||
|
merge_request: merge_request)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:merge_request) do
|
||||||
|
create(:merge_request,
|
||||||
|
source_project: project,
|
||||||
|
source_branch: source_branch,
|
||||||
|
target_project: project,
|
||||||
|
target_branch: target_branch)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns an associated merge request' do
|
||||||
|
expect(pipeline.all_merge_requests).to eq([merge_request])
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when there is another merge request pipeline that targets a different branch' do
|
||||||
|
let(:target_branch_2) { 'merge-test' }
|
||||||
|
|
||||||
|
let!(:pipeline_2) do
|
||||||
|
create(:ci_pipeline,
|
||||||
|
source: :merge_request,
|
||||||
|
project: project,
|
||||||
|
ref: source_branch,
|
||||||
|
merge_request: merge_request_2)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:merge_request_2) do
|
||||||
|
create(:merge_request,
|
||||||
|
source_project: project,
|
||||||
|
source_branch: source_branch,
|
||||||
|
target_project: project,
|
||||||
|
target_branch: target_branch_2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not return an associated merge request' do
|
||||||
|
expect(pipeline.all_merge_requests).not_to include(merge_request_2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#stuck?' do
|
describe '#stuck?' do
|
||||||
|
|
|
@ -1206,6 +1206,119 @@ describe MergeRequest do
|
||||||
expect(subject.all_pipelines).to contain_exactly(pipeline)
|
expect(subject.all_pipelines).to contain_exactly(pipeline)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when pipelines exist for the branch and merge request' do
|
||||||
|
let(:source_ref) { 'feature' }
|
||||||
|
let(:target_ref) { 'master' }
|
||||||
|
|
||||||
|
let!(:branch_pipeline) do
|
||||||
|
create(:ci_pipeline,
|
||||||
|
source: :push,
|
||||||
|
project: project,
|
||||||
|
ref: source_ref,
|
||||||
|
sha: shas.second)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:merge_request_pipeline) do
|
||||||
|
create(:ci_pipeline,
|
||||||
|
source: :merge_request,
|
||||||
|
project: project,
|
||||||
|
ref: source_ref,
|
||||||
|
sha: shas.second,
|
||||||
|
merge_request: merge_request)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:merge_request) do
|
||||||
|
create(:merge_request,
|
||||||
|
source_project: project,
|
||||||
|
source_branch: source_ref,
|
||||||
|
target_project: project,
|
||||||
|
target_branch: target_ref)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:project) { create(:project, :repository) }
|
||||||
|
let(:shas) { project.repository.commits(source_ref, limit: 2).map(&:id) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(merge_request).to receive(:all_commit_shas) { shas }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns merge request pipeline first' do
|
||||||
|
expect(merge_request.all_pipelines)
|
||||||
|
.to eq([merge_request_pipeline,
|
||||||
|
branch_pipeline])
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when there are a branch pipeline and a merge request pipeline' do
|
||||||
|
let!(:branch_pipeline_2) do
|
||||||
|
create(:ci_pipeline,
|
||||||
|
source: :push,
|
||||||
|
project: project,
|
||||||
|
ref: source_ref,
|
||||||
|
sha: shas.first)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:merge_request_pipeline_2) do
|
||||||
|
create(:ci_pipeline,
|
||||||
|
source: :merge_request,
|
||||||
|
project: project,
|
||||||
|
ref: source_ref,
|
||||||
|
sha: shas.first,
|
||||||
|
merge_request: merge_request)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns merge request pipelines first' do
|
||||||
|
expect(merge_request.all_pipelines)
|
||||||
|
.to eq([merge_request_pipeline_2,
|
||||||
|
merge_request_pipeline,
|
||||||
|
branch_pipeline_2,
|
||||||
|
branch_pipeline])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when there are multiple merge request pipelines from the same branch' do
|
||||||
|
let!(:branch_pipeline_2) do
|
||||||
|
create(:ci_pipeline,
|
||||||
|
source: :push,
|
||||||
|
project: project,
|
||||||
|
ref: source_ref,
|
||||||
|
sha: shas.first)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:merge_request_pipeline_2) do
|
||||||
|
create(:ci_pipeline,
|
||||||
|
source: :merge_request,
|
||||||
|
project: project,
|
||||||
|
ref: source_ref,
|
||||||
|
sha: shas.first,
|
||||||
|
merge_request: merge_request_2)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:merge_request_2) do
|
||||||
|
create(:merge_request,
|
||||||
|
source_project: project,
|
||||||
|
source_branch: source_ref,
|
||||||
|
target_project: project,
|
||||||
|
target_branch: 'stable')
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(merge_request_2).to receive(:all_commit_shas) { shas }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns only related merge request pipelines' do
|
||||||
|
expect(merge_request.all_pipelines)
|
||||||
|
.to eq([merge_request_pipeline,
|
||||||
|
branch_pipeline_2,
|
||||||
|
branch_pipeline])
|
||||||
|
|
||||||
|
expect(merge_request_2.all_pipelines)
|
||||||
|
.to eq([merge_request_pipeline_2,
|
||||||
|
branch_pipeline_2,
|
||||||
|
branch_pipeline])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#has_test_reports?' do
|
describe '#has_test_reports?' do
|
||||||
|
|
|
@ -18,7 +18,8 @@ describe Ci::CreatePipelineService do
|
||||||
message: 'Message',
|
message: 'Message',
|
||||||
ref: ref_name,
|
ref: ref_name,
|
||||||
trigger_request: nil,
|
trigger_request: nil,
|
||||||
variables_attributes: nil)
|
variables_attributes: nil,
|
||||||
|
merge_request: nil)
|
||||||
params = { ref: ref,
|
params = { ref: ref,
|
||||||
before: '00000000',
|
before: '00000000',
|
||||||
after: after,
|
after: after,
|
||||||
|
@ -26,7 +27,7 @@ describe Ci::CreatePipelineService do
|
||||||
variables_attributes: variables_attributes }
|
variables_attributes: variables_attributes }
|
||||||
|
|
||||||
described_class.new(project, user, params).execute(
|
described_class.new(project, user, params).execute(
|
||||||
source, trigger_request: trigger_request)
|
source, trigger_request: trigger_request, merge_request: merge_request)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'valid params' do
|
context 'valid params' do
|
||||||
|
@ -60,10 +61,10 @@ describe Ci::CreatePipelineService do
|
||||||
|
|
||||||
context 'when merge requests already exist for this source branch' do
|
context 'when merge requests already exist for this source branch' do
|
||||||
let(:merge_request_1) do
|
let(:merge_request_1) do
|
||||||
create(:merge_request, source_branch: 'master', target_branch: "branch_1", source_project: project)
|
create(:merge_request, source_branch: 'feature', target_branch: "master", source_project: project)
|
||||||
end
|
end
|
||||||
let(:merge_request_2) do
|
let(:merge_request_2) do
|
||||||
create(:merge_request, source_branch: 'master', target_branch: "branch_2", source_project: project)
|
create(:merge_request, source_branch: 'feature', target_branch: "v1.1.0", source_project: project)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when related merge request is already merged' do
|
context 'when related merge request is already merged' do
|
||||||
|
@ -83,7 +84,7 @@ describe Ci::CreatePipelineService do
|
||||||
merge_request_1
|
merge_request_1
|
||||||
merge_request_2
|
merge_request_2
|
||||||
|
|
||||||
head_pipeline = execute_service
|
head_pipeline = execute_service(ref: 'feature', after: nil)
|
||||||
|
|
||||||
expect(merge_request_1.reload.head_pipeline).to eq(head_pipeline)
|
expect(merge_request_1.reload.head_pipeline).to eq(head_pipeline)
|
||||||
expect(merge_request_2.reload.head_pipeline).to eq(head_pipeline)
|
expect(merge_request_2.reload.head_pipeline).to eq(head_pipeline)
|
||||||
|
@ -123,12 +124,12 @@ describe Ci::CreatePipelineService do
|
||||||
let!(:target_project) { create(:project, :repository) }
|
let!(:target_project) { create(:project, :repository) }
|
||||||
|
|
||||||
it 'updates head pipeline for merge request' do
|
it 'updates head pipeline for merge request' do
|
||||||
merge_request = create(:merge_request, source_branch: 'master',
|
merge_request = create(:merge_request, source_branch: 'feature',
|
||||||
target_branch: "branch_1",
|
target_branch: "master",
|
||||||
source_project: project,
|
source_project: project,
|
||||||
target_project: target_project)
|
target_project: target_project)
|
||||||
|
|
||||||
head_pipeline = execute_service
|
head_pipeline = execute_service(ref: 'feature', after: nil)
|
||||||
|
|
||||||
expect(merge_request.reload.head_pipeline).to eq(head_pipeline)
|
expect(merge_request.reload.head_pipeline).to eq(head_pipeline)
|
||||||
end
|
end
|
||||||
|
@ -656,6 +657,212 @@ describe Ci::CreatePipelineService do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'Merge request pipelines' do
|
||||||
|
let(:pipeline) do
|
||||||
|
execute_service(source: source, merge_request: merge_request, ref: ref_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_ci_pipeline_yaml_file(YAML.dump(config))
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:ref_name) { 'feature' }
|
||||||
|
|
||||||
|
context 'when source is merge request' do
|
||||||
|
let(:source) { :merge_request }
|
||||||
|
|
||||||
|
context "when config has merge_requests keywords" do
|
||||||
|
let(:config) do
|
||||||
|
{
|
||||||
|
build: {
|
||||||
|
stage: 'build',
|
||||||
|
script: 'echo'
|
||||||
|
},
|
||||||
|
test: {
|
||||||
|
stage: 'test',
|
||||||
|
script: 'echo',
|
||||||
|
only: ['merge_requests']
|
||||||
|
},
|
||||||
|
pages: {
|
||||||
|
stage: 'deploy',
|
||||||
|
script: 'echo',
|
||||||
|
except: ['merge_requests']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when merge request is specified' do
|
||||||
|
let(:merge_request) do
|
||||||
|
create(:merge_request,
|
||||||
|
source_project: project,
|
||||||
|
source_branch: ref_name,
|
||||||
|
target_project: project,
|
||||||
|
target_branch: 'master')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a merge request pipeline' do
|
||||||
|
expect(pipeline).to be_persisted
|
||||||
|
expect(pipeline).to be_merge_request
|
||||||
|
expect(pipeline.merge_request).to eq(merge_request)
|
||||||
|
expect(pipeline.builds.order(:stage_id).map(&:name)).to eq(%w[test])
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when ref is tag' do
|
||||||
|
let(:ref_name) { 'v1.1.0' }
|
||||||
|
|
||||||
|
it 'does not create a merge request pipeline' do
|
||||||
|
expect(pipeline).not_to be_persisted
|
||||||
|
expect(pipeline.errors[:tag]).to eq(["is not included in the list"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when merge request is created from a forked project' do
|
||||||
|
let(:merge_request) do
|
||||||
|
create(:merge_request,
|
||||||
|
source_project: project,
|
||||||
|
source_branch: ref_name,
|
||||||
|
target_project: target_project,
|
||||||
|
target_branch: 'master')
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:project) { fork_project(target_project, nil, repository: true) }
|
||||||
|
let!(:target_project) { create(:project, :repository) }
|
||||||
|
|
||||||
|
it 'creates a merge request pipeline in the forked project' do
|
||||||
|
expect(pipeline).to be_persisted
|
||||||
|
expect(project.pipelines).to eq([pipeline])
|
||||||
|
expect(target_project.pipelines).to be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when there are no matched jobs" do
|
||||||
|
let(:config) do
|
||||||
|
{
|
||||||
|
test: {
|
||||||
|
stage: 'test',
|
||||||
|
script: 'echo',
|
||||||
|
except: ['merge_requests']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not create a merge request pipeline' do
|
||||||
|
expect(pipeline).not_to be_persisted
|
||||||
|
expect(pipeline.errors[:base]).to eq(["No stages / jobs for this pipeline."])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when merge request is not specified' do
|
||||||
|
let(:merge_request) { nil }
|
||||||
|
|
||||||
|
it 'does not create a merge request pipeline' do
|
||||||
|
expect(pipeline).not_to be_persisted
|
||||||
|
expect(pipeline.errors[:merge_request]).to eq(["can't be blank"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when config does not have merge_requests keywords" do
|
||||||
|
let(:config) do
|
||||||
|
{
|
||||||
|
build: {
|
||||||
|
stage: 'build',
|
||||||
|
script: 'echo'
|
||||||
|
},
|
||||||
|
test: {
|
||||||
|
stage: 'test',
|
||||||
|
script: 'echo'
|
||||||
|
},
|
||||||
|
pages: {
|
||||||
|
stage: 'deploy',
|
||||||
|
script: 'echo'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when merge request is specified' do
|
||||||
|
let(:merge_request) do
|
||||||
|
create(:merge_request,
|
||||||
|
source_project: project,
|
||||||
|
source_branch: ref_name,
|
||||||
|
target_project: project,
|
||||||
|
target_branch: 'master')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not create a merge request pipeline' do
|
||||||
|
expect(pipeline).not_to be_persisted
|
||||||
|
|
||||||
|
expect(pipeline.errors[:base])
|
||||||
|
.to eq(['No stages / jobs for this pipeline.'])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when merge request is not specified' do
|
||||||
|
let(:merge_request) { nil }
|
||||||
|
|
||||||
|
it 'does not create a merge request pipeline' do
|
||||||
|
expect(pipeline).not_to be_persisted
|
||||||
|
|
||||||
|
expect(pipeline.errors[:base])
|
||||||
|
.to eq(['No stages / jobs for this pipeline.'])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when source is web' do
|
||||||
|
let(:source) { :web }
|
||||||
|
|
||||||
|
context "when config has merge_requests keywords" do
|
||||||
|
let(:config) do
|
||||||
|
{
|
||||||
|
build: {
|
||||||
|
stage: 'build',
|
||||||
|
script: 'echo'
|
||||||
|
},
|
||||||
|
test: {
|
||||||
|
stage: 'test',
|
||||||
|
script: 'echo',
|
||||||
|
only: ['merge_requests']
|
||||||
|
},
|
||||||
|
pages: {
|
||||||
|
stage: 'deploy',
|
||||||
|
script: 'echo',
|
||||||
|
except: ['merge_requests']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when merge request is specified' do
|
||||||
|
let(:merge_request) do
|
||||||
|
create(:merge_request,
|
||||||
|
source_project: project,
|
||||||
|
source_branch: ref_name,
|
||||||
|
target_project: project,
|
||||||
|
target_branch: 'master')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not create a merge request pipeline' do
|
||||||
|
expect(pipeline).not_to be_persisted
|
||||||
|
expect(pipeline.errors[:merge_request]).to eq(["must be blank"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when merge request is not specified' do
|
||||||
|
let(:merge_request) { nil }
|
||||||
|
|
||||||
|
it 'creates a branch pipeline' do
|
||||||
|
expect(pipeline).to be_persisted
|
||||||
|
expect(pipeline).to be_web
|
||||||
|
expect(pipeline.merge_request).to be_nil
|
||||||
|
expect(pipeline.builds.order(:stage_id).map(&:name)).to eq(%w[build pages])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#execute!' do
|
describe '#execute!' do
|
||||||
|
|
|
@ -159,6 +159,78 @@ describe MergeRequests::CreateService do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'Merge request pipelines' do
|
||||||
|
before do
|
||||||
|
stub_ci_pipeline_yaml_file(YAML.dump(config))
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when .gitlab-ci.yml has merge_requests keywords" do
|
||||||
|
let(:config) do
|
||||||
|
{
|
||||||
|
test: {
|
||||||
|
stage: 'test',
|
||||||
|
script: 'echo',
|
||||||
|
only: ['merge_requests']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a merge request pipeline and sets it as a head pipeline' do
|
||||||
|
expect(merge_request).to be_persisted
|
||||||
|
|
||||||
|
merge_request.reload
|
||||||
|
expect(merge_request.merge_request_pipelines.count).to eq(1)
|
||||||
|
expect(merge_request.actual_head_pipeline).to be_merge_request
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when branch pipeline was created before a merge request pipline has been created" do
|
||||||
|
before do
|
||||||
|
create(:ci_pipeline, project: merge_request.source_project,
|
||||||
|
sha: merge_request.diff_head_sha,
|
||||||
|
ref: merge_request.source_branch,
|
||||||
|
tag: false)
|
||||||
|
|
||||||
|
merge_request
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sets the latest merge request pipeline as the head pipeline' do
|
||||||
|
expect(merge_request.actual_head_pipeline).to be_merge_request
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the 'ci_merge_request_pipeline' feature flag is disabled" do
|
||||||
|
before do
|
||||||
|
stub_feature_flags(ci_merge_request_pipeline: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not create a merge request pipeline' do
|
||||||
|
expect(merge_request).to be_persisted
|
||||||
|
|
||||||
|
merge_request.reload
|
||||||
|
expect(merge_request.merge_request_pipelines.count).to eq(0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when .gitlab-ci.yml does not have merge_requests keywords" do
|
||||||
|
let(:config) do
|
||||||
|
{
|
||||||
|
test: {
|
||||||
|
stage: 'test',
|
||||||
|
script: 'echo'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not create a merge request pipeline' do
|
||||||
|
expect(merge_request).to be_persisted
|
||||||
|
|
||||||
|
merge_request.reload
|
||||||
|
expect(merge_request.merge_request_pipelines.count).to eq(0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'new issuable record that supports quick actions' do
|
it_behaves_like 'new issuable record that supports quick actions' do
|
||||||
|
|
|
@ -132,6 +132,94 @@ describe MergeRequests::RefreshService do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'Merge request pipelines' do
|
||||||
|
before do
|
||||||
|
stub_ci_pipeline_yaml_file(YAML.dump(config))
|
||||||
|
end
|
||||||
|
|
||||||
|
subject { service.new(@project, @user).execute(@oldrev, @newrev, 'refs/heads/master') }
|
||||||
|
|
||||||
|
context "when .gitlab-ci.yml has merge_requests keywords" do
|
||||||
|
let(:config) do
|
||||||
|
{
|
||||||
|
test: {
|
||||||
|
stage: 'test',
|
||||||
|
script: 'echo',
|
||||||
|
only: ['merge_requests']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'create merge request pipeline' do
|
||||||
|
expect { subject }
|
||||||
|
.to change { @merge_request.merge_request_pipelines.count }.by(1)
|
||||||
|
.and change { @fork_merge_request.merge_request_pipelines.count }.by(1)
|
||||||
|
.and change { @another_merge_request.merge_request_pipelines.count }.by(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when branch pipeline was created before a merge request pipline has been created" do
|
||||||
|
before do
|
||||||
|
create(:ci_pipeline, project: @merge_request.source_project,
|
||||||
|
sha: @merge_request.diff_head_sha,
|
||||||
|
ref: @merge_request.source_branch,
|
||||||
|
tag: false)
|
||||||
|
|
||||||
|
subject
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sets the latest merge request pipeline as a head pipeline' do
|
||||||
|
@merge_request.reload
|
||||||
|
expect(@merge_request.actual_head_pipeline).to be_merge_request
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns pipelines in correct order' do
|
||||||
|
@merge_request.reload
|
||||||
|
expect(@merge_request.all_pipelines.first).to be_merge_request
|
||||||
|
expect(@merge_request.all_pipelines.second).to be_push
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when MergeRequestUpdateWorker is retried by an exception" do
|
||||||
|
it 'does not re-create a duplicate merge request pipeline' do
|
||||||
|
expect do
|
||||||
|
service.new(@project, @user).execute(@oldrev, @newrev, 'refs/heads/master')
|
||||||
|
end.to change { @merge_request.merge_request_pipelines.count }.by(1)
|
||||||
|
|
||||||
|
expect do
|
||||||
|
service.new(@project, @user).execute(@oldrev, @newrev, 'refs/heads/master')
|
||||||
|
end.not_to change { @merge_request.merge_request_pipelines.count }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the 'ci_merge_request_pipeline' feature flag is disabled" do
|
||||||
|
before do
|
||||||
|
stub_feature_flags(ci_merge_request_pipeline: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not create a merge request pipeline' do
|
||||||
|
expect { subject }
|
||||||
|
.not_to change { @merge_request.merge_request_pipelines.count }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when .gitlab-ci.yml does not have merge_requests keywords" do
|
||||||
|
let(:config) do
|
||||||
|
{
|
||||||
|
test: {
|
||||||
|
stage: 'test',
|
||||||
|
script: 'echo'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not create a merge request pipeline' do
|
||||||
|
expect { subject }
|
||||||
|
.not_to change { @merge_request.merge_request_pipelines.count }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'push to origin repo source branch when an MR was reopened' do
|
context 'push to origin repo source branch when an MR was reopened' do
|
||||||
let(:refresh_service) { service.new(@project, @user) }
|
let(:refresh_service) { service.new(@project, @user) }
|
||||||
let(:notification_service) { spy('notification_service') }
|
let(:notification_service) { spy('notification_service') }
|
||||||
|
|
|
@ -34,5 +34,33 @@ describe UpdateHeadPipelineForMergeRequestWorker do
|
||||||
expect { subject.perform(merge_request.id) }.not_to change { merge_request.reload.head_pipeline_id }
|
expect { subject.perform(merge_request.id) }.not_to change { merge_request.reload.head_pipeline_id }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when a merge request pipeline exists' do
|
||||||
|
let!(:merge_request_pipeline) do
|
||||||
|
create(:ci_pipeline,
|
||||||
|
project: project,
|
||||||
|
source: :merge_request,
|
||||||
|
sha: latest_sha,
|
||||||
|
merge_request: merge_request)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sets the merge request pipeline as the head pipeline' do
|
||||||
|
expect { subject.perform(merge_request.id) }
|
||||||
|
.to change { merge_request.reload.head_pipeline_id }
|
||||||
|
.from(nil).to(merge_request_pipeline.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when branch pipeline exists' do
|
||||||
|
let!(:branch_pipeline) do
|
||||||
|
create(:ci_pipeline, project: project, source: :push, sha: latest_sha)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'prioritizes the merge request pipeline as the head pipeline' do
|
||||||
|
expect { subject.perform(merge_request.id) }
|
||||||
|
.to change { merge_request.reload.head_pipeline_id }
|
||||||
|
.from(nil).to(merge_request_pipeline.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue