#!/usr/bin/ruby require 'rubygems' require 'cgi-spa' require 'time' # configurations to include in the dashboard CONFIGS = [#dir rails ruby book %w(work 3.0pre 1.8.7 3), %w(work-188 3.0pre 1.8.8 3), %w(work-191 3.0pre 1.9.1 3), %w(work-192 3.0pre 1.9.2 3), %w(work-235 2.3.5 1.8.7 3), %w(work-188-235 2.3.5 1.8.8 3), %w(work-191-235 2.3.5 1.9.1 3), %w(work-192-235 2.3.5 1.9.2 3), %w(work 3.0pre 1.8.7 4), %w(work-191 3.0pre 1.9.1 4), ] HOMEDIR = { '3' => '/home/rubys/git/awdwr', '4' => '/home/rubys/svn/rails4/Book/util', } DOCROOT = { '3' => 'AWDwR3', '4' => 'AWDwR4', } # submit a new run in the background if $HTTP_POST submit "ruby #{$HOME}/bin/testrails #{$param.args} " + "> #{$HOME}/logs/post.out 2> #{$HOME}/logs/post.err " sleep 2 end # determine if any processes are active ACTIVE = `ps xo start,args`.scan(/^.{8} .*/).grep(/testrails /) LOG = [] unless ACTIVE.empty? start = Time.parse(ACTIVE.first.split.first) Dir["#{$HOME}/logs/makedepot*.log"].each do |log| next unless File.stat(log).mtime >= start LOG.push *open(log) {|file| file.read.grep(/====>/)[-3..-1] or []} end LOG.map! {|line| line.chomp} LOG.unshift '' unless LOG.empty? end # dynamic status updates if $XHR_JSON output = {'config'=>{}, 'active'=>ACTIVE + LOG} CONFIGS.each do |dir, rails, ruby, book| statfile = "#{HOMEDIR[book]}/#{dir}/status" status = open(statfile) {|file| file.read.chomp} rescue 'missing' status.gsub! /, 0 (pendings|omissions|notifications)/, '' mtime = File.stat(statfile).mtime.iso8601 rescue 'missing' running = File.exist?(statfile+'.run') output['config'][dir.gsub('.','') + '-' + book] = { 'status' => status, 'mtime' => mtime.sub('T',' ').sub(/[+-]\d\d:\d\d$/,''), 'running' => running } end $cgi.out 'type' => 'application/json', 'Cache-Control' => 'no-cache' do JSON.pretty_generate output end exit end # main output $cgi.out 'type' => 'application/xhtml+xml', 'charset' => 'UTF-8' do $x.declare! :DOCTYPE, :html $x.html :xmlns => 'http://www.w3.org/1999/xhtml' do |x| x.header do x.title 'Depot Dashboard' x.style :type => "text/css" do x.indented_text! <<-'EOF' body {margin:0; background: #f5f5dc} h1 { background: #9C9; color: #282; text-align: center; font: 40px "Times New Roman",serif; font-weight: normal; font-variant: small-caps; padding: 10px 0; border-bottom: 2px solid; margin: 0 0 0.5em 0; } h2 {border-spacing: 0; margin-left: 2em} table {border-spacing: 0; margin-left: auto; margin-right:auto} th {color: #000; background-color: #99C; border: solid #000 1px} .r23 {background-color: #CCC} th, td {border: solid 1px; padding: 0.5em} .hilite {background-color: #FF9} .pass {background-color: #9C9} .fail {background-color: #F99} td a, td a:visited {color: black} thead th:first-child { -webkit-border-top-left-radius: 1em; -moz-border-radius: 1em 0 0 0; border-radius: 1em 0 0 0; } thead th:last-child { -webkit-border-top-right-radius: 1em; -moz-border-radius: 0 1em 0 0; border-radius: 0 1em 0 0; } tfoot th:first-child { -webkit-border-bottom-left-radius: 1em; -moz-border-radius: 0 0 0 1em; border-radius: 0 0 0 1em; } tfoot th:last-child { -webkit-border-bottom-right-radius: 1em; -moz-border-radius: 0 0 1em 0; border-radius: 0 0 1em 0; } EOF x.indented_text! <<-'EOF' unless $param.static #executing pre {margin-left: 5em} form {width: 16.2em; margin: 1em auto} EOF x.indented_text! 'form {display: none}' unless ACTIVE.empty? x.indented_text! '#executing {display: none}' if ACTIVE.empty? end x.script '', :src => 'jquery-1.3.2.min.js' x.script '', :src => 'jquery.tablesorter.min.js' x.script :type => "text/javascript" do x.indented_text! <<-'EOF' setTimeout(update, 1000); function update() { $.getJSON("awdwr.cgi?", {}, function(data) { for (var id in data.config) { var line = data.config[id]; $('#'+id+' td:eq(3) a').text(line.status); $('#'+id+' td:eq(4)').text(line.mtime); if (line.running) { $('#'+id+' td:eq(3)').addClass('hilite'). removeClass('pass').removeClass('fail'); } else if (line.status.match(/ 0 failures, 0 errors/)) { $('#'+id+' td:eq(3)').addClass('pass'). removeClass('hilite').removeClass('fail'); } else { $('#'+id+' td:eq(3)').addClass('fail'). removeClass('hilite').removeClass('pass'); } } if (data.active.length == 0) { $('form').show(); $('#executing').hide(); } else { $('form').hide(); $('#executing').show(); $('#active').text(data.active.join("\n")); } $('table').trigger('update'); setTimeout(update, 5000); }); } $(document).ready(function() { $('tr td:nth-child(5)').click(function() { var parent = $(this).parent(); $('input[name=args]').val( parent.find('td:eq(0)').text().replace(/\D/g,'') + ' ' + parent.find('td:eq(1)').text().replace(/\D/g,'') + ' ' + parent.find('td:eq(2)').text().replace(/\D/g,'') ); }); }); EOF end unless $param.static x.script :type => "text/javascript" do x.indented_text! <<-'EOF' $(document).ready(function() { $.tablesorter.addParser({ id: 'summary', is: function(s) {return false}, format: function(value, table, cell) { var vals = $(cell).text().match(/\d+/g); if (!vals) return '9999'; if (vals.length == 4) { vals = [vals[3],vals[2],9999-vals[0],9999-vals[1]]; } var rjust = function(x) { return ('000'+x).substr(x.toString().length-1,4) }; return vals.map(rjust).join(''); }, type: 'text' }); $('table').tablesorter({headers: {3:{sorter:'summary'}}}); }); EOF end end x.body do x.h1 'The Depot Dashboard' x.table do x.thead do x.tr do x.th 'Book' x.th 'Ruby' x.th 'Rails' x.th 'Status' x.th 'Time' end end x.tbody do CONFIGS.each do |dir, rails, ruby, book| statfile = "#{HOMEDIR[book]}/#{dir}/status" status = open(statfile) {|file| file.read.chomp} rescue 'missing' status.gsub! /, 0 (pendings|omissions|notifications)/, '' mtime = File.stat(statfile).mtime.iso8601 rescue 'missing' attrs = {:id => dir.gsub('.','') + '-' + book} attrs[:class]='r23' if rails=='2.3.5' if $param.static link = "#{dir.sub('work','checkdepot')}.html" else link = "#{dir}/checkdepot.html" end if File.exist?(statfile+'.run') color = 'hilite' elsif status =~ / 0 failures, 0 errors/ color = 'pass' else color = 'fail' end x.tr attrs do x.td book, {:align=>'right'}. merge(book=='3' ? {} : {:class=>'hilite'}) x.td ruby, ({:class=>'hilite'} if ruby != '1.8.7') x.td rails, ({:class=>'hilite'} if rails != '3.0pre') x.td :class=>color, :align=>'center' do x.a status, :href => "#{DOCROOT[book]}/#{link}" #todos" end x.td mtime.sub('T',' ').sub(/[+-]\d\d:\d\d$/,'') end end end x.tfoot do x.tr do 5.times { x.th '' } end end end unless $param.static x.form :method=>'post' do x.input :name=>'args' x.input :type=>'submit', :value=>'submit' end x.div :id=>'executing' do x.h2 'Currently Executing' x.pre ACTIVE.join("\n"), :id => 'active' end end end end end