summaryrefslogtreecommitdiff
path: root/app/workers/irker_worker.rb
blob: 3097a9fbc038b02b870cf056fd3f75f2521817ca (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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# frozen_string_literal: true

require 'json'
require 'socket'

class IrkerWorker # rubocop:disable Scalability/IdempotentWorker
  include ApplicationWorker

  data_consistency :always
  sidekiq_options retry: 3
  feature_category :integrations
  urgency :low

  def perform(project_id, channels, colors, push_data, settings)
    # Establish connection to irker server
    return false unless start_connection(settings['server_host'],
                                         settings['server_port'])

    @project = Project.find(project_id)
    @colors = colors
    @channels = channels

    @repo_path = @project.full_path
    @repo_name = push_data['repository']['name']
    @committer = push_data['user_name']
    @branch = push_data['ref'].gsub(%r'refs/[^/]*/', '')

    if @colors
      @repo_name = "\x0304#{@repo_name}\x0f"
      @branch = "\x0305#{@branch}\x0f"
    end

    # First messages are for branch creation/deletion
    send_branch_updates(push_data)

    # Next messages are for commits
    send_commits(push_data)

    close_connection
    true
  end

  private

  def start_connection(irker_server, irker_port)
    begin
      @socket = TCPSocket.new irker_server, irker_port
    rescue Errno::ECONNREFUSED => e
      logger.fatal "Can't connect to Irker daemon: #{e}"
      return false
    end

    true
  end

  def send_to_irker(privmsg)
    to_send = { to: @channels, privmsg: privmsg }

    @socket.puts Gitlab::Json.dump(to_send)
  end

  def close_connection
    @socket.close
  end

  def send_branch_updates(push_data)
    message =
      if Gitlab::Git.blank_ref?(push_data['before'])
        new_branch_message
      elsif Gitlab::Git.blank_ref?(push_data['after'])
        delete_branch_message
      end

    send_to_irker(message)
  end

  def new_branch_message
    newbranch = "#{Gitlab.config.gitlab.url}/#{@repo_path}/-/branches"
    newbranch = "\x0302\x1f#{newbranch}\x0f" if @colors

    "[#{@repo_name}] #{@committer} has created a new branch #{@branch}: #{newbranch}"
  end

  def delete_branch_message
    "[#{@repo_name}] #{@committer} has deleted the branch #{@branch}"
  end

  def send_commits(push_data)
    return if push_data['total_commits_count'] == 0

    # Next message is for number of commit pushed, if any
    if Gitlab::Git.blank_ref?(push_data['before'])
      # Tweak on push_data["before"] in order to have a nice compare URL
      push_data['before'] = before_on_new_branch(push_data)
    end

    send_commits_count(push_data)

    # One message per commit, limited by 3 messages (same limit as the
    # github irc hook)
    commits = push_data['commits'].first(3)
    commits.each do |commit_attrs|
      send_one_commit(commit_attrs)
    end
  end

  def before_on_new_branch(push_data)
    commit = commit_from_id(push_data['commits'][0]['id'])
    parents = commit.parents

    # Return old value if there's no new one
    return push_data['before'] if parents.empty?

    # Or return the first parent-commit
    parents[0].id
  end

  def send_commits_count(push_data)
    url = compare_url(push_data['before'], push_data['after'])
    commits = colorize_commits(push_data['total_commits_count'])
    new_commits = 'new commit'.pluralize(push_data['total_commits_count'])

    send_to_irker("[#{@repo_name}] #{@committer} pushed #{commits} #{new_commits} " \
                  "to #{@branch}: #{url}")
  end

  def compare_url(sha_before, sha_after)
    sha1 = Commit.truncate_sha(sha_before)
    sha2 = Commit.truncate_sha(sha_after)
    compare_url = "#{Gitlab.config.gitlab.url}/#{@repo_path}/-/compare" \
                  "/#{sha1}...#{sha2}"

    colorize_url(compare_url)
  end

  def send_one_commit(commit_attrs)
    commit = commit_from_id(commit_attrs['id'])
    sha = colorize_sha(Commit.truncate_sha(commit_attrs['id']))
    author = commit_attrs['author']['name']
    files = colorize_nb_files(files_count(commit))
    title = commit.title

    send_to_irker("#{@repo_name}/#{@branch} #{sha} #{author} (#{files}): #{title}")
  end

  def commit_from_id(id)
    @project.commit(id)
  end

  def files_count(commit)
    diff_size = commit.raw_deltas.size

    "#{diff_size} file".pluralize(diff_size)
  end

  def colorize_sha(sha)
    sha = "\x0314#{sha}\x0f" if @colors
    sha
  end

  def colorize_nb_files(nb_files)
    nb_files = "\x0312#{nb_files}\x0f" if @colors
    nb_files
  end

  def colorize_url(url)
    url = "\x0302\x1f#{url}\x0f" if @colors
    url
  end

  def colorize_commits(commits)
    commits = "\x02#{commits}\x0f" if @colors
    commits
  end
end