build-buddy 1.12.2 → 1.14.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9c8370ef9234e1e2e2b53c9504a8626262ae7918
4
- data.tar.gz: 405570c57c751895749b446cef081120f7e2787b
3
+ metadata.gz: 7db95cc8816c610a427233d3082ed8209c9a65b6
4
+ data.tar.gz: 235ad6277e52ad8be9747933133476562af77633
5
5
  SHA512:
6
- metadata.gz: 672ffd5191ace8f70d7aac002625a1fb7b09ce8978c610acb9ae886767e542cdc9f9f3a4723e4f199a32c62ca29ca739ef9f4ad527e7ce0363246896f753ced4
7
- data.tar.gz: 6d2eb7e0a8f71ade20c51db7fbc651c8ca5ff5cf91db354525f7d0b5da7167f1ee2efb5ac82d91995465de8f5a405ab10807a6f0f0dfaef85b12ddedaaa0b5e3
6
+ metadata.gz: af52e4b43610c3ff26c5d76c99c3b069f7ba393cb374a020e8954b75ea890447a950451f52bd96da1820b3afb0b85ca2eb17d707739ba1cec51d45f84ae63a91
7
+ data.tar.gz: a82009f93b686be80f338175408dc5d03bc8374189f60985fbd44112e4adce023205733f6b5a94e0e7d03a04d3921688dd0040c0f1081590fa582ffbf04b62a5
@@ -19,8 +19,7 @@ module BuildBuddy
19
19
 
20
20
  load config_file_name
21
21
 
22
- Dir.mkdir(Config.build_log_dir) unless Dir.exist?(Config.build_log_dir)
23
- Dir.mkdir(Config.hud_image_dir) unless Dir.exist?(Config.hud_image_dir)
22
+ Dir.mkdir(Config.build_output_dir) unless Dir.exist?(Config.build_output_dir)
24
23
 
25
24
  Slack.configure do |config|
26
25
  config.token = Config.slack_api_token
@@ -9,5 +9,5 @@ require 'build_buddy/recorder'
9
9
  require 'build_buddy/build_data'
10
10
 
11
11
  module BuildBuddy
12
- VERSION = "1.12.2"
12
+ VERSION = "1.14.0"
13
13
  end
@@ -1,6 +1,25 @@
1
+ require 'thread'
2
+
1
3
  module BuildBuddy
4
+ @@bb_id = 100
5
+ @@bb_id_mutex = Mutex.new
6
+
7
+ def self.bb_id
8
+ @@bb_id
9
+ end
10
+
11
+ def self.bb_id=(bb_id)
12
+ @@bb_id = bb_id
13
+ end
14
+
15
+ def self.bb_id_mutex
16
+ @@bb_id_mutex
17
+ end
18
+
2
19
  class BuildData
3
20
  attr_accessor :_id # Mongo ID
21
+ attr_accessor :bb_id # Build Buddy id
22
+ attr_accessor :create_time
4
23
  attr_accessor :type # one of :master, :release or :pull_request
5
24
  attr_accessor :repo_full_name
6
25
  attr_accessor :branch
@@ -24,18 +43,32 @@ module BuildBuddy
24
43
  end
25
44
  end
26
45
 
27
- # Set the id here so that we can use it to refer to this build_data in queue
28
- @_id = BSON::ObjectId.new
46
+ BuildBuddy::bb_id_mutex.synchronize {
47
+ bb_id = BuildBuddy::bb_id
48
+ @bb_id = 'BB-' + bb_id.to_s
49
+ BuildBuddy::bb_id = bb_id + 1
50
+ }
51
+ @create_time = Time.now.utc
29
52
  end
30
53
 
31
54
  def to_h
32
55
  hash = {}
33
56
  instance_variables.each {|var| hash[var.to_s.delete("@")] = instance_variable_get(var) }
57
+ # Remove the bb_id field as it's only meaningful while the build-buddy is running
58
+ hash.delete(:bb_id)
34
59
  hash
35
60
  end
36
61
 
37
62
  def server_log_uri
38
- Config.server_base_uri + '/log/' + @_id.to_s
63
+ BuildData.server_log_uri(@_id)
64
+ end
65
+
66
+ def self.server_log_uri(id)
67
+ Config.server_base_uri + '/build/' + id.to_s + '/log.html'
68
+ end
69
+
70
+ def self.server_report_uri(id)
71
+ Config.server_base_uri + '/build/' + id.to_s + '/report.html'
39
72
  end
40
73
 
41
74
  def status_verb
@@ -52,7 +85,7 @@ module BuildBuddy
52
85
 
53
86
  def log_file_name
54
87
  return nil if @start_time.nil?
55
- File.join(Config.build_log_dir, "#{@start_time.strftime('%Y%m%d-%H%M%S')}.log")
88
+ File.join(Config.build_output_dir, "#{@start_time.strftime('%Y%m%d-%H%M%S')}.log")
56
89
  end
57
90
  end
58
91
  end
@@ -16,6 +16,8 @@ module BuildBuddy
16
16
  @gid = nil
17
17
  @watcher = nil
18
18
  @metrics_tempfile = nil
19
+ @build_output_dir = nil
20
+ @log_file_name = nil
19
21
  end
20
22
 
21
23
  def start_build(build_data)
@@ -26,6 +28,10 @@ module BuildBuddy
26
28
  @build_data = build_data
27
29
  @metrics_tempfile = Tempfile.new('build-metrics')
28
30
  @metrics_tempfile.close()
31
+ @build_output_dir = File.join(Config.build_output_dir, @build_data._id.to_s)
32
+ @log_file_name = File.join(@build_output_dir, "log.txt")
33
+
34
+ FileUtils.mkdir(@build_output_dir)
29
35
 
30
36
  repo_parts = @build_data.repo_full_name.split('/')
31
37
  git_repo_owner = repo_parts[0]
@@ -33,8 +39,8 @@ module BuildBuddy
33
39
  env = {}
34
40
  build_script = %q(#!/bin/bash
35
41
 
36
- if [[ -z "$GIT_REPO_OWNER" || -z "$GIT_REPO_NAME" || -z "$BUILD_SCRIPT" ]]; then
37
- echo Must set GIT_REPO_OWNER, GIT_REPO_NAME, GIT_PULL_REQUEST and BUILD_SCRIPT before calling
42
+ if [[ -z "$BB_GIT_REPO_OWNER" || -z "$BB_GIT_REPO_NAME" || -z "$BB_BUILD_SCRIPT" ]]; then
43
+ echo Must set BB_GIT_REPO_OWNER, BB_GIT_REPO_NAME, BB_GIT_PULL_REQUEST and BB_BUILD_SCRIPT before calling
38
44
  exit 1
39
45
  fi
40
46
  )
@@ -42,14 +48,14 @@ fi
42
48
  case build_data.type
43
49
  when :pull_request
44
50
  build_script += %q(
45
- if [[ -z "$GIT_PULL_REQUEST" ]]; then
46
- echo Must set GIT_PULL_REQUEST before calling
51
+ if [[ -z "$BB_GIT_PULL_REQUEST" ]]; then
52
+ echo Must set BB_GIT_PULL_REQUEST before calling
47
53
  fi
48
54
  )
49
55
  when :branch
50
56
  build_script += %q(
51
- if [[ -z "$GIT_BRANCH" ]]; then
52
- echo Must set GIT_BRANCH before calling
57
+ if [[ -z "$BB_GIT_BRANCH" ]]; then
58
+ echo Must set BB_GIT_BRANCH before calling
53
59
  fi
54
60
  )
55
61
  else
@@ -57,18 +63,18 @@ fi
57
63
  end
58
64
 
59
65
  build_script += %q(
60
- if [[ -d ${GIT_REPO_NAME} ]]; then
61
- echo WARNING: Deleting old clone directory $(pwd)/${GIT_REPO_NAME}
62
- rm -rf ${GIT_REPO_NAME}
66
+ if [[ -d ${BB_GIT_REPO_NAME} ]]; then
67
+ echo WARNING: Deleting old clone directory $(pwd)/${BB_GIT_REPO_NAME}
68
+ rm -rf ${BB_GIT_REPO_NAME}
63
69
  fi
64
70
 
65
- echo Pulling sources to $(pwd)/${GIT_REPO_NAME}
66
- if ! git clone git@github.com:${GIT_REPO_OWNER}/${GIT_REPO_NAME}.git ${GIT_REPO_NAME}; then
71
+ echo Pulling sources to $(pwd)/${BB_GIT_REPO_NAME}
72
+ if ! git clone git@github.com:${BB_GIT_REPO_OWNER}/${BB_GIT_REPO_NAME}.git ${BB_GIT_REPO_NAME}; then
67
73
  echo ERROR: Unable to clone repository
68
74
  exit 1
69
75
  fi
70
76
 
71
- cd ${GIT_REPO_NAME}
77
+ cd ${BB_GIT_REPO_NAME}
72
78
  )
73
79
 
74
80
  build_root_dir = nil
@@ -77,35 +83,37 @@ cd ${GIT_REPO_NAME}
77
83
  when :pull_request
78
84
  build_root_dir = expand_vars(Config.pull_request_root_dir)
79
85
  env.merge!({
80
- "GIT_PULL_REQUEST" => build_data.pull_request.to_s,
81
- "BUILD_SCRIPT" => Config.pull_request_build_script
86
+ "BB_GIT_PULL_REQUEST" => build_data.pull_request.to_s,
87
+ "BB_BUILD_SCRIPT" => Config.pull_request_build_script
82
88
  })
83
89
  # See https://gist.github.com/piscisaureus/3342247
84
90
  build_script += %q(
85
- echo Switching to pr/${GIT_PULL_REQUEST} branch
91
+ echo Switching to pr/${BB_GIT_PULL_REQUEST} branch
86
92
  git config --add remote.origin.fetch '+refs/pull/*/head:refs/remotes/origin/pr/*'
87
93
  git fetch -q origin
88
- git checkout pr/$GIT_PULL_REQUEST
94
+ git checkout pr/$BB_GIT_PULL_REQUEST
89
95
  )
90
96
  when :branch
91
97
  build_root_dir = expand_vars(Config.branch_root_dir)
92
98
  env.merge!({
93
- "GIT_BRANCH" => build_data.branch.to_s,
94
- "BUILD_SCRIPT" => Config.branch_build_script
99
+ "BB_GIT_BRANCH" => build_data.branch.to_s,
100
+ "BB_BUILD_SCRIPT" => Config.branch_build_script
95
101
  })
96
102
  build_script += %q(
97
- echo Switching to ${GIT_BRANCH} branch
98
- git checkout ${GIT_BRANCH}
103
+ echo Switching to ${BB_GIT_BRANCH} branch
104
+ git checkout ${BB_GIT_BRANCH}
99
105
  )
100
106
  end
101
107
 
102
108
  build_script += %q(
103
- source ${BUILD_SCRIPT}
109
+ source ${BB_BUILD_SCRIPT}
104
110
  )
105
111
  env.merge!({
106
- "GIT_REPO_OWNER" => git_repo_owner,
107
- "GIT_REPO_NAME" => git_repo_name,
108
- "METRICS_DATA_FILE" => @metrics_tempfile.path,
112
+ "BB_GIT_REPO_OWNER" => git_repo_owner,
113
+ "BB_GIT_REPO_NAME" => git_repo_name,
114
+ "BB_METRICS_DATA_FILE" => @metrics_tempfile.path,
115
+ "BB_BUILD_OUTPUT_DIR" => @build_output_dir,
116
+ "BB_MONGO_URI" => Config.mongo_uri,
109
117
  "RBENV_DIR" => nil,
110
118
  "RBENV_VERSION" => nil,
111
119
  "RBENV_HOOK_PATH" => nil,
@@ -115,12 +123,11 @@ source ${BUILD_SCRIPT}
115
123
 
116
124
  unless build_data.flags.nil?
117
125
  build_data.flags.each do |flag|
118
- env["BUILD_FLAG_#{key.to_s.upcase}"] = 1
126
+ env["BB_BUILD_FLAG_#{flag.to_s.upcase}"] = 1
119
127
  end
120
128
  end
121
129
 
122
130
  @build_data.start_time = Time.now.utc
123
- log_file_name = @build_data.log_file_name
124
131
 
125
132
  # Ensure build root and git user directory exists
126
133
  clone_dir = File.join(build_root_dir, git_repo_owner)
@@ -139,10 +146,10 @@ source ${BUILD_SCRIPT}
139
146
 
140
147
  # Run the build script
141
148
  Bundler.with_clean_env do
142
- @pid = Process.spawn(env, "bash #{script_filename}", :pgroup => true, :chdir => clone_dir, [:out, :err] => log_file_name)
149
+ @pid = Process.spawn(env, "bash #{script_filename}", :pgroup => true, :chdir => clone_dir, [:out, :err] => @log_file_name)
143
150
  @gid = Process.getpgid(@pid)
144
151
  end
145
- info "Running build script (pid #{@pid}, gid #{@gid}) : Log #{log_file_name}"
152
+ info "Running build script (pid #{@pid}, gid #{@gid}) : Log #{@log_file_name}"
146
153
 
147
154
  if @watcher
148
155
  @watcher.terminate
@@ -152,6 +159,10 @@ source ${BUILD_SCRIPT}
152
159
  @watcher.async.watch_pid
153
160
  end
154
161
 
162
+ def self.get_log_file_name(build_data)
163
+ File.join(Config.build_output_dir, build_data._id.to_s, "log.txt")
164
+ end
165
+
155
166
  def process_done(status)
156
167
  @build_data.end_time = Time.now.utc
157
168
  @build_data.termination_type = (status.signaled? ? :killed : :exited)
@@ -180,18 +191,51 @@ source ${BUILD_SCRIPT}
180
191
  @build_data.server_log_uri)
181
192
  end
182
193
 
194
+ # Create a log.html file
195
+ log_contents = ''
196
+ File.open(@log_file_name) do |io|
197
+ log_contents = io.read
198
+ end
199
+ html = %Q(
200
+ <!doctype html>
201
+ <html lang="en">
202
+ <head>
203
+ <meta charset="utf-8">
204
+ <title>Build Log</title>
205
+ <meta name="description" content="Build Log">
206
+ <style>
207
+ body {
208
+ background-color: black;
209
+ color: #f0f0f0;
210
+ }
211
+ pre {
212
+ font-family: "Menlo", "Courier New";
213
+ font-size: 10pt;
214
+ }
215
+ </style>
216
+ </head>
217
+
218
+ <body>
219
+ <pre>
220
+ #{log_contents}
221
+ </pre>
222
+ </body>
223
+ </html>
224
+ )
225
+ File.open(File.join(@build_output_dir, "log.html"), 'w') { |f| f.write(html)}
226
+
183
227
  # Log the build completion and clean-up
184
228
  info "Process #{status.pid} #{@build_data.termination_type == :killed ? 'was terminated' : "exited (#{@build_data.exit_code})"}"
185
229
  Celluloid::Actor[:scheduler].async.on_build_completed(@build_data)
186
230
  @watcher.terminate
187
231
  @watcher = nil
188
232
 
189
- # Delete older log files
190
- log_file_names = Dir.entries(Config.build_log_dir).select { |f| !f.match(/\d{8}-\d{6}\.log/).nil? }.sort()
191
- while log_file_names.count > Config.build_log_limit
192
- file_name = log_file_names.shift
193
- FileUtils.rm(File.join(Config.build_log_dir, file_name))
194
- info "Removing oldest log file #{file_name}"
233
+ # Delete older log directories
234
+ log_dir_names = Dir.entries(Config.build_output_dir).select { |f| !f.match(/\d{8}-\d{6}\.log/).nil? }.sort()
235
+ while log_dir_names.count > Config.num_saved_build_outputs
236
+ dir_name = log_dir_names.shift
237
+ FileUtils.rm_rf(File.join(Config.build_output_dir, dir_name))
238
+ info "Removing oldest log directory #{dir_name}"
195
239
  end
196
240
  end
197
241
 
@@ -9,9 +9,10 @@ module BuildBuddy
9
9
  :github_api_token,
10
10
  :slack_api_token,
11
11
  :slack_build_channel,
12
+ :slack_pr_channel,
12
13
  :slack_builders,
13
- :build_log_dir,
14
- :build_log_limit,
14
+ :build_output_dir,
15
+ :num_saved_build_outputs,
15
16
  :pull_request_build_script,
16
17
  :branch_build_script,
17
18
  :pull_request_root_dir,
@@ -20,8 +21,6 @@ module BuildBuddy
20
21
  :kill_build_after_mins,
21
22
  :server_base_uri,
22
23
  :mongo_uri,
23
- :hud_secret_token,
24
- :hud_image_dir,
25
24
  ]
26
25
  attr_accessor(*ATTRS)
27
26
  end
@@ -31,10 +30,9 @@ module BuildBuddy
31
30
  config.github_webhook_port = 4567
32
31
  config.kill_build_after_mins = 30
33
32
  config.mongo_uri = 'mongodb://localhost:27017/build-buddy'
34
- config.build_log_limit = 30
33
+ config.num_saved_build_outputs = 30
35
34
  block_given? ? yield(Config) : Config
36
- config.build_log_dir = File.expand_path(Config.build_log_dir.gsub(/\$(\w+)/) { ENV[$1] })
37
- config.hud_image_dir = File.expand_path(Config.hud_image_dir.gsub(/\$(\w+)/) { ENV[$1] })
35
+ config.build_output_dir = File.expand_path(Config.build_output_dir.gsub(/\$(\w+)/) { ENV[$1] })
38
36
  Config::ATTRS.map {|attr| ('@' + attr.to_s).to_sym }.each {|var|
39
37
  if config.instance_variable_get(var).nil?
40
38
  raise "Config value '#{var.to_s.delete('@')}' not set"
@@ -2,7 +2,9 @@ require 'rubygems'
2
2
  require 'bundler'
3
3
  require 'celluloid'
4
4
  require 'mongo'
5
+ require 'bson'
5
6
  require 'gruff'
7
+ require 'securerandom'
6
8
  require_relative './config.rb'
7
9
 
8
10
  module BuildBuddy
@@ -17,14 +19,18 @@ module BuildBuddy
17
19
  mongo_uri = BuildBuddy::Config.mongo_uri
18
20
  @mongo ||= Mongo::Client.new(mongo_uri)
19
21
  info "Connected to MongoDB at '#{mongo_uri}'"
20
- @mongo[:builds].indexes.create_one({:start_time => -1}, :name => "reverse_build_order")
22
+ @mongo[:builds].indexes.create_one({:create_time => -1}, :name => "reverse_order")
21
23
  end
22
24
 
23
25
  def record_build_data(build_data)
24
26
  builds = @mongo[:builds]
25
- result = builds.insert_one(build_data.to_h)
26
- if build_data._id.nil?
27
- build_data._id = result.inserted_id
27
+ begin
28
+ # Do this to prevent build _id's from being sequential and so reduce risk
29
+ # of someone guessing a valid build URL.
30
+ build_data._id = BSON::ObjectId.from_string(SecureRandom.hex(12).to_s)
31
+ builds.insert_one(build_data.to_h)
32
+ rescue Mongo::Error::OperationFailure => e
33
+ retry if e.to_s.start_with?('E11000') # Duplicate key error
28
34
  end
29
35
  end
30
36
 
@@ -35,8 +41,6 @@ module BuildBuddy
35
41
 
36
42
  builds = @mongo[:builds]
37
43
  builds.replace_one({ :_id => build_data._id }, build_data.to_h)
38
-
39
- gen_charts
40
44
  end
41
45
 
42
46
  def get_build_data(id)
@@ -48,236 +52,21 @@ module BuildBuddy
48
52
  end
49
53
 
50
54
  def get_build_data_history(limit)
51
- @mongo[:builds].find().sort(:start_time => -1).limit(limit).map do |doc|
55
+ @mongo[:builds].find().sort(:create_time => -1).limit(limit).map do |doc|
52
56
  BuildData.new(doc)
53
57
  end
54
58
  end
55
-
56
- def gen_charts
57
- gen_daily_builds_chart
58
- gen_coverage_chart
59
- gen_lines_chart
60
- gen_localized_chart
61
- gen_times_chart
62
- gen_warnings_chart
63
- end
64
-
65
- def gen_daily_builds_chart
66
- # Generates a chart showing builds per day
67
- data = []
68
- keys = []
69
- @mongo[:builds].find().sort(:start_time => -1).limit(LIMIT).each do |doc|
70
- next if doc['start_time'].nil?
71
- start_data_components = doc['start_time'].to_s.split(' ')[0].split('-')
72
- date_string = start_data_components[1] + "/" + start_data_components[2]
73
-
74
- if keys.include?(date_string)
75
- data.each { |arr|
76
- if arr[0] == date_string
77
- if doc['pull_request'] != nil
78
- arr[1] = arr[1] + 1
79
- else
80
- arr[2] = arr[2] + 1
81
- end
82
- end
83
- }
84
- else
85
- keys.push(date_string)
86
- if doc['pull_request'] != nil
87
- data.insert(0, [date_string, 1, 0])
88
- else
89
- data.insert(0, [date_string, 0, 1])
90
- end
91
- end
92
- end
93
-
94
- g = Gruff::StackedBar.new
95
- g.title = "Builds per Day"
96
- g.labels = data.each_with_index.map {|d, i| [ i, d[0] ]}.to_h
97
- g.theme = {
98
- :colors => ['#12a702', '#aedaa9'],
99
- :font_color => 'black',
100
- :background_colors => 'white'
101
- }
102
- g.font = '/Library/Fonts/Verdana.ttf'
103
- g.data("Pull Request (#{data.size > 0 ? data[data.size - 1][1] : 0})", data.map {|d| d[1]})
104
- g.data("Other (#{data.size > 0 ? data[data.size - 1][2] : 0})", data.map {|d| d[2]})
105
- g.x_axis_label = 'Date'
106
- g.y_axis_label = 'Number of Builds'
107
- g.y_axis_increment = 2
108
- g.write File.join(Config.hud_image_dir, "daily_builds.png")
109
- end
110
-
111
- def gen_coverage_chart
112
- # Generate code coverage numbers
113
- data = []
114
- keys = []
115
- @mongo[:builds].find().sort(:start_time => -1).limit(LIMIT).map do |doc|
116
- next if doc['start_time'].nil?
117
- start_date_components = doc['start_time'].to_s.split(' ')[0].split('-')
118
- date_string = start_date_components[1] + "/" + start_date_components[2]
119
- pull_number = doc['pull_request'].to_i
120
- if pull_number == 0 && doc['metrics']['coverage'] != nil && !keys.include?(date_string)
121
- keys.push(date_string)
122
-
123
- missed_lines = doc['metrics']['coverage']['swift_files']['missed_lines'] + doc['metrics']['coverage']['m_files']['missed_lines']
124
- total_lines = doc['metrics']['coverage']['swift_files']['total_lines'] + doc['metrics']['coverage']['m_files']['total_lines']
125
- coverage_percent = ((1.0 * (total_lines - missed_lines) / total_lines) * 100).to_i
126
- data.insert(0, [date_string, coverage_percent])
127
- end
128
- end
129
-
130
- g = Gruff::Bar.new
131
- g.minimum_value = 0
132
- g.maximum_value = 100
133
- g.title = "Coverage"
134
- g.labels = data.each_with_index.map {|d, i| [ i, d[0] ]}.to_h
135
- g.theme = {
136
- :colors => ['#822F92'],
137
- :font_color => 'black',
138
- :background_colors => 'white'
139
- }
140
- g.font = '/Library/Fonts/Verdana.ttf'
141
- g.data("covered % (#{data.size > 0 ? data[data.size - 1][1] : 0})", data.map {|d| d[1]})
142
- g.x_axis_label = 'Date'
143
- g.y_axis_label = 'Coverage %'
144
- g.write File.join(Config.hud_image_dir, "code_coverage.png")
145
- end
146
-
147
- def gen_lines_chart
148
- # Generates a chart displaying number of lines of code by Swift/Objective-C.
149
- data = []
150
- keys = []
151
- @mongo[:builds].find().sort(:start_time => -1).limit(LIMIT).map do |doc|
152
- next if doc['start_time'].nil?
153
- start_date_components = doc['start_time'].to_s.split(' ')[0].split('-')
154
- date_string = start_date_components[1] + "/" + start_date_components[2]
155
- pull_number = doc['pull_request'].to_i
156
- if pull_number == 0 && doc['metrics']['coverage'] != nil && !keys.include?(date_string)
157
- keys.push(date_string)
158
- data.insert(0, [date_string, doc['metrics']['coverage']['swift_files']['total_lines'], doc['metrics']['coverage']['m_files']['total_lines']])
159
- end
160
- end
161
-
162
- g = Gruff::StackedBar.new
163
- g.title = "Lines of Code"
164
- g.labels = data.each_with_index.map {|d, i| [ i, d[0] ]}.to_h
165
- g.theme = {
166
- :colors => ['#C17B3A', '#C1BA3A'],
167
- :font_color => 'black',
168
- :background_colors => 'white'
169
- }
170
- g.font = '/Library/Fonts/Verdana.ttf'
171
- g.data("Swift (#{data.size > 0 ? data[data.size - 1][1] : 0})", data.map {|d| d[1]})
172
- g.data("Objective-C (#{data.size > 0 ? data[data.size - 1][2] : 0})", data.map {|d| d[2]})
173
- g.x_axis_label = 'Day'
174
- g.y_axis_label = 'Lines of Code'
175
- g.write File.join(Config.hud_image_dir, "lines_of_code.png")
176
- end
177
-
178
- def gen_localized_chart
179
- # Generates a chart displaying localization information for past pull requests.
180
- # Displays # strings, # descriptions, and # words.
181
- data = []
182
- keys = []
183
- @mongo[:builds].find().sort(:start_time => -1).limit(LIMIT).map do |doc|
184
- pull_number = doc['pull_request'].to_i
185
- if pull_number != 0 && !keys.include?(pull_number)
186
- keys.push(pull_number)
187
- data.insert(0, [pull_number, doc['metrics']['strings'], doc['metrics']['descriptions'], doc['metrics']['words']])
188
- end
189
- end
190
-
191
- g = Gruff::Line.new
192
- g.title = 'Localization'
193
- g.labels = data.each_with_index.map {|d, i| [ i, d[0] ]}.to_h
194
- g.dot_style = :square
195
- g.font = '/Library/Fonts/Verdana.ttf'
196
- g.data("# strings (#{data.size > 0 ? data[data.size - 1][1] : 0})", data.map {|d| d[1]})
197
- g.data("# descriptions (#{data.size > 0 ? data[data.size - 1][2] : 0})", data.map {|d| d[2]})
198
- g.data("# words (#{data.size > 0 ? data[data.size - 1][3] : 0})", data.map {|d| d[3]})
199
- g.x_axis_label = 'Pull Request'
200
-
201
- g.theme = {
202
- :colors => ['#e65954', '#0a2154', '#2a7b20'],
203
- :font_color => 'black',
204
- :background_colors => 'white'
205
- }
206
-
207
- g.write(File.join(Config.hud_image_dir, 'localization_data.png'))
208
- end
209
-
210
- def gen_times_chart
211
- # Generates a chart displaying build time information for past pull requests.
212
- data = []
213
- keys = []
214
- @mongo[:builds].find().sort(:start_time => -1).limit(LIMIT).map do |doc|
215
- next if doc['metrics'].nil?
216
- pull_number = doc['pull_request'].to_i
217
- if pull_number != 0 && !keys.include?(pull_number) && doc['metrics']['test_build_end'] != nil && doc['metrics']['test_build_start'] != nil
218
- end_time_components = doc['metrics']['test_build_end'].to_s.split(' ')[1].split(':')
219
- end_date_components = doc['metrics']['test_build_end'].to_s.split(' ')[0].split('-')
220
- end_time = Time.new(end_date_components[0], end_date_components[1], end_date_components[2], end_time_components[0], end_time_components[1], end_time_components[2])
221
-
222
- start_time_components = doc['metrics']['test_build_start'].to_s.split(' ')[1].split(':')
223
- start_date_components = doc['metrics']['test_build_start'].to_s.split(' ')[0].split('-')
224
- start_time = Time.new(start_date_components[0], start_date_components[1], start_date_components[2], start_time_components[0], start_time_components[1], start_time_components[2])
225
59
 
226
- build_time_mins = ((end_time - start_time) / 60).to_f.round(2)
227
-
228
- keys.push(pull_number)
229
- data.insert(0, [pull_number, build_time_mins])
230
- end
231
- end
232
-
233
- g = Gruff::Line.new
234
- g.minimum_value = 0
235
- g.maximum_value = 10
236
- g.title = 'Build Times'
237
- g.labels = data.each_with_index.map {|d, i| [ i, d[0] ]}.to_h
238
- g.dot_style = :square
239
- g.font = '/Library/Fonts/Verdana.ttf'
240
- g.data("minutes (#{data.size > 0 ? data[data.size - 1][1] : 0})", data.map {|d| d[1]})
241
- g.x_axis_label = 'Pull Request'
242
- g.y_axis_label = 'Time (mins)'
243
- g.theme = {
244
- :colors => ['#38c0df'],
245
- :font_color => 'black',
246
- :background_colors => 'white'
247
- }
248
-
249
- g.write(File.join(Config.hud_image_dir, 'build_times.png'))
250
- end
251
-
252
- def gen_warnings_chart
253
- # Script that generates a chart displaying warning count information for past pull requests.
254
- data = []
255
- keys = []
256
- @mongo[:builds].find().sort(:start_time => -1).limit(LIMIT).map do |doc|
257
- next if doc['start_time'].nil?
258
- start_date_components = doc['start_time'].to_s.split(' ')[0].split('-')
259
- date_string = start_date_components[1] + "/" + start_date_components[2]
260
- pull_number = doc['pull_request'].to_i
261
- if pull_number == 0 && doc['metrics']['test_build_start'] != nil && !keys.include?(date_string)
262
- keys.push(date_string)
263
- data.insert(0, [date_string, doc['metrics']['warning_count']])
60
+ def find_report_uri
61
+ uri = nil
62
+ @mongo[:builds].find().sort(:create_time => -1).each do |doc|
63
+ file_name = File.join(Config.build_output_dir, doc[:_id].to_s, "report.html")
64
+ if File.exist?(file_name)
65
+ uri = BuildData.server_report_uri(doc[:_id])
66
+ break
264
67
  end
265
68
  end
266
-
267
- g = Gruff::Line.new
268
- g.title = 'Warnings'
269
- g.labels = data.each_with_index.map {|d, i| [ i, d[0] ]}.to_h
270
- g.dot_style = :square
271
- g.font = '/Library/Fonts/Verdana.ttf'
272
- g.data("# warnings (#{data.size > 0 ? data[data.size - 1][1] : 0})", data.map {|d| d[1]})
273
- g.x_axis_label = 'Date'
274
- g.y_axis_label = 'Number of Warnings'
275
- g.theme = {
276
- :colors => ['#c3a00b'],
277
- :font_color => 'black',
278
- :background_colors => 'white'
279
- }
280
- g.write(File.join(Config.hud_image_dir, 'warning_count.png'))
69
+ uri
281
70
  end
282
71
  end
283
72
  end
@@ -17,24 +17,45 @@ module BuildBuddy
17
17
  end
18
18
 
19
19
  def queue_a_build(build_data)
20
- @build_queue.unshift(build_data)
21
-
22
20
  case build_data.type
23
21
  when :pull_request
22
+ existing_bb_id = find_bb_id_for_pr(build_data.pull_request)
23
+
24
24
  Celluloid::Actor[:gitter].async.set_status(
25
25
  build_data.repo_full_name, build_data.repo_sha, :pending, "Build is queued",
26
26
  build_data.server_log_uri)
27
27
  info "Pull request build queued"
28
+
29
+ unless existing_bb_id.nil?
30
+ info "Stopping existing build #{existing_bb_id} for this PR"
31
+ stop_build(existing_bb_id, 'github')
32
+ end
28
33
  when :branch
29
34
  info "'#{build_data.branch}' branch build queued"
30
35
  end
31
36
 
37
+ @build_queue.unshift(build_data)
38
+
32
39
  if @build_timer.nil?
33
40
  @build_timer = every(5) { on_build_interval }
34
41
  info "Build timer started"
35
42
  end
36
43
  end
37
44
 
45
+ def find_bb_id_for_pr(pull_request)
46
+ if @active_build and @active_build.pull_request == pull_request
47
+ return @active_build.bb_id
48
+ end
49
+
50
+ build_data = @build_queue.find { |build_data| build_data.pull_request == pull_request}
51
+
52
+ if build_data.nil?
53
+ nil
54
+ else
55
+ build_data.bb_id
56
+ end
57
+ end
58
+
38
59
  def queue_length
39
60
  @build_queue.length
40
61
  end
@@ -43,17 +64,17 @@ module BuildBuddy
43
64
  @active_build
44
65
  end
45
66
 
46
- def stop_build(id, slack_user_name)
67
+ def stop_build(bb_id, slack_user_name)
47
68
  # Centralize stopping builds here
48
- if @active_build != nil and @active_build._id == id
69
+ if @active_build != nil and @active_build.bb_id == bb_id
49
70
  @active_build.stopped_by = slack_user_name
50
- Celluloid::Actor[:builder].stop_build
71
+ Celluloid::Actor[:builder].async.stop_build
51
72
  # Build data will be recorded when the build stops
52
73
  return :active
53
74
  end
54
75
 
55
76
  # Look for the build in the queue
56
- i = @build_queue.find_index { |build_data| build_data._id == id}
77
+ i = @build_queue.find_index { |build_data| build_data.bb_id == bb_id}
57
78
  if i != nil
58
79
  build_data = @build_queue[i]
59
80
  @build_queue.delete_at(i)
@@ -87,7 +108,7 @@ module BuildBuddy
87
108
  # Make sure that the build has not run too long and kill if necessary
88
109
  start_time = @active_build.start_time
89
110
  if !start_time.nil? and Time.now.utc - start_time > Config.kill_build_after_mins * 60
90
- Celluloid::Actor[:builder].async.stop_build()
111
+ Celluloid::Actor[:builder].async.stop_build
91
112
  end
92
113
  end
93
114
  end
@@ -38,148 +38,50 @@ module BuildBuddy
38
38
  :pull_request => pull_request['number'],
39
39
  :flags => {},
40
40
  :repo_sha => pull_request['head']['sha'],
41
- :repo_full_name => pull_request['base']['repo']['full_name'])
41
+ :repo_full_name => pull_request['base']['repo']['full_name'],
42
+ :started_by => 'github')
42
43
  info "Got #{action} pull request #{build_data.pull_request} from GitHub"
43
44
  Celluloid::Actor[:scheduler].queue_a_build(build_data)
44
45
  request.respond 200, "Building"
45
- return
46
46
  else
47
47
  request.respond 200, "Ignoring"
48
- return
49
48
  end
50
49
  end
51
50
  when 'ping'
52
51
  info "Got pinged from #{forwarded_for(request)}"
53
52
  request.respond 200, "Running"
54
- return
55
53
  else
56
54
  request.respond 404, "Event not supported"
57
- return
58
55
  end
59
56
  else
60
57
  request.respond 404, "Method not supported"
61
- return
62
58
  end
63
- when /^\/log\/([0-9abcdef]{24})$/
64
- case request.method
65
- when 'GET'
66
- build_data = Celluloid::Actor[:recorder].get_build_data($1)
67
- if build_data.nil?
68
- request.respond 404, "Not found"
69
- return
70
- end
71
- log_file_name = build_data.log_file_name
72
- if log_file_name.nil? or !File.exist?(log_file_name)
73
- log_contents = 'Log file has been deleted.'
74
- else
75
- File.open(log_file_name) do |io|
76
- log_contents = io.read
77
- end
78
- end
79
- html = %Q(
80
- <!doctype html>
81
- <html lang="en">
82
- <head>
83
- <meta charset="utf-8">
84
- <title>Build Log</title>
85
- <meta name="description" content="Build Log">
86
- <style>
87
- body {
88
- background-color: black;
89
- color: #f0f0f0;
90
- }
91
- pre {
92
- font-family: "Menlo", "Courier New";
93
- font-size: 10pt;
94
- }
95
- </style>
96
- </head>
97
-
98
- <body>
99
- <pre>
100
- #{log_contents}
101
- </pre>
102
- </body>
103
- </html>
104
- )
105
- request.respond 200, html
106
- return
107
- else
59
+ when /^\/build\/([0-9abcdef]{24})\/(log\.html|report\.html|[a-z_]+\.png)$/
60
+ if request.method != 'GET'
108
61
  request.respond 404, "Method not supported"
109
62
  return
110
63
  end
111
- when Regexp.new("^/hud/#{Config.hud_secret_token}/(index\\.html|[a-z_]+\\.png)$")
112
- if request.method != 'GET'
113
- request.response 404, "Method not supported"
64
+
65
+ build_id = $1
66
+ resource_name = $2
67
+ if build_id.nil? or resource_name.nil?
68
+ request.respond 404, "Not found"
114
69
  return
115
70
  end
116
71
 
117
- if $1 == "index.html"
118
- html = %q(
119
- <!doctype html>
120
- <html lang="en">
121
- <head>
122
- <title>Build Metrics</title>
123
- <meta charset="utf-8">
124
- <meta name="viewport" content="width=device-width, initial-scale=1">
125
- <link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
126
- <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.3/css/bootstrap.min.css" integrity="sha384-MIwDKRSSImVFAZCVLtU0LMDdON6KVCrZHyVQQj6e8wIEJkW4tvwqXrbMIya1vriY" crossorigin="anonymous">
127
- <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.3/js/bootstrap.min.js" integrity="sha384-ux8v3A6CPtOTqOzMKiuo3d/DomGaaClxFYdCu2HPMBEkf6x2xiDyJ7gkXU0MWwaD" crossorigin="anonymous"></script>
128
- </head>
129
- <body>
130
-
131
- <div class="jumbotron text-center">
132
- <h1>Build Metrics</h1>
133
- <p>Build server data</p>
134
- </div>
135
-
136
- <div class="container-fluid">
137
- <div class="row">
138
- <div class="col-sm-4">
139
- <img class="img-fluid" src="code_coverage.png" alt="Code Coverage">
140
- </div>
141
- <div class="col-sm-4">
142
- <img class="img-fluid" src="build_times.png" alt="Build Times">
143
- </div>
144
- <div class="col-sm-4">
145
- <img class="img-fluid" src="daily_builds.png" alt="Daily Builds">
146
- </div>
147
- </div>
148
- <div class="row">
149
- <div class="col-sm-4">
150
- <img class="img-fluid" src="lines_of_code.png" alt="Lines of Code">
151
- </div>
152
- <div class="col-sm-4">
153
- <img class="img-fluid" src="localization_data.png" alt="Localization Data">
154
- </div>
155
- <div class="col-sm-4">
156
- <img class="img-fluid" src="warning_count.png" alt="Warning Count">
157
- </div>
158
- </div>
159
- </div>
160
-
161
- </body>
162
- </html>
163
- )
164
- request.respond 200, html
72
+ resource_file_name = File.join(Config.build_output_dir, build_id, resource_name)
73
+ if !File.exist?(resource_file_name)
74
+ request.respond 404, "Not found"
165
75
  return
166
- elsif
167
- png_file_name = File.join(Config.hud_image_dir, $1)
76
+ end
168
77
 
169
- if File.exist?(png_file_name)
170
- request.respond Reel::Response.new(200, {"content-type" => "image/png"}, File.open(png_file_name))
171
- return
172
- else
173
- request.response 404, "Image not found"
174
- return
175
- end
78
+ if resource_name.end_with?('.html')
79
+ request.respond Reel::Response.new(200, { 'content-type' => 'text/html'}, File.open(resource_file_name, 'r'))
176
80
  else
177
- request.response 404, "Path not found"
178
- return
81
+ request.respond Reel::Response.new(200, { 'content-type' => 'image/png'}, File.open(resource_file_name, 'rb'))
179
82
  end
180
83
  else
181
- request.respond 404, "Path not found"
182
- return
84
+ request.respond 404, "Not found"
183
85
  end
184
86
  end
185
87
  end
@@ -34,6 +34,7 @@ module BuildBuddy
34
34
  end
35
35
 
36
36
  @build_channel_id = nil
37
+ @pr_channel_id = nil
37
38
  end
38
39
 
39
40
  def self.extract_build_flags(message)
@@ -57,9 +58,10 @@ module BuildBuddy
57
58
  end
58
59
  else
59
60
  scheduler = Celluloid::Actor[:scheduler]
61
+ message = message.strip
60
62
 
61
63
  case message
62
- when /^master(?: with )?(?<flags>.*)?/i
64
+ when /^master(?: +with +(?<flags>[a-z ]+))?/i
63
65
  flags = Slacker.extract_build_flags($~[:flags])
64
66
  response = "OK, I've queued a build of the `master` branch."
65
67
  scheduler.queue_a_build(BuildData.new(
@@ -90,14 +92,16 @@ module BuildBuddy
90
92
  end
91
93
 
92
94
  def do_stop(message, is_from_slack_channel, slack_user_name)
95
+ message = message.strip
93
96
  response = ''
94
- m = message.match(/[0-9abcdef]{24}/)
97
+ m = message.match(/^(?:build +)?(bb-\d+)$/i)
95
98
 
96
99
  unless m.nil?
97
- result = Celluloid::Actor[:scheduler].stop_build(BSON::ObjectId.from_string(m[0]), slack_user_name)
100
+ bb_id = m[1].upcase
101
+ result = Celluloid::Actor[:scheduler].stop_build(bb_id, slack_user_name)
98
102
  response = case result
99
103
  when :active, :in_queue
100
- "OK#{is_from_slack_channel ? ' @' + slack_user_name : ''}, I #{result == :active ? "stopped" : "dequeued"} the build with identifier #{m[0]}."
104
+ "OK#{is_from_slack_channel ? ' @' + slack_user_name : ''}, I #{result == :active ? "stopped" : "dequeued"} the build with identifier #{bb_id}."
101
105
  when :not_found
102
106
  "I could not find a queued or active build with that identifier"
103
107
  end
@@ -115,7 +119,9 @@ I understand types of build - pull requests and branch. A pull request build hap
115
119
 
116
120
  For branch builds, 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 in by configuration file.
117
121
 
118
- I can stop any running build if you ask me to `stop build X`, even pull request builds if you give the id X from the `show status` or `show queue` command. I am configured to let the *#{Config.slack_build_channel}* channel know if builds are stopped.
122
+ I can stop any running build if you ask me to `stop build X`, even pull request builds if you give the id X from the `show status` or `show queue` command.
123
+
124
+ I will let the *#{Config.slack_build_channel}* channel know about branch build activity and the *#{Config.slack_pr_channel} channel know about PR activity.
119
125
 
120
126
  I have lots of `show` commands:
121
127
 
@@ -123,11 +129,26 @@ I have lots of `show` commands:
123
129
  - `show queue` and I will show you what is in the queue
124
130
  - `show options` to a see a list of build options
125
131
  - `show builds` to see the last 5 builds or `show last N builds` to see a list of the last N builds
126
-
127
- Build metrics and charts are available at #{Config.server_base_uri}/hud/#{Config.hud_secret_token}/index.html
132
+ - `show report` to get a link to the latest build report
128
133
  )
129
134
  end
130
135
 
136
+ def do_relay(message, slack_user_name)
137
+ sender_is_a_builder = (Config.slack_builders.nil? ? true : Config.slack_builders.include?('@' + slack_user_name))
138
+ unless sender_is_a_builder
139
+ if is_from_slack_channel
140
+ response = "I'm sorry @#{slack_user_name} you are not on my list of allowed builders so I can't relay a message for you."
141
+ else
142
+ response = "I'm sorry but you are not on my list of allowed builders so I cannot relay a message for you."
143
+ end
144
+ else
145
+ message = message.strip.gsub('"', '')
146
+ @rt_client.message(channel: @build_channel_id, text: message) unless @build_channel_id.nil?
147
+ end
148
+ "Message relayed to #{Config.slack_build_channel}"
149
+ info "I relayed a message for #{slack_user_name} to #{Config.slack_build_channel}, \"#{message}\""
150
+ end
151
+
131
152
  def do_show(request)
132
153
  request = request.lstrip.rstrip
133
154
 
@@ -155,14 +176,14 @@ Build metrics and charts are available at #{Config.server_base_uri}/hud/#{Config
155
176
  when :pull_request
156
177
  "pull request build #{build_data.pull_request_uri}"
157
178
  end
158
- response += " at #{build_data.start_time.to_s}. #{Config.server_base_uri + '/log/' + build_data._id.to_s}"
179
+ response += " at #{build_data.start_time.to_s}. #{BuildData.server_log_uri(build_data._id)}"
159
180
  unless build_data.started_by.nil?
160
181
  response += " started by #{build_data.started_by}"
161
182
  end
183
+ response += " #{build_data.status_verb}"
162
184
  unless build_data.stopped_by.nil?
163
- response += " stopped by #{build_data.stopped_by}"
185
+ response += " by #{build_data.stopped_by}"
164
186
  end
165
- response += " #{build_data.status_verb}"
166
187
  response += ".\n"
167
188
  end
168
189
  end
@@ -181,9 +202,9 @@ Build metrics and charts are available at #{Config.server_base_uri}/hud/#{Config
181
202
  else
182
203
  case build_data.type
183
204
  when :pull_request
184
- response = "There is a pull request build in progress for https://github.com/#{build_data.repo_full_name}/pull/#{build_data.pull_request} (identifier `#{build_data._id.to_s}`)"
205
+ response = "There is a pull request build in progress for https://github.com/#{build_data.repo_full_name}/pull/#{build_data.pull_request} (#{build_data.bb_id})"
185
206
  when :branch
186
- response = "There is a build of the `#{build_data.branch}` branch of https://github.com/#{build_data.repo_full_name} in progress (identifier `#{build_data._id.to_s}`)"
207
+ response = "There is a build of the `#{build_data.branch}` branch of https://github.com/#{build_data.repo_full_name} in progress (#{build_data.bb_id})"
187
208
  end
188
209
  unless build_data.started_by.nil?
189
210
  response += " started by " + build_data.started_by
@@ -217,7 +238,7 @@ Build metrics and charts are available at #{Config.server_base_uri}/hud/#{Config
217
238
  when :pull_request
218
239
  "pull request build #{build_data.pull_request_uri}"
219
240
  end
220
- response += " (identifier `#{build_data._id.to_s}`)"
241
+ response += " (#{build_data.bb_id})"
221
242
  unless build_data.started_by.nil?
222
243
  response += " started by #{build_data.started_by}"
223
244
  end
@@ -227,6 +248,14 @@ Build metrics and charts are available at #{Config.server_base_uri}/hud/#{Config
227
248
  response += ".\n"
228
249
  }
229
250
  end
251
+ when /report/
252
+ response = ''
253
+ report_uri = Celluloid::Actor[:recorder].find_report_uri
254
+ if report_uri.nil?
255
+ response = "There do not appear to be any reports generated yet"
256
+ else
257
+ response = "The last build report is at #{report_uri}"
258
+ end
230
259
  else
231
260
  response = "I'm not sure what to say..."
232
261
  end
@@ -248,10 +277,18 @@ Build metrics and charts are available at #{Config.server_base_uri}/hud/#{Config
248
277
  @build_channel_id = Slacker.get_channel_id(Config.slack_build_channel, map_channel_name_to_id, map_group_name_to_id)
249
278
 
250
279
  if @build_channel_id.nil?
251
- error "Unable to identify the build slack channel #{channel}"
280
+ error "Unable to identify the slack build channel #{Config.slack_build_channel}"
252
281
  else
253
282
  info "Slack build notification channel is #{@build_channel_id} (#{Config.slack_build_channel})"
254
283
  end
284
+
285
+ @pr_channel_id = Slacker.get_channel_id(Config.slack_pr_channel, map_channel_name_to_id, map_group_name_to_id)
286
+
287
+ if @pr_channel_id.nil?
288
+ error "Unable to identify the PR slack channel #{channel}"
289
+ else
290
+ info "Slack PR notification channel is #{@pr_channel_id} (#{Config.slack_pr_channel})"
291
+ end
255
292
  end
256
293
 
257
294
  def on_slack_data(data)
@@ -298,18 +335,20 @@ Build metrics and charts are available at #{Config.server_base_uri}/hud/#{Config
298
335
  return
299
336
  end
300
337
 
301
- # Remove
338
+ message = message.strip
302
339
 
303
340
  response = case message
304
- when /^.*?build (.*)/i
341
+ when /^build(.*)/i
305
342
  do_build $1, is_from_slack_channel, slack_user_name
306
- when /^.*?status/ # Legacy support
343
+ when /^status/ # Legacy support
307
344
  do_show 'status'
308
- when /^.*?show(.*)/
345
+ when /^show(.*)/
309
346
  do_show $1
310
- when /^*?help/i
347
+ when /^help/i
311
348
  do_help is_from_slack_channel
312
- when /^.*?stop(?: build)(.*)/i
349
+ when /^relay(.*)/i
350
+ do_relay $1, slack_user_name
351
+ when /^stop(.*)/i
313
352
  do_stop $1, is_from_slack_channel, slack_user_name
314
353
  else
315
354
  "Sorry#{is_from_slack_channel ? ' ' + slack_user_name : ''}, I'm not sure how to respond."
@@ -323,9 +362,11 @@ Build metrics and charts are available at #{Config.server_base_uri}/hud/#{Config
323
362
 
324
363
  if build_data.type == :branch
325
364
  message = "A `#{build_data.branch}` branch build #{status_verb}"
365
+ short_message = message
326
366
  info "Branch build #{status_verb}"
327
367
  else
328
- message = "Pull request `#{build_data.pull_request}` #{status_verb}"
368
+ message = "A pull request https://github.com/#{build_data.repo_full_name}/pull/#{build_data.pull_request} build #{status_verb}"
369
+ short_message = "Pull request #{build_data.pull_request} #{status_verb}"
329
370
  info "Pull request build #{status_verb}"
330
371
  end
331
372
 
@@ -336,13 +377,17 @@ Build metrics and charts are available at #{Config.server_base_uri}/hud/#{Config
336
377
  message += ". Log file at #{build_data.server_log_uri}"
337
378
 
338
379
  # See https://api.slack.com/docs/attachments for more information about formatting Slack attachments
339
- attach = [
380
+ attachments = [
340
381
  :title => build_data.type == :pull_request ? "Pull Request" : "Branch Build",
341
382
  :text => message,
342
383
  :color => build_data.termination_type == :killed ? :warning : build_data.exit_code != 0 ? :danger : :good,
343
384
  ]
344
385
 
345
- @rt_client.message(channel: @build_channel_id, text: message, attachments: attach) unless @build_channel_id.nil?
386
+ if build_data.type == :branch
387
+ @rt_client.web_client.chat_postMessage(channel: @build_channel_id, text: short_message, attachments: attachments, as_user: true) unless @build_channel_id.nil?
388
+ else
389
+ @rt_client.web_client.chat_postMessage(channel: @pr_channel_id, text: short_message, attachments: attachments, as_user: true) unless @pr_channel_id.nil?
390
+ end
346
391
  end
347
392
  end
348
393
  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.12.2
4
+ version: 1.14.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-08-29 00:00:00.000000000 Z
11
+ date: 2016-09-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: timers