summaryrefslogtreecommitdiff
path: root/app/models/deployment.rb
blob: 056c49e7162e879c08376cb287d5cee2822d411d (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
127
128
129
130
131
132
133
class Deployment < ActiveRecord::Base
  include InternalId

  belongs_to :project, required: true, validate: true
  belongs_to :environment, required: true, validate: true
  belongs_to :user
  belongs_to :deployable, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations

  validates :sha, presence: true
  validates :ref, presence: true

  delegate :name, to: :environment, prefix: true

  after_create :create_ref
  after_create :invalidate_cache

  def commit
    project.commit(sha)
  end

  def commit_title
    commit.try(:title)
  end

  def short_sha
    Commit.truncate_sha(sha)
  end

  def last?
    self == environment.last_deployment
  end

  def create_ref
    project.repository.create_ref(ref, ref_path)
  end

  def invalidate_cache
    environment.expire_etag_cache
  end

  def manual_actions
    @manual_actions ||= deployable.try(:other_actions)
  end

  def includes_commit?(commit)
    return false unless commit

    # Before 8.10, deployments didn't have keep-around refs. Any deployment
    # created before then could have a `sha` referring to a commit that no
    # longer exists in the repository, so just ignore those.
    begin
      project.repository.is_ancestor?(commit.id, sha)
    rescue Rugged::OdbError
      false
    end
  end

  def update_merge_request_metrics!
    return unless environment.update_merge_request_metrics?

    merge_requests = project.merge_requests
                     .joins(:metrics)
                     .where(target_branch: self.ref, merge_request_metrics: { first_deployed_to_production_at: nil })
                     .where("merge_request_metrics.merged_at <= ?", self.created_at)

    if previous_deployment
      merge_requests = merge_requests.where("merge_request_metrics.merged_at >= ?", previous_deployment.created_at)
    end

    # Need to use `map` instead of `select` because MySQL doesn't allow `SELECT`ing from the same table
    # that we're updating.
    merge_request_ids =
      if Gitlab::Database.postgresql?
        merge_requests.select(:id)
      elsif Gitlab::Database.mysql?
        merge_requests.map(&:id)
      end

    MergeRequest::Metrics
      .where(merge_request_id: merge_request_ids, first_deployed_to_production_at: nil)
      .update_all(first_deployed_to_production_at: self.created_at)
  end

  def previous_deployment
    @previous_deployment ||=
      project.deployments.joins(:environment)
      .where(environments: { name: self.environment.name }, ref: self.ref)
      .where.not(id: self.id)
      .take
  end

  def stop_action
    return unless on_stop.present?
    return unless manual_actions

    @stop_action ||= manual_actions.find_by(name: on_stop)
  end

  def stop_action?
    stop_action.present?
  end

  def formatted_deployment_time
    created_at.to_time.in_time_zone.to_s(:medium)
  end

  def has_metrics?
    project.monitoring_service.present?
  end

  def metrics
    return {} unless has_metrics?

    project.monitoring_service.deployment_metrics(self)
  end

  def has_additional_metrics?
    project.prometheus_service.present?
  end

  def additional_metrics
    return {} unless project.prometheus_service.present?

    metrics = project.prometheus_service.additional_deployment_metrics(self)
    metrics&.merge(deployment_time: created_at.to_i) || {}
  end

  private

  def ref_path
    File.join(environment.ref_path, 'deployments', iid.to_s)
  end
end