Class: Deployments::LinkMergeRequestsService

Inherits:
Object
  • Object
show all
Defined in:
app/services/deployments/link_merge_requests_service.rb

Overview

Service class for linking merge requests to deployments.

Constant Summary collapse

COMMITS_PER_QUERY =

The number of commits per query for which to find merge requests.

5_000

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(deployment) ⇒ LinkMergeRequestsService

Returns a new instance of LinkMergeRequestsService.


11
12
13
# File 'app/services/deployments/link_merge_requests_service.rb', line 11

def initialize(deployment)
  @deployment = deployment
end

Instance Attribute Details

#deploymentObject (readonly)

Returns the value of attribute deployment.


6
7
8
# File 'app/services/deployments/link_merge_requests_service.rb', line 6

def deployment
  @deployment
end

Instance Method Details

#executeObject


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
# File 'app/services/deployments/link_merge_requests_service.rb', line 15

def execute
  # Review apps have the environment type set (e.g. to `review`, though the
  # exact value may differ). We don't want to link merge requests to review
  # app deployments, as this is not useful.
  return unless deployment.environment.should_link_to_merge_requests?

  # This service is triggered by a Sidekiq worker, which only runs when a
  # deployment is successful. We add an extra check here in case we ever
  # call this service elsewhere and forget to check the status there.
  #
  # The reason we only want to link successful deployments is as follows:
  # when we link a merge request, we don't link it to future deployments for
  # the same environment. If we were to link an MR to a failed deploy, we
  # wouldn't be able to later on link it to a successful deploy (e.g. after
  # the deploy is retried).
  #
  # In addition, showing failed deploys in the UI of a merge request isn't
  # useful to users, as they can't act upon the information in any
  # meaningful way (i.e. they can't just retry the deploy themselves).
  return unless deployment.success?

  if (prev = deployment.previous_deployment)
    link_merge_requests_for_range(prev.sha, deployment.sha)
  else
    # When no previous deployment is found we fall back to linking all merge
    # requests merged into the deployed branch. This will not always be
    # accurate, but it's better than having no data.
    #
    # We can't use the first commit in the repository as a base to compare
    # to, as this will not scale to large repositories. For example, GitLab
    # itself has over 150 000 commits.
    link_all_merged_merge_requests
  end
end

83
84
85
86
87
88
# File 'app/services/deployments/link_merge_requests_service.rb', line 83

def link_all_merged_merge_requests
  merge_requests =
    project.merge_requests.merged.by_target_branch(deployment.ref)

  deployment.link_merge_requests(merge_requests)
end

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
# File 'app/services/deployments/link_merge_requests_service.rb', line 50

def link_merge_requests_for_range(from, to)
  commits = project
    .repository
    .commits_between(from, to)
    .map(&:id)

  # For some projects the list of commits to deploy may be very large. To
  # ensure we do not end up running SQL queries with thousands of WHERE IN
  # values, we run one query per a certain number of commits.
  #
  # In most cases this translates to only a single query. For very large
  # deployment we may end up running a handful of queries to get and insert
  # the data.
  commits.each_slice(COMMITS_PER_QUERY) do |slice|
    merge_requests =
      project.merge_requests.merged.by_merge_commit_sha(slice)

    deployment.link_merge_requests(merge_requests)

    # The cherry picked commits are tracked via `notes.commit_id`
    # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22209
    #
    # NOTE: cross-joining `merge_requests` table and `notes` table could
    # result in very poor performance because PG planner often uses an
    # inappropriate index.
    # See https://gitlab.com/gitlab-org/gitlab/-/issues/321032.
    mr_ids = project.notes.cherry_picked_merge_requests(slice)
    picked_merge_requests = project.merge_requests.id_in(mr_ids)

    deployment.link_merge_requests(picked_merge_requests)
  end
end