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 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
|
# frozen_string_literal: true
require 'json'
require 'socket'
require 'resolv'
module Integrations
class IrkerWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
data_consistency :sticky
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)
ip_address = Resolv.getaddress(irker_server)
# handle IP6 addresses
domain = Resolv::IPv6::Regex.match?(ip_address) ? "[#{ip_address}]" : ip_address
begin
Gitlab::HTTP_V2::UrlBlocker.validate!(
"irc://#{domain}",
allow_localhost: allow_local_requests?,
allow_local_network: allow_local_requests?,
schemes: ['irc'],
deny_all_requests_except_allowed: Gitlab::CurrentSettings.deny_all_requests_except_allowed?,
outbound_local_requests_allowlist: Gitlab::CurrentSettings.outbound_local_requests_whitelist) # rubocop:disable Naming/InclusiveLanguage -- existing setting
@socket = TCPSocket.new ip_address, irker_port
rescue Errno::ECONNREFUSED, Gitlab::HTTP_V2::UrlBlocker::BlockedUrlError => e
logger.fatal "Can't connect to Irker daemon: #{e}"
return false
end
true
end
def allow_local_requests?
Gitlab::CurrentSettings.allow_local_requests_from_web_hooks_and_services?
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
end
|