mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 14:45:29 +00:00
9b22e34502
keep in CVS: * Clean up rules, sheriff, and status messages so they don't automatically stick a break at the end of the text. Tinderbox page admins should manage that directly. Also trim the text up to avoid extra whitespace triggering inclusion of the text. * In the who/guilty column, only pass the "above-build-time" in to who_menu() when we're not in the first row. If we are in the first row, then pass in undefined. Previously, we passed this value in regardless and the top row of committers would end up with a non- sensical commit range. This fixes that problem. * Add build start, end, and elapsed time where possible. This is wrapped in an always-true conditional in case we ever need to turn it off or decide we want to trigger this behaviour through a config parameter. * In who_menu, properly handle when an undefined value is passed in for $maxdate. Assume that one won't be given and then append it to the href string if it is given. * Add the supporting JavaScript code for the start, end, and elapsed time work. * This code gracefully handles the case that no time_info or elapsed time info is given by just not including that information in the pop-up window. * Some functions are included here for archive's sake. My earlier, non-committed implementation did more work in the JS itself but I later moved most of it into the Perl code to simplify things. * I used this opportunity to do general clean-up on the blurb text so that the two separate codepaths (based on the availability of layers) are more unified.
1261 lines
35 KiB
Perl
Executable File
1261 lines
35 KiB
Perl
Executable File
#!/usr/bonsaitools/bin/perl --
|
|
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
|
#
|
|
# The contents of this file are subject to the Netscape Public
|
|
# License Version 1.1 (the "License"); you may not use this file
|
|
# except in compliance with the License. You may obtain a copy of
|
|
# the License at http://www.mozilla.org/NPL/
|
|
#
|
|
# Software distributed under the License is distributed on an "AS
|
|
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
# implied. See the License for the specific language governing
|
|
# rights and limitations under the License.
|
|
#
|
|
# The Original Code is the Tinderbox build tool.
|
|
#
|
|
# The Initial Developer of the Original Code is Netscape Communications
|
|
# Corporation. Portions created by Netscape are
|
|
# Copyright (C) 1998 Netscape Communications Corporation. All
|
|
# Rights Reserved.
|
|
#
|
|
# Contributor(s):
|
|
|
|
use lib '../bonsai';
|
|
require 'tbglobals.pl';
|
|
require 'lloydcgi.pl';
|
|
require 'imagelog.pl';
|
|
require 'header.pl';
|
|
|
|
# Hack this until I can figure out how to do get default root. -slamm
|
|
$default_root = '/cvsroot';
|
|
|
|
# Show 12 hours by default
|
|
#
|
|
$nowdate = time;
|
|
if (not defined($maxdate = $form{maxdate})) {
|
|
$maxdate = $nowdate;
|
|
}
|
|
if ($form{showall}) {
|
|
$mindate = 0;
|
|
}
|
|
else {
|
|
$default_hours = 12;
|
|
$hours = $default_hours;
|
|
$hours = $form{hours} if $form{hours};
|
|
$mindate = $maxdate - ($hours*60*60);
|
|
}
|
|
|
|
%colormap = (
|
|
success => '11DD11',
|
|
busted => 'EE0000',
|
|
building => 'EEFF00',
|
|
testfailed => 'FFAA00'
|
|
);
|
|
|
|
%titlemap = (
|
|
success => 'success',
|
|
busted => 'busted',
|
|
building => 'building',
|
|
testfailed => 'testfailed',
|
|
flames => 'burning',
|
|
star => ''
|
|
);
|
|
|
|
%textmap = (
|
|
success => 'L',
|
|
busted => 'L!',
|
|
building => 'L/',
|
|
testfailed => 'L-',
|
|
flames => '%',
|
|
star => '*'
|
|
);
|
|
|
|
%images = (
|
|
flames => '1afi003r.gif',
|
|
star => 'star.gif'
|
|
);
|
|
|
|
$::tree = $form{tree};
|
|
|
|
# $rel_path is the relative path to webtools/tinderbox used for links.
|
|
# It changes to "../" if the page is generated statically, because then
|
|
# it is placed in tinderbox/$::tree.
|
|
$rel_path = '';
|
|
|
|
&show_tree_selector, exit if $::tree eq '';
|
|
&do_quickparse, exit if $form{quickparse};
|
|
&do_express, exit if $form{express};
|
|
&do_rdf, exit if $form{rdf};
|
|
&do_static, exit if $form{static};
|
|
&do_flash, exit if $form{flash};
|
|
&do_panel, exit if $form{panel};
|
|
&do_hdml, exit if $form{hdml};
|
|
&do_vxml, exit if $form{vxml};
|
|
&do_wml, exit if $form{wml};
|
|
&do_tinderbox, exit;
|
|
|
|
# end of main
|
|
#=====================================================================
|
|
|
|
sub make_tree_list {
|
|
my @result;
|
|
while(<*>) {
|
|
if( -d $_ && $_ ne 'data' && $_ ne 'CVS' && -f "$_/treedata.pl") {
|
|
push @result, $_;
|
|
}
|
|
}
|
|
return @result;
|
|
}
|
|
|
|
sub show_tree_selector {
|
|
|
|
print "Content-type: text/html\n\n";
|
|
|
|
EmitHtmlHeader("tinderbox");
|
|
|
|
print "<P><TABLE WIDTH=\"100%\">";
|
|
print "<TR><TD ALIGN=CENTER>Select one of the following trees:</TD></TR>";
|
|
print "<TR><TD ALIGN=CENTER>\n";
|
|
print " <TABLE><TR><TD><UL>\n";
|
|
|
|
my @list = make_tree_list();
|
|
|
|
foreach (@list) {
|
|
print "<LI><a href=showbuilds.cgi?tree=$_>$_</a>\n";
|
|
}
|
|
print "</UL></TD></TR></TABLE></TD></TR></TABLE>";
|
|
|
|
print "<P><TABLE WIDTH=\"100%\">";
|
|
print "<TR><TD ALIGN=CENTER><a href=admintree.cgi>";
|
|
print "Administer</a> one of the following trees:</TD></TR>";
|
|
print "<TR><TD ALIGN=CENTER>\n";
|
|
print " <TABLE><TR><TD><UL>\n";
|
|
|
|
foreach (@list) {
|
|
print "<LI><a href=admintree.cgi?tree=$_>$_</a>\n";
|
|
}
|
|
print "</UL></TD></TR></TABLE></TD></TR></TABLE>";
|
|
}
|
|
|
|
sub do_static {
|
|
local *OUT;
|
|
|
|
$form{legend}=0;
|
|
|
|
my @pages = ( ['index.html', 'do_tinderbox'],
|
|
['flash.rdf', 'do_flash'],
|
|
['panel.html', 'do_panel'],
|
|
['stats.hdml', 'do_hdml'],
|
|
['status.vxml', 'do_vxml'] );
|
|
|
|
$rel_path = '../';
|
|
while (($key, $value) = each %images) {
|
|
$images{$key} = "$rel_path$value";
|
|
}
|
|
|
|
my $oldfh = select;
|
|
|
|
foreach $pair (@pages) {
|
|
my ($page, $call) = @{$pair};
|
|
my $outfile = "$::tree/$page";
|
|
|
|
open(OUT,">$outfile.$$");
|
|
select OUT;
|
|
|
|
eval "$call";
|
|
|
|
close(OUT);
|
|
system("mv $outfile.$$ $outfile");
|
|
}
|
|
select $oldfh;
|
|
}
|
|
|
|
sub do_tinderbox {
|
|
my $tinderbox_data = &tb_load_data;
|
|
&print_page_head;
|
|
&print_table_header;
|
|
&print_table_body($tinderbox_data);
|
|
&print_table_footer;
|
|
}
|
|
|
|
sub print_page_head {
|
|
print "Content-type: text/html\n\n<HTML>\n" unless $form{static};
|
|
|
|
use POSIX qw(strftime);
|
|
# Print time in format "YYYY-MM-DD HH:MM timezone"
|
|
my $now = strftime("%Y-%m-%d %H:%M %Z", localtime);
|
|
|
|
EmitHtmlTitleAndHeader("tinderbox: $::tree", "tinderbox",
|
|
"tree: $::tree ($now)");
|
|
|
|
&print_javascript;
|
|
|
|
# Print rules, sheriff, and status. Only on the first pageful.
|
|
if ($nowdate eq $maxdate) {
|
|
unless ($form{norules}) {
|
|
do "$::tree/rules.pl";
|
|
print "<a NAME=\"rules\"></a>$rules_message"; # from $::tree/rules.pl
|
|
}
|
|
|
|
do "$::tree/sheriff.pl";
|
|
$current_sheriff =~ s:^\s*|\s*$::gs;
|
|
if ($current_sheriff and length($current_sheriff) gt 0) {
|
|
print "<a NAME=\"sheriff\"></a>$current_sheriff"; # from $::tree/sheriff.pl
|
|
}
|
|
|
|
do "$::tree/status.pl";
|
|
$status_message =~ s:^\s*|\s*$::gs;
|
|
if ($status_message and length($status_message) gt 0) {
|
|
print "<a NAME=\"status\"></a>$status_message"; # from $::tree/status.pl
|
|
}
|
|
|
|
# keeps the main table from clearing the IFRAME
|
|
print "<br clear=\"all\">\n";
|
|
}
|
|
|
|
# Quote and Legend
|
|
#
|
|
if ($form{legend}) {
|
|
my ($imageurl,$imagewidth,$imageheight,$quote) = &get_image;
|
|
print qq{
|
|
<table width="100%" cellpadding=0 cellspacing=0>
|
|
<tr>
|
|
<td valign=bottom>
|
|
<p><center><a href=addimage.cgi><img src="$rel_path$imageurl"
|
|
width=$imagewidth height=$imageheight><br>
|
|
$quote</a><br>
|
|
</center>
|
|
<p>
|
|
<td align=right valign=bottom>
|
|
<table cellspacing=0 cellpadding=1 border=0>
|
|
<tr>
|
|
<td align=center><TT>L</TT></td>
|
|
<td>= Show Build Log</td>
|
|
</tr>
|
|
<tr>
|
|
<td align=center><TT>C</TT></td>
|
|
<td>= Show Checkins</td>
|
|
</tr>
|
|
<tr>
|
|
<td align=center><TT>D</TT></td>
|
|
<td>= Download Build</td>
|
|
</tr>
|
|
<tr>
|
|
<td align=center>
|
|
<img src="$images{star}" title="$titlemap{star}" alt="$textmap{star}"></td>
|
|
<td>= Show Log comments</td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan=2>
|
|
<table cellspacing=1 cellpadding=1 border=1>
|
|
<tr bgcolor="$colormap{success}">
|
|
<td>
|
|
Successful Build, optional bloaty stats:<br>
|
|
<tt>Lk:XXX</tt> (bytes leaked)<br>
|
|
<tt>Bl:YYYY</tt> (bytes allocated, bloat)<br>
|
|
<tt>Tp:TT.T</tt> (page-loader time, ms)<br>
|
|
<tt>Txul:TT.T</tt> (XUL openwindow time, ms)<br>
|
|
<tt>Ts:TT.T</tt> (startup time, sec)<br>
|
|
</td>
|
|
<tr bgcolor="$colormap{building}">
|
|
<td>Build in progress</td>
|
|
<tr bgcolor="$colormap{testfailed}">
|
|
<td>Successful build, but tests failed</td>
|
|
<tr bgcolor="$colormap{busted}">
|
|
<td>Build failed</td>
|
|
</table>
|
|
</td></tr></table>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
};
|
|
}
|
|
if (is_tree_state_available()) {
|
|
print "<a NAME=\"open\"></a>";
|
|
print "The tree is <font size=+2>";
|
|
print (is_tree_open() ? 'open' : 'closed');
|
|
print "</font>\n";
|
|
}
|
|
}
|
|
|
|
sub print_table_body {
|
|
my $tinderbox_data = $_[0];
|
|
for (my $tt=0; $tt < $time_count; $tt++) {
|
|
last if $build_time_times->[$tt] < $mindate;
|
|
print_table_row($tinderbox_data, $tt);
|
|
}
|
|
}
|
|
|
|
|
|
BEGIN {
|
|
# Make $lasthour persistent private variable for print_table_row().
|
|
my $lasthour = '';
|
|
|
|
sub print_table_row {
|
|
my ($td, $tt) = @_;
|
|
|
|
# Time column
|
|
#
|
|
my $query_link = '';
|
|
my $end_query = '';
|
|
my $pretty_time = print_time($build_time_times->[$tt]);
|
|
|
|
($hour) = $pretty_time =~ /(\d\d):/;
|
|
|
|
if ($lasthour ne $hour or has_who_list($tt)) {
|
|
$query_link = query_ref($td, $build_time_times->[$tt]);
|
|
$end_query = '</a>';
|
|
}
|
|
if ($lasthour eq $hour) {
|
|
$pretty_time =~ s/^.* //;
|
|
} else {
|
|
$lasthour = $hour;
|
|
}
|
|
|
|
my $hour_color = '';
|
|
$hour_color = ' bgcolor=#e7e7e7'
|
|
if ($build_time_times->[$tt] + 1) % 7200 <= 3600;
|
|
print "<tr align=center><td align=right$hour_color>",
|
|
"$query_link\n$pretty_time$end_query</td>\n";
|
|
|
|
# Guilty
|
|
#
|
|
print '<td>';
|
|
for $who (sort keys %{$who_list->[$tt]} ){
|
|
my $qr;
|
|
if ($tt eq 0) {
|
|
$qr = &who_menu($td, $build_time_times->[$tt],
|
|
undef,$who);
|
|
} else {
|
|
$qr = &who_menu($td, $build_time_times->[$tt],
|
|
$build_time_times->[$tt-1],$who);
|
|
}
|
|
$who =~ s/%.*$//;
|
|
print " $qr$who</a>\n";
|
|
}
|
|
print '</td>';
|
|
|
|
# Build Status
|
|
#
|
|
for (my $build_index=0; $build_index < $name_count; $build_index++) {
|
|
if (not defined($br = $build_table->[$tt][$build_index])) {
|
|
# No build data for this time (e.g. no build after this time).
|
|
print "<td></td>\n";
|
|
next;
|
|
}
|
|
next if $br == -1; # Covered by rowspan
|
|
|
|
my $rowspan = $br->{rowspan};
|
|
$rowspan = $mindate_time_count - $tt + 1
|
|
if $tt + $rowspan - 1 > $mindate_time_count;
|
|
print "<td rowspan=$rowspan bgcolor=$colormap{$br->{buildstatus}}>\n";
|
|
|
|
my $logfile = $br->{logfile};
|
|
my $buildtree = $br->{td}->{name};
|
|
|
|
print "<tt>\n";
|
|
|
|
# Build Note
|
|
#
|
|
my $logurl = "${rel_path}showlog.cgi?log=$buildtree/$logfile";
|
|
|
|
if ($br->{hasnote}) {
|
|
print qq|
|
|
<a href="$logurl"
|
|
onclick="return note(event,$br->{noteid},'$logfile');">
|
|
<img src="$images{star}" title="$titlemap{star}" alt="$textmap{star}" border=0></a>
|
|
|;
|
|
}
|
|
|
|
# Build Log
|
|
#
|
|
# Uncomment this line to print logfile names in build rectangle.
|
|
# print "$logfile<br>";
|
|
|
|
if ( 1 ) {
|
|
# Add build start, end, and elapsed time where possible.
|
|
my($start, $end, $elapsed);
|
|
|
|
# Treat buildtime as the build's start and mailtime as the build's
|
|
# end. We should add in explicit setting of endtime in the client
|
|
# scripts if they don't already have it and then use that here.
|
|
my $start_timet = $br->{buildtime};
|
|
my $end_timet = $br->{mailtime};
|
|
|
|
# If either of the times aren't today, we need to qualify both with
|
|
# the month and day-of-month.
|
|
my $need_to_qualify;
|
|
if ( both_are_today($start_timet, $end_timet) ) {
|
|
$need_to_qualify = 0;
|
|
} else {
|
|
$need_to_qualify = 1;
|
|
}
|
|
|
|
# Grab the human-readable start time.
|
|
$start = get_local_hms($start_timet, $need_to_qualify);
|
|
|
|
# If we're still building, the mailtime only reflects the opening
|
|
# mail that the build has started, not the time at which the build
|
|
# ended. In that case, don't use it. Use the current time, instead.
|
|
my $time_info = "";
|
|
if ($br->{buildstatus} eq 'building') {
|
|
$elapsed = get_time_difference(time(), $start_timet);
|
|
|
|
$time_info = "Started $start, still building..";
|
|
} else {
|
|
$end = get_local_hms($end_timet, $need_to_qualify);
|
|
$elapsed = get_time_difference($end_timet, $start_timet);
|
|
|
|
$time_info = "Started $start, finished $end";
|
|
}
|
|
|
|
print qq|
|
|
<A HREF="$logurl"
|
|
onclick="return log(event,$build_index,'$logfile','$time_info','$elapsed');"
|
|
title="$titlemap{$br->{buildstatus}}">
|
|
$textmap{$br->{buildstatus}}</a>
|
|
|;
|
|
} else {
|
|
print qq|
|
|
<A HREF="$logurl"
|
|
onclick="return log(event,$build_index,'$logfile');"
|
|
title="$titlemap{$br->{buildstatus}}">
|
|
$textmap{$br->{buildstatus}}</a>
|
|
|;
|
|
}
|
|
|
|
# What Changed
|
|
#
|
|
# Only add the "C" link if there have been changes since the last build.
|
|
if ($br->{previousbuildtime}) {
|
|
my $previous_br = $build_table->[$tt+$rowspan][$build_index];
|
|
my $previous_rowspan = $previous_br->{rowspan};
|
|
if (&has_who_list($tt+$rowspan,
|
|
$tt+$rowspan+$previous_rowspan-1)) {
|
|
print "\n", &query_ref($br->{td},
|
|
$br->{previousbuildtime},
|
|
$br->{buildtime} - 1);
|
|
print "C</a>";
|
|
}
|
|
}
|
|
|
|
# Binary URL
|
|
#
|
|
# Only add the "D" link if there is a url to a downloadable binary
|
|
if( $br->{binaryurl} ){
|
|
$binaryurl = $br->{binaryurl};
|
|
print" <A HREF=$binaryurl>D</A>";
|
|
}
|
|
|
|
|
|
# Scrape data
|
|
if (defined $td->{scrape}{$logfile}) {
|
|
my (@scrape_data)
|
|
= @{ $td->{scrape}{$logfile} };
|
|
# ex: Tp:5.45s
|
|
my $i;
|
|
foreach $i (@scrape_data) {
|
|
print "<br>$i";
|
|
}
|
|
}
|
|
|
|
# Warnings
|
|
if (defined $td->{warnings}{$logfile}) {
|
|
my ($warning_count) = $td->{warnings}{$logfile};
|
|
my $warn_file = "$::tree/warn$logfile";
|
|
$warn_file =~ s/\.gz$/.html/;
|
|
print "<br><br><a href='${rel_path}$warn_file'>Warn:$warning_count</a>";
|
|
}
|
|
|
|
print "</tt>\n</td>";
|
|
}
|
|
print "</tr>\n";
|
|
}
|
|
}
|
|
|
|
sub print_table_header {
|
|
print "<table border=1 bgcolor='#FFFFFF' cellspacing=1 cellpadding=1>\n";
|
|
|
|
print "<tr align=center>\n";
|
|
|
|
print "<TH>Build Time</TH>\n";
|
|
print "<TH>Guilty</th>\n";
|
|
|
|
for (my $ii=0; $ii < $name_count; $ii++) {
|
|
|
|
my $bn = $build_names->[$ii];
|
|
$bn =~ s/Clobber/Clbr/g;
|
|
$bn =~ s/Depend/Dep/g;
|
|
$bn = "<font face='Helvetica,Arial' size=-1>$bn</font>";
|
|
|
|
my $last_status = tb_last_status($ii);
|
|
if ($last_status eq 'busted') {
|
|
if ($form{noflames}) {
|
|
print "<td rowspan=2 bgcolor=$colormap{busted}><a title='$titlemap{flames}'>$bn $textmap{flames}</a></td>";
|
|
} else {
|
|
print "<td rowspan=2 bgcolor=000000 background='$images{flames}' style='background-position: bottom; background-repeat: repeat-x;'>";
|
|
print "<font color=white><a title='$titlemap{flames}'>$bn $textmap{flames}</a></font></td>";
|
|
}
|
|
}
|
|
else {
|
|
print "<td rowspan=2 bgcolor=$colormap{$last_status}>$bn</td>";
|
|
}
|
|
}
|
|
print "</tr><tr>\n";
|
|
|
|
print "<td rowspan=1><font size=-1>Click time to <br>see changes <br>",
|
|
"since then</font></td>";
|
|
print "<td><font size=-1>",
|
|
"Click name to see what they did</font></td>";
|
|
|
|
print "</tr>\n";
|
|
}
|
|
|
|
sub print_table_footer {
|
|
print "</table>\n";
|
|
|
|
my $nextdate = $maxdate - $hours*60*60;
|
|
print &open_showbuilds_href(maxdate=>"$nextdate", legend=>'0')
|
|
."Show previous $hours hours</a><br>";
|
|
if ($hours != 24) {
|
|
my $save_hours = $hours;
|
|
$hours = 24;
|
|
print &open_showbuilds_href(maxdate=>"$nextdate", legend=>'0')
|
|
."Show previous 24 hours</a>";
|
|
$hours = $save_hours;
|
|
}
|
|
print "<p><a href='${rel_path}admintree.cgi?tree=$::tree'>",
|
|
"Administrate Tinderbox Trees</a><br>\n";
|
|
}
|
|
|
|
sub open_showbuilds_url {
|
|
my %args = (
|
|
legend => "$form{legend}",
|
|
norules => "$form{norules}",
|
|
@_
|
|
);
|
|
|
|
my $url = "${rel_path}showbuilds.cgi?tree=$::tree";
|
|
$url .= "&hours=$hours" if $hours ne $default_hours;
|
|
while (my ($key, $value) = each %args) {
|
|
$url .= "&$key=$value" if $value ne '';
|
|
}
|
|
return $url;
|
|
}
|
|
|
|
sub open_showbuilds_href {
|
|
return "<a href=".open_showbuilds_url(@_).">";
|
|
}
|
|
|
|
# Same as open_showbuilds_href, but adding parent target
|
|
# so that URL's in iframes take over the parent window.
|
|
sub open_showbuilds_href_target {
|
|
return "<a href=".open_showbuilds_url(@_)." target=\"_parent\">";
|
|
}
|
|
|
|
sub query_ref {
|
|
my ($td, $mindate, $maxdate, $who) = @_;
|
|
my $output = '';
|
|
|
|
$output = "<a href=${rel_path}../bonsai/cvsquery.cgi";
|
|
$output .= "?module=$td->{cvs_module}";
|
|
$output .= "&branch=$td->{cvs_branch}" if $td->{cvs_branch} ne 'HEAD';
|
|
$output .= "&branchtype=regexp"
|
|
if $td->{cvs_branch} =~ /\+|\?|\*/;
|
|
$output .= "&cvsroot=$td->{cvs_root}" if $td->{cvs_root} ne $default_root;
|
|
$output .= "&date=explicit&mindate=$mindate";
|
|
$output .= "&maxdate=$maxdate" if $maxdate and $maxdate ne '';
|
|
$output .= "&who=$who" if $who and $who ne '';
|
|
$output .= ">";
|
|
}
|
|
|
|
sub who_menu {
|
|
my ($td, $mindate, $maxdate, $who) = @_;
|
|
my $treeflag;
|
|
# this variable isn't doing anything, so i'm going to use it shamelessly
|
|
$treeflag = $td->{cvs_branch};
|
|
# trick who.cgi into using regexps, escaping & and =
|
|
$treeflag .= '%26branchtype%3Dregexp' if $treeflag =~ /\+|\?|\*/;
|
|
|
|
my $qr = "${rel_path}../registry/who.cgi?email=". url_encode($who)
|
|
. "&d=$td->{cvs_module}|$treeflag|$td->{cvs_root}|$mindate";
|
|
|
|
$qr = $qr . "|$maxdate" if defined($maxdate);
|
|
|
|
return "<a href='$qr' onclick=\"return who(event);\">";
|
|
}
|
|
|
|
# Check to see if anyone checked in during time slot.
|
|
# ex. has_who_list(1); # Check for checkins in most recent time slot.
|
|
# ex. has_who_list(1,5); # Check range of times.
|
|
sub has_who_list {
|
|
my ($time1, $time2) = @_;
|
|
|
|
if (not defined(@who_check_list)) {
|
|
# Build a static array of true/false values for each time slot.
|
|
$who_check_list[$time_count - 1] = 0;
|
|
for (my $tt = 0; $tt < $time_count; $tt++) {
|
|
$who_check_list[$tt] = 1 if each %{$who_list->[$tt]};
|
|
}
|
|
}
|
|
if ($time2) {
|
|
for (my $ii=$time1; $ii<=$time2; $ii++) {
|
|
return 1 if $who_check_list[$ii]
|
|
}
|
|
return 0
|
|
} else {
|
|
return $who_check_list[$time1];
|
|
}
|
|
}
|
|
|
|
BEGIN {
|
|
# Check bonsai tree for open/close state
|
|
|
|
my $treestate = undef;
|
|
my $checked_state = 0;
|
|
|
|
sub _check_tree_state {
|
|
my $tree = shift;
|
|
|
|
$checked_state = 1;
|
|
tb_load_treedata($tree); # Loading for the global, $bonsai_tree
|
|
return unless defined $bonsai_tree and $bonsai_tree ne '';
|
|
|
|
local $_;
|
|
$::BatchID='';
|
|
eval qq(require "../bonsai/data/$bonsai_tree/batchid.pl");
|
|
if ($::BatchID eq '') {
|
|
warn "No BatchID in ../bonsai/data/$bonsai_tree/batchid.pl\n";
|
|
return;
|
|
}
|
|
open(BATCH, "<../bonsai/data/$bonsai_tree/batch-$::BatchID.pl")
|
|
or print "can't open batch-$::BatchID.pl<br>";
|
|
while (<BATCH>) {
|
|
if (/^\$::TreeOpen = '(\d+)';/) {
|
|
$treestate = $1;
|
|
last;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
sub is_tree_state_available {
|
|
my $tree = shift;
|
|
$tree = $::tree unless defined $tree;
|
|
return 1 if defined $treestate;
|
|
return 0 if $checked_state;
|
|
_check_tree_state($tree);
|
|
return is_tree_state_available();
|
|
}
|
|
|
|
sub is_tree_open {
|
|
my $tree = shift;
|
|
$tree = $::tree unless defined $tree;
|
|
_check_tree_state($tree) unless $checked_state;
|
|
return $treestate;
|
|
}
|
|
}
|
|
|
|
sub print_javascript {
|
|
my $script;
|
|
($script = <<"__ENDJS") =~ s/^ //gm;
|
|
<style type="text/css">
|
|
#popup {
|
|
position: absolute;
|
|
margin: -5em 0 0 -5em;
|
|
}
|
|
.who#popup{
|
|
border: 0px;
|
|
height: 8em;
|
|
width: 16em;
|
|
}
|
|
.note#popup {
|
|
width: 25em;
|
|
}
|
|
.log#popup {
|
|
}
|
|
.note#popup, .log#popup {
|
|
border: 2px solid black;
|
|
background: white;
|
|
color: black;
|
|
padding: 0.5em;
|
|
}
|
|
</style>
|
|
<script>
|
|
var noDHTML = false;
|
|
if (parseInt(navigator.appVersion) < 4) {
|
|
window.event = 0;
|
|
noDHTML = true;
|
|
} else if (navigator.userAgent.indexOf("MSIE") > 0 ) {
|
|
noDHTML = true;
|
|
}
|
|
if (document.body && document.body.addEventListener) {
|
|
document.body.addEventListener("click",maybeclosepopup,false);
|
|
}
|
|
function nodewrite(n,t) {
|
|
var r = document.createRange();
|
|
r.setStart(n,0);
|
|
n.appendChild(r.createContextualFragment(t));
|
|
}
|
|
function closepopup() {
|
|
var p = document.getElementById("popup");
|
|
if (p && p.parentNode) {
|
|
p.parentNode.removeChild(p);
|
|
}
|
|
}
|
|
function maybeclosepopup(e) {
|
|
var n = e.target;
|
|
var close = true;
|
|
while(close && n && (n != document)) {
|
|
close = (n.id != "popup") && !(n.tagName && (n.tagName.toLowerCase() == "a"));
|
|
n = n.parentNode;
|
|
}
|
|
if (close) closepopup();
|
|
}
|
|
function who(d) {
|
|
if (noDHTML) {
|
|
return true;
|
|
}
|
|
if (typeof document.layers != 'undefined') {
|
|
var l = document.layers['popup'];
|
|
l.src = d.target.href;
|
|
l.top = d.target.y - 6;
|
|
l.left = d.target.x - 6;
|
|
if (l.left + l.clipWidth > window.width) {
|
|
l.left = window.width - l.clipWidth;
|
|
}
|
|
l.visibility="show";
|
|
} else {
|
|
var t = d.target;
|
|
while (t.nodeType != 1) {
|
|
t = t.parentNode;
|
|
}
|
|
closepopup()
|
|
l = document.createElement("iframe");
|
|
l.setAttribute("src", t.href);
|
|
l.setAttribute("id", "popup");
|
|
l.className = "who";
|
|
t.appendChild(l);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function convert_timet_to_gmtdate(timet) {
|
|
var timeconv = new Date();
|
|
timeconv.setTime( (timet * 1000) + \
|
|
(timeconv.getTimezoneOffset() * 60 * 1000) );
|
|
return timeconv.toLocaleString();
|
|
}
|
|
|
|
function convert_timet_to_localdate(timet) {
|
|
var timeconv = new Date();
|
|
timeconv.setTime(timet * 1000);
|
|
return timeconv.toLocaleString();
|
|
}
|
|
|
|
function convert_timet_to_localhms(timet) {
|
|
var timeconv = new Date();
|
|
timeconv.setTime(timet * 1000);
|
|
|
|
hours = timeconv.getHours();
|
|
if (hours < 10)
|
|
hours = "0" + hours;
|
|
|
|
mins = timeconv.getMinutes();
|
|
if (mins < 10)
|
|
mins = "0" + mins;
|
|
|
|
secs = timeconv.getSeconds();
|
|
if (secs < 10)
|
|
secs = "0" + secs;
|
|
|
|
return hours + ":" + mins;
|
|
}
|
|
|
|
function log_url(logfile) {
|
|
return "${rel_path}showlog.cgi?log=" + buildtree + "/" + logfile;
|
|
}
|
|
function note(d,noteid,logfile) {
|
|
if (noDHTML) {
|
|
document.location = log_url(logfile);
|
|
return false;
|
|
}
|
|
if (typeof document.layers != 'undefined') {
|
|
var l = document.layers['popup'];
|
|
l.document.write("<table border=1 cellspacing=1><tr><td>"
|
|
+ notes[noteid] + "</tr></table>");
|
|
l.document.close();
|
|
l.top = d.y-10;
|
|
var zz = d.x;
|
|
if (zz + l.clip.right > window.innerWidth) {
|
|
zz = (window.innerWidth-30) - l.clip.right;
|
|
if (zz < 0) { zz = 0; }
|
|
}
|
|
l.left = zz;
|
|
l.visibility="show";
|
|
} else {
|
|
var t = d.target;
|
|
while (t.nodeType != 1) {
|
|
t = t.parentNode;
|
|
}
|
|
closepopup()
|
|
l = document.createElement("div");
|
|
nodewrite(l,notes[noteid]);
|
|
l.setAttribute("id", "popup");
|
|
l.style.position = "absolute";
|
|
l.className = "note";
|
|
t.parentNode.parentNode.appendChild(l);
|
|
}
|
|
return false;
|
|
}
|
|
function log(e,buildindex,logfile,time_info,elapsed)
|
|
{
|
|
var logurl = log_url(logfile);
|
|
var commenturl = "${rel_path}addnote.cgi?log=" + buildtree + "/" + logfile;
|
|
if (noDHTML) {
|
|
document.location = logurl;
|
|
return false;
|
|
}
|
|
|
|
var blurb = "<B>" + builds[buildindex] + "</B><BR>";
|
|
|
|
// If time_info is set, it will contain either the start time of the
|
|
// build or both the start and end time.
|
|
if (time_info) {
|
|
blurb = blurb + time_info + "<BR>"
|
|
}
|
|
|
|
// elapsed tracks the time the build started to either its end time or
|
|
// now.
|
|
if (elapsed) {
|
|
blurb = blurb + elapsed + " elapsed<BR>"
|
|
}
|
|
|
|
blurb = blurb + "<A HREF=" + logurl + ">View Brief Log</A><BR>"
|
|
+ "<A HREF=" + logurl + "&fulltext=1"+">View Full Log</A><BR>"
|
|
+ "<A HREF=" + commenturl + ">Add a Comment</A>";
|
|
|
|
if (typeof document.layers != 'undefined') {
|
|
var q = document.layers["logpopup"];
|
|
q.top = e.target.y - 6;
|
|
|
|
var yy = e.target.x;
|
|
if ( yy + q.clip.right > window.innerWidth) {
|
|
yy = (window.innerWidth-30) - q.clip.right;
|
|
if (yy < 0) { yy = 0; }
|
|
}
|
|
q.left = yy;
|
|
q.visibility="show";
|
|
q.document.write("<TABLE BORDER=1><TR><TD>" + blurb
|
|
+ "</TD></TR></TABLE>");
|
|
q.document.close();
|
|
} else {
|
|
var t = e.target;
|
|
while (t.nodeType != 1) {
|
|
t = t.parentNode;
|
|
}
|
|
closepopup();
|
|
var l = document.createElement("div");
|
|
nodewrite(l,blurb + "<BR>");
|
|
l.setAttribute("id", "popup");
|
|
l.className = "log";
|
|
t.parentNode.appendChild(l);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
var notes = new Array();
|
|
var builds = new Array();
|
|
|
|
__ENDJS
|
|
print $script;
|
|
|
|
$ii = 0;
|
|
while ($ii < @note_array) {
|
|
$ss = $note_array[$ii];
|
|
while ($ii < @note_array && $note_array[$ii] eq $ss) {
|
|
print "notes[$ii] = ";
|
|
$ii++;
|
|
}
|
|
$ss =~ s/\\/\\\\/g;
|
|
$ss =~ s/\"/\\\"/g;
|
|
$ss =~ s/\n/\\n/g;
|
|
print "\"$ss\";\n";
|
|
}
|
|
for ($ii=0; $ii < $name_count; $ii++) {
|
|
if (defined($br = $build_table->[0][$ii])) {
|
|
my $bn = $build_names->[$ii];
|
|
print "builds[$ii]='$bn';\n";
|
|
}
|
|
}
|
|
print "var buildtree = '$::tree';\n";
|
|
|
|
# Use JavaScript to refresh the page every 15 minutes
|
|
print "setTimeout('location.reload()',900000);\n" if $nowdate eq $maxdate;
|
|
|
|
($script = <<'__ENDJS') =~ s/^ //gm;
|
|
</script>
|
|
|
|
<layer name="popup" onMouseOut="this.visibility='hide';"
|
|
left=0 top=0 bgcolor="#ffffff" visibility="hide">
|
|
</layer>
|
|
|
|
<layer name="logpopup" onMouseOut="this.visibility='hide';"
|
|
left=0 top=0 bgcolor="#ffffff" visibility="hide">
|
|
</layer>
|
|
__ENDJS
|
|
print $script;
|
|
}
|
|
|
|
sub do_express {
|
|
print "Content-type: text/html\nRefresh: 900\n\n<HTML>\n";
|
|
|
|
my (%build, %times);
|
|
tb_loadquickparseinfo($::tree, \%build, \%times);
|
|
|
|
my @keys = sort keys %build;
|
|
my $keycount = @keys;
|
|
my $tm = &print_time(time);
|
|
print "<table border=1 cellpadding=1 cellspacing=1><tr>";
|
|
print "<th align=left colspan=$keycount>";
|
|
print &open_showbuilds_href_target."$::tree";
|
|
if (is_tree_state_available()) {
|
|
print (is_tree_open() ? ' is open' : ' is closed');
|
|
}
|
|
print ", $tm</a></tr><tr>\n";
|
|
foreach my $buildname (@keys) {
|
|
print "<td bgcolor='$colormap{$build{$buildname}}'>$buildname</td>";
|
|
}
|
|
print "</tr></table>\n";
|
|
}
|
|
|
|
# This is essentially do_express but it outputs a different format
|
|
sub do_panel {
|
|
print "Content-type: text/html\n\n<HTML>\n" unless $form{static};
|
|
|
|
my (%build, %times);
|
|
tb_loadquickparseinfo($::tree, \%build, \%times);
|
|
|
|
print q(
|
|
<head>
|
|
<META HTTP-EQUIV="Refresh" CONTENT="300">
|
|
<style>
|
|
body, td {
|
|
font-family: Verdana, Sans-Serif;
|
|
font-size: 8pt;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body BGCOLOR="#FFFFFF" TEXT="#000000"
|
|
LINK="#0000EE" VLINK="#551A8B" ALINK="#FF0000">
|
|
);
|
|
# Hack the panel link for now.
|
|
print "<a target='_content' href='http://tinderbox.mozilla.org/$::tree/'>$::tree";
|
|
|
|
if (is_tree_state_available()) {
|
|
print " is ", is_tree_open() ? 'open' : 'closed';
|
|
}
|
|
# Add the current time
|
|
my ($minute,$hour,$mday,$mon) = (localtime)[1..4];
|
|
my $tm = sprintf("%d/%d %d:%02d",$mon+1,$mday,$hour,$minute);
|
|
print ", $tm</a><br>";
|
|
|
|
print "<table border=0 cellpadding=1 cellspacing=1>";
|
|
while (my ($name, $status) = each %build) {
|
|
print "<tr><td bgcolor='$colormap{$status}'>$name</td></tr>";
|
|
}
|
|
print "</table></body>";
|
|
}
|
|
|
|
sub do_flash {
|
|
print "Content-type: text/rdf\n\n" unless $form{static};
|
|
|
|
my (%build, %times);
|
|
tb_loadquickparseinfo($::tree, \%build, \%times);
|
|
|
|
my ($mac,$unix,$win) = (0,0,0);
|
|
|
|
while (my ($name, $status) = each %build) {
|
|
next if $status eq 'success';
|
|
$mac = 1, next if $name =~ /Mac/;
|
|
$win = 1, next if $name =~ /Win/;
|
|
$unix = 1;
|
|
}
|
|
|
|
print q{
|
|
<RDF:RDF xmlns:RDF='http://www.w3.org/1999/02/22-rdf-syntax-ns#'
|
|
xmlns:NC='http://home.netscape.com/NC-rdf#'>
|
|
<RDF:Description about='NC:FlashRoot'>
|
|
};
|
|
|
|
my $busted = $mac + $unix + $win;
|
|
if ($busted) {
|
|
|
|
# Construct a legible sentence; e.g., "Mac, Unix, and Windows
|
|
# are busted", "Windows is busted", etc. This is hideous. If
|
|
# you can think of something better, please fix it.
|
|
|
|
my $text;
|
|
if ($mac) {
|
|
$text .= 'Mac' . ($busted > 2 ? ', ' : ($busted > 1 ? ' and ' : ''));
|
|
}
|
|
if ($unix) {
|
|
$text .= 'Unix' . ($busted > 2 ? ', and ' : ($win ? ' and ' : ''));
|
|
}
|
|
if ($win) {
|
|
$text .= 'Windows';
|
|
}
|
|
$text .= ($busted > 1 ? ' are ' : ' is ') . 'busted';
|
|
|
|
# The Flash spec says we need to give ctime.
|
|
use POSIX;
|
|
my $tm = POSIX::ctime(time());
|
|
$tm =~ s/^...\s//; # Strip day of week
|
|
$tm =~ s/:\d\d\s/ /; # Strip seconds
|
|
chop $tm;
|
|
|
|
print qq{
|
|
<NC:child>
|
|
<RDF:Description ID='flash'>
|
|
<NC:type resource='http://www.mozilla.org/RDF#TinderboxFlash' />
|
|
<NC:source>$::tree</NC:source>
|
|
<NC:description>$text</NC:description>
|
|
<NC:timestamp>$tm</NC:timestamp>
|
|
</RDF:Description>
|
|
</NC:child>
|
|
};
|
|
}
|
|
print q{
|
|
</RDF:Description>
|
|
</RDF:RDF>
|
|
};
|
|
}
|
|
|
|
sub do_quickparse {
|
|
print "Content-type: text/plain\n\n";
|
|
|
|
my @treelist = split /,/, $::tree;
|
|
foreach my $tt (@treelist) {
|
|
if (is_tree_state_available($tt)) {
|
|
my $state = is_tree_open($tt) ? 'open' : 'closed';
|
|
print "State|$tt|$bonsai_tree|$state\n";
|
|
}
|
|
my (%build, %times);
|
|
tb_loadquickparseinfo($tt, \%build, \%times);
|
|
|
|
foreach my $buildname (sort keys %build) {
|
|
print "Build|$tt|$buildname|$build{$buildname}\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
sub do_rdf {
|
|
print "Content-type: text/plain\n\n";
|
|
|
|
my $mainurl = "http://$ENV{SERVER_NAME}$ENV{SCRIPT_NAME}?tree=$::tree";
|
|
my $dirurl = $mainurl;
|
|
|
|
$dirurl =~ s@/[^/]*$@@;
|
|
|
|
my (%build, %times);
|
|
tb_loadquickparseinfo($::tree, \%build, \%times);
|
|
|
|
my $image = "channelok.gif";
|
|
my $imagetitle = "OK";
|
|
foreach my $buildname (sort keys %build) {
|
|
if ($build{$buildname} eq 'busted') {
|
|
$image = "channelflames.gif";
|
|
$imagetitle = "Bad";
|
|
last;
|
|
}
|
|
}
|
|
print qq{<?xml version="1.0"?>
|
|
<rdf:RDF
|
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
xmlns="http://my.netscape.com/rdf/simple/0.9/">
|
|
<channel>
|
|
<title>Tinderbox - $::tree</title>
|
|
<description>Build bustages for $::tree</description>
|
|
<link>$mainurl</link>
|
|
</channel>
|
|
<image>
|
|
<title>$imagetitle</title>
|
|
<url>$dirurl/$image</url>
|
|
<link>$mainurl</link>
|
|
</image>
|
|
};
|
|
|
|
if (is_tree_state_available()) {
|
|
my $state = is_tree_open() ? 'open' : 'closed';
|
|
print "<item><title>The tree is currently $state</title>",
|
|
"<link>$mainurl</link></item>\n";
|
|
}
|
|
|
|
foreach my $buildname (sort keys %build) {
|
|
if ($build{$buildname} eq 'busted') {
|
|
print "<item><title>$buildname is in flames</title>",
|
|
"<link>$mainurl</link></item>\n";
|
|
}
|
|
}
|
|
print "</rdf:RDF>\n";
|
|
}
|
|
|
|
# This is for Sprint phones
|
|
sub do_hdml {
|
|
print "Content-type: text/hdml\n\n" unless $form{static};
|
|
|
|
print q{<hdml public=true version=2.0 ttl=0>
|
|
<display title=Tinderbox>
|
|
<action type=help task=go dest=#help>
|
|
};
|
|
%state_symbols = (success=>'+',busted=>'!',testfailed=>'~');
|
|
|
|
if (is_tree_state_available()) {
|
|
print "<LINE>$::tree is " . (is_tree_open() ? 'open' : 'closed');
|
|
}
|
|
my (%build, %times);
|
|
tb_loadquickparseinfo($::tree, \%build, \%times);
|
|
|
|
foreach my $buildname (sort keys %build) {
|
|
print "<LINE>$state_symbols{$build{$buildname}} $buildname\n";
|
|
}
|
|
|
|
print q{
|
|
</display>
|
|
<DISPLAY NAME=help>
|
|
Legend:<BR>
|
|
+ : Good Build<BR>
|
|
! : Broken Build<BR>
|
|
~ : Tests Failed
|
|
</DISPLAY>
|
|
</HDML>
|
|
};
|
|
|
|
}
|
|
|
|
sub do_vxml {
|
|
print "Content-type: text/vxml\n\n";
|
|
print '<?xml version="1.0"?><!DOCTYPE vxml PUBLIC "-//Tellme Networks//Voice Markup Language 1.0//EN" "http://resources.tellme.com/toolbox/vxml-tellme.dtd">';
|
|
|
|
print '<vxml><form id="tinderbox"><block>';
|
|
print "\n\n";
|
|
print '<audio src="http://www.boulderdesign.com/sounds/wargames.wav">Welcome</audio>';
|
|
print "\n";
|
|
print '<pause>500</pause>';
|
|
print "\n";
|
|
|
|
%state_symbols = (success=>'green.',busted=>'red.',testfailed=>'orange.');
|
|
|
|
if (is_tree_state_available()) {
|
|
print "<audio>$::tree is " . (is_tree_open() ? 'open.' : 'closed.') . "</audio>";
|
|
}
|
|
my (%build, %times);
|
|
tb_loadquickparseinfo($::tree, \%build, \%times);
|
|
|
|
$testFailed = 0;
|
|
$flames = 0;
|
|
|
|
print "\n";
|
|
foreach my $buildname (sort keys %build) {
|
|
if ($build{$buildname} eq 'busted') {
|
|
$flames = 1;
|
|
} elsif ($build{$buildname} eq 'testfailed') {
|
|
$testFailed = 1;
|
|
}
|
|
}
|
|
print '<pause>500</pause>';
|
|
|
|
if ($testFailed == 1 || $flames == 1) {
|
|
if ($testFailed == 1) {
|
|
print '<audio src="http://www.boulderdesign.com/sounds/hansolo_badfeeling.wav">a test failed</audio>';
|
|
} elsif ($flames == 1) {
|
|
print '<audio src="http://www.boulderdesign.com/sounds/brimstone.wav">something is on fire</audio>';
|
|
}
|
|
print "\n";
|
|
|
|
foreach my $buildname (sort keys %build) {
|
|
print "<pause>500</pause>";
|
|
print "\n";
|
|
if ($build{$buildname} eq 'busted' ||
|
|
$build{$buildname} eq 'testfailed') {
|
|
print '<audio src="http://www.boulderdesign.com/sounds/getfixed.wav">bustage</audio>';
|
|
print "\n";
|
|
}
|
|
|
|
print "<pause>500</pause>";
|
|
print "\n";
|
|
print "<audio>$buildname is $state_symbols{$build{$buildname}} </audio>";
|
|
print "\n";
|
|
}
|
|
} else {
|
|
print '<audio>All clear, move along.</audio>';
|
|
print "\n";
|
|
}
|
|
|
|
print '<pause>1000</pause><audio src="http://www.boulderdesign.com/sounds/goodbye.wav">goodbye</audio>';
|
|
print "\n";
|
|
print '<disconnect/></block></form></vxml>';
|
|
}
|
|
|
|
sub do_wml {
|
|
print "Content-type: text/vnd.wap.wml\n";
|
|
print "Pragma: No-Cache\n\n";
|
|
|
|
print '<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">';
|
|
|
|
print '<wml><card id="Status" title="Status">';
|
|
print '<do type="accept" label="Builds"><go href="#Builds"/></do>';
|
|
print '<p align ="center"><b><u>Moz Tinderbox</u></b></p>';
|
|
|
|
%state_symbols = (success=>'green.',busted=>'red.',testfailed=>'orange.');
|
|
|
|
if (is_tree_state_available()) {
|
|
print "<p align='left'>$::tree is " . (is_tree_open() ? 'open.' : 'closed.') . "</p>";
|
|
}
|
|
my (%build, %times);
|
|
tb_loadquickparseinfo($::tree, \%build, \%times);
|
|
|
|
$testFailed = 0;
|
|
$flames = 0;
|
|
|
|
print "\n";
|
|
foreach my $buildname (sort keys %build) {
|
|
if ($build{$buildname} eq 'busted') {
|
|
$flames = 1;
|
|
} elsif ($build{$buildname} eq 'testfailed') {
|
|
$testFailed = 1;
|
|
}
|
|
}
|
|
|
|
print '<p align="left">';
|
|
if ($flames) {
|
|
print "There's bustage.</p>";
|
|
}
|
|
if ($testFailed) {
|
|
print "Tests are failing.</p>";
|
|
}
|
|
|
|
if ($flames == 0 && $testFailed == 0) {
|
|
print "No bustage.</p>";
|
|
}
|
|
print "</card>\n";
|
|
|
|
print '<card id="Builds" title="Builds">';
|
|
print '<p align="center"><b><u>Builds</u></b></p>';
|
|
print '<p mode="nowrap"><table columns="2">';
|
|
|
|
foreach my $buildname (sort keys %build) {
|
|
print "<tr><td>[";
|
|
if ($build{$buildname} eq 'busted') {
|
|
print '<b>RED</b>';
|
|
} elsif ($build{$buildname} eq 'testfailed') {
|
|
print '<b>TEST FAILED</b>';
|
|
} else {
|
|
print 'GREEN';
|
|
}
|
|
print "]</td><td>$buildname</td></tr>";
|
|
print "\n";
|
|
}
|
|
print '</table></p></card></wml>';
|
|
}
|