build-buddy 1.3.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/build-buddy +8 -1
- data/lib/build_buddy.rb +5 -2
- data/lib/build_buddy/build_data.rb +1 -1
- data/lib/build_buddy/builder.rb +1 -2
- data/lib/build_buddy/gitter.rb +22 -0
- data/lib/build_buddy/scheduler.rb +106 -0
- data/lib/build_buddy/server.rb +24 -260
- data/lib/build_buddy/slacker.rb +175 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 329f4a21f34dfda0663a97f4d74b691bba541491
|
4
|
+
data.tar.gz: 7f3da88951d7fd2a1a7bd982cc324eb550a82e0d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0608f25926cd5646ea0665b86e53d1a5a1aa18079504a9d166dc99fb8ab85cd3c21e34f47566a8504bd6f8f598ef5569e67bea6112e901124ad6249735f20665
|
7
|
+
data.tar.gz: 5d5e776f0f9115d2636f3f8095132d8da9316e4d33acb39b208a2fc6ebca5f8016d926fead214a012670e784a438b0dbb8652829d2aaeeb8d746613d749ab37f
|
data/bin/build-buddy
CHANGED
@@ -41,9 +41,16 @@ module BuildBuddy
|
|
41
41
|
Celluloid.logger = Reel::Logger.logger
|
42
42
|
|
43
43
|
Builder.supervise as: :builder
|
44
|
+
Slacker.supervise as: :slacker
|
45
|
+
Gitter.supervise as: :gitter
|
46
|
+
Scheduler.supervise as: :scheduler
|
44
47
|
Server.supervise as: :server
|
45
48
|
|
46
|
-
|
49
|
+
begin
|
50
|
+
sleep
|
51
|
+
rescue Interrupt => e
|
52
|
+
puts
|
53
|
+
end
|
47
54
|
end
|
48
55
|
|
49
56
|
version BuildBuddy::VERSION
|
data/lib/build_buddy.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
require 'build_buddy/config'
|
2
|
-
require 'build_buddy/
|
2
|
+
require 'build_buddy/slacker'
|
3
|
+
require 'build_buddy/gitter'
|
3
4
|
require 'build_buddy/builder'
|
4
5
|
require 'build_buddy/watcher'
|
6
|
+
require 'build_buddy/scheduler'
|
7
|
+
require 'build_buddy/server'
|
5
8
|
require 'build_buddy/build_data'
|
6
9
|
|
7
10
|
module BuildBuddy
|
8
|
-
VERSION = "1.
|
11
|
+
VERSION = "1.4.0"
|
9
12
|
end
|
@@ -6,7 +6,7 @@ module BuildBuddy
|
|
6
6
|
attr_accessor :pull_request
|
7
7
|
attr_accessor :repo_full_name
|
8
8
|
attr_accessor :repo_sha
|
9
|
-
attr_accessor :termination_type
|
9
|
+
attr_accessor :termination_type # :killed or :exited
|
10
10
|
attr_accessor :exit_code
|
11
11
|
attr_accessor :start_time
|
12
12
|
attr_accessor :end_time
|
data/lib/build_buddy/builder.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'bundler'
|
3
3
|
require 'celluloid'
|
4
|
-
require 'ostruct'
|
5
4
|
require_relative './watcher.rb'
|
6
5
|
require_relative './config.rb'
|
7
6
|
|
@@ -70,7 +69,7 @@ module BuildBuddy
|
|
70
69
|
@build_data.termination_type = (status.signaled? ? :killed : :exited)
|
71
70
|
@build_data.exit_code = (status.exited? ? status.exitstatus : -1)
|
72
71
|
info "Process #{status.pid} #{@build_data.termination_type == :killed ? 'was terminated' : "exited (#{@build_data.exit_code})"}"
|
73
|
-
Celluloid::Actor[:
|
72
|
+
Celluloid::Actor[:scheduler].async.on_build_completed(@build_data)
|
74
73
|
@watcher.terminate
|
75
74
|
@watcher = nil
|
76
75
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
require 'celluloid'
|
4
|
+
require 'octokit'
|
5
|
+
require_relative './config.rb'
|
6
|
+
|
7
|
+
module BuildBuddy
|
8
|
+
class Gitter
|
9
|
+
include Celluloid
|
10
|
+
include Celluloid::Internals::Logger
|
11
|
+
|
12
|
+
def initialize()
|
13
|
+
@gh_client ||= Octokit::Client.new(:access_token => Config.github_api_token)
|
14
|
+
info "Connected to Github"
|
15
|
+
end
|
16
|
+
|
17
|
+
def set_status(repo_full_name, repo_sha, status, description)
|
18
|
+
@gh_client.create_status(
|
19
|
+
repo_full_name, repo_sha, status.to_s, { :description => description.length > 140 ? "#{description[0..136]}..." : description})
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
require 'celluloid'
|
4
|
+
require 'timers'
|
5
|
+
require_relative './config.rb'
|
6
|
+
|
7
|
+
module BuildBuddy
|
8
|
+
class Scheduler
|
9
|
+
include Celluloid
|
10
|
+
include Celluloid::Internals::Logger
|
11
|
+
|
12
|
+
attr_reader :active_build
|
13
|
+
|
14
|
+
def initialize()
|
15
|
+
@build_queue = Queue.new
|
16
|
+
@done_queue = Queue.new
|
17
|
+
@build_timer = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def queue_a_build(build_data)
|
21
|
+
@build_queue.push(build_data)
|
22
|
+
|
23
|
+
case build_data.build_type
|
24
|
+
when :pull_request
|
25
|
+
Celluloid::Actor[:gitter].async.set_status(
|
26
|
+
build_data.repo_full_name, build_data.repo_sha, :pending, "This build is in the queue")
|
27
|
+
info "Pull request build queued"
|
28
|
+
when :master
|
29
|
+
info "`master` branch build queued"
|
30
|
+
when :release
|
31
|
+
info "Release branch build queued"
|
32
|
+
end
|
33
|
+
|
34
|
+
if @build_timer.nil?
|
35
|
+
@build_timer = every(5) { on_build_interval }
|
36
|
+
info "Build timer started"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def queue_length
|
41
|
+
@build_queue.length
|
42
|
+
end
|
43
|
+
|
44
|
+
def stop_build
|
45
|
+
# Centralize stopping bulids here in case we allow multiple active builders in future
|
46
|
+
unless @active_build.nil?
|
47
|
+
Celluloid::Actor[:builder].stop_build
|
48
|
+
true
|
49
|
+
else
|
50
|
+
false
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def on_build_interval
|
55
|
+
if @active_build.nil?
|
56
|
+
if @build_queue.length > 0
|
57
|
+
build_data = @build_queue.pop()
|
58
|
+
@active_build = build_data
|
59
|
+
if build_data.build_type == :pull_request
|
60
|
+
Celluloid::Actor[:gitter].async.set_status(
|
61
|
+
build_data.repo_full_name, build_data.repo_sha, :pending, "This build has started")
|
62
|
+
end
|
63
|
+
Celluloid::Actor[:builder].async.start_build(build_data)
|
64
|
+
elsif @done_queue.length > 0
|
65
|
+
build_data = @done_queue.pop
|
66
|
+
status_message = build_data.termination_type == :killed ? "was stopped" : build_data.exit_code != 0 ? "failed" : "succeeded"
|
67
|
+
|
68
|
+
if build_data.build_type == :pull_request
|
69
|
+
message = "The buddy build #{status_message}"
|
70
|
+
Celluloid::Actor[:gitter].async.set_status(
|
71
|
+
build_data.repo_full_name, build_data.repo_sha,
|
72
|
+
build_data.termination_type == :killed ? :failure : build_data.exit_code != 0 ? :error : :success,
|
73
|
+
message)
|
74
|
+
info "Pull request build #{status_message}"
|
75
|
+
else
|
76
|
+
if build_data.exit_code != 0
|
77
|
+
status_message += ". Exit code #{build_data.exit_code}, log file `#{build_data.build_log_filename}`."
|
78
|
+
end
|
79
|
+
if build_data.build_type == :master
|
80
|
+
message = "A build of the `master` branch #{status_message}"
|
81
|
+
info "`master` branch build #{status_message}"
|
82
|
+
else
|
83
|
+
message = "A build of the `#{build_data.build_version}` branch #{status_message}"
|
84
|
+
info "Release branch build #{status_message}"
|
85
|
+
end
|
86
|
+
Celluloid::Actor[:slacker].async.notify_channel(message)
|
87
|
+
end
|
88
|
+
else
|
89
|
+
@build_timer.cancel
|
90
|
+
@build_timer = nil
|
91
|
+
info "Build timer stopped"
|
92
|
+
end
|
93
|
+
else
|
94
|
+
# Make sure that the build has not run too long and kill if necessary
|
95
|
+
if Time.now.utc - @active_build.start_time > Config.kill_build_after_mins * 60
|
96
|
+
Celluloid::Actor[:builder].async.stop_build()
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def on_build_completed(build_data)
|
102
|
+
@active_build = nil
|
103
|
+
@done_queue.push(build_data)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
data/lib/build_buddy/server.rb
CHANGED
@@ -1,11 +1,7 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'celluloid/current'
|
3
3
|
require 'reel'
|
4
|
-
require 'slack-ruby-client'
|
5
4
|
require 'json'
|
6
|
-
require 'octokit'
|
7
|
-
require 'thread'
|
8
|
-
require 'timers'
|
9
5
|
require 'rack'
|
10
6
|
require_relative './builder.rb'
|
11
7
|
|
@@ -13,164 +9,9 @@ module BuildBuddy
|
|
13
9
|
class Server < Reel::Server::HTTP
|
14
10
|
include Celluloid::Internals::Logger
|
15
11
|
|
16
|
-
def initialize(
|
17
|
-
super(
|
18
|
-
|
19
|
-
@rt_client = Slack::RealTime::Client.new
|
20
|
-
@rt_client.on :hello do
|
21
|
-
self.on_slack_hello()
|
22
|
-
end
|
23
|
-
@rt_client.on :message do |data|
|
24
|
-
self.on_slack_data(data)
|
25
|
-
end
|
26
|
-
@rt_client.on :error do |error|
|
27
|
-
self.on_slack_error(error)
|
28
|
-
end
|
29
|
-
@rt_client.start_async
|
30
|
-
@active_build = nil
|
31
|
-
@build_queue = Queue.new
|
32
|
-
@done_queue = Queue.new
|
33
|
-
@notify_slack_channel = nil
|
34
|
-
@reverse_user_map = nil
|
35
|
-
end
|
36
|
-
|
37
|
-
def on_slack_error(error)
|
38
|
-
sub_error = error['error']
|
39
|
-
error "Whoops! Slack error #{sub_error['code']} - #{sub_error['msg']}}"
|
40
|
-
end
|
41
|
-
|
42
|
-
def on_slack_hello
|
43
|
-
user_id = @rt_client.self['id']
|
44
|
-
user_map = @rt_client.users.map {|user| [user['name'], user['id']]}.to_h
|
45
|
-
@reverse_user_map = user_map.invert
|
46
|
-
info "Connected to Slack as user id #{user_id} (@#{@reverse_user_map[user_id]})"
|
47
|
-
|
48
|
-
channel_map = @rt_client.channels.map {|channel| [channel['name'], channel['id']]}.to_h
|
49
|
-
group_map = @rt_client.groups.map {|group| [group['name'], group['id']]}.to_h
|
50
|
-
channel = Config.slack_build_channel
|
51
|
-
is_channel = (channel[0] == '#')
|
52
|
-
|
53
|
-
@notify_slack_channel = (is_channel ? channel_map[channel[1..-1]] : group_map[channel])
|
54
|
-
if @notify_slack_channel.nil?
|
55
|
-
error "Unable to identify the slack channel #{channel}"
|
56
|
-
else
|
57
|
-
info "Slack notification channel is #{@notify_slack_channel} (#{channel})"
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def on_slack_data(data)
|
62
|
-
message = data['text']
|
63
|
-
|
64
|
-
# If no message, then there's nothing to do
|
65
|
-
if message.nil?
|
66
|
-
return
|
67
|
-
end
|
68
|
-
|
69
|
-
sending_user_id = data['user']
|
70
|
-
|
71
|
-
# Only respond to messages from users and bots
|
72
|
-
if sending_user_id.nil?
|
73
|
-
if data['username'].nil? or data['subtype'] != 'bot_message'
|
74
|
-
return
|
75
|
-
end
|
76
|
-
sending_user_name = data['username']
|
77
|
-
else
|
78
|
-
sending_user_name = @reverse_user_map[sending_user_id]
|
79
|
-
end
|
80
|
-
|
81
|
-
# Don't respond if _we_ sent the message!
|
82
|
-
if sending_user_id == @rt_client.self['id']
|
83
|
-
return
|
84
|
-
end
|
85
|
-
|
86
|
-
sender_is_a_builder = (Config.slack_builders.nil? ? true : Config.slack_builders.include?('@' + sending_user_name))
|
87
|
-
|
88
|
-
c = data['channel'][0]
|
89
|
-
in_channel = (c == 'C' || c == 'G')
|
90
|
-
|
91
|
-
# Don't respond if the message is to a channel and our name is not in the message
|
92
|
-
if in_channel and !message.match(@rt_client.self['id'])
|
93
|
-
return
|
94
|
-
end
|
95
|
-
|
96
|
-
case message
|
97
|
-
when /build/i
|
98
|
-
unless sender_is_a_builder
|
99
|
-
if in_channel
|
100
|
-
response = "I'm sorry @#{sending_user_name} you are not on my list of allowed builders."
|
101
|
-
else
|
102
|
-
response = "I'm sorry but you are not on my list of allowed builders."
|
103
|
-
end
|
104
|
-
else
|
105
|
-
case message
|
106
|
-
when /master/i
|
107
|
-
response = "OK, I've queued a build of the `master` branch."
|
108
|
-
queue_a_build(BuildData.new(:master,
|
109
|
-
:repo_full_name => Config.github_webhook_repo_full_name))
|
110
|
-
when /(?<version>v\d+\.\d+)/
|
111
|
-
version = $~[:version]
|
112
|
-
if Config.valid_release_versions.include?(version)
|
113
|
-
response = "OK, I've queued a build of `#{version}` branch."
|
114
|
-
queue_a_build(BuildData.new(:release,
|
115
|
-
:build_version => version,
|
116
|
-
:repo_full_name => Config.github_webhook_repo_full_name))
|
117
|
-
else
|
118
|
-
response = "I'm sorry, I cannot build the #{version} release branch"
|
119
|
-
end
|
120
|
-
when /stop/i
|
121
|
-
build_data = @active_build
|
122
|
-
if build_data.nil?
|
123
|
-
response = "There is no build running to stop"
|
124
|
-
else
|
125
|
-
response = "OK, I killed the currently running build..."
|
126
|
-
Celluloid::Actor[:builder].async.stop_build
|
127
|
-
end
|
128
|
-
else
|
129
|
-
response = "Sorry#{in_channel ? " <@#{data['user']}>" : ""}, I'm not sure if you want do a *master*, or *M.m* branch build, or maybe *stop* any running build?"
|
130
|
-
end
|
131
|
-
end
|
132
|
-
when /status/i
|
133
|
-
build_data = @active_build
|
134
|
-
queue_length = @build_queue.length
|
135
|
-
if build_data == nil
|
136
|
-
response = "There is currently no build running"
|
137
|
-
if queue_length == 0
|
138
|
-
response += " and no builds in the queue."
|
139
|
-
else
|
140
|
-
response += " and #{queue_length} in the queue."
|
141
|
-
end
|
142
|
-
else
|
143
|
-
case build_data.build_type
|
144
|
-
when :pull_request
|
145
|
-
response = "There is a pull request build in progress for https://github.com/#{build_data.repo_full_name}/pull/#{build_data.pull_request}."
|
146
|
-
when :master
|
147
|
-
response = "There is a build of the `master` branch of https://github.com/#{build_data.repo_full_name} in progress."
|
148
|
-
when :release
|
149
|
-
response = "There is a build of the `#{build_data.build_version}` branch of https://github.com/#{build_data.repo_full_name} in progress."
|
150
|
-
end
|
151
|
-
if queue_length == 1
|
152
|
-
response += " There is one build in the queue."
|
153
|
-
elsif queue_length > 1
|
154
|
-
response += " There are #{queue_length} builds in the queue."
|
155
|
-
end
|
156
|
-
end
|
157
|
-
when /help/i, /what can/i
|
158
|
-
# TODO: The repository should be a link to GitHub
|
159
|
-
response = %Q(Hello#{in_channel ? " <@#{data['user']}>" : ""}, I'm the *@#{@rt_client.self['name']}* build bot! I look after 3 types of build: pull request, master and release.
|
160
|
-
|
161
|
-
A pull request *build* happens when you make a pull request to the *#{Config.github_webhook_repo_full_name}* GitHub repository. I can stop those builds if you ask me too through Slack, but you have to start them with a pull request.
|
162
|
-
|
163
|
-
I can run builds of the *master* branch when you ask me, as well as doing builds of a release branch, e.g. *v1.0*, *v2.3*, etc..
|
164
|
-
|
165
|
-
You can also ask me about the *status* of builds and I'll tell you if anything is currently happening.
|
166
|
-
|
167
|
-
I am configured to let the *\##{Config.slack_build_channel}* channel know if master or release builds fail. Note the words I have highlighted in bold. These are the keywords that I'll look for to understand what you are asking me.
|
168
|
-
)
|
169
|
-
else
|
170
|
-
response = "Sorry#{in_channel ? " <@#{data['user']}>" : ""}, I'm not sure how to respond."
|
171
|
-
end
|
172
|
-
@rt_client.message channel: data['channel'], text: response
|
173
|
-
info "Slack message '#{message}' from #{data['channel']} handled"
|
12
|
+
def initialize()
|
13
|
+
super("127.0.0.1", Config.github_webhook_port, &method(:on_connection))
|
14
|
+
info "Listening on port #{Config.github_webhook_port}"
|
174
15
|
end
|
175
16
|
|
176
17
|
def on_connection(connection)
|
@@ -180,114 +21,37 @@ module BuildBuddy
|
|
180
21
|
case request.path
|
181
22
|
when Config.github_webhook_path
|
182
23
|
case request.headers["X-GitHub-Event"]
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
request.respond 500, "Signatures didn't match!"
|
188
|
-
else
|
189
|
-
payload = JSON.parse(payload_text)
|
190
|
-
pull_request = payload['pull_request']
|
191
|
-
build_data = BuildData.new(:pull_request,
|
192
|
-
:pull_request => pull_request['number'],
|
193
|
-
:repo_sha => pull_request['head']['sha'],
|
194
|
-
:repo_full_name => pull_request['base']['repo']['full_name'])
|
195
|
-
info "Got pull request #{build_data.pull_request} from GitHub"
|
196
|
-
queue_a_build(build_data)
|
197
|
-
request.respond 200
|
198
|
-
end
|
199
|
-
when 'ping'
|
200
|
-
request.respond 200, "Running"
|
24
|
+
when 'pull_request'
|
25
|
+
payload_text = request.body.to_s
|
26
|
+
if !verify_signature(payload_text, request.headers["X-Hub-Signature"])
|
27
|
+
request.respond 500, "Signatures didn't match!"
|
201
28
|
else
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
case build_data.build_type
|
215
|
-
when :pull_request
|
216
|
-
@gh_client.create_status(
|
217
|
-
build_data.repo_full_name, build_data.repo_sha, 'pending',
|
218
|
-
{ :description => "This build is in the queue" })
|
219
|
-
info "Pull request build queued"
|
220
|
-
when :master
|
221
|
-
info "`master` branch build queued"
|
222
|
-
when :release
|
223
|
-
info "Release branch build queued"
|
224
|
-
end
|
225
|
-
|
226
|
-
if @build_timer.nil?
|
227
|
-
@build_timer = every(5) { on_build_interval }
|
228
|
-
info "Build timer started"
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
def on_build_interval
|
233
|
-
if @active_build.nil?
|
234
|
-
if @build_queue.length > 0
|
235
|
-
build_data = @build_queue.pop()
|
236
|
-
@active_build = build_data
|
237
|
-
if build_data.build_type == :pull_request
|
238
|
-
@gh_client.create_status(
|
239
|
-
build_data.repo_full_name, build_data.repo_sha, 'pending',
|
240
|
-
{ :description => "This build has started" })
|
241
|
-
end
|
242
|
-
Celluloid::Actor[:builder].async.start_build(build_data)
|
243
|
-
elsif @done_queue.length > 0
|
244
|
-
build_data = @done_queue.pop
|
245
|
-
term_msg = (build_data.termination_type == :killed ? "was stopped" : "completed")
|
246
|
-
if build_data.termination_type == :exited
|
247
|
-
if build_data.exit_code != 0
|
248
|
-
term_msg += " with errors (exit code #{build_data.exit_code}). See log file `#{build_data.build_log_filename}` for more details."
|
29
|
+
payload = JSON.parse(payload_text)
|
30
|
+
pull_request = payload['pull_request']
|
31
|
+
build_data = BuildData.new(:pull_request,
|
32
|
+
:pull_request => pull_request['number'],
|
33
|
+
:repo_sha => pull_request['head']['sha'],
|
34
|
+
:repo_full_name => pull_request['base']['repo']['full_name'])
|
35
|
+
info "Got pull request #{build_data.pull_request} from GitHub"
|
36
|
+
Celluloid::Actor[:scheduler].queue_a_build(build_data)
|
37
|
+
request.respond 200
|
38
|
+
end
|
39
|
+
when 'ping'
|
40
|
+
request.respond 200, "Running"
|
249
41
|
else
|
250
|
-
|
42
|
+
request.respond 404, "Path not found"
|
251
43
|
end
|
252
|
-
end
|
253
|
-
if build_data.build_type == :pull_request
|
254
|
-
description = "The buddy build #{term_msg}"
|
255
|
-
@gh_client.create_status(
|
256
|
-
build_data.repo_full_name, build_data.repo_sha,
|
257
|
-
build_data.termination_type == :killed ? 'failure' : build_data.exit_code != 0 ? 'error' : 'success',
|
258
|
-
{ :description => description.length > 140 ? "#{description[0..137]}..." : description })
|
259
|
-
info "Pull request build #{term_msg}"
|
260
44
|
else
|
261
|
-
|
262
|
-
when :master
|
263
|
-
message = "A build of the `master` branch #{term_msg}."
|
264
|
-
info "`master` branch build #{term_msg}"
|
265
|
-
when :release
|
266
|
-
message = "A build of the `#{build_data.build_version}` branch #{term_msg}."
|
267
|
-
info "Release branch build #{term_msg}"
|
268
|
-
end
|
269
|
-
unless @notify_slack_channel.nil?
|
270
|
-
@rt_client.message(channel: @notify_slack_channel, text: message)
|
271
|
-
end
|
45
|
+
request.respond 404, "Path not found"
|
272
46
|
end
|
47
|
+
# TODO: Implement basic access authentication from config file
|
48
|
+
# TODO: Implement getting the log file
|
273
49
|
else
|
274
|
-
|
275
|
-
@build_timer = nil
|
276
|
-
info "Build timer stopped"
|
277
|
-
end
|
278
|
-
else
|
279
|
-
# Make sure that the build has not run too long and kill if necessary
|
280
|
-
if Time.now.utc - @active_build.start_time > Config.kill_build_after_mins * 60
|
281
|
-
Celluloid::Actor[:builder].async.stop_build()
|
50
|
+
request.respond 404, "Method not supported"
|
282
51
|
end
|
283
52
|
end
|
284
53
|
end
|
285
54
|
|
286
|
-
def on_build_completed(build_data)
|
287
|
-
@active_build = nil
|
288
|
-
@done_queue.push(build_data)
|
289
|
-
end
|
290
|
-
|
291
55
|
def verify_signature(payload_body, gh_signature)
|
292
56
|
signature = 'sha1=' + OpenSSL::HMAC.hexdigest(
|
293
57
|
OpenSSL::Digest.new('sha1'), Config.github_webhook_secret_token, payload_body)
|
@@ -0,0 +1,175 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
require 'celluloid'
|
4
|
+
require 'slack-ruby-client'
|
5
|
+
require_relative './config.rb'
|
6
|
+
|
7
|
+
module BuildBuddy
|
8
|
+
class Slacker
|
9
|
+
include Celluloid
|
10
|
+
include Celluloid::Internals::Logger
|
11
|
+
|
12
|
+
def initialize()
|
13
|
+
@rt_client = Slack::RealTime::Client.new
|
14
|
+
@rt_client.on :hello do
|
15
|
+
self.on_slack_hello()
|
16
|
+
end
|
17
|
+
@rt_client.on :message do |data|
|
18
|
+
self.on_slack_data(data)
|
19
|
+
end
|
20
|
+
@rt_client.on :error do |error|
|
21
|
+
self.on_slack_error(error)
|
22
|
+
end
|
23
|
+
@rt_client.start_async
|
24
|
+
@notify_slack_channel = nil
|
25
|
+
@reverse_user_map = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def on_slack_error(error)
|
29
|
+
sub_error = error['error']
|
30
|
+
error "Whoops! Slack error #{sub_error['code']} - #{sub_error['msg']}}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def on_slack_hello
|
34
|
+
user_id = @rt_client.self['id']
|
35
|
+
user_map = @rt_client.users.map {|user| [user['name'], user['id']]}.to_h
|
36
|
+
@reverse_user_map = user_map.invert
|
37
|
+
info "Connected to Slack as user id #{user_id} (@#{@reverse_user_map[user_id]})"
|
38
|
+
|
39
|
+
channel_map = @rt_client.channels.map {|channel| [channel['name'], channel['id']]}.to_h
|
40
|
+
group_map = @rt_client.groups.map {|group| [group['name'], group['id']]}.to_h
|
41
|
+
channel = Config.slack_build_channel
|
42
|
+
is_channel = (channel[0] == '#')
|
43
|
+
|
44
|
+
@notify_slack_channel = (is_channel ? channel_map[channel[1..-1]] : group_map[channel])
|
45
|
+
if @notify_slack_channel.nil?
|
46
|
+
error "Unable to identify the slack channel #{channel}"
|
47
|
+
else
|
48
|
+
info "Slack notification channel is #{@notify_slack_channel} (#{channel})"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def on_slack_data(data)
|
53
|
+
message = data['text']
|
54
|
+
|
55
|
+
# If no message, then there's nothing to do
|
56
|
+
if message.nil?
|
57
|
+
return
|
58
|
+
end
|
59
|
+
|
60
|
+
sending_user_id = data['user']
|
61
|
+
|
62
|
+
# Only respond to messages from users and bots
|
63
|
+
if sending_user_id.nil?
|
64
|
+
if data['username'].nil? or data['subtype'] != 'bot_message'
|
65
|
+
return
|
66
|
+
end
|
67
|
+
sending_user_name = data['username']
|
68
|
+
else
|
69
|
+
sending_user_name = @reverse_user_map[sending_user_id]
|
70
|
+
end
|
71
|
+
|
72
|
+
# Don't respond if _we_ sent the message!
|
73
|
+
if sending_user_id == @rt_client.self['id']
|
74
|
+
return
|
75
|
+
end
|
76
|
+
|
77
|
+
sender_is_a_builder = (Config.slack_builders.nil? ? true : Config.slack_builders.include?('@' + sending_user_name))
|
78
|
+
|
79
|
+
c = data['channel'][0]
|
80
|
+
in_channel = (c == 'C' || c == 'G')
|
81
|
+
|
82
|
+
# Don't respond if the message is to a channel and our name is not in the message
|
83
|
+
if in_channel and !message.match(@rt_client.self['id'])
|
84
|
+
return
|
85
|
+
end
|
86
|
+
|
87
|
+
scheduler = Celluloid::Actor[:scheduler]
|
88
|
+
|
89
|
+
case message
|
90
|
+
when /build/i
|
91
|
+
unless sender_is_a_builder
|
92
|
+
if in_channel
|
93
|
+
response = "I'm sorry @#{sending_user_name} you are not on my list of allowed builders."
|
94
|
+
else
|
95
|
+
response = "I'm sorry but you are not on my list of allowed builders."
|
96
|
+
end
|
97
|
+
else
|
98
|
+
case message
|
99
|
+
when /master/i
|
100
|
+
response = "OK, I've queued a build of the `master` branch."
|
101
|
+
scheduler.queue_a_build(BuildData.new(:master,
|
102
|
+
:repo_full_name => Config.github_webhook_repo_full_name))
|
103
|
+
when /(?<version>v\d+\.\d+)/
|
104
|
+
version = $~[:version]
|
105
|
+
if Config.valid_release_versions.include?(version)
|
106
|
+
response = "OK, I've queued a build of `#{version}` branch."
|
107
|
+
scheduler.queue_a_build(BuildData.new(:release,
|
108
|
+
:build_version => version,
|
109
|
+
:repo_full_name => Config.github_webhook_repo_full_name))
|
110
|
+
else
|
111
|
+
response = "I'm sorry, I cannot build the #{version} release branch"
|
112
|
+
end
|
113
|
+
when /stop/i
|
114
|
+
if scheduler.stop_build
|
115
|
+
response = "OK, I stopped the currently running build."
|
116
|
+
else
|
117
|
+
response = "There is no build running to stop."
|
118
|
+
end
|
119
|
+
else
|
120
|
+
response = "Sorry#{in_channel ? " <@#{data['user']}>" : ""}, I'm not sure if you want do a `master` or release branch build, or maybe `stop` any running build?"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
when /status/i
|
124
|
+
build_data = scheduler.active_build
|
125
|
+
queue_length = scheduler.queue_length
|
126
|
+
if build_data.nil?
|
127
|
+
response = "There is currently no build running"
|
128
|
+
if queue_length == 0
|
129
|
+
response += " and no builds in the queue."
|
130
|
+
else
|
131
|
+
response += " and #{queue_length} in the queue."
|
132
|
+
end
|
133
|
+
else
|
134
|
+
case build_data.build_type
|
135
|
+
when :pull_request
|
136
|
+
response = "There is a pull request build in progress for https://github.com/#{build_data.repo_full_name}/pull/#{build_data.pull_request}."
|
137
|
+
when :master
|
138
|
+
response = "There is a build of the `master` branch of https://github.com/#{build_data.repo_full_name} in progress."
|
139
|
+
when :release
|
140
|
+
response = "There is a build of the `#{build_data.build_version}` branch of https://github.com/#{build_data.repo_full_name} in progress."
|
141
|
+
end
|
142
|
+
if queue_length == 1
|
143
|
+
response += " There is one build in the queue."
|
144
|
+
elsif queue_length > 1
|
145
|
+
response += " There are #{queue_length} builds in the queue."
|
146
|
+
end
|
147
|
+
end
|
148
|
+
when /help/i, /what can/i
|
149
|
+
# TODO: The repository should be a link to GitHub
|
150
|
+
response = %Q(Hello#{in_channel ? " <@#{data['user']}>" : ""}, I'm the *@#{@rt_client.self['name']}* build bot version #{BuildBuddy::VERSION}! I look after 3 types of build: pull request, master and release.
|
151
|
+
|
152
|
+
A pull request build happens when you make a pull request to the *#{Config.github_webhook_repo_full_name}* GitHub repository.
|
153
|
+
|
154
|
+
I can run builds of the master branch if you say `build master`. I can do builds of release branches, e.g. `build v2.3` but only for those branches that I am allowed to build.
|
155
|
+
|
156
|
+
I can stop any running build if you ask me to `stop build`, even pull request builds.
|
157
|
+
|
158
|
+
You can also ask me for `status` and I'll tell you what's being built and what's in the queue.
|
159
|
+
|
160
|
+
I am configured to let the *#{Config.slack_build_channel}* channel know if master or release builds are stopped.
|
161
|
+
)
|
162
|
+
else
|
163
|
+
response = "Sorry#{in_channel ? " <@#{data['user']}>" : ""}, I'm not sure how to respond."
|
164
|
+
end
|
165
|
+
@rt_client.message channel: data['channel'], text: response
|
166
|
+
info "Slack message '#{message}' from #{data['channel']} handled"
|
167
|
+
end
|
168
|
+
|
169
|
+
def notify_channel(message)
|
170
|
+
unless @notify_slack_channel.nil?
|
171
|
+
@rt_client.message(channel: @notify_slack_channel, text: message)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: build-buddy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Lyon-smith
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-02-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: timers
|
@@ -176,7 +176,10 @@ files:
|
|
176
176
|
- lib/build_buddy/build_data.rb
|
177
177
|
- lib/build_buddy/builder.rb
|
178
178
|
- lib/build_buddy/config.rb
|
179
|
+
- lib/build_buddy/gitter.rb
|
180
|
+
- lib/build_buddy/scheduler.rb
|
179
181
|
- lib/build_buddy/server.rb
|
182
|
+
- lib/build_buddy/slacker.rb
|
180
183
|
- lib/build_buddy/watcher.rb
|
181
184
|
homepage: http://rubygems.org/gems/build-buddy
|
182
185
|
licenses:
|