build-buddy 1.11.0 → 1.12.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 +17 -15
- data/lib/build_buddy.rb +1 -1
- data/lib/build_buddy/build_data.rb +31 -7
- data/lib/build_buddy/builder.rb +32 -7
- data/lib/build_buddy/config.rb +33 -22
- data/lib/build_buddy/gitter.rb +4 -5
- data/lib/build_buddy/recorder.rb +245 -9
- data/lib/build_buddy/scheduler.rb +36 -21
- data/lib/build_buddy/server.rb +101 -8
- data/lib/build_buddy/slacker.rb +149 -125
- metadata +18 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4d50fe65cd2806a970546102b56304bdae80f2b6
|
4
|
+
data.tar.gz: 7fdd7e59a6b62df8fe884ec009020bdb85ec9cdd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cdd33b96f5b8e83ec9eff07b0555038c03f629dca285de77975928d264ab68a5a6942d5550d0fedb4a01b0211757a55512dd66ea883f262abc30bbe03015d617
|
7
|
+
data.tar.gz: afec166a8cce90aa46b85ff38bc3b4eddc8204afc2eeeca57a1b994dc75c151bcd6da50591d171fa772e259d65d5dfca6797a18f11cc0274d5f5e0504b362e18
|
data/bin/build-buddy
CHANGED
@@ -2,8 +2,6 @@
|
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'celluloid/current'
|
5
|
-
require 'celluloid/supervision'
|
6
|
-
require 'celluloid/supervision/container'
|
7
5
|
require 'methadone'
|
8
6
|
require 'build_buddy'
|
9
7
|
|
@@ -21,12 +19,8 @@ module BuildBuddy
|
|
21
19
|
|
22
20
|
load config_file_name
|
23
21
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
unless Dir.exist?(build_log_dir)
|
28
|
-
Dir.mkdir(build_log_dir)
|
29
|
-
end
|
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)
|
30
24
|
|
31
25
|
Slack.configure do |config|
|
32
26
|
config.token = Config.slack_api_token
|
@@ -34,15 +28,23 @@ module BuildBuddy
|
|
34
28
|
|
35
29
|
Celluloid.logger = Reel::Logger.logger
|
36
30
|
|
37
|
-
Builder.
|
38
|
-
Slacker.
|
39
|
-
Gitter.
|
40
|
-
Scheduler.
|
41
|
-
Server.
|
42
|
-
Recorder.
|
31
|
+
Celluloid::Actor[:builder] = Builder.new
|
32
|
+
Celluloid::Actor[:slacker] = Slacker.new
|
33
|
+
Celluloid::Actor[:gitter] = Gitter.new
|
34
|
+
Celluloid::Actor[:scheduler] = Scheduler.new
|
35
|
+
Celluloid::Actor[:server] = Server.new
|
36
|
+
Celluloid::Actor[:recorder] = Recorder.new
|
37
|
+
|
38
|
+
Celluloid::Actor[:recorder].async.gen_charts
|
43
39
|
|
44
40
|
begin
|
45
|
-
|
41
|
+
loop {
|
42
|
+
sleep(5)
|
43
|
+
|
44
|
+
unless Celluloid.actor_system.running.any? {|k| k.is_a?(BuildBuddy::Slacker)}
|
45
|
+
Celluloid::Actor[:slacker] = Slacker.new
|
46
|
+
end
|
47
|
+
}
|
46
48
|
rescue Interrupt => e
|
47
49
|
puts
|
48
50
|
end
|
data/lib/build_buddy.rb
CHANGED
@@ -5,24 +5,27 @@ module BuildBuddy
|
|
5
5
|
attr_accessor :repo_full_name
|
6
6
|
attr_accessor :branch
|
7
7
|
attr_accessor :pull_request
|
8
|
-
attr_accessor :repo_full_name
|
9
8
|
attr_accessor :repo_sha
|
10
9
|
attr_accessor :termination_type # :killed or :exited
|
10
|
+
attr_accessor :started_by
|
11
|
+
attr_accessor :stopped_by
|
11
12
|
attr_accessor :exit_code
|
12
13
|
attr_accessor :start_time
|
13
14
|
attr_accessor :end_time
|
14
|
-
attr_accessor :
|
15
|
-
attr_accessor :flags # :no_upload, :test_channel
|
15
|
+
attr_accessor :flags
|
16
16
|
attr_accessor :metrics
|
17
17
|
|
18
18
|
def initialize(args)
|
19
19
|
args.each do |key, value|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
begin
|
21
|
+
self.method((key.to_s + '=').to_sym).call(value)
|
22
|
+
rescue NameError
|
23
|
+
# Ignore fields in the database we don't understand
|
24
24
|
end
|
25
25
|
end
|
26
|
+
|
27
|
+
# Set the id here so that we can use it to refer to this build_data in queue
|
28
|
+
@_id = BSON::ObjectId.new
|
26
29
|
end
|
27
30
|
|
28
31
|
def to_h
|
@@ -30,5 +33,26 @@ module BuildBuddy
|
|
30
33
|
instance_variables.each {|var| hash[var.to_s.delete("@")] = instance_variable_get(var) }
|
31
34
|
hash
|
32
35
|
end
|
36
|
+
|
37
|
+
def server_log_uri
|
38
|
+
Config.server_base_uri + '/log/' + @_id.to_s
|
39
|
+
end
|
40
|
+
|
41
|
+
def status_verb
|
42
|
+
if @termination_type == :killed
|
43
|
+
'was stopped'
|
44
|
+
else
|
45
|
+
@exit_code != 0 ? 'failed' : 'succeeded'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def pull_request_uri
|
50
|
+
"https://github.com/#{@repo_full_name}/pull/#{@pull_request}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def log_file_name
|
54
|
+
return nil if @start_time.nil?
|
55
|
+
File.join(Config.build_log_dir, "#{@start_time.strftime('%Y%m%d-%H%M%S')}.log")
|
56
|
+
end
|
33
57
|
end
|
34
58
|
end
|
data/lib/build_buddy/builder.rb
CHANGED
@@ -27,7 +27,7 @@ module BuildBuddy
|
|
27
27
|
@metrics_tempfile = Tempfile.new('build-metrics')
|
28
28
|
@metrics_tempfile.close()
|
29
29
|
|
30
|
-
repo_parts = build_data.repo_full_name.split('/')
|
30
|
+
repo_parts = @build_data.repo_full_name.split('/')
|
31
31
|
git_repo_owner = repo_parts[0]
|
32
32
|
git_repo_name = repo_parts[1]
|
33
33
|
env = {}
|
@@ -71,6 +71,8 @@ fi
|
|
71
71
|
cd ${GIT_REPO_NAME}
|
72
72
|
)
|
73
73
|
|
74
|
+
build_root_dir = nil
|
75
|
+
|
74
76
|
case build_data.type
|
75
77
|
when :pull_request
|
76
78
|
build_root_dir = expand_vars(Config.pull_request_root_dir)
|
@@ -113,14 +115,12 @@ source ${BUILD_SCRIPT}
|
|
113
115
|
|
114
116
|
unless build_data.flags.nil?
|
115
117
|
build_data.flags.each do |flag|
|
116
|
-
env["BUILD_FLAG_#{
|
118
|
+
env["BUILD_FLAG_#{key.to_s.upcase}"] = 1
|
117
119
|
end
|
118
120
|
end
|
119
121
|
|
120
122
|
@build_data.start_time = Time.now.utc
|
121
|
-
|
122
|
-
"build_#{build_data.type.to_s}_#{build_data.start_time.strftime('%Y%m%d_%H%M%S')}.log")
|
123
|
-
@build_data.log_filename = log_filename
|
123
|
+
log_file_name = @build_data.log_file_name
|
124
124
|
|
125
125
|
# Ensure build root and git user directory exists
|
126
126
|
clone_dir = File.join(build_root_dir, git_repo_owner)
|
@@ -130,12 +130,19 @@ source ${BUILD_SCRIPT}
|
|
130
130
|
script_filename = File.join(clone_dir, "build-buddy-bootstrap.sh")
|
131
131
|
File.write(script_filename, build_script)
|
132
132
|
|
133
|
+
# Notify GitHub that build has started
|
134
|
+
if @build_data.type == :pull_request
|
135
|
+
Celluloid::Actor[:gitter].async.set_status(
|
136
|
+
build_data.repo_full_name, build_data.repo_sha, :pending, "Build has started",
|
137
|
+
build_data.server_log_uri)
|
138
|
+
end
|
139
|
+
|
133
140
|
# Run the build script
|
134
141
|
Bundler.with_clean_env do
|
135
|
-
@pid = Process.spawn(env, "bash #{script_filename}", :pgroup => true, :chdir => clone_dir, [:out, :err] =>
|
142
|
+
@pid = Process.spawn(env, "bash #{script_filename}", :pgroup => true, :chdir => clone_dir, [:out, :err] => log_file_name)
|
136
143
|
@gid = Process.getpgid(@pid)
|
137
144
|
end
|
138
|
-
info "Running build script (pid #{@pid}, gid #{@gid}) : Log #{
|
145
|
+
info "Running build script (pid #{@pid}, gid #{@gid}) : Log #{log_file_name}"
|
139
146
|
|
140
147
|
if @watcher
|
141
148
|
@watcher.terminate
|
@@ -164,10 +171,28 @@ source ${BUILD_SCRIPT}
|
|
164
171
|
|
165
172
|
@build_data.metrics = metrics
|
166
173
|
|
174
|
+
# Send status to GitHub if pull request
|
175
|
+
if @build_data.type == :pull_request
|
176
|
+
git_state = (@build_data.termination_type == :killed ? :failure : @build_data.exit_code != 0 ? :error : :success)
|
177
|
+
status_verb = @build_data.status_verb
|
178
|
+
Celluloid::Actor[:gitter].async.set_status(
|
179
|
+
@build_data.repo_full_name, @build_data.repo_sha, git_state, "Build #{status_verb}",
|
180
|
+
@build_data.server_log_uri)
|
181
|
+
end
|
182
|
+
|
183
|
+
# Log the build completion and clean-up
|
167
184
|
info "Process #{status.pid} #{@build_data.termination_type == :killed ? 'was terminated' : "exited (#{@build_data.exit_code})"}"
|
168
185
|
Celluloid::Actor[:scheduler].async.on_build_completed(@build_data)
|
169
186
|
@watcher.terminate
|
170
187
|
@watcher = nil
|
188
|
+
|
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}"
|
195
|
+
end
|
171
196
|
end
|
172
197
|
|
173
198
|
def stop_build
|
data/lib/build_buddy/config.rb
CHANGED
@@ -2,33 +2,44 @@ module BuildBuddy
|
|
2
2
|
module Config
|
3
3
|
extend self
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
5
|
+
ATTRS = [
|
6
|
+
:github_webhook_port,
|
7
|
+
:github_webhook_secret_token,
|
8
|
+
:github_webhook_repo_full_name,
|
9
|
+
:github_api_token,
|
10
|
+
:slack_api_token,
|
11
|
+
:slack_build_channel,
|
12
|
+
:slack_builders,
|
13
|
+
:build_log_dir,
|
14
|
+
:build_log_limit,
|
15
|
+
:pull_request_build_script,
|
16
|
+
:branch_build_script,
|
17
|
+
:pull_request_root_dir,
|
18
|
+
:branch_root_dir,
|
19
|
+
:allowed_build_branches,
|
20
|
+
:kill_build_after_mins,
|
21
|
+
:server_base_uri,
|
22
|
+
:mongo_uri,
|
23
|
+
:hud_secret_token,
|
24
|
+
:hud_image_dir,
|
25
|
+
]
|
26
|
+
attr_accessor(*ATTRS)
|
24
27
|
end
|
25
28
|
|
26
29
|
class << self
|
27
30
|
def configure
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
+
config.github_webhook_port = 4567
|
32
|
+
config.kill_build_after_mins = 30
|
33
|
+
config.mongo_uri = 'mongodb://localhost:27017/build-buddy'
|
34
|
+
config.build_log_limit = 30
|
31
35
|
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] })
|
38
|
+
Config::ATTRS.map {|attr| ('@' + attr.to_s).to_sym }.each {|var|
|
39
|
+
if config.instance_variable_get(var).nil?
|
40
|
+
raise "Config value '#{var.to_s.delete('@')}' not set"
|
41
|
+
end
|
42
|
+
}
|
32
43
|
end
|
33
44
|
|
34
45
|
def config
|
data/lib/build_buddy/gitter.rb
CHANGED
@@ -14,14 +14,13 @@ module BuildBuddy
|
|
14
14
|
info "Connected to Github"
|
15
15
|
end
|
16
16
|
|
17
|
+
# state is one of :pending, :killed, :failure, :error, :success
|
17
18
|
def set_status(repo_full_name, repo_sha, state, description, target_url)
|
18
19
|
options = {
|
19
|
-
|
20
|
-
|
20
|
+
:description => description.length > 140 ? "#{description[0..136]}..." : description,
|
21
|
+
:context => 'build-buddy',
|
22
|
+
:target_url => target_url || ''
|
21
23
|
}
|
22
|
-
unless target_url.nil?
|
23
|
-
options[:target_url] = target_url
|
24
|
-
end
|
25
24
|
@gh_client.create_status(repo_full_name, repo_sha, state.to_s, options)
|
26
25
|
end
|
27
26
|
end
|
data/lib/build_buddy/recorder.rb
CHANGED
@@ -2,6 +2,7 @@ require 'rubygems'
|
|
2
2
|
require 'bundler'
|
3
3
|
require 'celluloid'
|
4
4
|
require 'mongo'
|
5
|
+
require 'gruff'
|
5
6
|
require_relative './config.rb'
|
6
7
|
|
7
8
|
module BuildBuddy
|
@@ -9,6 +10,8 @@ module BuildBuddy
|
|
9
10
|
include Celluloid
|
10
11
|
include Celluloid::Internals::Logger
|
11
12
|
|
13
|
+
LIMIT = 50
|
14
|
+
|
12
15
|
def initialize()
|
13
16
|
Mongo::Logger.logger.level = ::Logger::FATAL
|
14
17
|
mongo_uri = BuildBuddy::Config.mongo_uri
|
@@ -20,28 +23,261 @@ module BuildBuddy
|
|
20
23
|
def record_build_data(build_data)
|
21
24
|
builds = @mongo[:builds]
|
22
25
|
result = builds.insert_one(build_data.to_h)
|
23
|
-
build_data._id
|
26
|
+
if build_data._id.nil?
|
27
|
+
build_data._id = result.inserted_id
|
28
|
+
end
|
24
29
|
end
|
25
30
|
|
26
31
|
def update_build_data(build_data)
|
27
|
-
|
28
|
-
|
29
|
-
builds.replace_one({ :_id => build_data._id }, build_data.to_h)
|
32
|
+
if build_data._id.nil?
|
33
|
+
return
|
30
34
|
end
|
35
|
+
|
36
|
+
builds = @mongo[:builds]
|
37
|
+
builds.replace_one({ :_id => build_data._id }, build_data.to_h)
|
38
|
+
|
39
|
+
gen_charts
|
31
40
|
end
|
32
41
|
|
33
42
|
def get_build_data(id)
|
34
|
-
|
35
|
-
if
|
43
|
+
doc = @mongo[:builds].find({ :_id => BSON::ObjectId.from_string(id) }, { :limit => 1 }).first
|
44
|
+
if doc.nil?
|
36
45
|
return nil
|
37
46
|
end
|
38
|
-
BuildData.new(
|
47
|
+
BuildData.new(doc)
|
39
48
|
end
|
40
49
|
|
41
50
|
def get_build_data_history(limit)
|
42
|
-
@mongo[:builds].find().sort(:start_time => -1).limit(limit).map do |
|
43
|
-
BuildData.new(
|
51
|
+
@mongo[:builds].find().sort(:start_time => -1).limit(limit).map do |doc|
|
52
|
+
BuildData.new(doc)
|
53
|
+
end
|
54
|
+
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
|
44
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
|
+
|
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']])
|
264
|
+
end
|
265
|
+
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'))
|
45
281
|
end
|
46
282
|
end
|
47
283
|
end
|