Add multiple output support

This requires turning the outPath columns in the Builds and BuildSteps
tables into separate tables, and so requires a schema upgrade.
This commit is contained in:
Eelco Dolstra 2013-02-13 16:49:28 +00:00
parent 799e5437bd
commit 10882a1ffd
23 changed files with 465 additions and 344 deletions

View file

@ -123,11 +123,12 @@ static void findJobsWrapped(EvalState & state, XMLWriter & doc,
XMLAttrs xmlAttrs; XMLAttrs xmlAttrs;
Path drvPath; Path drvPath;
DrvInfo::Outputs outputs = drv.queryOutputs(state);
xmlAttrs["jobName"] = attrPath; xmlAttrs["jobName"] = attrPath;
xmlAttrs["nixName"] = drv.name; xmlAttrs["nixName"] = drv.name;
xmlAttrs["system"] = drv.system; xmlAttrs["system"] = drv.system;
xmlAttrs["drvPath"] = drvPath = drv.queryDrvPath(state); xmlAttrs["drvPath"] = drvPath = drv.queryDrvPath(state);
xmlAttrs["outPath"] = drv.queryOutPath(state);
MetaInfo meta = drv.queryMetaInfo(state); MetaInfo meta = drv.queryMetaInfo(state);
xmlAttrs["description"] = queryMetaFieldString(meta, "description"); xmlAttrs["description"] = queryMetaFieldString(meta, "description");
xmlAttrs["longDescription"] = queryMetaFieldString(meta, "longDescription"); xmlAttrs["longDescription"] = queryMetaFieldString(meta, "longDescription");
@ -164,6 +165,14 @@ static void findJobsWrapped(EvalState & state, XMLWriter & doc,
} }
XMLOpenElement _(doc, "job", xmlAttrs); XMLOpenElement _(doc, "job", xmlAttrs);
foreach (DrvInfo::Outputs::iterator, j, outputs) {
XMLAttrs attrs2;
attrs2["name"] = j->first;
attrs2["path"] = j->second;
doc.writeEmptyElement("output", attrs2);
}
showArgsUsed(doc, argsUsed); showArgsUsed(doc, argsUsed);
} }

View file

@ -78,7 +78,9 @@ sub nix : Chained('get_builds') PathPart('channel') CaptureArgs(1) {
$c->stash->{channelName} = $c->stash->{channelBaseName} . "-latest"; $c->stash->{channelName} = $c->stash->{channelBaseName} . "-latest";
$c->stash->{channelBuilds} = $c->stash->{latestSucceeded} $c->stash->{channelBuilds} = $c->stash->{latestSucceeded}
->search_literal("exists (select 1 from buildproducts where build = me.id and type = 'nix-build')") ->search_literal("exists (select 1 from buildproducts where build = me.id and type = 'nix-build')")
->search({}, { columns => [@buildListColumns, 'drvpath', 'outpath', 'description', 'homepage'] }); ->search({}, { join => ["buildoutputs"]
, columns => [@buildListColumns, 'drvpath', 'description', 'homepage']
, '+select' => ['buildoutputs.path', 'buildoutputs.name'], '+as' => ['outpath', 'outname'] });
} }
else { else {
notFound($c, "Unknown channel `$channelName'."); notFound($c, "Unknown channel `$channelName'.");

View file

@ -3,6 +3,7 @@ package Hydra::Base::Controller::NixChannel;
use strict; use strict;
use warnings; use warnings;
use base 'Catalyst::Controller'; use base 'Catalyst::Controller';
use List::MoreUtils qw(all);
use Nix::Store; use Nix::Store;
use Hydra::Helper::Nix; use Hydra::Helper::Nix;
use Hydra::Helper::CatalystUtils; use Hydra::Helper::CatalystUtils;
@ -12,26 +13,23 @@ sub getChannelData {
my ($c, $checkValidity) = @_; my ($c, $checkValidity) = @_;
my @storePaths = (); my @storePaths = ();
$c->stash->{nixPkgs} = [];
foreach my $build ($c->stash->{channelBuilds}->all) { foreach my $build ($c->stash->{channelBuilds}->all) {
next if $checkValidity && !isValidPath($build->outpath); my $outPath = $build->get_column("outpath");
#if (isValidPath($build->drvpath)) { my $outName = $build->get_column("outname");
# # Adding `drvpath' implies adding `outpath' because of the next if $checkValidity && !isValidPath($outPath);
# # `--include-outputs' flag passed to `nix-store'. push @storePaths, $outPath;
# push @storePaths, $build->drvpath; my $pkgName = $build->nixname . "-" . $build->system . "-" . $build->id . ($outName ne "out" ? "-" . $outName : "");
#} else { push @{$c->stash->{nixPkgs}}, { build => $build, name => $pkgName, outPath => $outPath, outName => $outName };
# push @storePaths, $build->outpath;
#}
push @storePaths, $build->outpath;
my $pkgName = $build->nixname . "-" . $build->system . "-" . $build->id;
$c->stash->{nixPkgs}->{"${pkgName}.nixpkg"} = {build => $build, name => $pkgName};
# Put the system type in the manifest (for top-level paths) as # Put the system type in the manifest (for top-level paths) as
# a hint to the binary patch generator. (It shouldn't try to # a hint to the binary patch generator. (It shouldn't try to
# generate patches between builds for different systems.) It # generate patches between builds for different systems.) It
# would be nice if Nix stored this info for every path but it # would be nice if Nix stored this info for every path but it
# doesn't. # doesn't.
$c->stash->{systemForPath}->{$build->outpath} = $build->system; $c->stash->{systemForPath}->{$outPath} = $build->system;
}; };
print STDERR @storePaths, "\n";
$c->stash->{storePaths} = [@storePaths]; $c->stash->{storePaths} = [@storePaths];
} }
@ -42,6 +40,8 @@ sub closure : Chained('nix') PathPart {
getChannelData($c, 1); getChannelData($c, 1);
# FIXME: get the closure of the selected path only.
# !!! quick hack; this is to make HEAD requests return the right # !!! quick hack; this is to make HEAD requests return the right
# MIME type. This is set in the view as well, but the view isn't # MIME type. This is set in the view as well, but the view isn't
# called for HEAD requests. There should be a cleaner solution... # called for HEAD requests. There should be a cleaner solution...
@ -62,11 +62,14 @@ sub pkg : Chained('nix') PathPart Args(1) {
if (!$c->stash->{build}) { if (!$c->stash->{build}) {
$pkgName =~ /-(\d+)\.nixpkg$/ or notFound($c, "Bad package name."); $pkgName =~ /-(\d+)\.nixpkg$/ or notFound($c, "Bad package name.");
# FIXME: need to handle multiple outputs: channelBuilds is
# joined with the build outputs, so find() can return multiple
# results.
$c->stash->{build} = $c->stash->{channelBuilds}->find({ id => $1 }) $c->stash->{build} = $c->stash->{channelBuilds}->find({ id => $1 })
|| notFound($c, "No such package in this channel."); || notFound($c, "No such package in this channel.");
} }
if (!isValidPath($c->stash->{build}->outpath)) { unless (all { isValidPath($_->path) } $c->stash->{build}->buildoutputs->all) {
$c->response->status(410); # "Gone" $c->response->status(410); # "Gone"
error($c, "Build " . $c->stash->{build}->id . " is no longer available."); error($c, "Build " . $c->stash->{build}->id . " is no longer available.");
} }
@ -115,7 +118,7 @@ sub channel_contents : Chained('nix') PathPart('') Args(0) {
# channel. # channel.
getChannelData($c, 0); getChannelData($c, 0);
$c->stash->{template} = 'channel-contents.tt'; $c->stash->{template} = 'channel-contents.tt';
$c->stash->{nixPkgs} = [sortPkgs (values %{$c->stash->{nixPkgs}})]; $c->stash->{nixPkgs} = [sortPkgs @{$c->stash->{nixPkgs}}];
} }

View file

@ -10,6 +10,7 @@ use File::stat;
use File::Slurp; use File::Slurp;
use Data::Dump qw(dump); use Data::Dump qw(dump);
use Nix::Store; use Nix::Store;
use List::MoreUtils qw(all);
sub build : Chained('/') PathPart CaptureArgs(1) { sub build : Chained('/') PathPart CaptureArgs(1) {
@ -32,28 +33,44 @@ sub build : Chained('/') PathPart CaptureArgs(1) {
} }
sub findBuildStepByOutPath {
my ($self, $c, $path, $status) = @_;
return $c->model('DB::BuildSteps')->search(
{ path => $path, busy => 0, status => $status },
{ join => ["buildstepoutputs"], order_by => ["stopTime"], limit => 1 })->single;
}
sub findBuildStepByDrvPath {
my ($self, $c, $drvPath, $status) = @_;
return $c->model('DB::BuildSteps')->search(
{ drvpath => $drvPath, busy => 0, status => $status },
{ order_by => ["stopTime"], limit => 1 })->single;
}
sub view_build : Chained('build') PathPart('') Args(0) { sub view_build : Chained('build') PathPart('') Args(0) {
my ($self, $c) = @_; my ($self, $c) = @_;
my $build = $c->stash->{build}; my $build = $c->stash->{build};
$c->stash->{template} = 'build.tt'; $c->stash->{template} = 'build.tt';
$c->stash->{available} = isValidPath $build->outpath; $c->stash->{available} = all { isValidPath($_->path) } $build->buildoutputs->all;
$c->stash->{drvAvailable} = isValidPath $build->drvpath; $c->stash->{drvAvailable} = isValidPath $build->drvpath;
$c->stash->{flashMsg} = $c->flash->{buildMsg}; $c->stash->{flashMsg} = $c->flash->{buildMsg};
$c->stash->{pathHash} = $c->stash->{available} ? queryPathHash($build->outpath) : undef;
if (!$build->finished && $build->busy) { if (!$build->finished && $build->busy) {
$c->stash->{logtext} = read_file($build->logfile, err_mode => 'quiet') // ""; $c->stash->{logtext} = read_file($build->logfile, err_mode => 'quiet') // "";
} }
if ($build->finished && $build->iscachedbuild) { if ($build->finished && $build->iscachedbuild) {
(my $cachedBuildStep) = $c->model('DB::BuildSteps')->search({ outpath => $build->outpath }, {}); my $path = ($build->buildoutputs)[0]->path or die;
my $cachedBuildStep = findBuildStepByOutPath($self, $c, $path,
$build->buildstatus == 0 || $build->buildstatus == 6 ? 0 : 1);
$c->stash->{cachedBuild} = $cachedBuildStep->build if defined $cachedBuildStep; $c->stash->{cachedBuild} = $cachedBuildStep->build if defined $cachedBuildStep;
} }
if ($build->finished) { if ($build->finished && 0) {
$c->stash->{prevBuilds} = [$c->model('DB::Builds')->search( $c->stash->{prevBuilds} = [$c->model('DB::Builds')->search(
{ project => $c->stash->{project}->name { project => $c->stash->{project}->name
, jobset => $c->stash->{build}->jobset->name , jobset => $c->stash->{build}->jobset->name
@ -279,35 +296,31 @@ sub deps : Chained('build') PathPart('deps') {
my ($self, $c) = @_; my ($self, $c) = @_;
my $build = $c->stash->{build}; my $build = $c->stash->{build};
$c->stash->{available} = isValidPath $build->outpath; my $drvPath = $build->drvpath;
$c->stash->{drvAvailable} = isValidPath $build->drvpath; my @outPaths = map { $_->path } $build->buildoutputs->all;
my $drvpath = $build->drvpath; $c->stash->{available} = all { isValidPath($_) } @outPaths;
my $outpath = $build->outpath; $c->stash->{drvAvailable} = isValidPath $drvPath;
my @buildtimepaths = (); my @buildtimepaths = $c->stash->{drvAvailable} ? computeFSClosure(0, 0, $drvPath) : ();
my @buildtimedeps = (); my @buildtimedeps = ();
@buildtimepaths = split '\n', `nix-store --query --requisites --include-outputs $drvpath` if isValidPath($build->drvpath);
my @runtimepaths = (); my @runtimepaths = $c->stash->{available} ? computeFSClosure(0, 0, @outPaths) : ();
my @runtimedeps = (); my @runtimedeps = ();
@runtimepaths = split '\n', `nix-store --query --requisites --include-outputs $outpath` if isValidPath($build->outpath);
foreach my $p (@buildtimepaths) { foreach my $p (@buildtimepaths) {
my $buildStep; next unless $p =~ /\.drv$/;
($buildStep) = $c->model('DB::BuildSteps')->search({ outpath => $p }, {}) ; my ($buildStep) = findBuildStepByDrvPath($self, $c, $p, 0);
my %dep = ( buildstep => $buildStep, path => $p ) ; my %dep = ( buildstep => $buildStep, path => $p );
push(@buildtimedeps, \%dep); push(@buildtimedeps, \%dep);
} }
foreach my $p (@runtimepaths) { foreach my $p (@runtimepaths) {
my $buildStep; my ($buildStep) = findBuildStepByOutPath($self, $c, $p, 0);
($buildStep) = $c->model('DB::BuildSteps')->search({ outpath => $p }, {}) ; my %dep = ( buildstep => $buildStep, path => $p );
my %dep = ( buildstep => $buildStep, path => $p ) ;
push(@runtimedeps, \%dep); push(@runtimedeps, \%dep);
} }
$c->stash->{buildtimedeps} = \@buildtimedeps; $c->stash->{buildtimedeps} = \@buildtimedeps;
$c->stash->{runtimedeps} = \@runtimedeps; $c->stash->{runtimedeps} = \@runtimedeps;
@ -321,12 +334,17 @@ sub nix : Chained('build') PathPart('nix') CaptureArgs(0) {
my $build = $c->stash->{build}; my $build = $c->stash->{build};
notFound($c, "Build cannot be downloaded as a closure or Nix package.") notFound($c, "Build cannot be downloaded as a closure or Nix package.")
if !$build->buildproducts->find({type => "nix-build"}); if $build->buildproducts->search({type => "nix-build"})->count == 0;
notFound($c, "Path " . $build->outpath . " is no longer available.") foreach my $out ($build->buildoutputs) {
unless isValidPath($build->outpath); notFound($c, "Path " . $out->path . " is no longer available.")
unless isValidPath($out->path);
}
$c->stash->{channelBuilds} = $c->model('DB::Builds')->search({id => $build->id}); $c->stash->{channelBuilds} = $c->model('DB::Builds')->search(
{ id => $build->id },
{ join => ["buildoutputs"]
, '+select' => ['buildoutputs.path', 'buildoutputs.name'], '+as' => ['outpath', 'outname'] });
} }
@ -377,22 +395,23 @@ sub cancel : Chained('build') PathPart Args(0) {
sub keep : Chained('build') PathPart Args(1) { sub keep : Chained('build') PathPart Args(1) {
my ($self, $c, $newStatus) = @_; my ($self, $c, $x) = @_;
my $keep = $x eq "1" ? 1 : 0;
my $build = $c->stash->{build}; my $build = $c->stash->{build};
requireProjectOwner($c, $build->project); requireProjectOwner($c, $build->project);
die unless $newStatus == 0 || $newStatus == 1; if ($keep) {
registerRoot $_->path foreach $build->buildoutputs;
registerRoot $build->outpath if $newStatus == 1; }
txn_do($c->model('DB')->schema, sub { txn_do($c->model('DB')->schema, sub {
$build->update({keep => int $newStatus}); $build->update({keep => $keep});
}); });
$c->flash->{buildMsg} = $c->flash->{buildMsg} =
$newStatus == 0 ? "Build will not be kept." : "Build will be kept."; $keep ? "Build will be kept." : "Build will not be kept.";
$c->res->redirect($c->uri_for($self->action_for("view_build"), $c->req->captures)); $c->res->redirect($c->uri_for($self->action_for("view_build"), $c->req->captures));
} }
@ -414,9 +433,10 @@ sub add_to_release : Chained('build') PathPart('add-to-release') Args(0) {
error($c, "This build is already a part of release `$releaseName'.") error($c, "This build is already a part of release `$releaseName'.")
if $release->releasemembers->find({build => $build->id}); if $release->releasemembers->find({build => $build->id});
registerRoot $build->outpath; foreach my $output ($build->buildoutputs) {
error($c, "This build is no longer available.") unless isValidPath $output->path;
error($c, "This build is no longer available.") unless isValidPath $build->outpath; registerRoot $output->path;
}
$release->releasemembers->create({build => $build->id, description => $build->description}); $release->releasemembers->create({build => $build->id, description => $build->description});
@ -509,7 +529,8 @@ sub get_info : Chained('build') PathPart('api/get-info') Args(0) {
# !!! strip the json prefix # !!! strip the json prefix
$c->stash->{jsonBuildId} = $build->id; $c->stash->{jsonBuildId} = $build->id;
$c->stash->{jsonDrvPath} = $build->drvpath; $c->stash->{jsonDrvPath} = $build->drvpath;
$c->stash->{jsonOutPath} = $build->outpath; my $out = $build->buildoutputs->find({name => "out"});
$c->stash->{jsonOutPath} = $out->path if defined $out;
$c->forward('View::JSON'); $c->forward('View::JSON');
} }

View file

@ -141,7 +141,10 @@ sub nix : Chained('eval') PathPart('channel') CaptureArgs(0) {
$c->stash->{channelName} = $c->stash->{project}->name . "-" . $c->stash->{jobset}->name . "-latest"; $c->stash->{channelName} = $c->stash->{project}->name . "-" . $c->stash->{jobset}->name . "-latest";
$c->stash->{channelBuilds} = $c->stash->{eval}->builds $c->stash->{channelBuilds} = $c->stash->{eval}->builds
->search_literal("exists (select 1 from buildproducts where build = build.id and type = 'nix-build')") ->search_literal("exists (select 1 from buildproducts where build = build.id and type = 'nix-build')")
->search({ finished => 1, buildstatus => 0 }, { columns => [@buildListColumns, 'drvpath', 'outpath', 'description', 'homepage'] }); ->search({ finished => 1, buildstatus => 0 },
{ columns => [@buildListColumns, 'drvpath', 'description', 'homepage']
, join => ["buildoutputs"]
, '+select' => ['buildoutputs.path', 'buildoutputs.name'], '+as' => ['outpath', 'outname'] });
} }

View file

@ -13,6 +13,7 @@ use File::Basename;
use File::stat; use File::stat;
use File::Path; use File::Path;
use File::Temp; use File::Temp;
use File::Slurp;
our @ISA = qw(Exporter); our @ISA = qw(Exporter);
our @EXPORT = qw( our @EXPORT = qw(
@ -43,14 +44,9 @@ sub getStorePathHash {
sub getReleaseName { sub getReleaseName {
my ($outPath) = @_; my ($outPath) = @_;
return undef unless -f "$outPath/nix-support/hydra-release-name";
my $releaseName; my $releaseName = read_file("$outPath/nix-support/hydra-release-name");
if (-e "$outPath/nix-support/hydra-release-name") { chomp $releaseName;
open FILE, "$outPath/nix-support/hydra-release-name" or die;
$releaseName = <FILE>;
chomp $releaseName;
close FILE;
}
return $releaseName; return $releaseName;
} }
@ -242,7 +238,7 @@ sub fetchInputBuild {
{ order_by => "me.id DESC", rows => 1 { order_by => "me.id DESC", rows => 1
, where => \ attrsToSQL($attrs, "me.id") }); , where => \ attrsToSQL($attrs, "me.id") });
if (!defined $prevBuild || !isValidPath($prevBuild->outpath)) { if (!defined $prevBuild || !isValidPath(getMainOutput($prevBuild)->path)) {
print STDERR "input `", $name, "': no previous build available\n"; print STDERR "input `", $name, "': no previous build available\n";
return undef; return undef;
} }
@ -256,7 +252,7 @@ sub fetchInputBuild {
my $version = $2 if $relName =~ /^($pkgNameRE)-($versionRE)$/; my $version = $2 if $relName =~ /^($pkgNameRE)-($versionRE)$/;
return return
{ storePath => $prevBuild->outpath { storePath => getMainOutput($prevBuild)->path
, id => $prevBuild->id , id => $prevBuild->id
, version => $version , version => $version
}; };
@ -275,7 +271,7 @@ sub fetchInputSystemBuild {
my @validBuilds = (); my @validBuilds = ();
foreach my $build (@latestBuilds) { foreach my $build (@latestBuilds) {
push(@validBuilds, $build) if isValidPath($build->outpath); push(@validBuilds, $build) if !isValidPath(getMainOutput($build)->path);
} }
if (scalar(@validBuilds) == 0) { if (scalar(@validBuilds) == 0) {
@ -293,7 +289,7 @@ sub fetchInputSystemBuild {
my $version = $2 if $relName =~ /^($pkgNameRE)-($versionRE)$/; my $version = $2 if $relName =~ /^($pkgNameRE)-($versionRE)$/;
my $input = my $input =
{ storePath => $prevBuild->outpath { storePath => getMainOutput($prevBuild)->path
, id => $prevBuild->id , id => $prevBuild->id
, version => $version , version => $version
, system => $prevBuild->system , system => $prevBuild->system
@ -666,6 +662,7 @@ sub buildInputToString {
return $result; return $result;
} }
sub inputsToArgs { sub inputsToArgs {
my ($inputInfo, $exprType) = @_; my ($inputInfo, $exprType) = @_;
my @res = (); my @res = ();
@ -742,8 +739,8 @@ sub evalJobs {
my $jobs = XMLin( my $jobs = XMLin(
$jobsXml, $jobsXml,
ForceArray => ['error', 'job', 'arg'], ForceArray => ['error', 'job', 'arg', 'output'],
KeyAttr => [], KeyAttr => { output => "+name" },
SuppressEmpty => '') SuppressEmpty => '')
or die "cannot parse XML output"; or die "cannot parse XML output";
@ -751,7 +748,7 @@ sub evalJobs {
foreach my $job (@{$jobs->{job}}) { foreach my $job (@{$jobs->{job}}) {
my $validJob = 1; my $validJob = 1;
foreach my $arg (@{$job->{arg}}) { foreach my $arg (@{$job->{arg}}) {
my $input = $inputInfo->{$arg->{name}}->[$arg->{altnr}] ; my $input = $inputInfo->{$arg->{name}}->[$arg->{altnr}];
if ($input->{type} eq "sysbuild" && $input->{system} ne $job->{system}) { if ($input->{type} eq "sysbuild" && $input->{system} ne $job->{system}) {
$validJob = 0; $validJob = 0;
} }
@ -769,60 +766,68 @@ sub evalJobs {
sub addBuildProducts { sub addBuildProducts {
my ($db, $build) = @_; my ($db, $build) = @_;
my $outPath = $build->outpath;
my $productnr = 1; my $productnr = 1;
my $explicitProducts = 0;
if (-e "$outPath/nix-support/hydra-build-products") { foreach my $output ($build->buildoutputs->all) {
open LIST, "$outPath/nix-support/hydra-build-products" or die; my $outPath = $output->path;
while (<LIST>) { if (-e "$outPath/nix-support/hydra-build-products") {
/^([\w\-]+)\s+([\w\-]+)\s+(\S+)(\s+(\S+))?$/ or next; $explicitProducts = 1;
my $type = $1;
my $subtype = $2 eq "none" ? "" : $2;
my $path = $3;
my $defaultPath = $5;
next unless -e $path;
my $fileSize, my $sha1, my $sha256; open LIST, "$outPath/nix-support/hydra-build-products" or die;
while (<LIST>) {
/^([\w\-]+)\s+([\w\-]+)\s+(\S+)(\s+(\S+))?$/ or next;
my $type = $1;
my $subtype = $2 eq "none" ? "" : $2;
my $path = $3;
my $defaultPath = $5;
next unless -e $path;
# !!! validate $path, $defaultPath my $fileSize, my $sha1, my $sha256;
if (-f $path) { # !!! validate $path, $defaultPath
my $st = stat($path) or die "cannot stat $path: $!";
$fileSize = $st->size;
$sha1 = `nix-hash --flat --type sha1 $path` if (-f $path) {
or die "cannot hash $path: $?";; my $st = stat($path) or die "cannot stat $path: $!";
chomp $sha1; $fileSize = $st->size;
$sha256 = `nix-hash --flat --type sha256 $path` $sha1 = `nix-hash --flat --type sha1 $path`
or die "cannot hash $path: $?";; or die "cannot hash $path: $?";;
chomp $sha256; chomp $sha1;
$sha256 = `nix-hash --flat --type sha256 $path`
or die "cannot hash $path: $?";;
chomp $sha256;
}
my $name = $path eq $outPath ? "" : basename $path;
$db->resultset('BuildProducts')->create(
{ build => $build->id
, productnr => $productnr++
, type => $type
, subtype => $subtype
, path => $path
, filesize => $fileSize
, sha1hash => $sha1
, sha256hash => $sha256
, name => $name
, defaultpath => $defaultPath
});
} }
close LIST;
my $name = $path eq $outPath ? "" : basename $path;
$db->resultset('BuildProducts')->create(
{ build => $build->id
, productnr => $productnr++
, type => $type
, subtype => $subtype
, path => $path
, filesize => $fileSize
, sha1hash => $sha1
, sha256hash => $sha256
, name => $name
, defaultpath => $defaultPath
});
} }
close LIST;
} }
else { return if $explicitProducts;
foreach my $output ($build->buildoutputs->all) {
my $outPath = $output->path;
$db->resultset('BuildProducts')->create( $db->resultset('BuildProducts')->create(
{ build => $build->id { build => $build->id
, productnr => $productnr++ , productnr => $productnr++
, type => "nix-build" , type => "nix-build"
, subtype => "" , subtype => $output->name eq "out" ? "" : $output->name
, path => $outPath , path => $outPath
, name => $build->nixname , name => $build->nixname
}); });
@ -846,9 +851,17 @@ sub getPrevJobsetEval {
sub checkBuild { sub checkBuild {
my ($db, $project, $jobset, $inputInfo, $nixExprInput, $buildInfo, $buildIds, $prevEval, $jobOutPathMap) = @_; my ($db, $project, $jobset, $inputInfo, $nixExprInput, $buildInfo, $buildIds, $prevEval, $jobOutPathMap) = @_;
my $jobName = $buildInfo->{jobName}; my @outputNames = sort keys %{$buildInfo->{output}};
my $drvPath = $buildInfo->{drvPath}; die unless scalar @outputNames;
my $outPath = $buildInfo->{outPath};
# In various checks we can use an arbitrary output (the first)
# rather than all outputs, since if one output is the same, the
# others will be as well.
my $firstOutputName = $outputNames[0];
my $firstOutputPath = $buildInfo->{output}->{$firstOutputName}->{path};
my $jobName = $buildInfo->{jobName} or die;
my $drvPath = $buildInfo->{drvPath} or die;
my $priority = 100; my $priority = 100;
$priority = int($buildInfo->{schedulingPriority}) $priority = int($buildInfo->{schedulingPriority})
@ -873,18 +886,21 @@ sub checkBuild {
# will be performed (though the last one will probably use the # will be performed (though the last one will probably use the
# cached result from the first). This ensures that the builds # cached result from the first). This ensures that the builds
# with the highest ID will always be the ones that we want in # with the highest ID will always be the ones that we want in
# the channels. # the channels. FIXME: Checking the output paths doesn't take
# !!! Checking $outPath doesn't take meta-attributes into # meta-attributes into account. For instance, do we want a
# account. For instance, do we want a new build to be # new build to be scheduled if the meta.maintainers field is
# scheduled if the meta.maintainers field is changed? # changed?
if (defined $prevEval) { if (defined $prevEval) {
# Only check one output: if it's the same, the other will be as well.
my $firstOutput = $outputNames[0];
my ($prevBuild) = $prevEval->builds->search( my ($prevBuild) = $prevEval->builds->search(
# The "project" and "jobset" constraints are # The "project" and "jobset" constraints are
# semantically unnecessary (because they're implied by # semantically unnecessary (because they're implied by
# the eval), but they give a factor 1000 speedup on # the eval), but they give a factor 1000 speedup on
# the Nixpkgs jobset with PostgreSQL. # the Nixpkgs jobset with PostgreSQL.
{ project => $project->name, jobset => $jobset->name, job => $job->name, outPath => $outPath }, { project => $project->name, jobset => $jobset->name, job => $job->name,
{ rows => 1, columns => ['id'] }); name => $firstOutputName, path => $firstOutputPath },
{ rows => 1, columns => ['id'], join => ['buildoutputs'] });
if (defined $prevBuild) { if (defined $prevBuild) {
print STDERR " already scheduled/built as build ", $prevBuild->id, "\n"; print STDERR " already scheduled/built as build ", $prevBuild->id, "\n";
$buildIds->{$prevBuild->id} = 0; $buildIds->{$prevBuild->id} = 0;
@ -894,7 +910,7 @@ sub checkBuild {
# Prevent multiple builds with the same (job, outPath) from # Prevent multiple builds with the same (job, outPath) from
# being added. # being added.
my $prev = $$jobOutPathMap{$job->name . "\t" . $outPath}; my $prev = $$jobOutPathMap{$job->name . "\t" . $firstOutputPath};
if (defined $prev) { if (defined $prev) {
print STDERR " already scheduled as build ", $prev, "\n"; print STDERR " already scheduled as build ", $prev, "\n";
return; return;
@ -902,22 +918,41 @@ sub checkBuild {
my $time = time(); my $time = time();
# Nope, so add it. # Are the outputs already in the Nix store? Then add a cached
# build.
my %extraFlags; my %extraFlags;
if (isValidPath($outPath)) { my $allValid = 1;
my $buildStatus;
my $releaseName;
foreach my $name (@outputNames) {
my $path = $buildInfo->{output}->{$name}->{path};
if (isValidPath($path)) {
if (-f "$path/nix-support/failed") {
$buildStatus = 6;
} else {
$buildStatus //= 0;
}
$releaseName //= getReleaseName($path);
} else {
$allValid = 0;
last;
}
}
if ($allValid) {
%extraFlags = %extraFlags =
( finished => 1 ( finished => 1
, iscachedbuild => 1 , iscachedbuild => 1
, buildstatus => -f "$outPath/nix-support/failed" ? 6 : 0 , buildstatus => $buildStatus
, starttime => $time , starttime => $time
, stoptime => $time , stoptime => $time
, errormsg => "" , releasename => $releaseName
, releasename => getReleaseName($outPath)
); );
} else { } else {
%extraFlags = ( finished => 0 ); %extraFlags = ( finished => 0 );
} }
# Add the build to the database.
$build = $job->builds->create( $build = $job->builds->create(
{ timestamp => $time { timestamp => $time
, description => $buildInfo->{description} , description => $buildInfo->{description}
@ -929,7 +964,6 @@ sub checkBuild {
, timeout => $buildInfo->{timeout} , timeout => $buildInfo->{timeout}
, nixname => $buildInfo->{nixName} , nixname => $buildInfo->{nixName}
, drvpath => $drvPath , drvpath => $drvPath
, outpath => $outPath
, system => $buildInfo->{system} , system => $buildInfo->{system}
, nixexprinput => $jobset->nixexprinput , nixexprinput => $jobset->nixexprinput
, nixexprpath => $jobset->nixexprpath , nixexprpath => $jobset->nixexprpath
@ -939,8 +973,11 @@ sub checkBuild {
, %extraFlags , %extraFlags
}); });
$build->buildoutputs->create({ name => $_, path => $buildInfo->{output}->{$_}->{path} })
foreach @outputNames;
$buildIds->{$build->id} = 1; $buildIds->{$build->id} = 1;
$$jobOutPathMap{$job->name . "\t" . $outPath} = $build->id; $$jobOutPathMap{$job->name . "\t" . $firstOutputPath} = $build->id;
if ($build->iscachedbuild) { if ($build->iscachedbuild) {
print STDERR " marked as cached build ", $build->id, "\n"; print STDERR " marked as cached build ", $build->id, "\n";
@ -988,15 +1025,11 @@ sub restartBuild {
my ($db, $build) = @_; my ($db, $build) = @_;
txn_do($db, sub { txn_do($db, sub {
my $drvpath = $build->drvpath; my @paths;
my $outpath = $build->outpath; push @paths, $build->drvpath;
push @paths, $_->drvpath foreach $build->buildsteps;
my $paths = ""; my $r = `nix-store --clear-failed-paths @paths`;
foreach my $bs ($build->buildsteps) {
$paths = $paths . " " . $bs->outpath;
}
my $r = `nix-store --clear-failed-paths $paths $outpath`;
$build->update( $build->update(
{ finished => 0 { finished => 0

View file

@ -14,7 +14,8 @@ our @EXPORT = qw(
getPrimaryBuildsForView getPrimaryBuildsForView
getPrimaryBuildTotal getPrimaryBuildTotal
getViewResult getLatestSuccessfulViewResult getViewResult getLatestSuccessfulViewResult
jobsetOverview removeAsciiEscapes getDrvLogPath logContents); jobsetOverview removeAsciiEscapes getDrvLogPath logContents
getMainOutput);
sub getHydraHome { sub getHydraHome {
@ -278,4 +279,12 @@ sub removeAsciiEscapes {
} }
sub getMainOutput {
my ($build) = @_;
return
$build->buildoutputs->find({name => "out"}) //
$build->buildoutputs->find({}, {limit => 1, order_by => ["name"]});
}
1; 1;

View file

@ -44,11 +44,6 @@ __PACKAGE__->table("BuildSteps");
data_type: 'text' data_type: 'text'
is_nullable: 1 is_nullable: 1
=head2 outpath
data_type: 'text'
is_nullable: 1
=head2 busy =head2 busy
data_type: 'integer' data_type: 'integer'
@ -96,8 +91,6 @@ __PACKAGE__->add_columns(
{ data_type => "integer", is_nullable => 0 }, { data_type => "integer", is_nullable => 0 },
"drvpath", "drvpath",
{ data_type => "text", is_nullable => 1 }, { data_type => "text", is_nullable => 1 },
"outpath",
{ data_type => "text", is_nullable => 1 },
"busy", "busy",
{ data_type => "integer", is_nullable => 0 }, { data_type => "integer", is_nullable => 0 },
"status", "status",
@ -145,8 +138,23 @@ __PACKAGE__->belongs_to(
{ is_deferrable => 0, on_delete => "CASCADE", on_update => "NO ACTION" }, { is_deferrable => 0, on_delete => "CASCADE", on_update => "NO ACTION" },
); );
=head2 buildstepoutputs
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36 Type: has_many
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ItI1OvxHfLTzLVEqfPRjHg
Related object: L<Hydra::Schema::BuildStepOutputs>
=cut
__PACKAGE__->has_many(
"buildstepoutputs",
"Hydra::Schema::BuildStepOutputs",
{ "foreign.build" => "self.build", "foreign.stepnr" => "self.stepnr" },
undef,
);
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-30 16:36:03
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ZiA1nv73Fpp0/DTi4sLfEQ
1; 1;

View file

@ -72,11 +72,6 @@ __PACKAGE__->table("Builds");
data_type: 'text' data_type: 'text'
is_nullable: 0 is_nullable: 0
=head2 outpath
data_type: 'text'
is_nullable: 0
=head2 system =head2 system
data_type: 'text' data_type: 'text'
@ -225,8 +220,6 @@ __PACKAGE__->add_columns(
{ data_type => "text", is_nullable => 1 }, { data_type => "text", is_nullable => 1 },
"drvpath", "drvpath",
{ data_type => "text", is_nullable => 0 }, { data_type => "text", is_nullable => 0 },
"outpath",
{ data_type => "text", is_nullable => 0 },
"system", "system",
{ data_type => "text", is_nullable => 0 }, { data_type => "text", is_nullable => 0 },
"longdescription", "longdescription",
@ -321,6 +314,21 @@ __PACKAGE__->has_many(
undef, undef,
); );
=head2 buildoutputs
Type: has_many
Related object: L<Hydra::Schema::BuildOutputs>
=cut
__PACKAGE__->has_many(
"buildoutputs",
"Hydra::Schema::BuildOutputs",
{ "foreign.build" => "self.id" },
undef,
);
=head2 buildproducts =head2 buildproducts
Type: has_many Type: has_many
@ -336,6 +344,21 @@ __PACKAGE__->has_many(
undef, undef,
); );
=head2 buildstepoutputs
Type: has_many
Related object: L<Hydra::Schema::BuildStepOutputs>
=cut
__PACKAGE__->has_many(
"buildstepoutputs",
"Hydra::Schema::BuildStepOutputs",
{ "foreign.build" => "self.id" },
undef,
);
=head2 buildsteps =head2 buildsteps
Type: has_many Type: has_many
@ -442,8 +465,8 @@ __PACKAGE__->has_many(
); );
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:34:39 # Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-30 16:22:11
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:wPBFqpUWncuD9xki8Pbnvg # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:YBdqPWScG4dtGx+U3dJcwA
__PACKAGE__->has_many( __PACKAGE__->has_many(
"dependents", "dependents",
@ -459,13 +482,13 @@ __PACKAGE__->has_many(
{ "foreign.build" => "self.id" }, { "foreign.build" => "self.id" },
); );
__PACKAGE__->has_one( #__PACKAGE__->has_one(
"actualBuildStep", # "actualBuildStep",
"Hydra::Schema::BuildSteps", # "Hydra::Schema::BuildSteps",
{ 'foreign.outpath' => 'self.outpath' # { 'foreign.outpath' => 'self.outpath'
, 'foreign.build' => 'self.id' # , 'foreign.build' => 'self.id'
}, # },
); #);
sub makeSource { sub makeSource {
my ($name, $query) = @_; my ($name, $query) = @_;

View file

@ -21,13 +21,13 @@ sub process {
my $res = "[\n"; my $res = "[\n";
foreach my $name (keys %{$c->stash->{nixPkgs}}) { foreach my $pkg (@{$c->stash->{nixPkgs}}) {
my $build = $c->stash->{nixPkgs}->{$name}->{build}; my $build = $pkg->{build};
$res .= " # $name\n"; $res .= " # $pkg->{name}\n";
$res .= " { type = \"derivation\";\n"; $res .= " { type = \"derivation\";\n";
$res .= " name = " . escape ($build->get_column("releasename") or $build->nixname) . ";\n"; $res .= " name = " . escape ($build->get_column("releasename") or $build->nixname) . ";\n";
$res .= " system = " . (escape $build->system) . ";\n"; $res .= " system = " . (escape $build->system) . ";\n";
$res .= " outPath = " . (escape $build->outpath) . ";\n"; $res .= " outPath = " . (escape $pkg->{outPath}) . ";\n";
$res .= " meta = {\n"; $res .= " meta = {\n";
$res .= " description = " . (escape $build->description) . ";\n" $res .= " description = " . (escape $build->description) . ";\n"
if $build->description; if $build->description;

View file

@ -10,9 +10,10 @@ sub process {
my $build = $c->stash->{build}; my $build = $c->stash->{build};
# FIXME: add multiple output support
my $s = "NIXPKG1 " . $c->stash->{manifestUri} my $s = "NIXPKG1 " . $c->stash->{manifestUri}
. " " . $build->nixname . " " . $build->system . " " . $build->nixname . " " . $build->system
. " " . $build->drvpath . " " . $build->outpath . " " . $build->drvpath . " " . $build->buildoutputs->find({name => "out"})->path
. " " . $c->uri_for('/'); . " " . $c->uri_for('/');
$c->response->body($s); $c->response->body($s);

View file

@ -26,7 +26,7 @@
<td><tt>[% INCLUDE renderFullJobName project = step.build.project.name jobset = step.build.jobset.name job = step.build.job.name %]</tt></td> <td><tt>[% INCLUDE renderFullJobName project = step.build.project.name jobset = step.build.jobset.name job = step.build.job.name %]</tt></td>
<td><tt>[% step.system %]</tt></td> <td><tt>[% step.system %]</tt></td>
<td><a href="[% c.uri_for('/build' step.build.id) %]">[% step.build.id %]</a></td> <td><a href="[% c.uri_for('/build' step.build.id) %]">[% step.build.id %]</a></td>
<td><tt>[% step.outpath.match('-(.*)').0 %]</tt></td> <td><tt>[% step.drvpath.match('-(.*)').0 %]</tt></td>
<td class='right'>[% INCLUDE renderDuration duration = curTime - step.starttime %] </td> <td class='right'>[% INCLUDE renderDuration duration = curTime - step.starttime %] </td>
</tr> </tr>
[% END %] [% END %]

View file

@ -6,8 +6,7 @@
[% ELSIF jobset %]for Jobset [% project.name %]:[% jobset.name %] [% ELSIF jobset %]for Jobset [% project.name %]:[% jobset.name %]
[% ELSIF project %] for Project <tt>[% project.name %]</tt>[% END %]</h1></div> [% ELSIF project %] for Project <tt>[% project.name %]</tt>[% END %]</h1></div>
<p>Showing builds [% (page - 1) * resultsPerPage + 1 %] - [% (page - 1) * resultsPerPage + builds.size %] <p>Showing builds [% (page - 1) * resultsPerPage + 1 %] - [% (page - 1) * resultsPerPage + builds.size %] out of [% total %] in order of descending timestamp.</p>
out of [% total %] in order of descending timestamp.</p>
[% INCLUDE renderPager %] [% INCLUDE renderPager %]
[% INCLUDE renderBuildList hideProjectName=project hideJobsetName=jobset hideJobName=job %] [% INCLUDE renderBuildList hideProjectName=project hideJobsetName=jobset hideJobName=job %]

View file

@ -8,57 +8,60 @@
[% jobset = build.jobset %] [% jobset = build.jobset %]
[% job = build.job %] [% job = build.job %]
[% BLOCK renderBuildSteps %] [% BLOCK renderOutputs %]
<table class="tablesorter table table-striped table-condensed"> [% start=1; FOREACH output IN outputs %]
<thead> [% IF !start %],<br/>[% END; start=0; output.path %]
<tr><th>Nr</th><th>What</th><th>Duration</th><th>Machine</th><th>Status</th></tr> [% END %]
</thead> [% END %]
<tbody>
[% FOREACH step IN build.buildsteps -%]
[% IF ( type == "All" ) || ( type == "Failed" && step.status != 0 ) || ( type == "Running" && step.busy == 1 ) -%]
[% has_log = log_exists(step.drvpath);
log = c.uri_for('/build' build.id 'nixlog' step.stepnr); -%]
<tr [% IF has_log %] class="clickable" onclick="window.location = '[% log %]'" [% END %]>
<td>[% step.stepnr %]</td>
<td>
[% IF step.type == 0 %]
Build of <tt>[% step.outpath %]</tt>
[% ELSE %]
Substitution of <tt>[% step.outpath %]</tt>
[% END %]
</td>
<td>
[% IF step.busy == 0 %]
[% INCLUDE renderDuration duration = step.stoptime - step.starttime %]
[% ELSE %]
[% IF build.finished %]
[% INCLUDE renderDuration duration = build.stoptime - step.starttime %]
[% ELSE %]
[% INCLUDE renderDuration duration = curTime - step.starttime %]
[% END %]
[% END %]
</td>
<td>[% step.machine.split('@').1 %]</td>
<td>
[% IF step.busy == 1 %]
<strong>Building</strong>
[% ELSIF step.status == 0 %]
Succeeded
[% ELSIF step.status == 4 %]
<span class="error">Aborted</span>
[% ELSE %]
<span class="error">Failed: [% HTML.escape(step.errormsg) %]</span>
[% END %]
[% IF has_log %]
(<a href="[% log %]">log</a>, <a href="[% "$log/raw" %]">raw</a>, <a href="[% "$log/tail-reload" %]">tail</a>)
[% END %]
</td>
</tr>
[% END %]
[% END %]
</tbody>
</table>
[% BLOCK renderBuildSteps %]
<table class="tablesorter table table-striped table-condensed">
<thead>
<tr><th>Nr</th><th>What</th><th>Duration</th><th>Machine</th><th>Status</th></tr>
</thead>
<tbody>
[% FOREACH step IN build.buildsteps %]
[% IF ( type == "All" ) || ( type == "Failed" && step.status != 0 ) || ( type == "Running" && step.busy == 1 ) %]
[% has_log = log_exists(step.drvpath);
log = c.uri_for('/build' build.id 'nixlog' step.stepnr); %]
<tr [% IF has_log %] class="clickable" onclick="window.location = '[% log %]'" [% END %]>
<td>[% step.stepnr %]</td>
<td>
[% IF step.type == 0 %]
Build of <tt>[% INCLUDE renderOutputs outputs=step.buildstepoutputs %]</tt>
[% ELSE %]
Substitution of <tt>[% INCLUDE renderOutputs outputs=step.buildstepoutputs %]</tt>
[% END %]
</td>
<td>
[% IF step.busy == 0;
INCLUDE renderDuration duration = step.stoptime - step.starttime;
ELSIF build.finished;
INCLUDE renderDuration duration = build.stoptime - step.starttime;
ELSE;
INCLUDE renderDuration duration = curTime - step.starttime;
END %]
</td>
<td>[% step.machine.split('@').1 %]</td>
<td>
[% IF step.busy == 1 %]
<strong>Building</strong>
[% ELSIF step.status == 0 %]
Succeeded
[% ELSIF step.status == 4 %]
<span class="error">Aborted</span>
[% ELSE %]
<span class="error">Failed: [% HTML.escape(step.errormsg) %]</span>
[% END %]
[% IF has_log %]
(<a href="[% log %]">log</a>, <a href="[% "$log/raw" %]">raw</a>, <a href="[% "$log/tail-reload" %]">tail</a>)
[% END %]
</td>
</tr>
[% END %]
[% END %]
</tbody>
</table>
[% END %] [% END %]
@ -110,15 +113,18 @@
<th>System:</th> <th>System:</th>
<td><tt>[% build.system %]</tt></td> <td><tt>[% build.system %]</tt></td>
</tr> </tr>
[% IF build.finished %] [% IF build.iscachedbuild %]
<tr>
<th>Cached from:</th>
<td>
[% INCLUDE renderFullBuildLink build=cachedBuild %]
</td>
</tr>
[% ELSE %]
<tr> <tr>
<th>Duration:</th> <th>Duration:</th>
<td> <td>
[% IF build.iscachedbuild %] [% INCLUDE renderDuration duration = build.stoptime - build.starttime %]; finished at [% INCLUDE renderDateTime timestamp = build.stoptime %]
(cached[% IF cachedBuild %] from [% INCLUDE renderFullBuildLink build=cachedBuild %][% END %])
[% ELSE %]
[% INCLUDE renderDuration duration = build.stoptime - build.starttime %] finished at [% INCLUDE renderDateTime timestamp = build.stoptime %]
[% END %]
</td> </td>
</tr> </tr>
[% END %] [% END %]
@ -217,28 +223,6 @@
<h2>Information</h2> <h2>Information</h2>
<table class="layoutTable"> <table class="layoutTable">
<tr>
<th>Build ID:</th>
<td>[% build.id %]</td>
</tr>
<tr>
<th>Status:</th>
<td>
[% INCLUDE renderStatus build=build %]
</td>
</tr>
<tr>
<th>Project:</th>
<td>[% INCLUDE renderProjectName project=project.name %]</td>
</tr>
<tr>
<th>Jobset:</th>
<td>[% INCLUDE renderJobsetName project=project.name jobset=jobset.name %]</td>
</tr>
<tr>
<th>Job name:</th>
<td>[% INCLUDE renderJobName project=project.name jobset=jobset.name job=job.name %]</td>
</tr>
[% IF build.nixexprinput %] [% IF build.nixexprinput %]
<tr> <tr>
<th>Nix expression:</th> <th>Nix expression:</th>
@ -249,12 +233,6 @@
<th>Nix name:</th> <th>Nix name:</th>
<td><tt>[% build.nixname %]</tt></td> <td><tt>[% build.nixname %]</tt></td>
</tr> </tr>
[% IF build.releasename %]
<tr>
<th>Release name:</th>
<td><tt>[% HTML.escape(build.releasename) %]</tt></td>
</tr>
[% END %]
<tr> <tr>
<th>Short description:</th> <th>Short description:</th>
<td>[% IF build.description %][% HTML.escape(build.description) %][% ELSE %]<em>(not given)</em>[% END %]</td> <td>[% IF build.description %][% HTML.escape(build.description) %][% ELSE %]<em>(not given)</em>[% END %]</td>
@ -289,22 +267,14 @@
</td> </td>
</tr> </tr>
<tr> <tr>
<th>Output store path:</th> <th>Output store paths:</th>
<td> <td>
<tt>[% build.outpath %]</tt> <tt>[% INCLUDE renderOutputs outputs=build.buildoutputs %]</tt>
[% IF available %] [% IF available %]
(<a href="[% c.uri_for('/build' build.id 'deps') %]#runtime">runtime dependencies</a>) (<a href="[% c.uri_for('/build' build.id 'deps') %]#runtime">runtime dependencies</a>)
[% END %] [% END %]
</td> </td>
</tr> </tr>
[% IF pathHash %]
<tr>
<th>Output store path hash:</th>
<td>
<tt>[% pathHash %]</tt>
</td>
</tr>
[% END %]
<tr> <tr>
<th>Time added:</th> <th>Time added:</th>
<td>[% INCLUDE renderDateTime timestamp = build.timestamp %]</td> <td>[% INCLUDE renderDateTime timestamp = build.timestamp %]</td>

View file

@ -64,6 +64,7 @@ install the package simply by clicking on the packages below.</p>
[% ELSE -%] [% ELSE -%]
[% HTML.escape(build.description) -%] [% HTML.escape(build.description) -%]
[% END -%] [% END -%]
[% IF pkg.outName != 'out' %] [[% pkg.outName %]][% END %]
</td> </td>
</tr> </tr>

View file

@ -347,7 +347,7 @@
[% IF bi1.name == bi2.name %] [% IF bi1.name == bi2.name %]
[% IF bi1.type == bi2.type %] [% IF bi1.type == bi2.type %]
[% IF bi1.value != bi2.value || bi1.uri != bi2.uri %] [% IF bi1.value != bi2.value || bi1.uri != bi2.uri %]
<tr><td><b>[% bi1.name %]</b></td><td><tt>[% INCLUDE renderShortInputValue input=bi1 %] to [% INCLUDE renderShortInputValue input=bi2 %]</tt></td></tr> <tr><td><b>[% bi1.name %]</b></td><td><tt>[% INCLUDE renderShortInputValue input=bi1 %]</tt> to <tt>[% INCLUDE renderShortInputValue input=bi2 %]</tt></td></tr>
[% ELSIF bi1.uri == bi2.uri && bi1.revision != bi2.revision %] [% ELSIF bi1.uri == bi2.uri && bi1.revision != bi2.revision %]
[% IF bi1.type == "git" %] [% IF bi1.type == "git" %]
<tr><td> <tr><td>
@ -360,7 +360,7 @@
[% END %] [% END %]
[% ELSIF bi1.dependency.id != bi2.dependency.id || bi1.path != bi2.path %] [% ELSIF bi1.dependency.id != bi2.dependency.id || bi1.path != bi2.path %]
<tr><td> <tr><td>
<b>[% bi1.name %]</b></td><td><tt>[% INCLUDE renderShortInputValue input=bi1 %] to [% INCLUDE renderShortInputValue input=bi2 %]</tt> <b>[% bi1.name %]</b></td><td><tt>[% INCLUDE renderShortInputValue input=bi1 %]</tt> to <tt>[% INCLUDE renderShortInputValue input=bi2 %]</tt>
<br/> <br/>
<br/> <br/>
[% INCLUDE renderInputDiff build1=bi1.dependency, build2=bi2.dependency, nestedDiff=1, nestLevel=nestLevel+1 %] [% INCLUDE renderInputDiff build1=bi1.dependency, build2=bi2.dependency, nestedDiff=1, nestLevel=nestLevel+1 %]
@ -395,7 +395,7 @@
<td><tt>[% step.system %]</tt></td> <td><tt>[% step.system %]</tt></td>
<td><a href="[% c.uri_for('/build' step.build.id) %]">[% step.build.id %]</a></td> <td><a href="[% c.uri_for('/build' step.build.id) %]">[% step.build.id %]</a></td>
<td><a href="[% c.uri_for('/build' step.build.id 'nixlog' step.stepnr 'tail-reload') %]">[% step.stepnr %]</a></td> <td><a href="[% c.uri_for('/build' step.build.id 'nixlog' step.stepnr 'tail-reload') %]">[% step.stepnr %]</a></td>
<td><tt>[% step.outpath.match('-(.*)').0 %]</tt></td> <td><tt>[% step.drvpath.match('-(.*)').0 %]</tt></td>
<td class='right'>[% INCLUDE renderDuration duration = curTime - step.starttime %] </td> <td class='right'>[% INCLUDE renderDuration duration = curTime - step.starttime %] </td>
</tr> </tr>
[% END %] [% END %]

View file

@ -8,7 +8,7 @@
<a name="runtime"></a> <a name="runtime"></a>
[% IF available %] [% IF available %]
<h1>Runtime dependencies for [% build.outpath %]</h1> <h1>Runtime dependencies</h1>
<ul> <ul>
[% FOREACH dep IN runtimedeps -%] [% FOREACH dep IN runtimedeps -%]
<li> <li>
@ -26,7 +26,7 @@ Path not available anymore!<br />
<a name="buildtime"></a> <a name="buildtime"></a>
[% IF drvAvailable %] [% IF drvAvailable %]
<h1>Build time dependencies for [% build.drvpath %]</h1> <h1>Build time dependencies</h1>
<ul> <ul>
[% FOREACH dep IN buildtimedeps -%] [% FOREACH dep IN buildtimedeps -%]
<li> <li>

View file

@ -204,7 +204,7 @@
<blockquote> <blockquote>
[% IF activeJobs.size == 0 %]<em>(none)</em>[% END %] [% IF activeJobs.size == 0 %]<em>(none)</em>[% END %]
[% FOREACH j IN activeJobs %] [% INCLUDE renderJobName project=project.name jobset=jobset.name job=j %] [% END %] [% FOREACH j IN activeJobs %][% INCLUDE renderJobName project=project.name jobset=jobset.name job=j %]<br/>[% END %]
</blockquote> </blockquote>
</p> </p>
@ -212,7 +212,7 @@
<blockquote> <blockquote>
[% IF inactiveJobs.size == 0 %]<em>(none)</em>[% END %] [% IF inactiveJobs.size == 0 %]<em>(none)</em>[% END %]
[% FOREACH j IN inactiveJobs %] [% INCLUDE renderJobName project=project.name jobset=jobset.name job=j %] [% END %] [% FOREACH j IN inactiveJobs %][% INCLUDE renderJobName project=project.name jobset=jobset.name job=j %]<br/>[% END %]
</blockquote> </blockquote>
</p> </p>

View file

@ -4,7 +4,7 @@
<div class="page-header"><h1>Build log of [% INCLUDE renderFullJobNameOfBuild %] build <a href="[% c.uri_for('/build' build.id) %]">[% build.id %]</a>[%IF step %] step [% step.stepnr %][% END %]</h1></div> <div class="page-header"><h1>Build log of [% INCLUDE renderFullJobNameOfBuild %] build <a href="[% c.uri_for('/build' build.id) %]">[% build.id %]</a>[%IF step %] step [% step.stepnr %][% END %]</h1></div>
<p> <p>
This is the build log of path <tt>[% IF step; step.outpath; ELSE; build.outpath; END %]</tt>. This is the build log of derivation <tt>[% IF step; step.drvpath; ELSE; build.drvpath; END %]</tt>.
[% IF step && step.machine %] [% IF step && step.machine %]
It was built on <tt>[% step.machine %]</tt>. It was built on <tt>[% step.machine %]</tt>.
[% END %] [% END %]

View file

@ -16,7 +16,7 @@
[% CASE "nix-build" %] [% CASE "nix-build" %]
[% IF build.buildstatus == 6 %] [% IF build.buildstatus == 6 %]
[% filename = "${build.nixname}.closure.gz" %] [% filename = build.nixname _ (product.subtype ? "-" _ product.subtype : "") _ ".closure.gz" %]
[% uri = c.uri_for('/build' build.id 'nix' 'closure' filename ) %] [% uri = c.uri_for('/build' build.id 'nix' 'closure' filename ) %]
<tr class="product"> <tr class="product">
<td> <td>
@ -70,7 +70,7 @@
<tr class="product"> <tr class="product">
<td> <td>
[% filename = "${build.nixname}.closure.gz" %] [% filename = build.nixname _ (product.subtype ? "-" _ product.subtype : "") _ ".closure.gz" %]
[% uri = c.uri_for('/build' build.id 'nix' 'closure' filename ) %] [% uri = c.uri_for('/build' build.id 'nix' 'closure' filename ) %]
<a href="[% uri %]"> <a href="[% uri %]">

View file

@ -1,6 +1,7 @@
#! /var/run/current-system/sw/bin/perl -w #! /var/run/current-system/sw/bin/perl -w
use strict; use strict;
use List::MoreUtils qw(all);
use File::Basename; use File::Basename;
use File::stat; use File::stat;
use Nix::Store; use Nix::Store;
@ -58,6 +59,7 @@ sub sendTwitterNotification {
warn "$@\n" if $@; warn "$@\n" if $@;
} }
sub statusDescription { sub statusDescription {
my ($buildstatus) = @_; my ($buildstatus) = @_;
@ -72,6 +74,7 @@ sub statusDescription {
return $status; return $status;
} }
sub sendEmailNotification { sub sendEmailNotification {
my ($build) = @_; my ($build) = @_;
@ -127,7 +130,7 @@ sub sendEmailNotification {
[ "Maintainer(s):", $build->maintainers ], [ "Maintainer(s):", $build->maintainers ],
[ "System:", $build->system ], [ "System:", $build->system ],
[ "Derivation store path:", $build->drvpath ], [ "Derivation store path:", $build->drvpath ],
[ "Output store path:", $build->outpath ], [ "Output store path:", join(", ", map { $_->path } $build->buildoutputs) ],
[ "Time added:", showTime $build->timestamp ], [ "Time added:", showTime $build->timestamp ],
); );
push @lines, ( push @lines, (
@ -206,11 +209,21 @@ sub sendEmailNotification {
} }
sub addBuildStepOutputs {
my ($step) = @_;
my $drv = derivationFromPath($step->drvpath);
$step->buildstepoutputs->create({ name => $_, path => $drv->{outputs}->{$_} })
foreach keys %{$drv->{outputs}};
}
sub doBuild { sub doBuild {
my ($build) = @_; my ($build) = @_;
my %outputs;
$outputs{$_->name} = $_->path foreach $build->buildoutputs->all;
my $drvPath = $build->drvpath; my $drvPath = $build->drvpath;
my $outPath = $build->outpath;
my $maxsilent = $build->maxsilent; my $maxsilent = $build->maxsilent;
my $timeout = $build->timeout; my $timeout = $build->timeout;
@ -223,7 +236,7 @@ sub doBuild {
my $errormsg = undef; my $errormsg = undef;
if (!isValidPath($outPath)) { unless (all { isValidPath($_) } values(%outputs)) {
$isCachedBuild = 0; $isCachedBuild = 0;
# Do the build. # Do the build.
@ -235,12 +248,12 @@ sub doBuild {
# Run Nix to perform the build, and monitor the stderr output # Run Nix to perform the build, and monitor the stderr output
# to get notifications about specific build steps, the # to get notifications about specific build steps, the
# associated log files, etc. # associated log files, etc.
# Note: `--timeout' was added in Nix 1.0pre27564, June 2011.
my $cmd = "nix-store --realise $drvPath " . my $cmd = "nix-store --realise $drvPath " .
"--timeout $timeout " . "--timeout $timeout " .
"--max-silent-time $maxsilent --keep-going --fallback " . "--max-silent-time $maxsilent --keep-going --fallback " .
"--no-build-output --log-type flat --print-build-trace " . "--no-build-output --log-type flat --print-build-trace " .
"--add-root " . gcRootFor $outPath . " 2>&1"; "--add-root " . gcRootFor($outputs{out} // $outputs{(sort keys %outputs)[0]}) . " 2>&1";
print STDERR "$cmd\n";
my $max = $build->buildsteps->find( my $max = $build->buildsteps->find(
{}, {select => {max => 'stepnr + 1'}, as => ['max']}); {}, {select => {max => 'stepnr + 1'}, as => ['max']});
@ -261,15 +274,15 @@ sub doBuild {
if (/^@\s+build-started\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$/) { if (/^@\s+build-started\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$/) {
my $drvPathStep = $1; my $drvPathStep = $1;
txn_do($db, sub { txn_do($db, sub {
$build->buildsteps->create( my $step = $build->buildsteps->create(
{ stepnr => ($buildSteps{$drvPathStep} = $buildStepNr++) { stepnr => ($buildSteps{$drvPathStep} = $buildStepNr++)
, type => 0 # = build , type => 0 # = build
, drvpath => $drvPathStep , drvpath => $drvPathStep
, outpath => $2
, system => $3 , system => $3
, busy => 1 , busy => 1
, starttime => time , starttime => time
}); });
addBuildStepOutputs($step);
}); });
} }
@ -305,46 +318,47 @@ sub doBuild {
# failed previously. This can happen if this is a # failed previously. This can happen if this is a
# restarted build. # restarted build.
elsif (scalar $build->buildsteps->search({drvpath => $drvPathStep, type => 0, busy => 0, status => 1}) == 0) { elsif (scalar $build->buildsteps->search({drvpath => $drvPathStep, type => 0, busy => 0, status => 1}) == 0) {
$build->buildsteps->create( my $step = $build->buildsteps->create(
{ stepnr => ($buildSteps{$drvPathStep} = $buildStepNr++) { stepnr => ($buildSteps{$drvPathStep} = $buildStepNr++)
, type => 0 # = build , type => 0 # = build
, drvpath => $drvPathStep , drvpath => $drvPathStep
, outpath => $2
, busy => 0 , busy => 0
, status => 1 , status => 1
, starttime => time , starttime => time
, stoptime => time , stoptime => time
, errormsg => $errorMsg , errormsg => $errorMsg
}); });
addBuildStepOutputs($step);
} }
}); });
} }
elsif (/^@\s+substituter-started\s+(\S+)\s+(\S+)$/) { elsif (/^@\s+substituter-started\s+(\S+)\s+(\S+)$/) {
my $outPath = $1; my $path = $1;
txn_do($db, sub { txn_do($db, sub {
$build->buildsteps->create( my $step = $build->buildsteps->create(
{ stepnr => ($buildSteps{$outPath} = $buildStepNr++) { stepnr => ($buildSteps{$path} = $buildStepNr++)
, type => 1 # = substitution , type => 1 # = substitution
, outpath => $1
, busy => 1 , busy => 1
, starttime => time , starttime => time
}); });
# "out" is kinda fake (substitutions don't have named outputs).
$step->buildstepoutputs->create({ name => "out", path => $path });
}); });
} }
elsif (/^@\s+substituter-succeeded\s+(\S+)$/) { elsif (/^@\s+substituter-succeeded\s+(\S+)$/) {
my $outPath = $1; my $path = $1;
txn_do($db, sub { txn_do($db, sub {
my $step = $build->buildsteps->find({stepnr => $buildSteps{$outPath}}) or die; my $step = $build->buildsteps->find({stepnr => $buildSteps{$path}}) or die;
$step->update({busy => 0, status => 0, stoptime => time}); $step->update({busy => 0, status => 0, stoptime => time});
}); });
} }
elsif (/^@\s+substituter-failed\s+(\S+)\s+(\S+)\s+(\S+)$/) { elsif (/^@\s+substituter-failed\s+(\S+)\s+(\S+)\s+(\S+)$/) {
my $outPath = $1; my $path = $1;
txn_do($db, sub { txn_do($db, sub {
my $step = $build->buildsteps->find({stepnr => $buildSteps{$outPath}}) or die; my $step = $build->buildsteps->find({stepnr => $buildSteps{$path}}) or die;
$step->update({busy => 0, status => 1, errormsg => $3, stoptime => time}); $step->update({busy => 0, status => 1, errormsg => $3, stoptime => time});
}); });
} }
@ -371,24 +385,34 @@ sub doBuild {
} }
done: done:
my $size = 0;
my $closuresize = 0;
if (isValidPath($outPath)) {
my ($deriver, $hash, $time, $narSize, $refs) = queryPathInfo($outPath, 0);
$size = $narSize;
my @closure = computeFSClosure(0, 0, $outPath);
foreach my $path (@closure) {
my ($deriver, $hash, $time, $narSize, $refs) = queryPathInfo($path, 0);
$closuresize += $narSize;
}
}
txn_do($db, sub { txn_do($db, sub {
my $releaseName = getReleaseName($outPath); if ($buildStatus == 0) {
$buildStatus = 6 if $buildStatus == 0 && -f "$outPath/nix-support/failed"; my $size = 0;
my $closureSize = 0;
my $releaseName;
my @closure = computeFSClosure(0, 0, values %outputs);
foreach my $path (@closure) {
my ($deriver, $hash, $time, $narSize, $refs) = queryPathInfo($path, 0);
$closureSize += $narSize;
$size += $narSize if grep { $path eq $_ } values(%outputs);
}
foreach my $path (values %outputs) {
$buildStatus = 6 if $buildStatus == 0 && -f "$path/nix-support/failed";
$releaseName //= getReleaseName($path);
}
$build->update(
{ releasename => $releaseName
, size => $size
, closuresize => $closureSize
});
addBuildProducts($db, $build);
}
$build->update( $build->update(
{ finished => 1 { finished => 1
@ -400,15 +424,9 @@ sub doBuild {
, buildstatus => $buildStatus , buildstatus => $buildStatus
, starttime => $startTime , starttime => $startTime
, stoptime => $stopTime , stoptime => $stopTime
, size => $size
, closuresize => $closuresize
, errormsg => $errormsg , errormsg => $errormsg
, releasename => $releaseName
}); });
if ($buildStatus == 0 || $buildStatus == 6) {
addBuildProducts($db, $build);
}
}); });
sendEmailNotification $build; sendEmailNotification $build;

View file

@ -21,25 +21,27 @@ sub addRoot {
} }
my @columns = ( "id", "project", "jobset", "job", "system", "finished", "outpath", "drvpath", "timestamp" ); my @columns = ( "id", "project", "jobset", "job", "system", "finished", "drvpath", "timestamp" );
sub keepBuild { sub keepBuild {
my ($build) = @_; my ($build) = @_;
print STDERR " keeping ", ($build->finished ? "" : "scheduled "), "build ", $build->id, " (", print STDERR " keeping ", ($build->finished ? "" : "scheduled "), "build ", $build->id, " (",
$build->get_column('project'), ":", $build->get_column('jobset'), ":", $build->get_column('job'), "; ", $build->get_column('project'), ":", $build->get_column('jobset'), ":", $build->get_column('job'), "; ",
$build->system, "; ", $build->system, "; ",
strftime("%Y-%m-%d %H:%M:%S", localtime($build->timestamp)), ")\n"; strftime("%Y-%m-%d %H:%M:%S", localtime($build->timestamp)), ")\n";
if (isValidPath($build->outpath)) { foreach my $out ($build->buildoutputs->all) {
addRoot $build->outpath; if (isValidPath($out->path)) {
} else { addRoot $out->path;
print STDERR " warning: output ", $build->outpath, " has disappeared\n" if $build->finished; } else {
print STDERR " warning: output ", $out->path, " has disappeared\n" if $build->finished;
}
} }
if (!$build->finished) { if (!$build->finished) {
if (isValidPath($build->drvpath)) { if (isValidPath($build->drvpath)) {
addRoot $build->drvpath; addRoot $build->drvpath;
} else { } else {
print STDERR " warning: derivation ", $build->drvpath, " has disappeared\n"; print STDERR " warning: derivation ", $build->drvpath, " has disappeared\n";
} }
} }
} }
@ -57,7 +59,8 @@ closedir DIR;
# Keep every build in every release of every project. # Keep every build in every release of every project.
print STDERR "*** looking for release members\n"; print STDERR "*** looking for release members\n";
keepBuild $_ foreach $db->resultset('Builds')->search_literal( keepBuild $_ foreach $db->resultset('Builds')->search_literal(
"exists (select 1 from releasemembers where build = me.id)", { order_by => ["project", "jobset", "job", "id"] }); "exists (select 1 from releasemembers where build = me.id)",
{ order_by => ["project", "jobset", "job", "id"], columns => [ @columns ] });
# Keep all builds that have been marked as "keep". # Keep all builds that have been marked as "keep".
@ -74,8 +77,8 @@ foreach my $project ($db->resultset('Projects')->search({}, { order_by => ["name
foreach my $jobset ($project->jobsets->search({}, { order_by => ["name" ]})) { foreach my $jobset ($project->jobsets->search({}, { order_by => ["name" ]})) {
my $keepnr = $jobset->keepnr; my $keepnr = $jobset->keepnr;
# If the jobset has been hidden and disabled for more than one week, than # If the jobset has been hidden and disabled for more than one
# don't keep its builds anymore. # week, then don't keep its builds anymore.
if ($jobset->enabled == 0 && ($project->hidden == 1 || $jobset->hidden == 1) && (time() - ($jobset->lastcheckedtime || 0) > (7 * 24 * 3600))) { if ($jobset->enabled == 0 && ($project->hidden == 1 || $jobset->hidden == 1) && (time() - ($jobset->lastcheckedtime || 0) > (7 * 24 * 3600))) {
print STDERR "*** skipping disabled jobset ", $project->name, ":", $jobset->name, "\n"; print STDERR "*** skipping disabled jobset ", $project->name, ":", $jobset->name, "\n";
next; next;
@ -86,19 +89,20 @@ foreach my $project ($db->resultset('Projects')->search({}, { order_by => ["name
next; next;
} }
print STDERR "*** looking for the $keepnr most recent successful builds of each job in jobset ", # FIXME: base this on jobset evals?
$project->name, ":", $jobset->name, "\n"; print STDERR "*** looking for the $keepnr most recent successful builds of each job in jobset ",
$project->name, ":", $jobset->name, "\n";
keepBuild $_ foreach $jobset->builds->search( keepBuild $_ foreach $jobset->builds->search(
{ 'me.id' => { 'in' => \ { 'me.id' => { 'in' => \
[ "select b2.id from Builds b2 join " . [ "select b2.id from Builds b2 join " .
" (select distinct job, system, coalesce( " . " (select distinct job, system, coalesce( " .
" (select id from builds where project = b.project and jobset = b.jobset and job = b.job and system = b.system and finished = 1 and buildStatus = 0 order by id desc offset ? limit 1)" . " (select id from builds where project = b.project and jobset = b.jobset and job = b.job and system = b.system and finished = 1 and buildStatus = 0 order by id desc offset ? limit 1)" .
" , 0) as nth from builds b where project = ? and jobset = ? and isCurrent = 1) x " . " , 0) as nth from builds b where project = ? and jobset = ? and isCurrent = 1) x " .
" on b2.project = ? and b2.jobset = ? and b2.job = x.job and b2.system = x.system and (id >= x.nth) where finished = 1 and buildStatus = 0" " on b2.project = ? and b2.jobset = ? and b2.job = x.job and b2.system = x.system and (id >= x.nth) where finished = 1 and buildStatus = 0"
, [ '', $keepnr - 1 ], [ '', $project->name ], [ '', $jobset->name ], [ '', $project->name ], [ '', $jobset->name ] ] } , [ '', $keepnr - 1 ], [ '', $project->name ], [ '', $jobset->name ], [ '', $project->name ], [ '', $jobset->name ] ] }
}, },
{ order_by => ["job", "system", "id"], columns => [ @columns ] }); { order_by => ["job", "system", "id"], columns => [ @columns ] });
} }
# Go over all views in this project. # Go over all views in this project.
@ -135,10 +139,10 @@ foreach my $link (@roots) {
my $path = "/nix/store/$link"; my $path = "/nix/store/$link";
if (!defined $roots{$path}) { if (!defined $roots{$path}) {
print STDERR "removing root $path\n"; print STDERR "removing root $path\n";
$rootsDeleted++; $rootsDeleted++;
unlink "$gcRootsDir/$link" or warn "cannot remove $gcRootsDir/$link"; unlink "$gcRootsDir/$link" or warn "cannot remove $gcRootsDir/$link";
} else { } else {
$rootsKept++; $rootsKept++;
} }
} }

View file

@ -133,7 +133,6 @@ create table Builds (
nixName text, -- name attribute of the derivation nixName text, -- name attribute of the derivation
description text, -- meta.description description text, -- meta.description
drvPath text not null, drvPath text not null,
outPath text not null,
system text not null, system text not null,
longDescription text, -- meta.longDescription longDescription text, -- meta.longDescription
@ -174,6 +173,7 @@ create table Builds (
-- 3 = other failure (see errorMsg) -- 3 = other failure (see errorMsg)
-- 4 = build cancelled (removed from queue; never built) -- 4 = build cancelled (removed from queue; never built)
-- 5 = build not done because a dependency failed previously (obsolete) -- 5 = build not done because a dependency failed previously (obsolete)
-- 6 = failure with output
buildStatus integer, buildStatus integer,
errorMsg text, -- error message in case of a Nix failure errorMsg text, -- error message in case of a Nix failure
@ -191,6 +191,15 @@ create table Builds (
); );
create table BuildOutputs (
build integer not null,
name text not null,
path text not null,
primary key (build, name),
foreign key (build) references Builds(id) on delete cascade
);
create table BuildSteps ( create table BuildSteps (
build integer not null, build integer not null,
stepnr integer not null, stepnr integer not null,
@ -198,7 +207,6 @@ create table BuildSteps (
type integer not null, -- 0 = build, 1 = substitution type integer not null, -- 0 = build, 1 = substitution
drvPath text, drvPath text,
outPath text,
busy integer not null, busy integer not null,
@ -217,6 +225,17 @@ create table BuildSteps (
); );
create table BuildStepOutputs (
build integer not null,
stepnr integer not null,
name text not null,
path text not null,
primary key (build, stepnr, name),
foreign key (build) references Builds(id) on delete cascade,
foreign key (build, stepnr) references BuildSteps(build, stepnr) on delete cascade
);
-- Inputs of builds. -- Inputs of builds.
create table BuildInputs ( create table BuildInputs (
#ifdef POSTGRESQL #ifdef POSTGRESQL
@ -494,13 +513,13 @@ create table NewsItems (
create table BuildMachines ( create table BuildMachines (
hostname text primary key NOT NULL, hostname text primary key not null,
username text DEFAULT '' NOT NULL, username text default '' not null,
ssh_key text DEFAULT '' NOT NULL, ssh_key text default '' not null,
options text DEFAULT '' NOT NULL, options text default '' not null,
maxconcurrent integer DEFAULT 2 NOT NULL, maxconcurrent integer default 2 not null,
speedfactor integer DEFAULT 1 NOT NULL, speedfactor integer default 1 not null,
enabled integer DEFAULT 0 NOT NULL enabled integer default 0 not null
); );
@ -518,11 +537,9 @@ create index IndexBuildInputsOnBuild on BuildInputs(build);
create index IndexBuildInputsOnDependency on BuildInputs(dependency); create index IndexBuildInputsOnDependency on BuildInputs(dependency);
create index IndexBuildProducstOnBuildAndType on BuildProducts(build, type); create index IndexBuildProducstOnBuildAndType on BuildProducts(build, type);
create index IndexBuildProductsOnBuild on BuildProducts(build); create index IndexBuildProductsOnBuild on BuildProducts(build);
create index IndexBuildStepsOnBuild on BuildSteps(build);
create index IndexBuildStepsOnBusy on BuildSteps(busy); create index IndexBuildStepsOnBusy on BuildSteps(busy);
create index IndexBuildStepsOnDrvpathTypeBusyStatus on BuildSteps(drvpath, type, busy, status); create index IndexBuildStepsOnDrvpathTypeBusyStatus on BuildSteps(drvpath, type, busy, status);
create index IndexBuildStepsOnOutpath on BuildSteps(outpath); create index IndexBuildStepOutputsOnPath on BuildStepOutputs(path);
create index IndexBuildStepsOnOutpathBuild on BuildSteps (outpath, build);
create index IndexBuildsOnFinished on Builds(finished); create index IndexBuildsOnFinished on Builds(finished);
create index IndexBuildsOnFinishedBusy on Builds(finished, busy); create index IndexBuildsOnFinishedBusy on Builds(finished, busy);
create index IndexBuildsOnIsCurrent on Builds(isCurrent); create index IndexBuildsOnIsCurrent on Builds(isCurrent);