3 # See README for instructions.
10 BASE_DIR = File.expand_path(ARGV.shift || "/tmp/ci")
11 HTML_DIR = File.expand_path(ARGV.shift || File.join(BASE_DIR, "html"))
13 CI_DIR = File.join HTML_DIR, "ci"
14 DATA_DIR = File.join BASE_DIR, "data"
15 MRI_TRUNK = 'http://svn.ruby-lang.org/repos/ruby/trunk'
16 RBX_REPO = 'git://git.rubini.us/code'
17 SPEC_REPO = 'git://github.com/rubyspec/rubyspec.git'
18 TEMP_DIR = File.join BASE_DIR, "tmp"
19 TRIG_DIR = File.join HTML_DIR, "trigger"
21 SPEC_GIT_URL = "http://github.com/rubyspec/rubyspec/commit/"
22 RBX_GIT_URL = "http://git.rubini.us/?p=code;a=commit;h="
29 super { |h,k| h[k] = HashHash.new }
33 ############################################################
40 all_data = HashHash.new
41 flat_data = process_individual_results
42 platforms = flat_data.map { |h| h[:platform] }.uniq
43 hashes = flat_data.map { |h| h[:hash] }.uniq
45 flat_data.each do |data|
46 all_data[data[:incremental]][data[:hash]][data[:platform]] = data
49 hash_times = hashes.map { |hash|
50 # incrementals and fall back to fulls if the sky is falling
51 (all_data[true ][hash].map { |_,run| run[:submitted] }.max ||
52 all_data[false][hash].map { |_,run| run[:submitted] }.max)
55 hashes = Hash[*hashes.zip(hash_times).flatten]
57 write_index platforms, hashes, all_data
62 ############################################################
65 def abbreviate_platform(arch)
66 plat = Gem::Platform.new(arch)
67 cpu, os, _ = plat.to_a
68 o, c = os[0..0], cpu[0..0] rescue "?"
71 x = "6" if cpu =~ /64/
77 seconds_per_day = 86400
78 days *= seconds_per_day
80 results = Hash.new { |h,k| h[k] = [] }
81 fresh_path = "html/index.yaml"
83 files = Dir["data/*"].select { |f| File.file? f }.sort_by { |f| File.mtime f }
86 h = YAML.load(File.read(f))
89 h[:submitted] = File.mtime f
90 h[:time] = log[/^Finished in (.*) seconds/, 1].to_f
91 h[:result] = log[/^\d+ files.*/]
95 fresh, stale = files.partition { |h|
96 now - h[:submitted] < days
100 results[h[:submitted].strftime("%Y-%m")] << h
104 latest_mtime = fresh.last[:submitted] rescue Time.at(0)
105 safe_write fresh_path, latest_mtime do |f|
109 results.each do |date, data|
112 path = "html/index.#{date}.yaml"
114 if File.exist? path then
115 old_data = YAML.load_file path
116 data = old_data + data
119 safe_write path do |f|
125 def build_row runs, platforms
126 platforms.each do |platform|
128 if run.has_key? :log then
129 status = run[:status]
132 td_ " ", :class => :p
135 a_("doa", :href => "ci/#{run[:id]}.html")
139 a_(status, :href => "ci/#{run[:id]}.html")
143 td_ " ", :class => :m
149 old = YAML.load_file(File.join(TRIG_DIR, "#{n}.yaml"))[:revision] rescue nil
150 new = `git ls-remote -h #{repo} refs/heads/master`.split.first
155 FileUtils.rm_rf CI_DIR
156 FileUtils.mkdir_p [CI_DIR, TEMP_DIR, TRIG_DIR]
160 def process_individual_results
161 Dir[File.join(DATA_DIR, "*")].select { |f| File.file? f }.map { |f|
162 h = YAML.load(File.read(f))
163 h[:id] = File.basename(f)
164 h[:submitted] = File.mtime f
167 h[:status] = if log =~ /(\d+) failures?, (\d+) errors?$/ then
168 failures, errors = $1.to_i, $2.to_i
169 if failures + errors == 0 then
172 "#{failures}/#{errors}"
178 if h[:status] != :passed then
181 title_ "Build Result"
183 tagz << "pre { white-space: pre-wrap; }"
184 tagz << ".x { font-size: 0.7em; color: #999; }"
185 tagz << ".b { color: #009; }"
186 tagz << "th { text-align: right }"
194 [:id, :submitted, :incremental, :hash, :platform].each do |key|
203 log.gsub!(/^(\*\*.*not_needed.*)/, '<span class=x>\1</span>')
204 log.gsub!(/^(\*\*.*)/, '<span class=x>\1</span>')
205 log.gsub!(/^.*\.{10}.*/) { |l| l.scan(/.{78}/).join("\n") + "\n" }
206 log.gsub!(/<.span>\n/, "\n</span>")
211 path = File.join(HTML_DIR, "ci", "#{h[:id]}.html")
212 File.open path, 'w' do |out|
216 File.utime(t, t, path)
223 def safe_write path, mtime = nil
224 new_path = "#{path}.new"
225 File.open new_path, 'w' do |f|
228 File.utime mtime, mtime, new_path if mtime
229 File.rename new_path, path
232 def svn_diff repo, dir
233 Dir.chdir TEMP_DIR do
234 system "svn co -r 1 -q #{repo} #{dir}" unless File.directory? dir
237 old = `svnversion .`.chomp
239 new = `svnversion .`.chomp
240 return old.to_i, new.to_i
245 def update_mri_trigger
246 old, new = svn_diff MRI_TRUNK, "mri_trunk"
248 update_trigger "mri", :revision => new if old != new
251 def update_rbx_trigger
252 old, new = git_diff RBX_REPO, "rbx"
254 update_trigger "rbx", :revision => new if old != new || old.nil?
257 def update_rubyspec_trigger
258 old, new = git_diff SPEC_REPO, "spec"
260 update_trigger "spec", :revision => new if old != new || old.nil?
263 def update_trigger name, data = nil
264 Dir.chdir HTML_DIR do
265 File.open "trigger/#{name}.yaml", "w" do |f|
273 update_rubyspec_trigger
277 def write_index platforms, hashes, all_data
280 title_ "Rubinius CI Dashboard"
282 tagz << "body { font-family: Optima, Times }"
283 tagz << "a { color: black; }"
284 tagz << "h1,h3{ color: #339; text-align: center }"
286 tagz << "table.data { border-spacing: 2 0 }"
287 tagz << ".data tr { border-bottom: 1px solid black }"
288 tagz << ".data th { text-align: right; font-family: monospace }"
289 tagz << ".data td { text-align: center; }"
291 tagz << ".data #first th { text-align: center }"
293 tagz << "td.p { background-color: #cfc }"
294 tagz << "td.f { background-color: #fcc }"
295 tagz << "td.d { background-color: #fcc }"
296 tagz << "td.m { background-color: #ccc }"
301 h1_ "Rubinius CI Dashboard"
302 h3_ Time.now.strftime("%m-%d %H:%M")
304 table_ :id => "data" do
305 tr_ :id => "first" do
308 th_ "Incrs", :colspan => platforms.size
310 th_ "Fulls", :colspan => platforms.size
313 tr_ :id => "first" do
317 platforms.each do |platform|
318 th_ abbreviate_platform(platform)
323 platforms.each do |platform|
324 th_ abbreviate_platform(platform)
328 hashes.sort_by {|_,t| -t.to_i }.first(DISPLAY).each do |hash, time|
331 a_(hash[0..7], :href => "#{SPEC_GIT_URL}#{hash}")
334 th_ "#{time.strftime("%m-%d %H:%M")}"
336 build_row all_data[true][hash], platforms
338 build_row all_data[false][hash], platforms
344 tr_ { th_ "Legend", :colspan => 2 }
345 platforms.each do |plat|
346 tr_ { th_ abbreviate_platform(plat); td_ plat }
352 File.open File.join(HTML_DIR, "index.html"), "w" do |f|
358 ############################################################
360 _run if $0 == __FILE__