forked from lix-project/hydra
* Store info about all the build actions and allow them to be
monitored while the build is in progress.
This commit is contained in:
parent
632bb24687
commit
ee13f3cc0d
|
@ -37,7 +37,7 @@ sub index :Path :Args(0) {
|
|||
# Get the latest finished build for each unique job.
|
||||
$c->stash->{latestBuilds} = [$c->model('DB::Builds')->search(undef,
|
||||
{ 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"
|
||||
})];
|
||||
}
|
||||
|
@ -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 {
|
||||
my ($path) = @_;
|
||||
# !!! all a quick hack
|
||||
|
|
|
@ -8,8 +8,8 @@ use base 'DBIx::Class::Schema';
|
|||
__PACKAGE__->load_classes;
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:1AgCf4sf5h2RU24Slo0sTA
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 18:02:00
|
||||
# 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
|
||||
|
|
|
@ -38,8 +38,8 @@ __PACKAGE__->belongs_to(
|
|||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:dKMSSomUN+gJX57Z5e295w
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 18:02:00
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:MtL3cwH9upjNmhaZkGszRA
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
|
|
|
@ -21,8 +21,8 @@ __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-11 13:41:38
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ZOxJeT+ltgyc/zuDl9aEDQ
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 18:02:00
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:vvyGq3BeKyyK7K6uDxJHyQ
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
|
|
|
@ -21,8 +21,8 @@ __PACKAGE__->set_primary_key("build", "path");
|
|||
__PACKAGE__->belongs_to("build", "HydraFrontend::Schema::Builds", { id => "build" });
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:rZPTilX/PAiIoxffxc0nJw
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 18:02:00
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:et00AvSBi5LZUoIrIUOKFQ
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
|
|
|
@ -25,8 +25,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-11 13:41:38
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:2Vfqs9RUhbDrje18yZb3AA
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 18:02:00
|
||||
# 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
|
||||
|
|
|
@ -70,10 +70,15 @@ __PACKAGE__->has_many(
|
|||
"HydraFrontend::Schema::Buildlogs",
|
||||
{ "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
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:1GZeB3YVr064AZrGargmFg
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 18:02:00
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:c8feWTpKijITXXSdJICuFg
|
||||
|
||||
__PACKAGE__->has_many(dependents => 'HydraFrontend::Schema::Buildinputs', 'dependency');
|
||||
|
||||
|
|
|
@ -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-11 13:41:38
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:rN7v2+MnC8TkrEHUzt2Gqg
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 18:02:00
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Z65HteUghCT7sXfXpsHYXg
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
|
|
43
src/HydraFrontend/lib/HydraFrontend/Schema/Buildsteps.pm
Normal file
43
src/HydraFrontend/lib/HydraFrontend/Schema/Buildsteps.pm
Normal 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;
|
|
@ -33,8 +33,8 @@ __PACKAGE__->belongs_to(
|
|||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:bvEulSFMDlAMs39sIyHgZQ
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 18:02:00
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:mng7GAPMDxsznKupYdhwQw
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
|
|
|
@ -43,8 +43,8 @@ __PACKAGE__->has_many(
|
|||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:54xK3D1D0Jm5oKgRelXN7Q
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 18:02:00
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:vEXBbzKUTBQmGmL8uh9mIA
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
|
|
|
@ -48,8 +48,8 @@ __PACKAGE__->has_many(
|
|||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:JHirlq7Jc8dQOy+Op/VflA
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 18:02:00
|
||||
# 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
|
||||
|
|
|
@ -24,8 +24,8 @@ __PACKAGE__->has_many(
|
|||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 13:41:38
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:7Ag5ZfYVgfw3MJZkNUmBYw
|
||||
# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-11-11 18:02:00
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:1DTnCjRw929OuAfeJ5gsXA
|
||||
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
|
|
|
@ -33,10 +33,10 @@
|
|||
<strong>Success</strong>
|
||||
[% ELSIF build.resultInfo.buildstatus == 1 %]
|
||||
<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 %]
|
||||
<img src="/static/images/failure.gif" />
|
||||
<strong>Build failed</strong>
|
||||
<strong class="error-msg">Build failed</strong>
|
||||
[% END %]
|
||||
[% ELSIF build.schedulingInfo.busy %]
|
||||
<strong>Build in progress</strong>
|
||||
|
@ -134,6 +134,39 @@
|
|||
</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 %]
|
||||
|
||||
|
||||
|
|
|
@ -143,7 +143,7 @@ td.buildfarmMainColumn {
|
|||
border: solid;
|
||||
}
|
||||
|
||||
span.error-msg {
|
||||
.error-msg {
|
||||
color: red;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[% 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 -->
|
||||
<pre class="buildlog">
|
||||
|
|
67
src/build.pl
67
src/build.pl
|
@ -32,7 +32,66 @@ sub doBuild {
|
|||
|
||||
$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();
|
||||
|
||||
|
@ -64,7 +123,7 @@ sub doBuild {
|
|||
|
||||
my $logPath = "/nix/var/log/nix/drvs/" . basename $drvPath;
|
||||
if (-e $logPath) {
|
||||
print "found log $logPath\n";
|
||||
print STDERR "found log $logPath\n";
|
||||
$db->resultset('Buildlogs')->create(
|
||||
{ build => $build->id
|
||||
, logphase => "full"
|
||||
|
@ -77,7 +136,7 @@ sub doBuild {
|
|||
|
||||
if (-e "$outPath/log") {
|
||||
foreach my $logPath (glob "$outPath/log/*") {
|
||||
print "found log $logPath\n";
|
||||
print STDERR "found log $logPath\n";
|
||||
$db->resultset('Buildlogs')->create(
|
||||
{ build => $build->id
|
||||
, logphase => basename($logPath)
|
||||
|
@ -119,7 +178,7 @@ sub doBuild {
|
|||
|
||||
|
||||
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
|
||||
# process (runner.pl). This is so that if the runner dies, the
|
||||
|
|
|
@ -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.
|
||||
create table BuildInputs (
|
||||
id integer primary key autoincrement not null,
|
||||
|
|
|
@ -16,7 +16,6 @@ $db->txn_do(sub {
|
|||
my @jobs = $db->resultset('Builds')->search(
|
||||
{finished => 0, busy => 1}, {join => 'schedulingInfo'});
|
||||
foreach my $job (@jobs) {
|
||||
print $job, "\n";
|
||||
my $pid = $job->schedulingInfo->locker;
|
||||
if (kill(0, $pid) != 1) { # see if we can signal the process
|
||||
print "job ", $job->id, " pid $pid died, unlocking\n";
|
||||
|
|
Loading…
Reference in a new issue