summaryrefslogtreecommitdiff
path: root/lib/gitlab/data_builder/push.rb
blob: a83b03f540c9e4f314b090ea2cfeed38bb2c2b71 (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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# frozen_string_literal: true

module Gitlab
  module DataBuilder
    module Push
      extend self

      SAMPLE_DATA =
        {
          object_kind: "push",
          event_name: "push",
          before: "95790bf891e76fee5e1747ab589903a6a1f80f22",
          after: "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
          ref: "refs/heads/master",
          checkout_sha: "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
          message: "Hello World",
          user_id: 4,
          user_name: "John Smith",
          user_email: "john@example.com",
          user_avatar: "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80",
          project_id: 15,
          commits: [
            {
              id: "c5feabde2d8cd023215af4d2ceeb7a64839fc428",
              message: "Add simple search to projects in public area",
              timestamp: "2013-05-13T18:18:08+00:00",
              url: "https://test.example.com/gitlab/gitlabhq/commit/c5feabde2d8cd023215af4d2ceeb7a64839fc428",
              author: {
                name: "Test User",
                email: "test@example.com"
              }
            }
          ],
          total_commits_count: 1,
          push_options: { ci: { skip: true } }
        }.freeze

      # Produce a hash of post-receive data
      #
      # data = {
      #   before: String,
      #   after: String,
      #   ref: String,
      #   user_id: String,
      #   user_name: String,
      #   user_username: String,
      #   user_email: String
      #   project_id: String,
      #   repository: {
      #     name: String,
      #     url: String,
      #     description: String,
      #     homepage: String,
      #   },
      #   commits: Array,
      #   total_commits_count: Fixnum,
      #   push_options: Hash
      # }
      #
      # rubocop:disable Metrics/ParameterLists
      def build(
          project:, user:, ref:, oldrev: nil, newrev: nil,
          commits: [], commits_count: nil, message: nil, push_options: {},
          with_changed_files: true)

        commits = Array(commits)

        # Total commits count
        commits_count ||= commits.size

        # Get latest 20 commits ASC
        commits_limited = commits.last(20)

        # For performance purposes maximum 20 latest commits
        # will be passed as post receive hook data.
        # n+1: https://gitlab.com/gitlab-org/gitlab-foss/issues/38259
        commit_attrs = Gitlab::GitalyClient.allow_n_plus_1_calls do
          commits_limited.map do |commit|
            commit.hook_attrs(with_changed_files: with_changed_files)
          end
        end

        type = Gitlab::Git.tag_ref?(ref) ? 'tag_push' : 'push'

        # Hash to be passed as post_receive_data
        {
          object_kind: type,
          event_name: type,
          before: oldrev,
          after: newrev,
          ref: ref,
          checkout_sha: checkout_sha(project.repository, newrev, ref),
          message: message,
          user_id: user.id,
          user_name: user.name,
          user_username: user.username,
          user_email: user.public_email,
          user_avatar: user.avatar_url(only_path: false),
          project_id: project.id,
          project: project.hook_attrs,
          commits: commit_attrs,
          total_commits_count: commits_count,
          push_options: push_options,
          # DEPRECATED
          repository: project.hook_attrs.slice(:name, :url, :description, :homepage,
                                               :git_http_url, :git_ssh_url, :visibility_level)
        }
      end

      def build_bulk(action:, ref_type:, changes:)
        {
          action: action,
          ref_count: changes.count,
          ref_type: ref_type
        }
      end

      # This method provides a sample data generated with
      # existing project and commits to test webhooks
      def build_sample(project, user)
        # Use sample data if repo has no commit
        # (expect the case of test service configuration settings)
        return sample_data if project.empty_repo?

        ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{project.default_branch}"
        commits = project.repository.commits(project.default_branch.to_s, limit: 3)

        build(project: project,
              user: user,
              oldrev: commits.last&.id,
              newrev: commits.first&.id,
              ref: ref,
              commits: commits)
      end

      def sample_data
        SAMPLE_DATA
      end

      def checkout_sha(repository, newrev, ref)
        # Checkout sha is nil when we remove branch or tag
        return if Gitlab::Git.blank_ref?(newrev)

        # Find sha for tag, except when it was deleted.
        if Gitlab::Git.tag_ref?(ref)
          tag_name = Gitlab::Git.ref_name(ref)
          tag = repository.find_tag(tag_name)

          if tag
            commit = repository.commit(tag.dereferenced_target)
            commit.try(:sha)
          end
        else
          newrev
        end
      end
    end
  end
end