* Get rid of "positive failures" and separate log phases.

* Specifically log, for a build, whether the top-level derivation
  failed, or some dependency failed, or Nix itself barfed.  In the
  latter case log the error output from Nix.
This commit is contained in:
Eelco Dolstra 2008-11-25 00:38:16 +00:00
parent 125d7a985e
commit e742833da3
21 changed files with 118 additions and 152 deletions

View file

@ -301,20 +301,18 @@ sub build :Local {
sub log :Local {
my ( $self, $c, $id, $logPhase ) = @_;
my ( $self, $c, $id ) = @_;
my $build = getBuild($c, $id);
return error($c, "Build with ID $id doesn't exist.") if !defined $build;
return error($c, "Build $id doesn't exist.") if !defined $build;
return error($c, "Build $id didn't produce a log.") if !defined $build->resultInfo->logfile;
my $log = $build->buildlogs->find({logphase => $logPhase});
return error($c, "Build $id doesn't have a log phase named $logPhase.") if !defined $log;
$c->stash->{template} = 'log.tt';
$c->stash->{id} = $id;
$c->stash->{log} = $log;
$c->stash->{build} = $build;
# !!! should be done in the view (as a TT plugin).
$c->stash->{logtext} = loadLog($log->path);
$c->stash->{logtext} = loadLog($build->resultInfo->logfile);
}
@ -341,6 +339,8 @@ sub nixlog :Local {
sub loadLog {
my ($path) = @_;
die unless defined $path;
# !!! quick hack
my $pipeline = ($path =~ /.bz2$/ ? "cat $path | bzip2 -d" : "cat $path")
. " | nix-log2xml | xsltproc xsl/mark-errors.xsl - | xsltproc xsl/log2html.xsl - | tail -n +2";

View file

@ -8,8 +8,8 @@ use base 'DBIx::Class::Schema';
__PACKAGE__->load_classes;
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-17 17:09:46
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:uZCNqZeWS46Z2RdysLEDaA
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-24 17:46:46
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:rS2THZrlrDHnIAWmvduE1g
# You can replace this text with custom content, and it will be preserved on regeneration

View file

@ -29,7 +29,7 @@ __PACKAGE__->add_columns(
"path",
{ data_type => "text", is_nullable => 0, size => undef },
"sha256hash",
{ data_type => "VARCHAR", is_nullable => 0, size => undef },
{ data_type => "text", is_nullable => 0, size => undef },
);
__PACKAGE__->set_primary_key("id");
__PACKAGE__->belongs_to("build", "HydraFrontend::Schema::Builds", { id => "build" });
@ -40,8 +40,8 @@ __PACKAGE__->belongs_to(
);
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-17 17:09:46
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:96p2HKZ/6kk0zZKq3JuvDg
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-24 17:46:46
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:9u9ep3Cq/SginPyhrzXlTA
# You can replace this text with custom content, and it will be preserved on regeneration

View file

@ -1,29 +0,0 @@
package HydraFrontend::Schema::Buildlogs;
use strict;
use warnings;
use base 'DBIx::Class';
__PACKAGE__->load_components("Core");
__PACKAGE__->table("BuildLogs");
__PACKAGE__->add_columns(
"build",
{ data_type => "integer", is_nullable => 0, size => undef },
"logphase",
{ data_type => "text", is_nullable => 0, size => undef },
"path",
{ data_type => "text", is_nullable => 0, size => undef },
"type",
{ data_type => "text", is_nullable => 0, size => undef },
);
__PACKAGE__->set_primary_key("build", "logphase");
__PACKAGE__->belongs_to("build", "HydraFrontend::Schema::Builds", { id => "build" });
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-17 17:09:46
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:zWuDHHMl7eWUWU238D5MWg
# You can replace this text with custom content, and it will be preserved on regeneration
1;

View file

@ -33,8 +33,8 @@ __PACKAGE__->set_primary_key("build", "productnr");
__PACKAGE__->belongs_to("build", "HydraFrontend::Schema::Builds", { id => "build" });
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-17 17:09:46
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:kiP5lQxpuaZUZiqHigHYxQ
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-24 17:46:46
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:d85fCxlq/WDfQa20zXYuzw
# You can replace this text with custom content, and it will be preserved on regeneration

View file

@ -20,13 +20,15 @@ __PACKAGE__->add_columns(
{ data_type => "integer", is_nullable => 0, size => undef },
"stoptime",
{ data_type => "integer", is_nullable => 0, size => undef },
"logfile",
{ data_type => "text", is_nullable => 0, size => undef },
);
__PACKAGE__->set_primary_key("id");
__PACKAGE__->belongs_to("id", "HydraFrontend::Schema::Builds", { id => "id" });
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-17 17:09:46
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:MqQ972Qn6sjoWbbbzmE1cg
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-24 17:46:46
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:c2KXbqA8Xan4Lgf7AlK2EA
# You can replace this text with custom content, and it will be preserved on regeneration

View file

@ -72,15 +72,10 @@ __PACKAGE__->has_many(
"HydraFrontend::Schema::Buildproducts",
{ "foreign.build" => "self.id" },
);
__PACKAGE__->has_many(
"buildlogs",
"HydraFrontend::Schema::Buildlogs",
{ "foreign.build" => "self.id" },
);
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-17 17:09:46
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:yYcxVJ1KnjD3KKWt4XQFMg
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-24 17:46:46
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:/Iabv2HeyAsubLe+yPc/6Q
__PACKAGE__->has_many(dependents => 'HydraFrontend::Schema::Buildinputs', 'dependency');

View file

@ -23,8 +23,8 @@ __PACKAGE__->set_primary_key("id");
__PACKAGE__->belongs_to("id", "HydraFrontend::Schema::Builds", { id => "id" });
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-17 17:09:46
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:uAs9z69gMZRAQSzvOGsqEQ
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-24 17:46:46
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:vqJ7HEML5YNn5VIXEhZbnw
# You can replace this text with custom content, and it will be preserved on regeneration

View file

@ -35,8 +35,8 @@ __PACKAGE__->set_primary_key("id", "stepnr");
__PACKAGE__->belongs_to("id", "HydraFrontend::Schema::Builds", { id => "id" });
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-17 17:09:46
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:mM/rt5x2l2wMZn+EnctifQ
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-24 17:46:46
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:BuZp6PHq9l/9xyA/x7TOVQ
# You can replace this text with custom content, and it will be preserved on regeneration

View file

@ -31,8 +31,8 @@ __PACKAGE__->belongs_to(
);
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-17 17:09:46
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:xj0LojYsbdSMWCv+KUH8sw
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-24 17:46:46
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:x7OCv8YzB2L4H+RxEfwjbg
# You can replace this text with custom content, and it will be preserved on regeneration

View file

@ -43,8 +43,8 @@ __PACKAGE__->has_many(
);
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-17 17:09:46
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:VO/SU4tv5UxNAmiwsqn6UA
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-24 17:46:46
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:SKU48+1LqxIcuVY5gaDHCg
# You can replace this text with custom content, and it will be preserved on regeneration

View file

@ -48,8 +48,8 @@ __PACKAGE__->has_many(
);
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-17 17:09:46
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:jZ0kG9EzEJn5mJFSp2WFpw
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-24 17:46:46
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:F3WF5YS/Yas12dK2Gyekpg
# You can replace this text with custom content, and it will be preserved on regeneration

View file

@ -13,7 +13,7 @@ __PACKAGE__->add_columns(
"displayname",
{ data_type => "text", is_nullable => 0, size => undef },
"description",
{ data_type => "VARCHAR", is_nullable => 0, size => undef },
{ data_type => "text", is_nullable => 0, size => undef },
"enabled",
{ data_type => "integer", is_nullable => 0, size => undef },
);
@ -30,8 +30,8 @@ __PACKAGE__->has_many(
);
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-17 17:09:46
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:XkQtF1ABmLxvxND62rBlCw
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-24 17:46:46
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:M+HA5YEL1oKKTQlLvhb6dw
# You can replace this text with custom content, and it will be preserved on regeneration

View file

@ -36,10 +36,14 @@
<strong>Success</strong>
[% ELSIF build.resultInfo.buildstatus == 1 %]
<img src="/static/images/failure.gif" />
<strong class="error-msg">Build returned a non-zero exit code</strong>
<span class="error">Build returned a non-zero exit code</span>
[% ELSIF build.resultInfo.buildstatus == 2 %]
<img src="/static/images/failure.gif" />
<span class="error">A dependency of the build failed</span>
[% ELSE %]
<img src="/static/images/failure.gif" />
<strong class="error-msg">Build failed</strong>
<span class="error">Build failed</span>
(see <a href="#nix-error">below</a>)
[% END %]
[% ELSIF build.schedulingInfo.busy %]
<strong>Build in progress</strong>
@ -99,6 +103,14 @@
[% END %]
</td>
</tr>
[% IF build.resultInfo.logfile %]
<tr>
<th>Logfile:</th>
<td>
<a href="[% c.uri_for('/log' build.id) %]"><strong>Available</strong></a>
</td>
</tr>
[% END %]
[% ELSE %]
<tr>
<th>Priority:</th>
@ -143,7 +155,7 @@
[% IF build.buildsteps %]
<h2>Build steps</h2>
<h2 id="buildsteps">Build steps</h2>
<table class="tablesorter">
<thead>
@ -164,16 +176,24 @@
[% IF step.busy == 0 %]
[% step.stoptime - step.starttime %]s
[% ELSE %]
[% curTime - step.starttime %]s
[% IF build.finished %]
[% build.resultInfo.stoptime - step.starttime %]s
[% ELSE %]
[% curTime - step.starttime %]s
[% END %]
[% END %]
</td>
<td>
[% IF step.busy == 1 %]
<strong>Building</strong>
[% IF build.finished %]
<span class="error">Aborted</span>
[% ELSE %]
<strong>Building</strong>
[% END %]
[% ELSIF step.status == 0 %]
Succeeded
[% ELSE %]
<strong class="error-msg">Failed: [% step.errormsg %]</strong>
<span class="error">Failed: [% HTML.escape(step.errormsg) %]</span>
[% END %]
[% IF step.logfile %]
(<a href="[% c.uri_for('/nixlog' build.id step.stepnr) %]">log</a>)
@ -190,6 +210,17 @@
[% IF build.finished %]
[% IF build.resultInfo.errormsg %]
<h2 id="nix-error">Nix error output</h2>
<pre class="buildlog">
[% HTML.escape(build.resultInfo.errormsg) -%]
</pre>
[% END %]
[% IF build.buildproducts %]
@ -299,26 +330,6 @@
[% END %]
[% IF build.buildlogs %]
<div>
<h2>Logs</h2>
<table>
<tr><th>Phase</th></tr>
[% FOREACH log IN build.buildlogs -%]
<tr>
<td><a href="[% c.uri_for('/log' build.id log.logphase) %]">[% log.logphase %]</a></td>
</tr>
[% END -%]
</table>
</div>
[% END %]
[% IF build.dependents %]
<h2>Used by</h2>
@ -349,9 +360,8 @@
<h2>Log</h2>
<!-- !!! escaping -->
<pre class="buildlog">
[% logtext -%]
[% HTML.escape(logtext) -%]
</pre>

View file

@ -1,6 +1,6 @@
[% WRAPPER layout.tt title="Hydra Overview" %]
<h1>Build log [% IF step %] of step [% step.stepnr %] [% ELSE %]<tt>[% log.logphase %]</tt>[% END %] of build ID [% id %]</h1>
<h1>Logfile for <tt>[% build.project.name %]:[% build.attrname %]</tt> build [% build.id %]</h1>
<div class="buildlog">
[% logtext -%]

View file

@ -153,6 +153,11 @@ td.buildfarmMainColumn {
white-space: pre;
}
.error {
color: red;
font-weight: bold;
}
pre.buildlog {
border: 1px solid black;
padding: 0.3em;

View file

@ -75,7 +75,7 @@ em.storeref:hover span.popup {
font-size: larger;
}
.error {
.errorLine {
color: #ff0000;
font-weight: bold;
}

View file

@ -63,7 +63,7 @@
<xsl:template match="head|line">
<code>
<xsl:if test="@error">
<xsl:attribute name="class">error</xsl:attribute>
<xsl:attribute name="class">errorLine</xsl:attribute>
</xsl:if>
<xsl:apply-templates/>
</code>

View file

@ -27,12 +27,19 @@ sub doBuild {
my $outputCreated = 1; # i.e., the Nix build succeeded (but it could be a positive failure)
my $startTime = 0;
my $stopTime = 0;
my $buildStatus = 0; # = succeeded
my $errormsg = undef;
if (!isValidPath($outPath)) {
$isCachedBuild = 0;
$startTime = time();
my $thisBuildFailed = 0;
my $someBuildFailed = 0;
# Run Nix to perform the build, and monitor the stderr output
# to get notifications about specific build steps, the
# associated log files, etc.
@ -44,6 +51,8 @@ sub doBuild {
open OUT, "$cmd |" or die;
while (<OUT>) {
$errormsg .= $_;
unless (/^@\s+/) {
print STDERR "$_";
next;
@ -78,10 +87,12 @@ sub doBuild {
}
elsif (/^@\s+build-failed\s+(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/) {
my $drvPath = $1;
my $drvPathStep = $1;
$someBuildFailed = 1;
$thisBuildFailed = 1 if $drvPath eq $drvPathStep;
$db->txn_do(sub {
(my $step) = $db->resultset('Buildsteps')->search(
{id => $build->id, type => 0, drvpath => $drvPath}, {});
{id => $build->id, type => 0, drvpath => $drvPathStep}, {});
if ($step) {
die unless $step;
$step->busy(0);
@ -94,7 +105,7 @@ sub doBuild {
{ id => $build->id
, stepnr => $buildStepNr++
, type => 0 # = build
, drvpath => $drvPath
, drvpath => $drvPathStep
, outpath => $2
, logfile => $4
, busy => 0
@ -159,17 +170,14 @@ sub doBuild {
$stopTime = time();
$outputCreated = $res == 0;
}
if ($res != 0) {
if ($thisBuildFailed) { $buildStatus = 1; }
elsif ($someBuildFailed) { $buildStatus = 2; }
else { $buildStatus = 3; }
}
my $buildStatus;
if ($outputCreated) {
# "Positive" failures, e.g. the builder returned exit code 0
# but flagged some error condition.
$buildStatus = -e "$outPath/nix-support/failed" ? 2 : 0;
} else {
$buildStatus = 1; # = Nix failure
# Only store the output of running Nix if we have a miscellaneous error.
$errormsg = undef unless $buildStatus == 3;
}
$db->txn_do(sub {
@ -177,41 +185,23 @@ sub doBuild {
$build->timestamp(time());
$build->update;
my $logPath = "/nix/var/log/nix/drvs/" . basename $drvPath;
$logPath = undef unless -e $logPath;
$db->resultset('Buildresultinfo')->create(
{ id => $build->id
, iscachedbuild => $isCachedBuild
, buildstatus => $buildStatus
, starttime => $startTime
, stoptime => $stopTime
, logfile => $logPath
, errormsg => $errormsg
});
my $logPath = "/nix/var/log/nix/drvs/" . basename $drvPath;
if (-e $logPath) {
print STDERR "found log $logPath\n";
$db->resultset('Buildlogs')->create(
{ build => $build->id
, logphase => "full"
, path => $logPath
, type => "raw"
});
}
if ($outputCreated) {
if ($buildStatus == 0) {
my $productnr = 1;
if (-e "$outPath/log") {
foreach my $logPath (glob "$outPath/log/*") {
print STDERR "found log $logPath\n";
$db->resultset('Buildlogs')->create(
{ build => $build->id
, logphase => basename($logPath)
, path => $logPath
, type => "raw"
});
}
}
if (-e "$outPath/nix-support/hydra-build-products") {
open LIST, "$outPath/nix-support/hydra-build-products" or die;
while (<LIST>) {
@ -251,7 +241,7 @@ sub doBuild {
close LIST;
}
elsif ($buildStatus == 0) {
else {
$db->resultset('Buildproducts')->create(
{ build => $build->id
, productnr => $productnr++

View file

@ -2,8 +2,7 @@
-- scheduled builds, additional info (such as the priority) can be
-- found in the BuildSchedulingInfo table. For finished builds,
-- additional info (such as the logs, build products, etc.) can be
-- found in several tables, such as BuildResultInfo, BuildLogs and
-- BuildProducts.
-- found in several tables, such as BuildResultInfo and BuildProducts.
create table Builds (
id integer primary key autoincrement not null,
@ -48,14 +47,21 @@ create table BuildResultInfo (
id integer primary key not null,
isCachedBuild integer not null, -- boolean
buildStatus integer, -- 0 = succeeded, 1 = Nix build failure, 2 = positive build failure
-- Status codes:
-- 0 = succeeded
-- 1 = build of this derivation failed
-- 2 = build of some dependency failed
-- 3 = other failure (see errorMsg)
buildStatus integer,
errorMsg text, -- error message in case of a Nix failure
startTime integer, -- in Unix time, 0 = used cached build result
stopTime integer,
logfile text, -- the path of the logfile
foreign key (id) references Builds(id) on delete cascade -- ignored by sqlite
);
@ -126,16 +132,6 @@ create table BuildProducts (
);
create table BuildLogs (
build integer not null,
logPhase text not null,
path text not null,
type text not null,
primary key (build, logPhase),
foreign key (build) references Builds(id) on delete cascade -- ignored by sqlite
);
-- Emulate "on delete cascade" foreign key constraints.
create trigger cascadeBuildDeletion
before delete on Builds
@ -143,7 +139,6 @@ create trigger cascadeBuildDeletion
delete from BuildSchedulingInfo where id = old.id;
delete from BuildResultInfo where id = old.id;
delete from BuildInputs where build = old.id;
delete from BuildLogs where build = old.id;
delete from BuildProducts where build = old.id;
end;

View file

@ -4,8 +4,6 @@ insert into jobSetInputs(project, jobset, name, type) values('patchelf', 'trunk'
insert into jobSetInputAlts(project, jobset, input, altnr, value) values('patchelf', 'trunk', 'patchelfSrc', 0, '/home/eelco/Dev/patchelf-wc');
insert into jobSetInputs(project, jobset, name, type) values('patchelf', 'trunk', 'nixpkgs', 'path');
insert into jobSetInputAlts(project, jobset, input, altnr, value) values('patchelf', 'trunk', 'nixpkgs', 0, '/home/eelco/Dev/nixpkgs-wc');
insert into jobSetInputs(project, jobset, name, type) values('patchelf', 'trunk', 'release', 'path');
insert into jobSetInputAlts(project, jobset, input, altnr, value) values('patchelf', 'trunk', 'release', 0, '/home/eelco/Dev/release');
insert into jobSetInputs(project, jobset, name, type) values('patchelf', 'trunk', 'system', 'string');
insert into jobSetInputAlts(project, jobset, input, altnr, value) values('patchelf', 'trunk', 'system', 0, 'i686-linux');
insert into jobSetInputAlts(project, jobset, input, altnr, value) values('patchelf', 'trunk', 'system', 1, 'x86_64-linux');