2016-05-09 23:26:13 +00:00
|
|
|
module Ci
|
|
|
|
class CreatePipelineService < BaseService
|
2016-08-11 13:22:35 +00:00
|
|
|
attr_reader :pipeline
|
2016-05-18 18:02:10 +00:00
|
|
|
|
2017-05-24 13:13:51 +00:00
|
|
|
def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil)
|
2016-08-11 13:22:35 +00:00
|
|
|
@pipeline = Ci::Pipeline.new(
|
2017-05-24 13:13:51 +00:00
|
|
|
source: source,
|
2016-08-11 13:22:35 +00:00
|
|
|
project: project,
|
|
|
|
ref: ref,
|
|
|
|
sha: sha,
|
|
|
|
before_sha: before_sha,
|
|
|
|
tag: tag?,
|
|
|
|
trigger_requests: Array(trigger_request),
|
2017-05-07 22:35:56 +00:00
|
|
|
user: current_user,
|
2017-08-31 08:01:43 +00:00
|
|
|
pipeline_schedule: schedule,
|
|
|
|
protected: project.protected_for?(ref)
|
2016-08-11 13:22:35 +00:00
|
|
|
)
|
|
|
|
|
2017-09-05 20:57:42 +00:00
|
|
|
result = validate_project_and_git_items ||
|
|
|
|
validate_pipeline(ignore_skip_ci: ignore_skip_ci,
|
|
|
|
save_on_errors: save_on_errors)
|
2017-07-26 13:58:31 +00:00
|
|
|
|
2017-07-27 15:33:41 +00:00
|
|
|
return result if result
|
|
|
|
|
|
|
|
begin
|
2017-07-26 09:31:09 +00:00
|
|
|
Ci::Pipeline.transaction do
|
|
|
|
pipeline.save!
|
2017-07-21 09:49:32 +00:00
|
|
|
|
2017-07-26 09:31:09 +00:00
|
|
|
yield(pipeline) if block_given?
|
|
|
|
|
|
|
|
Ci::CreatePipelineStagesService
|
|
|
|
.new(project, current_user)
|
|
|
|
.execute(pipeline)
|
|
|
|
end
|
|
|
|
rescue ActiveRecord::RecordInvalid => e
|
|
|
|
return error("Failed to persist the pipeline: #{e}")
|
2017-07-21 09:49:32 +00:00
|
|
|
end
|
|
|
|
|
2017-07-26 09:31:09 +00:00
|
|
|
update_merge_requests_head_pipeline
|
|
|
|
|
2017-07-21 09:49:32 +00:00
|
|
|
cancel_pending_pipelines if project.auto_cancel_pending_pipelines?
|
|
|
|
|
|
|
|
pipeline_created_counter.increment(source: source)
|
|
|
|
|
|
|
|
pipeline.tap(&:process!)
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2017-09-05 20:57:42 +00:00
|
|
|
def validate_project_and_git_items
|
2016-08-11 13:22:35 +00:00
|
|
|
unless project.builds_enabled?
|
2017-07-27 15:33:41 +00:00
|
|
|
return error('Pipeline is disabled')
|
2016-08-11 13:22:35 +00:00
|
|
|
end
|
|
|
|
|
2017-09-05 20:57:42 +00:00
|
|
|
unless allowed_to_trigger_pipeline?
|
|
|
|
if can?(current_user, :create_pipeline, project)
|
2017-07-27 15:33:41 +00:00
|
|
|
return error("Insufficient permissions for protected ref '#{ref}'")
|
2017-07-19 08:42:47 +00:00
|
|
|
else
|
2017-07-27 15:33:41 +00:00
|
|
|
return error('Insufficient permissions to create a new pipeline')
|
2017-07-19 08:42:47 +00:00
|
|
|
end
|
2016-05-09 23:26:13 +00:00
|
|
|
end
|
|
|
|
|
2017-07-19 17:31:20 +00:00
|
|
|
unless branch? || tag?
|
2017-07-27 15:33:41 +00:00
|
|
|
return error('Reference not found')
|
2017-07-19 17:31:20 +00:00
|
|
|
end
|
|
|
|
|
2016-08-11 13:22:35 +00:00
|
|
|
unless commit
|
2017-07-27 15:33:41 +00:00
|
|
|
return error('Commit not found')
|
2016-05-09 23:26:13 +00:00
|
|
|
end
|
2017-09-05 20:57:42 +00:00
|
|
|
end
|
2016-05-09 23:26:13 +00:00
|
|
|
|
2017-09-05 20:57:42 +00:00
|
|
|
def validate_pipeline(ignore_skip_ci:, save_on_errors:)
|
2016-06-02 10:39:15 +00:00
|
|
|
unless pipeline.config_processor
|
2016-08-11 13:22:35 +00:00
|
|
|
unless pipeline.ci_yaml_file
|
2017-07-27 15:33:41 +00:00
|
|
|
return error("Missing #{pipeline.ci_yaml_file_path} file")
|
2016-08-11 13:22:35 +00:00
|
|
|
end
|
2017-07-27 15:33:41 +00:00
|
|
|
return error(pipeline.yaml_errors, save: save_on_errors)
|
2016-06-02 10:39:15 +00:00
|
|
|
end
|
2016-05-15 00:47:16 +00:00
|
|
|
|
2016-08-11 13:22:35 +00:00
|
|
|
if !ignore_skip_ci && skip_ci?
|
2016-08-11 18:54:02 +00:00
|
|
|
pipeline.skip if save_on_errors
|
2017-07-28 09:16:46 +00:00
|
|
|
return pipeline
|
2016-08-11 13:22:35 +00:00
|
|
|
end
|
2016-05-09 23:26:13 +00:00
|
|
|
|
2017-06-02 13:05:14 +00:00
|
|
|
unless pipeline.has_stage_seeds?
|
2017-07-27 15:33:41 +00:00
|
|
|
return error('No stages / jobs for this pipeline.')
|
2016-05-09 23:26:13 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-09-05 20:57:42 +00:00
|
|
|
def allowed_to_trigger_pipeline?
|
|
|
|
if current_user
|
|
|
|
allowed_to_create?
|
2017-07-06 09:37:27 +00:00
|
|
|
else # legacy triggers don't have a corresponding user
|
2017-07-03 21:20:44 +00:00
|
|
|
!project.protected_for?(ref)
|
2017-07-06 09:37:27 +00:00
|
|
|
end
|
2017-07-03 21:20:44 +00:00
|
|
|
end
|
|
|
|
|
2017-09-05 20:57:42 +00:00
|
|
|
def allowed_to_create?
|
2017-09-06 16:35:28 +00:00
|
|
|
return unless can?(current_user, :create_pipeline, project)
|
2017-07-05 13:55:35 +00:00
|
|
|
|
2017-09-06 16:35:28 +00:00
|
|
|
access = Gitlab::UserAccess.new(current_user, project: project)
|
|
|
|
if branch?
|
|
|
|
access.can_update_branch?(ref)
|
|
|
|
elsif tag?
|
|
|
|
access.can_create_tag?(ref)
|
|
|
|
else
|
|
|
|
true # Allow it for now and we'll reject when we check ref existence
|
|
|
|
end
|
2017-07-05 13:55:35 +00:00
|
|
|
end
|
|
|
|
|
2017-05-25 21:14:40 +00:00
|
|
|
def update_merge_requests_head_pipeline
|
2017-06-01 18:31:52 +00:00
|
|
|
return unless pipeline.latest?
|
2017-05-25 21:14:40 +00:00
|
|
|
|
2017-06-21 13:48:12 +00:00
|
|
|
MergeRequest.where(source_project: @pipeline.project, source_branch: @pipeline.ref)
|
|
|
|
.update_all(head_pipeline_id: @pipeline.id)
|
2017-05-25 21:14:40 +00:00
|
|
|
end
|
|
|
|
|
2016-08-11 13:22:35 +00:00
|
|
|
def skip_ci?
|
2017-02-10 10:32:59 +00:00
|
|
|
return false unless pipeline.git_commit_message
|
|
|
|
pipeline.git_commit_message =~ /\[(ci[ _-]skip|skip[ _-]ci)\]/i
|
2016-05-09 23:26:13 +00:00
|
|
|
end
|
|
|
|
|
2017-02-19 22:17:24 +00:00
|
|
|
def cancel_pending_pipelines
|
2017-03-18 10:04:14 +00:00
|
|
|
Gitlab::OptimisticLocking.retry_lock(auto_cancelable_pipelines) do |cancelables|
|
2017-02-19 22:17:24 +00:00
|
|
|
cancelables.find_each do |cancelable|
|
2017-04-06 13:32:56 +00:00
|
|
|
cancelable.auto_cancel_running(pipeline)
|
2017-02-19 22:17:24 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-03-18 10:04:14 +00:00
|
|
|
def auto_cancelable_pipelines
|
|
|
|
project.pipelines
|
|
|
|
.where(ref: pipeline.ref)
|
|
|
|
.where.not(id: pipeline.id)
|
|
|
|
.where.not(sha: project.repository.sha_from_ref(pipeline.ref))
|
|
|
|
.created_or_pending
|
|
|
|
end
|
|
|
|
|
2016-05-09 23:26:13 +00:00
|
|
|
def commit
|
2016-08-11 13:22:35 +00:00
|
|
|
@commit ||= project.commit(origin_sha || origin_ref)
|
|
|
|
end
|
|
|
|
|
|
|
|
def sha
|
|
|
|
commit.try(:id)
|
|
|
|
end
|
|
|
|
|
|
|
|
def before_sha
|
|
|
|
params[:checkout_sha] || params[:before] || Gitlab::Git::BLANK_SHA
|
|
|
|
end
|
|
|
|
|
|
|
|
def origin_sha
|
|
|
|
params[:checkout_sha] || params[:after]
|
|
|
|
end
|
|
|
|
|
|
|
|
def origin_ref
|
|
|
|
params[:ref]
|
|
|
|
end
|
|
|
|
|
|
|
|
def branch?
|
2017-06-05 16:56:38 +00:00
|
|
|
return @is_branch if defined?(@is_branch)
|
|
|
|
|
|
|
|
@is_branch =
|
|
|
|
project.repository.ref_exists?(Gitlab::Git::BRANCH_REF_PREFIX + ref)
|
2016-08-11 13:22:35 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def tag?
|
2017-06-05 16:56:38 +00:00
|
|
|
return @is_tag if defined?(@is_tag)
|
|
|
|
|
|
|
|
@is_tag =
|
|
|
|
project.repository.ref_exists?(Gitlab::Git::TAG_REF_PREFIX + ref)
|
2016-08-11 13:22:35 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def ref
|
2017-07-05 13:55:35 +00:00
|
|
|
@ref ||= Gitlab::Git.ref_name(origin_ref)
|
2016-08-11 13:22:35 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def valid_sha?
|
|
|
|
origin_sha && origin_sha != Gitlab::Git::BLANK_SHA
|
|
|
|
end
|
|
|
|
|
|
|
|
def error(message, save: false)
|
2017-08-18 12:12:40 +00:00
|
|
|
pipeline.tap do
|
|
|
|
pipeline.errors.add(:base, message)
|
|
|
|
|
|
|
|
if save
|
|
|
|
pipeline.drop
|
|
|
|
update_merge_requests_head_pipeline
|
|
|
|
end
|
|
|
|
end
|
2016-05-09 23:26:13 +00:00
|
|
|
end
|
2017-06-07 15:12:23 +00:00
|
|
|
|
|
|
|
def pipeline_created_counter
|
2017-07-19 07:43:57 +00:00
|
|
|
@pipeline_created_counter ||= Gitlab::Metrics.counter(:pipelines_created_total, "Counter of pipelines created")
|
2017-06-07 15:12:23 +00:00
|
|
|
end
|
2016-05-09 23:26:13 +00:00
|
|
|
end
|
2016-05-12 18:08:18 +00:00
|
|
|
end
|