summaryrefslogtreecommitdiff
path: root/app/services/ci/create_pipeline_service.rb
blob: f96c4741ba4c7f8c492cc957b986b07d92c4809e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
module Ci
  class CreatePipelineService < BaseService
    attr_reader :pipeline

    SEQUENCE = [Gitlab::Ci::Pipeline::Chain::Validate,
                Gitlab::Ci::Pipeline::Chain::Skip]

    def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil)
      @pipeline = Ci::Pipeline.new(
        source: source,
        project: project,
        ref: ref,
        sha: sha,
        before_sha: before_sha,
        tag: tag?,
        trigger_requests: Array(trigger_request),
        user: current_user,
        pipeline_schedule: schedule,
        protected: project.protected_for?(ref)
      )

      command = OpenStruct.new(ignore_skip_ci: ignore_skip_ci,
                               save_incompleted: save_on_errors,
                               trigger_request: trigger_request,
                               schedule: schedule,
                               project: project,
                               current_user: current_user)

      sequence = SEQUENCE.map { |chain| chain.new(pipeline, command) }

      done = sequence.any? do |chain|
        chain.perform!
        chain.break?
      end

      update_merge_requests_head_pipeline if pipeline.persisted?

      return pipeline if done

      begin
        Ci::Pipeline.transaction do
          pipeline.save!

          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}")
      end

      update_merge_requests_head_pipeline if pipeline.persisted?
      cancel_pending_pipelines if project.auto_cancel_pending_pipelines?
      pipeline_created_counter.increment(source: source)

      pipeline.tap(&:process!)
    end

    private

    def commit
      @commit ||= project.commit(origin_sha || origin_ref)
    end

    def sha
      commit.try(:id)
    end

    def update_merge_requests_head_pipeline
      return unless pipeline.latest?

      MergeRequest.where(source_project: @pipeline.project, source_branch: @pipeline.ref)
        .update_all(head_pipeline_id: @pipeline.id)
    end

    def cancel_pending_pipelines
      Gitlab::OptimisticLocking.retry_lock(auto_cancelable_pipelines) do |cancelables|
        cancelables.find_each do |cancelable|
          cancelable.auto_cancel_running(pipeline)
        end
      end
    end

    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


    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 tag?
      return @is_tag if defined?(@is_tag)

      @is_tag = project.repository.tag_exists?(ref)
    end

    def ref
      @ref ||= Gitlab::Git.ref_name(origin_ref)
    end

    def valid_sha?
      origin_sha && origin_sha != Gitlab::Git::BLANK_SHA
    end

    def pipeline_created_counter
      @pipeline_created_counter ||= Gitlab::Metrics
        .counter(:pipelines_created_total, "Counter of pipelines created")
    end
  end
end