build-buddy 1.12.2 → 1.14.0

Sign up to get free protection for your applications and to get access to all the features.
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