* Store info about all the build actions and allow them to be

monitored while the build is in progress.
This commit is contained in:
Eelco Dolstra 2008-11-11 17:49:50 +00:00
parent 632bb24687
commit ee13f3cc0d
19 changed files with 214 additions and 32 deletions

View file

@ -37,7 +37,7 @@ sub index :Path :Args(0) {
# Get the latest finished build for each unique job. # Get the latest finished build for each unique job.
$c->stash->{latestBuilds} = [$c->model('DB::Builds')->search(undef, $c->stash->{latestBuilds} = [$c->model('DB::Builds')->search(undef,
{ join => 'resultInfo' { join => 'resultInfo'
, where => "finished != 0 and timestamp = (select max(timestamp) from Builds where project == me.project and attrName == me.attrName)" , where => "finished != 0 and timestamp = (select max(timestamp) from Builds where project == me.project and attrName == me.attrName and finished != 0)"
, order_by => "project, attrname" , order_by => "project, attrname"
})]; })];
} }
@ -106,6 +106,24 @@ sub log :Local {
} }
sub nixlog :Local {
my ( $self, $c, $id, $stepnr ) = @_;
my $build = getBuild($c, $id);
return error($c, "Build with ID $id doesn't exist.") if !defined $build;
my $step = $build->buildsteps->find({stepnr => $stepnr});
return error($c, "Build $id doesn't have a build step $stepnr.") if !defined $step;
$c->stash->{template} = 'log.tt';
$c->stash->{id} = $id;
$c->stash->{step} = $step;
# !!! should be done in the view (as a TT plugin).
$c->stash->{logtext} = loadLog($step->logfile);
}
sub loadLog { sub loadLog {
my ($path) = @_; my ($path) = @_;
# !!! all a quick hack # !!! all a quick hack

View file

@ -8,8 +8,8 @@ use base 'DBIx::Class::Schema';
__PACKAGE__->load_classes; __PACKAGE__->load_classes;
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38 # Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 18:02:00
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:1AgCf4sf5h2RU24Slo0sTA # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:gS2Lp7T6IZ160iYQbEhd+g
# You can replace this text with custom content, and it will be preserved on regeneration # You can replace this text with custom content, and it will be preserved on regeneration

View file

@ -38,8 +38,8 @@ __PACKAGE__->belongs_to(
); );
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38 # Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 18:02:00
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:dKMSSomUN+gJX57Z5e295w # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:MtL3cwH9upjNmhaZkGszRA
# You can replace this text with custom content, and it will be preserved on regeneration # You can replace this text with custom content, and it will be preserved on regeneration

View file

@ -21,8 +21,8 @@ __PACKAGE__->set_primary_key("build", "logphase");
__PACKAGE__->belongs_to("build", "HydraFrontend::Schema::Builds", { id => "build" }); __PACKAGE__->belongs_to("build", "HydraFrontend::Schema::Builds", { id => "build" });
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38 # Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 18:02:00
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ZOxJeT+ltgyc/zuDl9aEDQ # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:vvyGq3BeKyyK7K6uDxJHyQ
# You can replace this text with custom content, and it will be preserved on regeneration # You can replace this text with custom content, and it will be preserved on regeneration

View file

@ -21,8 +21,8 @@ __PACKAGE__->set_primary_key("build", "path");
__PACKAGE__->belongs_to("build", "HydraFrontend::Schema::Builds", { id => "build" }); __PACKAGE__->belongs_to("build", "HydraFrontend::Schema::Builds", { id => "build" });
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38 # Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 18:02:00
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:rZPTilX/PAiIoxffxc0nJw # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:et00AvSBi5LZUoIrIUOKFQ
# You can replace this text with custom content, and it will be preserved on regeneration # You can replace this text with custom content, and it will be preserved on regeneration

View file

@ -25,8 +25,8 @@ __PACKAGE__->set_primary_key("id");
__PACKAGE__->belongs_to("id", "HydraFrontend::Schema::Builds", { id => "id" }); __PACKAGE__->belongs_to("id", "HydraFrontend::Schema::Builds", { id => "id" });
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38 # Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 18:02:00
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:2Vfqs9RUhbDrje18yZb3AA # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:8zXrs7iT2h3xp6C/2q37uQ
# You can replace this text with custom content, and it will be preserved on regeneration # You can replace this text with custom content, and it will be preserved on regeneration

View file

@ -70,10 +70,15 @@ __PACKAGE__->has_many(
"HydraFrontend::Schema::Buildlogs", "HydraFrontend::Schema::Buildlogs",
{ "foreign.build" => "self.id" }, { "foreign.build" => "self.id" },
); );
__PACKAGE__->has_many(
"buildsteps",
"HydraFrontend::Schema::Buildsteps",
{ "foreign.id" => "self.id" },
);
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38 # Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 18:02:00
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:1GZeB3YVr064AZrGargmFg # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:c8feWTpKijITXXSdJICuFg
__PACKAGE__->has_many(dependents => 'HydraFrontend::Schema::Buildinputs', 'dependency'); __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" }); __PACKAGE__->belongs_to("id", "HydraFrontend::Schema::Builds", { id => "id" });
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38 # Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 18:02:00
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:rN7v2+MnC8TkrEHUzt2Gqg # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Z65HteUghCT7sXfXpsHYXg
# You can replace this text with custom content, and it will be preserved on regeneration # You can replace this text with custom content, and it will be preserved on regeneration

View file

@ -0,0 +1,43 @@
package HydraFrontend::Schema::Buildsteps;
use strict;
use warnings;
use base 'DBIx::Class';
__PACKAGE__->load_components("Core");
__PACKAGE__->table("BuildSteps");
__PACKAGE__->add_columns(
"id",
{ data_type => "integer", is_nullable => 0, size => undef },
"stepnr",
{ data_type => "integer", is_nullable => 0, size => undef },
"type",
{ data_type => "integer", is_nullable => 0, size => undef },
"drvpath",
{ data_type => "text", is_nullable => 0, size => undef },
"outpath",
{ data_type => "text", is_nullable => 0, size => undef },
"logfile",
{ data_type => "text", is_nullable => 0, size => undef },
"busy",
{ data_type => "integer", is_nullable => 0, size => undef },
"status",
{ data_type => "integer", is_nullable => 0, size => undef },
"errormsg",
{ data_type => "text", is_nullable => 0, size => undef },
"starttime",
{ data_type => "integer", is_nullable => 0, size => undef },
"stoptime",
{ data_type => "integer", is_nullable => 0, size => undef },
);
__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-11 18:02:00
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:GmvM5Rhj4MY7eNQpqTz7bw
# You can replace this text with custom content, and it will be preserved on regeneration
1;

View file

@ -33,8 +33,8 @@ __PACKAGE__->belongs_to(
); );
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38 # Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 18:02:00
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:bvEulSFMDlAMs39sIyHgZQ # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:mng7GAPMDxsznKupYdhwQw
# You can replace this text with custom content, and it will be preserved on regeneration # 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-11 13:41:38 # Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 18:02:00
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:54xK3D1D0Jm5oKgRelXN7Q # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:vEXBbzKUTBQmGmL8uh9mIA
# You can replace this text with custom content, and it will be preserved on regeneration # 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-11 13:41:38 # Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 18:02:00
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:JHirlq7Jc8dQOy+Op/VflA # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:hMYI8zT3UB/k9IbddK1X4g
# You can replace this text with custom content, and it will be preserved on regeneration # You can replace this text with custom content, and it will be preserved on regeneration

View file

@ -24,8 +24,8 @@ __PACKAGE__->has_many(
); );
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38 # Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 18:02:00
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:7Ag5ZfYVgfw3MJZkNUmBYw # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:1DTnCjRw929OuAfeJ5gsXA
# You can replace this text with custom content, and it will be preserved on regeneration # You can replace this text with custom content, and it will be preserved on regeneration

View file

@ -33,10 +33,10 @@
<strong>Success</strong> <strong>Success</strong>
[% ELSIF build.resultInfo.buildstatus == 1 %] [% ELSIF build.resultInfo.buildstatus == 1 %]
<img src="/static/images/failure.gif" /> <img src="/static/images/failure.gif" />
<strong>Build returned a non-zero exit code</strong> <strong class="error-msg">Build returned a non-zero exit code</strong>
[% ELSE %] [% ELSE %]
<img src="/static/images/failure.gif" /> <img src="/static/images/failure.gif" />
<strong>Build failed</strong> <strong class="error-msg">Build failed</strong>
[% END %] [% END %]
[% ELSIF build.schedulingInfo.busy %] [% ELSIF build.schedulingInfo.busy %]
<strong>Build in progress</strong> <strong>Build in progress</strong>
@ -134,6 +134,39 @@
</table> </table>
[% IF build.buildsteps %]
<h2>Build steps</h2>
<table class="tablesorter">
<thead>
<tr><th>Nr</th><th>What</th><th>Status</th></tr>
</thead>
<tbody>
[% FOREACH step IN build.buildsteps -%]
<tr>
<td>[% step.stepnr %]</td>
<td>
Build of <tt>[% step.outpath %]</tt>
</td>
<td>
[% IF step.busy == 1 %]
<strong>Building</strong>
[% ELSIF step.status == 0 %]
Succeeded
[% ELSE %]
<strong class="error-msg">Failed: [% step.errormsg %]</strong>
[% END %]
(<a href="[% c.uri_for('/nixlog' build.id step.stepnr) %]">log</a>)
</td>
</tr>
[% END %]
</tbody>
</table>
[% END %]
[% IF build.finished %] [% IF build.finished %]

View file

@ -143,7 +143,7 @@ td.buildfarmMainColumn {
border: solid; border: solid;
} }
span.error-msg { .error-msg {
color: red; color: red;
} }

View file

@ -1,6 +1,6 @@
[% WRAPPER layout.tt title="Hydra Overview" %] [% WRAPPER layout.tt title="Hydra Overview" %]
<h1>Build log <tt>[% log.logphase %] of build ID [% id %]</h1> <h1>Build log [% IF step %] of step [% step.stepnr %] [% ELSE %]<tt>[% log.logphase %]</tt>[% END %] of build ID [% id %]</h1>
<!-- !!! escaping --> <!-- !!! escaping -->
<pre class="buildlog"> <pre class="buildlog">

View file

@ -32,7 +32,66 @@ sub doBuild {
$startTime = time(); $startTime = time();
my $res = system("nix-store --realise $drvPath"); # Run Nix to perform the build, and monitor the stderr output
# to get notifications about specific build steps, the
# associated log files, etc.
my $cmd = "nix-store --keep-going --no-build-output " .
"--log-type flat --print-build-trace --realise $drvPath 2>&1";
my $buildStepNr = 1;
open OUT, "$cmd |" or die;
while (<OUT>) {
unless (/^@\s+/) {
print STDERR "$_";
next;
}
print STDERR "GOT $_";
if (/^@\s+build-started\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$/) {
$db->txn_do(sub {
$db->resultset('Buildsteps')->create(
{ id => $build->id
, stepnr => $buildStepNr++
, type => 0 # = build
, drvpath => $1
, outpath => $2
, logfile => $4
, busy => 1
});
});
}
if (/^@\s+build-succeeded\s+(\S+)\s+(\S+)$/) {
$db->txn_do(sub {
my $drvPath = $1;
(my $step) = $db->resultset('Buildsteps')->search(
{id => $build->id, type => 0, drvpath => $drvPath}, {});
die unless $step;
$step->busy(0);
$step->status(0);
$step->update;
});
}
if (/^@\s+build-failed\s+(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/) {
$db->txn_do(sub {
my $drvPath = $1;
(my $step) = $db->resultset('Buildsteps')->search(
{id => $build->id, type => 0, drvpath => $drvPath}, {});
die unless $step;
$step->busy(0);
$step->status(1);
$step->errormsg($4);
$step->update;
});
}
}
close OUT;
my $res = $?;
$stopTime = time(); $stopTime = time();
@ -64,7 +123,7 @@ sub doBuild {
my $logPath = "/nix/var/log/nix/drvs/" . basename $drvPath; my $logPath = "/nix/var/log/nix/drvs/" . basename $drvPath;
if (-e $logPath) { if (-e $logPath) {
print "found log $logPath\n"; print STDERR "found log $logPath\n";
$db->resultset('Buildlogs')->create( $db->resultset('Buildlogs')->create(
{ build => $build->id { build => $build->id
, logphase => "full" , logphase => "full"
@ -77,7 +136,7 @@ sub doBuild {
if (-e "$outPath/log") { if (-e "$outPath/log") {
foreach my $logPath (glob "$outPath/log/*") { foreach my $logPath (glob "$outPath/log/*") {
print "found log $logPath\n"; print STDERR "found log $logPath\n";
$db->resultset('Buildlogs')->create( $db->resultset('Buildlogs')->create(
{ build => $build->id { build => $build->id
, logphase => basename($logPath) , logphase => basename($logPath)
@ -119,7 +178,7 @@ sub doBuild {
my $buildId = $ARGV[0] or die; my $buildId = $ARGV[0] or die;
print "performing build $buildId\n"; print STDERR "performing build $buildId\n";
# Lock the build. If necessary, steal the lock from the parent # Lock the build. If necessary, steal the lock from the parent
# process (runner.pl). This is so that if the runner dies, the # process (runner.pl). This is so that if the runner dies, the

View file

@ -59,6 +59,31 @@ create table BuildResultInfo (
); );
create table BuildSteps (
id integer not null,
stepnr integer not null,
type integer not null, -- 0 = build, 1 = substitution
drvPath text,
outPath text,
logfile text,
busy integer not null,
status integer,
errorMsg text,
startTime integer, -- in Unix time, 0 = used cached build result
stopTime integer,
primary key (id, stepnr),
foreign key (id) references Builds(id) on delete cascade -- ignored by sqlite
);
-- Inputs of builds. -- Inputs of builds.
create table BuildInputs ( create table BuildInputs (
id integer primary key autoincrement not null, id integer primary key autoincrement not null,

View file

@ -16,7 +16,6 @@ $db->txn_do(sub {
my @jobs = $db->resultset('Builds')->search( my @jobs = $db->resultset('Builds')->search(
{finished => 0, busy => 1}, {join => 'schedulingInfo'}); {finished => 0, busy => 1}, {join => 'schedulingInfo'});
foreach my $job (@jobs) { foreach my $job (@jobs) {
print $job, "\n";
my $pid = $job->schedulingInfo->locker; my $pid = $job->schedulingInfo->locker;
if (kill(0, $pid) != 1) { # see if we can signal the process if (kill(0, $pid) != 1) { # see if we can signal the process
print "job ", $job->id, " pid $pid died, unlocking\n"; print "job ", $job->id, " pid $pid died, unlocking\n";