diff --git a/src/HydraFrontend/lib/HydraFrontend/Controller/Root.pm b/src/HydraFrontend/lib/HydraFrontend/Controller/Root.pm index f5100ea5..b68aa9ff 100644 --- a/src/HydraFrontend/lib/HydraFrontend/Controller/Root.pm +++ b/src/HydraFrontend/lib/HydraFrontend/Controller/Root.pm @@ -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 diff --git a/src/HydraFrontend/lib/HydraFrontend/Schema.pm b/src/HydraFrontend/lib/HydraFrontend/Schema.pm index 052fc9cf..1a381468 100644 --- a/src/HydraFrontend/lib/HydraFrontend/Schema.pm +++ b/src/HydraFrontend/lib/HydraFrontend/Schema.pm @@ -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 diff --git a/src/HydraFrontend/lib/HydraFrontend/Schema/Buildinputs.pm b/src/HydraFrontend/lib/HydraFrontend/Schema/Buildinputs.pm index eda11d4d..6b96dd61 100644 --- a/src/HydraFrontend/lib/HydraFrontend/Schema/Buildinputs.pm +++ b/src/HydraFrontend/lib/HydraFrontend/Schema/Buildinputs.pm @@ -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 diff --git a/src/HydraFrontend/lib/HydraFrontend/Schema/Buildlogs.pm b/src/HydraFrontend/lib/HydraFrontend/Schema/Buildlogs.pm index 2f21bb8f..2d67b001 100644 --- a/src/HydraFrontend/lib/HydraFrontend/Schema/Buildlogs.pm +++ b/src/HydraFrontend/lib/HydraFrontend/Schema/Buildlogs.pm @@ -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 diff --git a/src/HydraFrontend/lib/HydraFrontend/Schema/Buildproducts.pm b/src/HydraFrontend/lib/HydraFrontend/Schema/Buildproducts.pm index 22ef546a..ff1f4cf3 100644 --- a/src/HydraFrontend/lib/HydraFrontend/Schema/Buildproducts.pm +++ b/src/HydraFrontend/lib/HydraFrontend/Schema/Buildproducts.pm @@ -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 diff --git a/src/HydraFrontend/lib/HydraFrontend/Schema/Buildresultinfo.pm b/src/HydraFrontend/lib/HydraFrontend/Schema/Buildresultinfo.pm index 1125256d..d14fd269 100644 --- a/src/HydraFrontend/lib/HydraFrontend/Schema/Buildresultinfo.pm +++ b/src/HydraFrontend/lib/HydraFrontend/Schema/Buildresultinfo.pm @@ -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 diff --git a/src/HydraFrontend/lib/HydraFrontend/Schema/Builds.pm b/src/HydraFrontend/lib/HydraFrontend/Schema/Builds.pm index 92d358d6..5f393542 100644 --- a/src/HydraFrontend/lib/HydraFrontend/Schema/Builds.pm +++ b/src/HydraFrontend/lib/HydraFrontend/Schema/Builds.pm @@ -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'); diff --git a/src/HydraFrontend/lib/HydraFrontend/Schema/Buildschedulinginfo.pm b/src/HydraFrontend/lib/HydraFrontend/Schema/Buildschedulinginfo.pm index 851a75b0..3f521f67 100644 --- a/src/HydraFrontend/lib/HydraFrontend/Schema/Buildschedulinginfo.pm +++ b/src/HydraFrontend/lib/HydraFrontend/Schema/Buildschedulinginfo.pm @@ -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 diff --git a/src/HydraFrontend/lib/HydraFrontend/Schema/Buildsteps.pm b/src/HydraFrontend/lib/HydraFrontend/Schema/Buildsteps.pm new file mode 100644 index 00000000..21064ad8 --- /dev/null +++ b/src/HydraFrontend/lib/HydraFrontend/Schema/Buildsteps.pm @@ -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; diff --git a/src/HydraFrontend/lib/HydraFrontend/Schema/Jobsetinputalts.pm b/src/HydraFrontend/lib/HydraFrontend/Schema/Jobsetinputalts.pm index f7603979..35013dce 100644 --- a/src/HydraFrontend/lib/HydraFrontend/Schema/Jobsetinputalts.pm +++ b/src/HydraFrontend/lib/HydraFrontend/Schema/Jobsetinputalts.pm @@ -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 diff --git a/src/HydraFrontend/lib/HydraFrontend/Schema/Jobsetinputs.pm b/src/HydraFrontend/lib/HydraFrontend/Schema/Jobsetinputs.pm index 61bfcfd3..71f22994 100644 --- a/src/HydraFrontend/lib/HydraFrontend/Schema/Jobsetinputs.pm +++ b/src/HydraFrontend/lib/HydraFrontend/Schema/Jobsetinputs.pm @@ -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 diff --git a/src/HydraFrontend/lib/HydraFrontend/Schema/Jobsets.pm b/src/HydraFrontend/lib/HydraFrontend/Schema/Jobsets.pm index 06f0d939..ca2c3c32 100644 --- a/src/HydraFrontend/lib/HydraFrontend/Schema/Jobsets.pm +++ b/src/HydraFrontend/lib/HydraFrontend/Schema/Jobsets.pm @@ -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 diff --git a/src/HydraFrontend/lib/HydraFrontend/Schema/Projects.pm b/src/HydraFrontend/lib/HydraFrontend/Schema/Projects.pm index 564297e5..bc10b085 100644 --- a/src/HydraFrontend/lib/HydraFrontend/Schema/Projects.pm +++ b/src/HydraFrontend/lib/HydraFrontend/Schema/Projects.pm @@ -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 diff --git a/src/HydraFrontend/root/build.tt b/src/HydraFrontend/root/build.tt index b824a0c9..09838701 100644 --- a/src/HydraFrontend/root/build.tt +++ b/src/HydraFrontend/root/build.tt @@ -33,10 +33,10 @@ Success [% ELSIF build.resultInfo.buildstatus == 1 %] - Build returned a non-zero exit code + Build returned a non-zero exit code [% ELSE %] - Build failed + Build failed [% END %] [% ELSIF build.schedulingInfo.busy %] Build in progress @@ -134,6 +134,39 @@ +[% IF build.buildsteps %] + +

Build steps

+ + + + + + + [% FOREACH step IN build.buildsteps -%] + + + + + + [% END %] + +
NrWhatStatus
[% step.stepnr %] + Build of [% step.outpath %] + + [% IF step.busy == 1 %] + Building + [% ELSIF step.status == 0 %] + Succeeded + [% ELSE %] + Failed: [% step.errormsg %] + [% END %] + (log) +
+ +[% END %] + + [% IF build.finished %] diff --git a/src/HydraFrontend/root/hydra.css b/src/HydraFrontend/root/hydra.css index bf29f776..d4c3e065 100644 --- a/src/HydraFrontend/root/hydra.css +++ b/src/HydraFrontend/root/hydra.css @@ -143,7 +143,7 @@ td.buildfarmMainColumn { border: solid; } -span.error-msg { +.error-msg { color: red; } diff --git a/src/HydraFrontend/root/log.tt b/src/HydraFrontend/root/log.tt index 5e788730..72702aa8 100644 --- a/src/HydraFrontend/root/log.tt +++ b/src/HydraFrontend/root/log.tt @@ -1,6 +1,6 @@ [% WRAPPER layout.tt title="Hydra Overview" %] -

Build log [% log.logphase %] of build ID [% id %]

+

Build log [% IF step %] of step [% step.stepnr %] [% ELSE %][% log.logphase %][% END %] of build ID [% id %]

diff --git a/src/build.pl b/src/build.pl
index 1a4dfae8..08e71d17 100644
--- a/src/build.pl
+++ b/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 () {
+            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
diff --git a/src/hydra.sql b/src/hydra.sql
index bd3b6d7c..d000b7ff 100644
--- a/src/hydra.sql
+++ b/src/hydra.sql
@@ -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,
diff --git a/src/runner.pl b/src/runner.pl
index 394ed1d0..438de2c9 100644
--- a/src/runner.pl
+++ b/src/runner.pl
@@ -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";