Compare commits
16 Commits
pure-eval
...
merge-tabl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f92a143c5f | ||
|
|
3eeb035d5a | ||
|
|
2a471ce0a4 | ||
|
|
a4b63db992 | ||
|
|
0420232324 | ||
|
|
4dfca7f83b | ||
|
|
36bd37d353 | ||
|
|
84834881c3 | ||
|
|
2560e97c1e | ||
|
|
33ba3cf330 | ||
|
|
5771fb33e0 | ||
|
|
a07a2e6687 | ||
|
|
8781c35de7 | ||
|
|
b061a8cea9 | ||
|
|
dd2e91e7cc | ||
|
|
c454ecb21c |
@@ -10,14 +10,12 @@ use Hydra::Helper::CatalystUtils;
|
||||
sub getJobStatus {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
my $latest = joinWithResultInfo($c, $c->stash->{jobStatus});
|
||||
|
||||
my $maintainer = $c->request->params->{"maintainer"};
|
||||
|
||||
$latest = $latest->search(
|
||||
my $latest = $c->stash->{jobStatus}->search(
|
||||
defined $maintainer ? { maintainers => { like => "%$maintainer%" } } : {},
|
||||
{ '+select' => ["me.statusChangeId", "me.statusChangeTime", "resultInfo.buildStatus"]
|
||||
, '+as' => ["statusChangeId", "statusChangeTime", "buildStatus"]
|
||||
{ '+select' => ["me.statusChangeId", "me.statusChangeTime"]
|
||||
, '+as' => ["statusChangeId", "statusChangeTime"]
|
||||
, order_by => "coalesce(statusChangeTime, 0) desc"
|
||||
});
|
||||
|
||||
@@ -43,7 +41,7 @@ sub errors : Chained('get_builds') PathPart Args(0) {
|
||||
[$c->stash->{allJobs}->search({errormsg => {'!=' => ''}})]
|
||||
if defined $c->stash->{allJobs};
|
||||
$c->stash->{brokenBuilds} =
|
||||
[getJobStatus($self, $c)->search({'resultInfo.buildstatus' => {'!=' => 0}})];
|
||||
[getJobStatus($self, $c)->search({buildStatus => {'!=' => 0}})];
|
||||
}
|
||||
|
||||
|
||||
@@ -64,11 +62,10 @@ sub all : Chained('get_builds') PathPart {
|
||||
$c->stash->{resultsPerPage} = $resultsPerPage;
|
||||
$c->stash->{totalBuilds} = $nrBuilds;
|
||||
|
||||
$c->stash->{builds} = [ joinWithResultInfo($c, $c->stash->{allBuilds})->search(
|
||||
$c->stash->{builds} = [ $c->stash->{allBuilds}->search(
|
||||
{ finished => 1 },
|
||||
{ '+select' => ["resultInfo.buildStatus"]
|
||||
, '+as' => ["buildStatus"]
|
||||
, order_by => "timestamp DESC"
|
||||
{ order_by => "timestamp DESC"
|
||||
, columns => [@buildListColumns]
|
||||
, rows => $resultsPerPage
|
||||
, page => $page }) ];
|
||||
}
|
||||
@@ -79,12 +76,10 @@ sub nix : Chained('get_builds') PathPart('channel') CaptureArgs(1) {
|
||||
eval {
|
||||
if ($channelName eq "latest") {
|
||||
$c->stash->{channelName} = $c->stash->{channelBaseName} . "-latest";
|
||||
getChannelData($c, scalar($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({}, { columns => [@buildListColumns, 'drvpath', 'outpath', 'description', 'homepage'] });
|
||||
}
|
||||
#elsif ($channelName eq "all") {
|
||||
# $c->stash->{channelName} = $c->stash->{channelBaseName} . "-all";
|
||||
# getChannelData($c, scalar($c->stash->{allBuilds}));
|
||||
#}
|
||||
else {
|
||||
notFound($c, "Unknown channel `$channelName'.");
|
||||
}
|
||||
@@ -97,8 +92,8 @@ sub nix : Chained('get_builds') PathPart('channel') CaptureArgs(1) {
|
||||
sub latest : Chained('get_builds') PathPart('latest') {
|
||||
my ($self, $c, @rest) = @_;
|
||||
|
||||
my ($latest) = joinWithResultInfo($c, $c->stash->{allBuilds})
|
||||
->search({finished => 1, buildstatus => 0}, {order_by => ["isCurrent DESC", "timestamp DESC"]});
|
||||
my ($latest) = $c->stash->{allBuilds}->search(
|
||||
{finished => 1, buildstatus => 0}, {order_by => ["isCurrent DESC", "timestamp DESC"]});
|
||||
|
||||
notFound($c, "There is no successful build to redirect to.") unless defined $latest;
|
||||
|
||||
@@ -112,8 +107,8 @@ sub latest_for : Chained('get_builds') PathPart('latest-for') {
|
||||
|
||||
notFound($c, "You need to specify a platform type in the URL.") unless defined $system;
|
||||
|
||||
my ($latest) = joinWithResultInfo($c, $c->stash->{allBuilds})
|
||||
->search({finished => 1, buildstatus => 0, system => $system}, {order_by => ["isCurrent DESC", "timestamp DESC"]});
|
||||
my ($latest) = $c->stash->{allBuilds}->search(
|
||||
{finished => 1, buildstatus => 0, system => $system}, {order_by => ["isCurrent DESC", "timestamp DESC"]});
|
||||
|
||||
notFound($c, "There is no successful build for platform `$system' to redirect to.") unless defined $latest;
|
||||
|
||||
|
||||
@@ -3,14 +3,45 @@ package Hydra::Base::Controller::NixChannel;
|
||||
use strict;
|
||||
use warnings;
|
||||
use base 'Catalyst::Controller';
|
||||
use Nix::Store;
|
||||
use Hydra::Helper::Nix;
|
||||
use Hydra::Helper::CatalystUtils;
|
||||
|
||||
|
||||
sub getChannelData {
|
||||
my ($c, $checkValidity) = @_;
|
||||
|
||||
my @storePaths = ();
|
||||
foreach my $build ($c->stash->{channelBuilds}->all) {
|
||||
next if $checkValidity && !isValidPath($build->outpath);
|
||||
#if (isValidPath($build->drvpath)) {
|
||||
# # Adding `drvpath' implies adding `outpath' because of the
|
||||
# # `--include-outputs' flag passed to `nix-store'.
|
||||
# push @storePaths, $build->drvpath;
|
||||
#} else {
|
||||
# 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
|
||||
# a hint to the binary patch generator. (It shouldn't try to
|
||||
# generate patches between builds for different systems.) It
|
||||
# would be nice if Nix stored this info for every path but it
|
||||
# doesn't.
|
||||
$c->stash->{systemForPath}->{$build->outpath} = $build->system;
|
||||
};
|
||||
|
||||
$c->stash->{storePaths} = [@storePaths];
|
||||
}
|
||||
|
||||
|
||||
sub closure : Chained('nix') PathPart {
|
||||
my ($self, $c) = @_;
|
||||
$c->stash->{current_view} = 'NixClosure';
|
||||
|
||||
getChannelData($c, 1);
|
||||
|
||||
# !!! 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
|
||||
# called for HEAD requests. There should be a cleaner solution...
|
||||
@@ -22,18 +53,23 @@ sub manifest : Chained('nix') PathPart("MANIFEST") Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
$c->stash->{current_view} = 'NixManifest';
|
||||
$c->stash->{narBase} = $c->uri_for($c->controller('Root')->action_for("nar"));
|
||||
getChannelData($c, 1);
|
||||
}
|
||||
|
||||
|
||||
sub pkg : Chained('nix') PathPart Args(1) {
|
||||
my ($self, $c, $pkgName) = @_;
|
||||
|
||||
my $pkg = $c->stash->{nixPkgs}->{$pkgName};
|
||||
if (!$c->stash->{build}) {
|
||||
$pkgName =~ /-(\d+)\.nixpkg$/ or notFound($c, "Bad package name.");
|
||||
$c->stash->{build} = $c->stash->{channelBuilds}->find({ id => $1 })
|
||||
|| notFound($c, "No such package in this channel.");
|
||||
}
|
||||
|
||||
notFound($c, "Unknown Nix package `$pkgName'.")
|
||||
unless defined $pkg;
|
||||
|
||||
$c->stash->{build} = $pkg->{build};
|
||||
if (!isValidPath($c->stash->{build}->outpath)) {
|
||||
$c->response->status(410); # "Gone"
|
||||
error($c, "Build " . $c->stash->{build}->id . " is no longer available.");
|
||||
}
|
||||
|
||||
$c->stash->{manifestUri} = $c->uri_for($self->action_for("manifest"), $c->req->captures);
|
||||
|
||||
@@ -46,26 +82,30 @@ sub pkg : Chained('nix') PathPart Args(1) {
|
||||
sub nixexprs : Chained('nix') PathPart('nixexprs.tar.bz2') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
$c->stash->{current_view} = 'NixExprs';
|
||||
getChannelData($c, 1);
|
||||
}
|
||||
|
||||
|
||||
sub name {
|
||||
my ($build) = @_;
|
||||
return $build->get_column('releasename') || $build->nixname;
|
||||
return $build->releasename || $build->nixname;
|
||||
}
|
||||
|
||||
|
||||
sub sortPkgs {
|
||||
# Sort by name, then timestamp.
|
||||
# Sort by name, then id.
|
||||
return sort
|
||||
{ lc(name($a->{build})) cmp lc(name($b->{build}))
|
||||
or $a->{build}->timestamp <=> $b->{build}->timestamp
|
||||
} @_;
|
||||
or $a->{build}->id <=> $b->{build}->id } @_;
|
||||
}
|
||||
|
||||
|
||||
sub channel_contents : Chained('nix') PathPart('') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
# Optimistically assume that none of the packages have been
|
||||
# garbage-collected. That should be true for the "latest"
|
||||
# channel.
|
||||
getChannelData($c, 0);
|
||||
$c->stash->{template} = 'channel-contents.tt';
|
||||
$c->stash->{nixPkgs} = [sortPkgs (values %{$c->stash->{nixPkgs}})];
|
||||
}
|
||||
|
||||
@@ -15,11 +15,13 @@ use File::Slurp;
|
||||
|
||||
# !!! Rewrite this to use View::JSON.
|
||||
|
||||
|
||||
sub api : Chained('/') PathPart('api') CaptureArgs(0) {
|
||||
my ($self, $c) = @_;
|
||||
$c->response->content_type('application/json');
|
||||
}
|
||||
|
||||
|
||||
sub projectToHash {
|
||||
my ($project) = @_;
|
||||
return {
|
||||
@@ -28,14 +30,15 @@ sub projectToHash {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
sub projects : Chained('api') PathPart('projects') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
my @projects = $c->model('DB::Projects')->search({hidden => 0}, {order_by => 'name'}) ;
|
||||
my @projects = $c->model('DB::Projects')->search({hidden => 0}, {order_by => 'name'});
|
||||
|
||||
my @list ;
|
||||
my @list;
|
||||
foreach my $p (@projects) {
|
||||
push @list, projectToHash($p) ;
|
||||
push @list, projectToHash($p);
|
||||
}
|
||||
|
||||
$c->stash->{'plain'} = {
|
||||
@@ -44,6 +47,7 @@ sub projects : Chained('api') PathPart('projects') Args(0) {
|
||||
$c->forward('Hydra::View::Plain');
|
||||
}
|
||||
|
||||
|
||||
sub buildToHash {
|
||||
my ($build) = @_;
|
||||
my $result = {
|
||||
@@ -58,7 +62,7 @@ sub buildToHash {
|
||||
};
|
||||
|
||||
if($build->finished) {
|
||||
$result->{'buildstatus'} = $build->get_column("buildstatus") ;
|
||||
$result->{'buildstatus'} = $build->get_column("buildstatus");
|
||||
} else {
|
||||
$result->{'busy'} = $build->get_column("busy");
|
||||
$result->{'priority'} = $build->get_column("priority");
|
||||
@@ -67,28 +71,27 @@ sub buildToHash {
|
||||
return $result;
|
||||
};
|
||||
|
||||
|
||||
sub latestbuilds : Chained('api') PathPart('latestbuilds') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
my $nr = $c->request->params->{nr} ;
|
||||
my $nr = $c->request->params->{nr};
|
||||
error($c, "Parameter not defined!") if !defined $nr;
|
||||
|
||||
my $project = $c->request->params->{project} ;
|
||||
my $jobset = $c->request->params->{jobset} ;
|
||||
my $job = $c->request->params->{job} ;
|
||||
my $system = $c->request->params->{system} ;
|
||||
my $project = $c->request->params->{project};
|
||||
my $jobset = $c->request->params->{jobset};
|
||||
my $job = $c->request->params->{job};
|
||||
my $system = $c->request->params->{system};
|
||||
|
||||
my $filter = {finished => 1} ;
|
||||
my $filter = {finished => 1};
|
||||
$filter->{project} = $project if ! $project eq "";
|
||||
$filter->{jobset} = $jobset if ! $jobset eq "";
|
||||
$filter->{job} = $job if !$job eq "";
|
||||
$filter->{system} = $system if !$system eq "";
|
||||
|
||||
my @latest = joinWithResultInfo($c, $c->model('DB::Builds'))->search($filter, {rows => $nr, order_by => ["timestamp DESC"] });
|
||||
my @latest = $c->model('DB::Builds')->search($filter, {rows => $nr, order_by => ["timestamp DESC"] });
|
||||
|
||||
my @list ;
|
||||
foreach my $b (@latest) {
|
||||
push @list, buildToHash($b) ;
|
||||
}
|
||||
my @list;
|
||||
push @list, buildToHash($_) foreach @latest;
|
||||
|
||||
$c->stash->{'plain'} = {
|
||||
data => scalar (JSON::Any->objToJson(\@list))
|
||||
@@ -96,6 +99,7 @@ sub latestbuilds : Chained('api') PathPart('latestbuilds') Args(0) {
|
||||
$c->forward('Hydra::View::Plain');
|
||||
}
|
||||
|
||||
|
||||
sub jobsetToHash {
|
||||
my ($jobset) = @_;
|
||||
return {
|
||||
@@ -108,10 +112,11 @@ sub jobsetToHash {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
sub jobsets : Chained('api') PathPart('jobsets') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
my $projectName = $c->request->params->{project} ;
|
||||
my $projectName = $c->request->params->{project};
|
||||
error($c, "Parameter 'project' not defined!") if !defined $projectName;
|
||||
|
||||
my $project = $c->model('DB::Projects')->find($projectName)
|
||||
@@ -119,10 +124,8 @@ sub jobsets : Chained('api') PathPart('jobsets') Args(0) {
|
||||
|
||||
my @jobsets = jobsetOverview($c, $project);
|
||||
|
||||
my @list ;
|
||||
foreach my $j (@jobsets) {
|
||||
push @list, jobsetToHash($j) ;
|
||||
}
|
||||
my @list;
|
||||
push @list, jobsetToHash($_) foreach @jobsets;
|
||||
|
||||
$c->stash->{'plain'} = {
|
||||
data => scalar (JSON::Any->objToJson(\@list))
|
||||
@@ -130,57 +133,59 @@ sub jobsets : Chained('api') PathPart('jobsets') Args(0) {
|
||||
$c->forward('Hydra::View::Plain');
|
||||
}
|
||||
|
||||
|
||||
sub queue : Chained('api') PathPart('queue') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
my $nr = $c->request->params->{nr} ;
|
||||
my $nr = $c->request->params->{nr};
|
||||
error($c, "Parameter not defined!") if !defined $nr;
|
||||
|
||||
my @builds = $c->model('DB::Builds')->search({finished => 0}, {rows => $nr, join => ['schedulingInfo'] , order_by => ["busy DESC", "priority DESC", "timestamp"], '+select' => ['schedulingInfo.priority', 'schedulingInfo.busy'], '+as' => ['priority', 'busy'] });
|
||||
my @builds = $c->model('DB::Builds')->search({finished => 0}, {rows => $nr, order_by => ["busy DESC", "priority DESC", "timestamp"]});
|
||||
|
||||
my @list ;
|
||||
foreach my $b (@builds) {
|
||||
push @list, buildToHash($b) ;
|
||||
}
|
||||
|
||||
my @list;
|
||||
push @list, buildToHash($_) foreach @builds;
|
||||
|
||||
$c->stash->{'plain'} = {
|
||||
data => scalar (JSON::Any->objToJson(\@list))
|
||||
};
|
||||
$c->forward('Hydra::View::Plain');
|
||||
}
|
||||
|
||||
|
||||
sub nrqueue : Chained('api') PathPart('nrqueue') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
my $nrQueuedBuilds = $c->model('DB::BuildSchedulingInfo')->count();
|
||||
my $nrQueuedBuilds = $c->model('DB::Builds')->search({finished => 0})->count();
|
||||
$c->stash->{'plain'} = {
|
||||
data => " $nrQueuedBuilds"
|
||||
data => "$nrQueuedBuilds"
|
||||
};
|
||||
$c->forward('Hydra::View::Plain');
|
||||
}
|
||||
|
||||
|
||||
sub nrrunning : Chained('api') PathPart('nrrunning') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
my $nrRunningBuilds = $c->model('DB::BuildSchedulingInfo')->search({ busy => 1 }, {})->count();
|
||||
$c->stash->{'plain'} = {
|
||||
data => " $nrRunningBuilds"
|
||||
my $nrRunningBuilds = $c->model('DB::Builds')->search({finished => 0, busy => 1 })->count();
|
||||
$c->stash->{'plain'} = {
|
||||
data => "$nrRunningBuilds"
|
||||
};
|
||||
$c->forward('Hydra::View::Plain');
|
||||
}
|
||||
|
||||
|
||||
sub nrbuilds : Chained('api') PathPart('nrbuilds') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
my $nr = $c->request->params->{nr} ;
|
||||
my $period = $c->request->params->{period} ;
|
||||
my $nr = $c->request->params->{nr};
|
||||
my $period = $c->request->params->{period};
|
||||
|
||||
error($c, "Parameter not defined!") if !defined $nr || !defined $period;
|
||||
my $base;
|
||||
|
||||
my $project = $c->request->params->{project} ;
|
||||
my $jobset = $c->request->params->{jobset} ;
|
||||
my $job = $c->request->params->{job} ;
|
||||
my $system = $c->request->params->{system} ;
|
||||
my $project = $c->request->params->{project};
|
||||
my $jobset = $c->request->params->{jobset};
|
||||
my $job = $c->request->params->{job};
|
||||
my $system = $c->request->params->{system};
|
||||
|
||||
my $filter = {finished => 1} ;
|
||||
my $filter = {finished => 1};
|
||||
$filter->{project} = $project if ! $project eq "";
|
||||
$filter->{jobset} = $jobset if ! $jobset eq "";
|
||||
$filter->{job} = $job if !$job eq "";
|
||||
@@ -189,11 +194,9 @@ sub nrbuilds : Chained('api') PathPart('nrbuilds') Args(0) {
|
||||
$base = 60*60 if($period eq "hour");
|
||||
$base = 24*60*60 if($period eq "day");
|
||||
|
||||
my @stats = $c->model('DB::Builds')->search($filter, {select => [{ count => "*" }], as => ["nr"], group_by => ["timestamp - timestamp % $base"], order_by => "timestamp - timestamp % $base DESC", rows => $nr}) ;
|
||||
my @arr ;
|
||||
foreach my $d (@stats) {
|
||||
push @arr, int($d->get_column("nr"));
|
||||
}
|
||||
my @stats = $c->model('DB::Builds')->search($filter, {select => [{ count => "*" }], as => ["nr"], group_by => ["timestamp - timestamp % $base"], order_by => "timestamp - timestamp % $base DESC", rows => $nr});
|
||||
my @arr;
|
||||
push @arr, int($_->get_column("nr")) foreach @stats;
|
||||
@arr = reverse(@arr);
|
||||
|
||||
$c->stash->{'plain'} = {
|
||||
@@ -202,38 +205,40 @@ sub nrbuilds : Chained('api') PathPart('nrbuilds') Args(0) {
|
||||
$c->forward('Hydra::View::Plain');
|
||||
}
|
||||
|
||||
|
||||
sub scmdiff : Chained('api') PathPart('scmdiff') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
||||
my $uri = $c->request->params->{uri} ;
|
||||
my $type = $c->request->params->{type} ;
|
||||
my $rev1 = $c->request->params->{rev1} ;
|
||||
my $rev2 = $c->request->params->{rev2} ;
|
||||
my $uri = $c->request->params->{uri};
|
||||
my $type = $c->request->params->{type};
|
||||
my $rev1 = $c->request->params->{rev1};
|
||||
my $rev2 = $c->request->params->{rev2};
|
||||
my $branch;
|
||||
|
||||
die("invalid revisions: [$rev1] [$rev2]") if $rev1 !~ m/^[a-zA-Z0-9_.]+$/ || $rev2 !~ m/^[a-zA-Z0-9_.]+$/ ;
|
||||
die("invalid revisions: [$rev1] [$rev2]") if $rev1 !~ m/^[a-zA-Z0-9_.]+$/ || $rev2 !~ m/^[a-zA-Z0-9_.]+$/;
|
||||
|
||||
my $diff = "";
|
||||
if($type eq "hg") {
|
||||
if ($type eq "hg") {
|
||||
my $clonePath = scmPath . "/" . sha256_hex($uri);
|
||||
die if ! -d $clonePath;
|
||||
$branch = `(cd $clonePath ; hg log --template '{branch}' -r $rev2)`;
|
||||
$diff .= `(cd $clonePath ; hg log -r $rev1 -r $rev2 -b $branch)`;
|
||||
$diff .= `(cd $clonePath ; hg diff -r $rev1:$rev2)`;
|
||||
$branch = `(cd $clonePath; hg log --template '{branch}' -r $rev2)`;
|
||||
$diff .= `(cd $clonePath; hg log -r $rev1 -r $rev2 -b $branch)`;
|
||||
$diff .= `(cd $clonePath; hg diff -r $rev1:$rev2)`;
|
||||
} elsif ($type eq "git") {
|
||||
my $clonePath = scmPath . "/" . sha256_hex($uri);
|
||||
die if ! -d $clonePath;
|
||||
$diff .= `(cd $clonePath ; git log $rev1..$rev2)`;
|
||||
$diff .= `(cd $clonePath ; git diff $rev1..$rev2)`;
|
||||
$diff .= `(cd $clonePath; git log $rev1..$rev2)`;
|
||||
$diff .= `(cd $clonePath; git diff $rev1..$rev2)`;
|
||||
}
|
||||
|
||||
$c->stash->{'plain'} = { data => (scalar $diff) || " " };
|
||||
$c->forward('Hydra::View::Plain');
|
||||
}
|
||||
|
||||
|
||||
sub readNormalizedLog {
|
||||
my ($file) = @_;
|
||||
my $pipe = (-f "$file.bz2" ? "cat $file.bz2 | bzip2 -d" : "cat $file") ;
|
||||
my $pipe = (-f "$file.bz2" ? "cat $file.bz2 | bzip2 -d" : "cat $file");
|
||||
my $res = `$pipe`;
|
||||
|
||||
$res =~ s/\/nix\/store\/[a-z0-9]*-/\/nix\/store\/...-/g;
|
||||
@@ -242,6 +247,7 @@ sub readNormalizedLog {
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
sub logdiff : Chained('api') PathPart('logdiff') Args(2) {
|
||||
my ($self, $c, $buildid1, $buildid2) = @_;
|
||||
|
||||
@@ -254,9 +260,9 @@ sub logdiff : Chained('api') PathPart('logdiff') Args(2) {
|
||||
notFound($c, "Build with ID $buildid2 doesn't exist.")
|
||||
if !defined $build2;
|
||||
|
||||
if (-f $build1->resultInfo->logfile && -f $build2->resultInfo->logfile) {
|
||||
my $logtext1 = readNormalizedLog($build1->resultInfo->logfile);
|
||||
my $logtext2 = readNormalizedLog($build2->resultInfo->logfile);
|
||||
if (-f $build1->logfile && -f $build2->logfile) {
|
||||
my $logtext1 = readNormalizedLog($build1->logfile);
|
||||
my $logtext2 = readNormalizedLog($build2->logfile);
|
||||
$diff = diff \$logtext1, \$logtext2;
|
||||
} else {
|
||||
$c->response->status(404);
|
||||
@@ -267,4 +273,5 @@ sub logdiff : Chained('api') PathPart('logdiff') Args(2) {
|
||||
$c->forward('Hydra::View::Plain');
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
||||
@@ -57,9 +57,9 @@ sub index : Chained('admin') PathPart('') Args(0) {
|
||||
, '+as' => ['idle']
|
||||
})];
|
||||
$c->stash->{steps} = [ $c->model('DB::BuildSteps')->search(
|
||||
{ 'me.busy' => 1, 'schedulingInfo.busy' => 1 },
|
||||
{ join => [ 'schedulingInfo', 'build' ]
|
||||
, order_by => [ 'machine' ]
|
||||
{ finished => 0, 'me.busy' => 1, 'build.busy' => 1, },
|
||||
{ join => [ 'build' ]
|
||||
, order_by => [ 'machine', 'stepnr' ]
|
||||
} ) ];
|
||||
$c->stash->{template} = 'admin.tt';
|
||||
}
|
||||
@@ -296,13 +296,15 @@ sub machine_disable : Chained('machine') PathPart('disable') Args(0) {
|
||||
|
||||
sub clear_queue_non_current : Chained('admin') Path('clear-queue-non-current') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
$c->model('DB::Builds')->search({iscurrent => 0, busy => 0}, { join => 'schedulingInfo' })->delete_all;
|
||||
# !!! Mark the builds as cancelled instead.
|
||||
$c->model('DB::Builds')->search({finished => 0, iscurrent => 0, busy => 0})->delete_all;
|
||||
$c->res->redirect("/admin");
|
||||
}
|
||||
|
||||
sub clear_queue : Chained('admin') Path('clear-queue') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
$c->model('DB::Builds')->search({busy => 0}, { join => 'schedulingInfo' })->delete_all;
|
||||
# !!! Mark the builds as cancelled instead.
|
||||
$c->model('DB::Builds')->search({finished => 0, busy => 0})->delete_all;
|
||||
$c->res->redirect("/admin");
|
||||
}
|
||||
|
||||
|
||||
@@ -41,28 +41,27 @@ sub view_build : Chained('build') PathPart('') Args(0) {
|
||||
$c->stash->{drvAvailable} = isValidPath $build->drvpath;
|
||||
$c->stash->{flashMsg} = $c->flash->{buildMsg};
|
||||
|
||||
my $pathHash = $c->stash->{available} ? queryPathHash($build->outpath) : "Not available";
|
||||
$c->stash->{pathHash} = $pathHash;
|
||||
$c->stash->{pathHash} = $c->stash->{available} ? queryPathHash($build->outpath) : undef;
|
||||
|
||||
if (!$build->finished && $build->schedulingInfo->busy) {
|
||||
my $logfile = $build->schedulingInfo->logfile;
|
||||
if (!$build->finished && $build->busy) {
|
||||
my $logfile = $build->logfile;
|
||||
$c->stash->{logtext} = `cat $logfile` if defined $logfile && -e $logfile;
|
||||
}
|
||||
|
||||
if (defined $build->resultInfo && $build->resultInfo->iscachedbuild) {
|
||||
if ($build->finished && $build->iscachedbuild) {
|
||||
(my $cachedBuildStep) = $c->model('DB::BuildSteps')->search({ outpath => $build->outpath }, {}) ;
|
||||
$c->stash->{cachedBuild} = $cachedBuildStep->build if defined $cachedBuildStep;
|
||||
}
|
||||
|
||||
(my $lastBuildStep) = $build->buildsteps->search({},{order_by => "stepnr DESC", rows => 1});
|
||||
my $path = defined $lastBuildStep ? $lastBuildStep->logfile : "" ;
|
||||
if (defined $build->resultInfo && ($build->resultInfo->buildstatus == 1 || $build->resultInfo->buildstatus == 6) && !($path eq "") && -f $lastBuildStep->logfile) {
|
||||
if ($build->finished && ($build->buildstatus == 1 || $build->buildstatus == 6) && !($path eq "") && -f $lastBuildStep->logfile) {
|
||||
my $logtext = `tail -n 50 $path`;
|
||||
$c->stash->{logtext} = removeAsciiEscapes($logtext);
|
||||
}
|
||||
|
||||
if($build->finished) {
|
||||
$c->stash->{prevBuilds} = [joinWithResultInfo($c, $c->model('DB::Builds'))->search(
|
||||
if ($build->finished) {
|
||||
$c->stash->{prevBuilds} = [$c->model('DB::Builds')->search(
|
||||
{ project => $c->stash->{project}->name
|
||||
, jobset => $c->stash->{build}->jobset->name
|
||||
, job => $c->stash->{build}->job->name
|
||||
@@ -81,13 +80,13 @@ sub view_build : Chained('build') PathPart('') Args(0) {
|
||||
];
|
||||
}
|
||||
|
||||
my $r = joinWithResultInfo( $c, $c->model('DB::Builds'))->search(
|
||||
{ eval => { -in => $build->jobsetevalmembers->get_column('eval')->as_query } }
|
||||
, { join => 'jobsetevalmembers', order_by => [ 'project', 'jobset', 'job'], distinct => 1 }
|
||||
);
|
||||
if ($r->count <= 100) {
|
||||
$c->stash->{relatedbuilds} = [$r->all];
|
||||
}
|
||||
my $maxRelated = 100;
|
||||
my $r = $c->model('DB::Builds')->search(
|
||||
{ eval => { -in => $build->jobsetevalmembers->search({}, {rows => 1})->get_column('eval')->as_query } },
|
||||
{ join => 'jobsetevalmembers', order_by => [ 'project', 'jobset', 'job'], distinct => 1, rows => $maxRelated + 1 }
|
||||
);
|
||||
$c->stash->{relatedbuilds} = [$r->all];
|
||||
delete $c->stash->{relatedbuilds} if scalar(@{$c->stash->{relatedbuilds}}) > $maxRelated;
|
||||
}
|
||||
|
||||
|
||||
@@ -106,9 +105,9 @@ sub view_nixlog : Chained('build') PathPart('nixlog') {
|
||||
sub view_log : Chained('build') PathPart('log') {
|
||||
my ($self, $c, $mode) = @_;
|
||||
|
||||
error($c, "Build didn't produce a log.") if !defined $c->stash->{build}->resultInfo->logfile;
|
||||
error($c, "Build didn't produce a log.") if !defined $c->stash->{build}->logfile;
|
||||
|
||||
showLog($c, $c->stash->{build}->resultInfo->logfile, $mode);
|
||||
showLog($c, $c->stash->{build}->logfile, $mode);
|
||||
}
|
||||
|
||||
|
||||
@@ -141,7 +140,7 @@ sub showLog {
|
||||
my $url = $c->request->uri->as_string;
|
||||
$url =~ s/tail-reload/tail/g;
|
||||
$c->stash->{url} = $url;
|
||||
$c->stash->{reload} = defined $c->stash->{build}->schedulingInfo && $c->stash->{build}->schedulingInfo->busy;
|
||||
$c->stash->{reload} = !$c->stash->{build}->finished && $c->stash->{build}->busy;
|
||||
$c->stash->{title} = "";
|
||||
$c->stash->{contents} = (scalar `$pipestart | tail -n 50`) || " ";
|
||||
$c->stash->{template} = 'plain-reload.tt';
|
||||
@@ -371,10 +370,7 @@ sub nix : Chained('build') PathPart('nix') CaptureArgs(0) {
|
||||
notFound($c, "Path " . $build->outpath . " is no longer available.")
|
||||
unless isValidPath($build->outpath);
|
||||
|
||||
$c->stash->{storePaths} = [$build->outpath];
|
||||
|
||||
my $pkgName = $build->nixname . "-" . $build->system;
|
||||
$c->stash->{nixPkgs} = {"${pkgName}.nixpkg" => {build => $build, name => $pkgName}};
|
||||
$c->stash->{channelBuilds} = $c->model('DB::Builds')->search({id => $build->id});
|
||||
}
|
||||
|
||||
|
||||
@@ -406,21 +402,16 @@ sub cancel : Chained('build') PathPart Args(0) {
|
||||
|
||||
txn_do($c->model('DB')->schema, sub {
|
||||
error($c, "This build cannot be cancelled.")
|
||||
if $build->finished || $build->schedulingInfo->busy;
|
||||
if $build->finished || $build->busy;
|
||||
|
||||
# !!! Actually, it would be nice to be able to cancel busy
|
||||
# builds as well, but we would have to send a signal or
|
||||
# something to the build process.
|
||||
|
||||
$build->update({finished => 1, timestamp => time});
|
||||
|
||||
$c->model('DB::BuildResultInfo')->create(
|
||||
{ id => $build->id
|
||||
, iscachedbuild => 0
|
||||
, buildstatus => 4 # = cancelled
|
||||
$build->update(
|
||||
{ finished => 1, busy => 0, timestamp => time
|
||||
, iscachedbuild => 0, buildstatus => 4 # = cancelled
|
||||
});
|
||||
|
||||
$build->schedulingInfo->delete;
|
||||
});
|
||||
|
||||
$c->flash->{buildMsg} = "Build has been cancelled.";
|
||||
@@ -441,7 +432,7 @@ sub keep : Chained('build') PathPart Args(1) {
|
||||
registerRoot $build->outpath if $newStatus == 1;
|
||||
|
||||
txn_do($c->model('DB')->schema, sub {
|
||||
$build->resultInfo->update({keep => int $newStatus});
|
||||
$build->update({keep => int $newStatus});
|
||||
});
|
||||
|
||||
$c->flash->{buildMsg} =
|
||||
@@ -541,7 +532,7 @@ sub clone_submit : Chained('build') PathPart('clone/submit') Args(0) {
|
||||
my %currentBuilds;
|
||||
my $newBuild = checkBuild(
|
||||
$c->model('DB'), $build->project, $build->jobset,
|
||||
$inputInfo, $nixExprInput, $job, \%currentBuilds);
|
||||
$inputInfo, $nixExprInput, $job, \%currentBuilds, undef);
|
||||
|
||||
error($c, "This build has already been performed.") unless $newBuild;
|
||||
|
||||
|
||||
@@ -25,20 +25,21 @@ sub overview : Chained('job') PathPart('') Args(0) {
|
||||
|
||||
#getBuildStats($c, scalar $c->stash->{job}->builds);
|
||||
|
||||
$c->stash->{currentBuilds} = [$c->stash->{job}->builds->search({iscurrent => 1}, { join => 'resultInfo', '+select' => ["resultInfo.releasename", "resultInfo.buildStatus"]
|
||||
, '+as' => ["releasename", "buildStatus"], order_by => 'system' })];
|
||||
$c->stash->{currentBuilds} = [$c->stash->{job}->builds->search({finished => 1, iscurrent => 1}, { order_by => 'system' })];
|
||||
|
||||
$c->stash->{lastBuilds} =
|
||||
[ $c->stash->{job}->builds->search({ finished => 1 },
|
||||
{ join => 'resultInfo',
|
||||
, '+select' => ["resultInfo.releasename", "resultInfo.buildStatus"]
|
||||
, '+as' => ["releasename", "buildStatus"]
|
||||
, order_by => 'timestamp DESC', rows => 10
|
||||
}) ];
|
||||
{ order_by => 'timestamp DESC', rows => 10, columns => [@buildListColumns] }) ];
|
||||
|
||||
$c->stash->{runningBuilds} = [$c->stash->{job}->builds->search({busy => 1}, { join => ['schedulingInfo', 'project'] , order_by => ["priority DESC", "timestamp"]
|
||||
, '+select' => ['project.enabled', 'schedulingInfo.priority', 'schedulingInfo.disabled', 'schedulingInfo.busy']
|
||||
, '+as' => ['enabled', 'priority', 'disabled', 'busy'] })];
|
||||
$c->stash->{runningBuilds} = [
|
||||
$c->stash->{job}->builds->search(
|
||||
{ busy => 1 },
|
||||
{ join => ['project']
|
||||
, order_by => ["priority DESC", "timestamp"]
|
||||
, '+select' => ['project.enabled']
|
||||
, '+as' => ['enabled']
|
||||
}
|
||||
) ];
|
||||
|
||||
$c->stash->{systems} = [$c->stash->{job}->builds->search({iscurrent => 1}, {select => ["system"], distinct => 1})];
|
||||
}
|
||||
|
||||
@@ -65,10 +65,10 @@ sub jobsetIndex {
|
||||
my @as = ();
|
||||
push(@select, "job"); push(@as, "job");
|
||||
foreach my $system (@systems) {
|
||||
push(@select, "(SELECT buildstatus FROM BuildResultInfo bri NATURAL JOIN Builds b WHERE b.id = (SELECT MAX(id) FROM Builds t WHERE t.project = me.project AND t.jobset = me.jobset AND t.job = me.job AND t.system = '$system' AND t.iscurrent = 1 ))");
|
||||
push(@select, "(select buildstatus from Builds b where b.id = (select max(id) from Builds t where t.project = me.project and t.jobset = me.jobset and t.job = me.job and t.system = '$system' and t.iscurrent = 1 ))");
|
||||
push(@as, $system);
|
||||
push(@select, "(SELECT b.id FROM BuildResultInfo bri NATURAL JOIN Builds b WHERE b.id = (SELECT MAX(id) FROM Builds t WHERE t.project = me.project AND t.jobset = me.jobset AND t.job = me.job AND t.system = '$system' AND t.iscurrent = 1 ))");
|
||||
push(@as, $system."-build");
|
||||
push(@select, "(select b.id from Builds b where b.id = (select max(id) from Builds t where t.project = me.project and t.jobset = me.jobset and t.job = me.job and t.system = '$system' and t.iscurrent = 1 ))");
|
||||
push(@as, "$system-build");
|
||||
}
|
||||
$c->stash->{activeJobsStatus} =
|
||||
[ $c->model('DB')->resultset('ActiveJobsForJobset')->search(
|
||||
@@ -81,13 +81,9 @@ sub jobsetIndex {
|
||||
}
|
||||
|
||||
# Last builds for jobset.
|
||||
my $tmp = $c->stash->{jobset}->builds;
|
||||
$c->stash->{lastBuilds} =
|
||||
[ joinWithResultInfo($c, $tmp)->search({ finished => 1 },
|
||||
{ order_by => "timestamp DESC", rows => 5
|
||||
, '+select' => ["resultInfo.buildStatus"]
|
||||
, '+as' => ["buildStatus"]
|
||||
}) ];
|
||||
[ $c->stash->{jobset}->builds->search({ finished => 1 },
|
||||
{ order_by => "timestamp DESC", rows => 5, columns => [@buildListColumns] }) ];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ sub begin :Private {
|
||||
$c->stash->{tracker} = $ENV{"HYDRA_TRACKER"} ;
|
||||
|
||||
if (scalar(@args) == 0 || $args[0] ne "static") {
|
||||
$c->stash->{nrRunningBuilds} = $c->model('DB::BuildSchedulingInfo')->search({ busy => 1 }, {})->count();
|
||||
$c->stash->{nrQueuedBuilds} = $c->model('DB::BuildSchedulingInfo')->count();
|
||||
$c->stash->{nrRunningBuilds} = $c->model('DB::Builds')->search({ finished => 0, busy => 1 }, {})->count();
|
||||
$c->stash->{nrQueuedBuilds} = $c->model('DB::Builds')->search({ finished => 0 })->count();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ sub queue :Local {
|
||||
my ($self, $c) = @_;
|
||||
$c->stash->{template} = 'queue.tt';
|
||||
$c->stash->{queue} = [$c->model('DB::Builds')->search(
|
||||
{finished => 0}, {join => ['schedulingInfo', 'project'] , order_by => ["priority DESC", "timestamp"], '+select' => ['project.enabled', 'schedulingInfo.priority', 'schedulingInfo.disabled', 'schedulingInfo.busy'], '+as' => ['enabled', 'priority', 'disabled', 'busy'] })];
|
||||
{finished => 0}, { join => ['project'], order_by => ["priority DESC", "timestamp"], columns => [@buildListColumns], '+select' => ['project.enabled'], '+as' => ['enabled'] })];
|
||||
$c->stash->{flashMsg} = $c->flash->{buildMsg};
|
||||
}
|
||||
|
||||
@@ -86,21 +86,18 @@ sub timeline :Local {
|
||||
$pit = $pit-(24*60*60)-1;
|
||||
|
||||
$c->stash->{template} = 'timeline.tt';
|
||||
$c->stash->{builds} = [$c->model('DB::Builds')->search(
|
||||
{finished => 1, stoptime => { '>' => $pit } }
|
||||
, { join => 'resultInfo'
|
||||
, order_by => ["starttime"]
|
||||
, '+select' => [ 'resultInfo.starttime', 'resultInfo.stoptime', 'resultInfo.buildstatus' ]
|
||||
, '+as' => [ 'starttime', 'stoptime', 'buildstatus' ]
|
||||
})];
|
||||
$c->stash->{builds} = [ $c->model('DB::Builds')->search
|
||||
( { finished => 1, stoptime => { '>' => $pit } }
|
||||
, { order_by => ["starttime"] }
|
||||
) ];
|
||||
}
|
||||
|
||||
|
||||
sub status :Local {
|
||||
my ($self, $c) = @_;
|
||||
$c->stash->{steps} = [ $c->model('DB::BuildSteps')->search(
|
||||
{ 'me.busy' => 1, 'schedulingInfo.busy' => 1 },
|
||||
{ join => [ 'schedulingInfo', 'build' ]
|
||||
{ 'me.busy' => 1, 'build.finished' => 0, 'build.busy' => 1 },
|
||||
{ join => [ 'build' ]
|
||||
, order_by => [ 'machine' ]
|
||||
} ) ];
|
||||
}
|
||||
|
||||
@@ -153,10 +153,7 @@ sub result : Chained('view') PathPart('') {
|
||||
|
||||
# Note: we don't actually check whether $id is a primary build,
|
||||
# but who cares?
|
||||
my $primaryBuild = $c->stash->{project}->builds->find($id,
|
||||
{ join => 'resultInfo',
|
||||
, '+select' => ["resultInfo.releasename", "resultInfo.buildstatus"]
|
||||
, '+as' => ["releasename", "buildstatus"] })
|
||||
my $primaryBuild = $c->stash->{project}->builds->find($id)
|
||||
or error($c, "Build $id doesn't exist.");
|
||||
|
||||
my $result = getViewResult($primaryBuild, $c->stash->{jobs});
|
||||
|
||||
@@ -17,6 +17,7 @@ our @ISA = qw(Exporter);
|
||||
our @EXPORT = qw(
|
||||
fetchInput evalJobs checkBuild inputsToArgs captureStdoutStderr
|
||||
getReleaseName getBuildLog addBuildProducts restartBuild scmPath
|
||||
getPrevJobsetEval
|
||||
);
|
||||
|
||||
|
||||
@@ -244,7 +245,7 @@ sub fetchInputBuild {
|
||||
(my $prevBuild) = $db->resultset('Builds')->search(
|
||||
{ finished => 1, project => $projectName, jobset => $jobsetName
|
||||
, job => $jobName, buildStatus => 0 },
|
||||
{ join => 'resultInfo', order_by => "me.id DESC", rows => 1
|
||||
{ order_by => "me.id DESC", rows => 1
|
||||
, where => \ attrsToSQL($attrs, "me.id") });
|
||||
|
||||
if (!defined $prevBuild || !isValidPath($prevBuild->outpath)) {
|
||||
@@ -257,7 +258,7 @@ sub fetchInputBuild {
|
||||
my $pkgNameRE = "(?:(?:[A-Za-z0-9]|(?:-[^0-9]))+)";
|
||||
my $versionRE = "(?:[A-Za-z0-9\.\-]+)";
|
||||
|
||||
my $relName = ($prevBuild->resultInfo->releasename or $prevBuild->nixname);
|
||||
my $relName = ($prevBuild->releasename or $prevBuild->nixname);
|
||||
my $version = $2 if $relName =~ /^($pkgNameRE)-($versionRE)$/;
|
||||
|
||||
return
|
||||
@@ -294,7 +295,7 @@ sub fetchInputSystemBuild {
|
||||
my $pkgNameRE = "(?:(?:[A-Za-z0-9]|(?:-[^0-9]))+)";
|
||||
my $versionRE = "(?:[A-Za-z0-9\.\-]+)";
|
||||
|
||||
my $relName = ($prevBuild->resultInfo->releasename or $prevBuild->nixname);
|
||||
my $relName = ($prevBuild->releasename or $prevBuild->nixname);
|
||||
my $version = $2 if $relName =~ /^($pkgNameRE)-($versionRE)$/;
|
||||
|
||||
my $input =
|
||||
@@ -787,9 +788,21 @@ sub addBuildProducts {
|
||||
}
|
||||
|
||||
|
||||
# Return the most recent evaluation of the given jobset (that
|
||||
# optionally had new builds), or undefined if no such evaluation
|
||||
# exists.
|
||||
sub getPrevJobsetEval {
|
||||
my ($db, $jobset, $hasNewBuilds) = @_;
|
||||
my ($prevEval) = $jobset->jobsetevals(
|
||||
($hasNewBuilds ? { hasnewbuilds => 1 } : { }),
|
||||
{ order_by => "id DESC", rows => 1 });
|
||||
return $prevEval;
|
||||
}
|
||||
|
||||
|
||||
# Check whether to add the build described by $buildInfo.
|
||||
sub checkBuild {
|
||||
my ($db, $project, $jobset, $inputInfo, $nixExprInput, $buildInfo, $currentBuilds) = @_;
|
||||
my ($db, $project, $jobset, $inputInfo, $nixExprInput, $buildInfo, $buildIds, $prevEval) = @_;
|
||||
|
||||
my $jobName = $buildInfo->{jobName};
|
||||
my $drvPath = $buildInfo->{drvPath};
|
||||
@@ -822,19 +835,36 @@ sub checkBuild {
|
||||
# !!! Checking $outPath doesn't take meta-attributes into
|
||||
# account. For instance, do we want a new build to be
|
||||
# scheduled if the meta.maintainers field is changed?
|
||||
my @previousBuilds = $job->builds->search({outPath => $outPath, isCurrent => 1});
|
||||
if (scalar(@previousBuilds) > 0) {
|
||||
print STDERR "already scheduled/built\n";
|
||||
$currentBuilds->{$_->id} = 0 foreach @previousBuilds;
|
||||
return;
|
||||
if (defined $prevEval) {
|
||||
my ($prevBuild) = $prevEval->builds->search({ job => $job->name, outPath => $outPath }, { rows => 1, columns => ['id'] });
|
||||
if (defined $prevBuild) {
|
||||
print STDERR " already scheduled/built as build ", $prevBuild->id, "\n";
|
||||
$buildIds->{$prevBuild->id} = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
my $time = time();
|
||||
|
||||
# Nope, so add it.
|
||||
my %extraFlags;
|
||||
if (isValidPath($outPath)) {
|
||||
%extraFlags =
|
||||
( finished => 1
|
||||
, iscachedbuild => 1
|
||||
, buildstatus => 0
|
||||
, starttime => $time
|
||||
, stoptime => $time
|
||||
, logfile => getBuildLog($drvPath)
|
||||
, errormsg => ""
|
||||
, releasename => getReleaseName($outPath)
|
||||
);
|
||||
} else {
|
||||
%extraFlags = ( finished => 0 );
|
||||
}
|
||||
|
||||
$build = $job->builds->create(
|
||||
{ finished => 0
|
||||
, timestamp => $time
|
||||
{ timestamp => $time
|
||||
, description => $buildInfo->{description}
|
||||
, longdescription => $buildInfo->{longDescription}
|
||||
, license => $buildInfo->{license}
|
||||
@@ -849,31 +879,19 @@ sub checkBuild {
|
||||
, iscurrent => 1
|
||||
, nixexprinput => $jobset->nixexprinput
|
||||
, nixexprpath => $jobset->nixexprpath
|
||||
, priority => $priority
|
||||
, busy => 0
|
||||
, locker => ""
|
||||
, %extraFlags
|
||||
});
|
||||
|
||||
$buildIds->{$build->id} = 1;
|
||||
|
||||
$currentBuilds->{$build->id} = 1;
|
||||
|
||||
if (isValidPath($outPath)) {
|
||||
print STDERR "marked as cached build ", $build->id, "\n";
|
||||
$build->update({ finished => 1 });
|
||||
$build->create_related('buildresultinfo',
|
||||
{ iscachedbuild => 1
|
||||
, buildstatus => 0
|
||||
, starttime => $time
|
||||
, stoptime => $time
|
||||
, logfile => getBuildLog($drvPath)
|
||||
, errormsg => ""
|
||||
, releasename => getReleaseName($outPath)
|
||||
});
|
||||
if ($build->iscachedbuild) {
|
||||
print STDERR " marked as cached build ", $build->id, "\n";
|
||||
addBuildProducts($db, $build);
|
||||
} else {
|
||||
print STDERR "added to queue as build ", $build->id, "\n";
|
||||
$build->create_related('buildschedulinginfo',
|
||||
{ priority => $priority
|
||||
, busy => 0
|
||||
, locker => ""
|
||||
});
|
||||
print STDERR " added to queue as build ", $build->id, "\n";
|
||||
}
|
||||
|
||||
my %inputs;
|
||||
@@ -906,24 +924,21 @@ sub restartBuild {
|
||||
my ($db, $build) = @_;
|
||||
|
||||
txn_do($db, sub {
|
||||
my $drvpath = $build->drvpath ;
|
||||
my $outpath = $build->outpath ;
|
||||
my $drvpath = $build->drvpath;
|
||||
my $outpath = $build->outpath;
|
||||
|
||||
my $paths = "";
|
||||
foreach my $bs ($build->buildsteps) {
|
||||
$paths = $paths . " " . $bs->outpath;
|
||||
$paths = $paths . " " . $bs->outpath;
|
||||
}
|
||||
|
||||
my $r = `nix-store --clear-failed-paths $paths $outpath`;
|
||||
$build->update({finished => 0, timestamp => time});
|
||||
|
||||
$build->resultInfo->delete;
|
||||
|
||||
$db->resultset('BuildSchedulingInfo')->create(
|
||||
{ id => $build->id
|
||||
, priority => 0 # don't know the original priority anymore...
|
||||
$build->update(
|
||||
{ finished => 0
|
||||
, timestamp => time
|
||||
, busy => 0
|
||||
, locker => ""
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8,20 +8,26 @@ use Hydra::Helper::Nix;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
our @EXPORT = qw(
|
||||
getBuild getPreviousBuild getNextBuild getPreviousSuccessfulBuild getBuildStats joinWithResultInfo getChannelData
|
||||
getBuild getPreviousBuild getNextBuild getPreviousSuccessfulBuild getBuildStats getChannelData
|
||||
error notFound
|
||||
requireLogin requireProjectOwner requireAdmin requirePost isAdmin isProjectOwner
|
||||
trim
|
||||
$pathCompRE $relPathRE $relNameRE $jobNameRE $systemRE
|
||||
@buildListColumns
|
||||
);
|
||||
|
||||
|
||||
# Columns from the Builds table needed to render build lists.
|
||||
Readonly our @buildListColumns => ('id', 'finished', 'timestamp', 'project', 'jobset', 'job', 'nixname', 'system', 'priority', 'busy', 'buildstatus', 'releasename');
|
||||
|
||||
|
||||
sub getBuild {
|
||||
my ($c, $id) = @_;
|
||||
my $build = $c->model('DB::Builds')->find($id);
|
||||
return $build;
|
||||
}
|
||||
|
||||
|
||||
sub getPreviousBuild {
|
||||
my ($c, $build) = @_;
|
||||
return undef if !defined $build;
|
||||
@@ -38,6 +44,7 @@ sub getPreviousBuild {
|
||||
return $prevBuild;
|
||||
}
|
||||
|
||||
|
||||
sub getNextBuild {
|
||||
my ($c, $build) = @_;
|
||||
return undef if !defined $build;
|
||||
@@ -54,11 +61,12 @@ sub getNextBuild {
|
||||
return $nextBuild;
|
||||
}
|
||||
|
||||
|
||||
sub getPreviousSuccessfulBuild {
|
||||
my ($c, $build) = @_;
|
||||
return undef if !defined $build;
|
||||
|
||||
(my $prevBuild) = joinWithResultInfo($c, $c->model('DB::Builds'))->search(
|
||||
(my $prevBuild) = $c->model('DB::Builds')->search(
|
||||
{ finished => 1
|
||||
, system => $build->system
|
||||
, project => $build->project->name
|
||||
@@ -71,75 +79,26 @@ sub getPreviousSuccessfulBuild {
|
||||
return $prevBuild;
|
||||
}
|
||||
|
||||
|
||||
sub getBuildStats {
|
||||
my ($c, $builds) = @_;
|
||||
|
||||
$c->stash->{finishedBuilds} = $builds->search({finished => 1}) || 0;
|
||||
|
||||
$c->stash->{succeededBuilds} = $builds->search(
|
||||
{finished => 1, buildStatus => 0},
|
||||
{join => 'resultInfo'}) || 0;
|
||||
$c->stash->{succeededBuilds} = $builds->search({finished => 1, buildStatus => 0}) || 0;
|
||||
|
||||
$c->stash->{scheduledBuilds} = $builds->search({finished => 0}) || 0;
|
||||
|
||||
$c->stash->{busyBuilds} = $builds->search(
|
||||
{finished => 0, busy => 1},
|
||||
{join => 'schedulingInfo'}) || 0;
|
||||
$c->stash->{busyBuilds} = $builds->search({finished => 0, busy => 1}) || 0;
|
||||
|
||||
my $res;
|
||||
$res = $builds->search({},
|
||||
{join => 'resultInfo', select => {sum => 'stoptime - starttime'}, as => ['sum']})
|
||||
->first ;
|
||||
$res = $builds->search({}, {select => {sum => 'stoptime - starttime'}, as => ['sum']})->first;
|
||||
|
||||
$c->stash->{totalBuildTime} = defined ($res) ? $res->get_column('sum') : 0 ;
|
||||
|
||||
}
|
||||
|
||||
|
||||
# Add the releaseName and buildStatus attributes from the
|
||||
# BuildResultInfo table for each build.
|
||||
sub joinWithResultInfo {
|
||||
my ($c, $source) = @_;
|
||||
|
||||
return $source->search(
|
||||
{ },
|
||||
{ join => 'resultInfo'
|
||||
, '+select' => ["resultInfo.releasename", "resultInfo.buildstatus"]
|
||||
, '+as' => ["releasename", "buildStatus"]
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
sub getChannelData {
|
||||
my ($c, $builds) = @_;
|
||||
|
||||
my @builds2 = joinWithResultInfo($c, $builds)
|
||||
->search_literal("exists (select 1 from buildproducts where build = resultInfo.id and type = 'nix-build')");
|
||||
|
||||
my @storePaths = ();
|
||||
foreach my $build (@builds2) {
|
||||
next unless isValidPath($build->outpath);
|
||||
if (isValidPath($build->drvpath)) {
|
||||
# Adding `drvpath' implies adding `outpath' because of the
|
||||
# `--include-outputs' flag passed to `nix-store'.
|
||||
push @storePaths, $build->drvpath;
|
||||
} else {
|
||||
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
|
||||
# a hint to the binary patch generator. (It shouldn't try to
|
||||
# generate patches between builds for different systems.) It
|
||||
# would be nice if Nix stored this info for every path but it
|
||||
# doesn't.
|
||||
$c->stash->{systemForPath}->{$build->outpath} = $build->system;
|
||||
};
|
||||
|
||||
$c->stash->{storePaths} = [@storePaths];
|
||||
}
|
||||
|
||||
|
||||
sub error {
|
||||
my ($c, $msg) = @_;
|
||||
$c->error($msg);
|
||||
@@ -161,12 +120,14 @@ sub requireLogin {
|
||||
$c->detach; # doesn't return
|
||||
}
|
||||
|
||||
|
||||
sub isProjectOwner {
|
||||
my ($c, $project) = @_;
|
||||
|
||||
return $c->user_exists && ($c->check_user_roles('admin') || $c->user->username eq $project->owner->username || defined $c->model('DB::ProjectMembers')->find({ project => $project, userName => $c->user->username }));
|
||||
}
|
||||
|
||||
|
||||
sub requireProjectOwner {
|
||||
my ($c, $project) = @_;
|
||||
|
||||
@@ -183,6 +144,7 @@ sub isAdmin {
|
||||
return $c->user_exists && $c->check_user_roles('admin');
|
||||
}
|
||||
|
||||
|
||||
sub requireAdmin {
|
||||
my ($c) = @_;
|
||||
|
||||
@@ -207,12 +169,12 @@ sub trim {
|
||||
|
||||
|
||||
# Security checking of filenames.
|
||||
Readonly::Scalar our $pathCompRE => "(?:[A-Za-z0-9-\+\._][A-Za-z0-9-\+\._]*)";
|
||||
Readonly::Scalar our $relPathRE => "(?:$pathCompRE(?:/$pathCompRE)*)";
|
||||
Readonly::Scalar our $relNameRE => "(?:[A-Za-z0-9-][A-Za-z0-9-\.]*)";
|
||||
Readonly::Scalar our $attrNameRE => "(?:[A-Za-z_][A-Za-z0-9_]*)";
|
||||
Readonly::Scalar our $jobNameRE => "(?:$attrNameRE(?:\\.$attrNameRE)*)";
|
||||
Readonly::Scalar our $systemRE => "(?:[a-z0-9_]+-[a-z0-9_]+)";
|
||||
Readonly our $pathCompRE => "(?:[A-Za-z0-9-\+\._][A-Za-z0-9-\+\._]*)";
|
||||
Readonly our $relPathRE => "(?:$pathCompRE(?:/$pathCompRE)*)";
|
||||
Readonly our $relNameRE => "(?:[A-Za-z0-9-][A-Za-z0-9-\.]*)";
|
||||
Readonly our $attrNameRE => "(?:[A-Za-z_][A-Za-z0-9_]*)";
|
||||
Readonly our $jobNameRE => "(?:$attrNameRE(?:\\.$attrNameRE)*)";
|
||||
Readonly our $systemRE => "(?:[a-z0-9_]+-[a-z0-9_]+)";
|
||||
|
||||
|
||||
1;
|
||||
|
||||
@@ -121,9 +121,7 @@ sub allPrimaryBuilds {
|
||||
my ($project, $primaryJob) = @_;
|
||||
my $allPrimaryBuilds = $project->builds->search(
|
||||
{ jobset => $primaryJob->get_column('jobset'), job => $primaryJob->get_column('job'), finished => 1 },
|
||||
{ join => 'resultInfo', order_by => "timestamp DESC"
|
||||
, '+select' => ["resultInfo.releasename", "resultInfo.buildstatus"]
|
||||
, '+as' => ["releasename", "buildstatus"]
|
||||
{ order_by => "timestamp DESC"
|
||||
, where => \ attrsToSQL($primaryJob->attrs, "me.id")
|
||||
});
|
||||
return $allPrimaryBuilds;
|
||||
@@ -165,10 +163,9 @@ sub findLastJobForBuilds {
|
||||
{
|
||||
$thisBuild = $ev->builds->find(
|
||||
{ job => $job->get_column('job'), finished => 1 },
|
||||
{ join => 'resultInfo', rows => 1
|
||||
{ rows => 1
|
||||
, order_by => ["build.id"]
|
||||
, where => \ attrsToSQL($job->attrs, "build.id")
|
||||
, '+select' => ["resultInfo.buildstatus"], '+as' => ["buildstatus"]
|
||||
});
|
||||
}
|
||||
|
||||
@@ -180,30 +177,31 @@ sub findLastJobForBuilds {
|
||||
{ project => $project, jobset => $jobset
|
||||
, job => $job->get_column('job'), finished => 1
|
||||
},
|
||||
{ join => 'resultInfo', rows => 1
|
||||
{ rows => 1
|
||||
, order_by => ["buildstatus", "timestamp"]
|
||||
, where => \ attrsToSQL($job->attrs, "build.id")
|
||||
, '+select' => ["resultInfo.buildstatus"], '+as' => ["buildstatus"]
|
||||
})
|
||||
unless defined $thisBuild;
|
||||
|
||||
return $thisBuild;
|
||||
}
|
||||
|
||||
|
||||
sub jobsetOverview {
|
||||
my ($c, $project) = @_;
|
||||
return $project->jobsets->search( isProjectOwner($c, $project) ? {} : { hidden => 0 },
|
||||
{ order_by => "name"
|
||||
, "+select" =>
|
||||
[ "(SELECT COUNT(*) FROM Builds AS a NATURAL JOIN BuildSchedulingInfo WHERE me.project = a.project AND me.name = a.jobset AND a.isCurrent = 1)"
|
||||
, "(SELECT COUNT(*) FROM Builds AS a NATURAL JOIN BuildResultInfo WHERE me.project = a.project AND me.name = a.jobset AND buildstatus <> 0 AND a.isCurrent = 1)"
|
||||
, "(SELECT COUNT(*) FROM Builds AS a NATURAL JOIN BuildResultInfo WHERE me.project = a.project AND me.name = a.jobset AND buildstatus = 0 AND a.isCurrent = 1)"
|
||||
, "(SELECT COUNT(*) FROM Builds AS a WHERE me.project = a.project AND me.name = a.jobset AND a.isCurrent = 1)"
|
||||
[ "(select count(*) from Builds as a where a.finished = 0 and me.project = a.project and me.name = a.jobset and a.isCurrent = 1)"
|
||||
, "(select count(*) from Builds as a where a.finished = 1 and me.project = a.project and me.name = a.jobset and buildstatus <> 0 and a.isCurrent = 1)"
|
||||
, "(select count(*) from Builds as a where a.finished = 1 and me.project = a.project and me.name = a.jobset and buildstatus = 0 and a.isCurrent = 1)"
|
||||
, "(select count(*) from Builds as a where me.project = a.project and me.name = a.jobset and a.isCurrent = 1)"
|
||||
]
|
||||
, "+as" => ["nrscheduled", "nrfailed", "nrsucceeded", "nrtotal"]
|
||||
});
|
||||
, "+as" => ["nrscheduled", "nrfailed", "nrsucceeded", "nrtotal"]
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
sub getViewResult {
|
||||
my ($primaryBuild, $jobs) = @_;
|
||||
|
||||
@@ -258,10 +256,12 @@ sub getLatestSuccessfulViewResult {
|
||||
return undef;
|
||||
}
|
||||
|
||||
|
||||
sub removeAsciiEscapes {
|
||||
my ($logtext) = @_;
|
||||
$logtext =~ s/\e\[[0-9]*[A-Za-z]//g;
|
||||
return $logtext;
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
||||
@@ -6,7 +6,10 @@ use Hydra::Helper::Nix;
|
||||
|
||||
__PACKAGE__->config(
|
||||
schema_class => 'Hydra::Schema',
|
||||
connect_info => [getHydraDBPath],
|
||||
connect_info => {
|
||||
dsn => getHydraDBPath,
|
||||
pg_server_prepare => 0,
|
||||
},
|
||||
);
|
||||
|
||||
=head1 NAME
|
||||
|
||||
@@ -46,7 +46,7 @@ __PACKAGE__->table("BuildProducts");
|
||||
|
||||
=head2 filesize
|
||||
|
||||
data_type: 'integer'
|
||||
data_type: 'bigint'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 sha1hash
|
||||
@@ -91,7 +91,7 @@ __PACKAGE__->add_columns(
|
||||
"subtype",
|
||||
{ data_type => "text", is_nullable => 0 },
|
||||
"filesize",
|
||||
{ data_type => "integer", is_nullable => 1 },
|
||||
{ data_type => "bigint", is_nullable => 1 },
|
||||
"sha1hash",
|
||||
{ data_type => "text", is_nullable => 1 },
|
||||
"sha256hash",
|
||||
@@ -133,8 +133,8 @@ Related object: L<Hydra::Schema::Builds>
|
||||
__PACKAGE__->belongs_to("build", "Hydra::Schema::Builds", { id => "build" }, {});
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2011-12-05 14:15:43
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:suSgQkBLXzu0yD4YicRS1A
|
||||
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2012-02-29 00:47:18
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:dzTKwZ7bby7kplnSgta3Gw
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
1;
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
use utf8;
|
||||
package Hydra::Schema::BuildResultInfo;
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader
|
||||
# DO NOT MODIFY THE FIRST PART OF THIS FILE
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Hydra::Schema::BuildResultInfo
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class::Core';
|
||||
|
||||
=head1 TABLE: C<BuildResultInfo>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->table("BuildResultInfo");
|
||||
|
||||
=head1 ACCESSORS
|
||||
|
||||
=head2 id
|
||||
|
||||
data_type: 'integer'
|
||||
is_auto_increment: 1
|
||||
is_foreign_key: 1
|
||||
is_nullable: 0
|
||||
|
||||
=head2 iscachedbuild
|
||||
|
||||
data_type: 'integer'
|
||||
is_nullable: 0
|
||||
|
||||
=head2 buildstatus
|
||||
|
||||
data_type: 'integer'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 errormsg
|
||||
|
||||
data_type: 'text'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 starttime
|
||||
|
||||
data_type: 'integer'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 stoptime
|
||||
|
||||
data_type: 'integer'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 logfile
|
||||
|
||||
data_type: 'text'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 logsize
|
||||
|
||||
data_type: 'bigint'
|
||||
default_value: 0
|
||||
is_nullable: 0
|
||||
|
||||
=head2 size
|
||||
|
||||
data_type: 'bigint'
|
||||
default_value: 0
|
||||
is_nullable: 0
|
||||
|
||||
=head2 closuresize
|
||||
|
||||
data_type: 'bigint'
|
||||
default_value: 0
|
||||
is_nullable: 0
|
||||
|
||||
=head2 releasename
|
||||
|
||||
data_type: 'text'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 keep
|
||||
|
||||
data_type: 'integer'
|
||||
default_value: 0
|
||||
is_nullable: 0
|
||||
|
||||
=head2 faileddepbuild
|
||||
|
||||
data_type: 'integer'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 faileddepstepnr
|
||||
|
||||
data_type: 'integer'
|
||||
is_nullable: 1
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
"id",
|
||||
{
|
||||
data_type => "integer",
|
||||
is_auto_increment => 1,
|
||||
is_foreign_key => 1,
|
||||
is_nullable => 0,
|
||||
},
|
||||
"iscachedbuild",
|
||||
{ data_type => "integer", is_nullable => 0 },
|
||||
"buildstatus",
|
||||
{ data_type => "integer", is_nullable => 1 },
|
||||
"errormsg",
|
||||
{ data_type => "text", is_nullable => 1 },
|
||||
"starttime",
|
||||
{ data_type => "integer", is_nullable => 1 },
|
||||
"stoptime",
|
||||
{ data_type => "integer", is_nullable => 1 },
|
||||
"logfile",
|
||||
{ data_type => "text", is_nullable => 1 },
|
||||
"logsize",
|
||||
{ data_type => "bigint", default_value => 0, is_nullable => 0 },
|
||||
"size",
|
||||
{ data_type => "bigint", default_value => 0, is_nullable => 0 },
|
||||
"closuresize",
|
||||
{ data_type => "bigint", default_value => 0, is_nullable => 0 },
|
||||
"releasename",
|
||||
{ data_type => "text", is_nullable => 1 },
|
||||
"keep",
|
||||
{ data_type => "integer", default_value => 0, is_nullable => 0 },
|
||||
"faileddepbuild",
|
||||
{ data_type => "integer", is_nullable => 1 },
|
||||
"faileddepstepnr",
|
||||
{ data_type => "integer", is_nullable => 1 },
|
||||
);
|
||||
|
||||
=head1 PRIMARY KEY
|
||||
|
||||
=over 4
|
||||
|
||||
=item * L</id>
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->set_primary_key("id");
|
||||
|
||||
=head1 RELATIONS
|
||||
|
||||
=head2 id
|
||||
|
||||
Type: belongs_to
|
||||
|
||||
Related object: L<Hydra::Schema::Builds>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->belongs_to("id", "Hydra::Schema::Builds", { id => "id" }, {});
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2011-12-05 14:15:43
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:hX3+iQYrGslQqY9vKvyw3g
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
"failedDep",
|
||||
"Hydra::Schema::BuildSteps",
|
||||
{ build => "faileddepbuild", stepnr => "faileddepstepnr" },
|
||||
);
|
||||
|
||||
1;
|
||||
@@ -1,120 +0,0 @@
|
||||
use utf8;
|
||||
package Hydra::Schema::BuildSchedulingInfo;
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader
|
||||
# DO NOT MODIFY THE FIRST PART OF THIS FILE
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Hydra::Schema::BuildSchedulingInfo
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class::Core';
|
||||
|
||||
=head1 TABLE: C<BuildSchedulingInfo>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->table("BuildSchedulingInfo");
|
||||
|
||||
=head1 ACCESSORS
|
||||
|
||||
=head2 id
|
||||
|
||||
data_type: 'integer'
|
||||
is_auto_increment: 1
|
||||
is_foreign_key: 1
|
||||
is_nullable: 0
|
||||
|
||||
=head2 priority
|
||||
|
||||
data_type: 'integer'
|
||||
default_value: 0
|
||||
is_nullable: 0
|
||||
|
||||
=head2 busy
|
||||
|
||||
data_type: 'integer'
|
||||
default_value: 0
|
||||
is_nullable: 0
|
||||
|
||||
=head2 locker
|
||||
|
||||
data_type: 'text'
|
||||
default_value: (empty string)
|
||||
is_nullable: 0
|
||||
|
||||
=head2 logfile
|
||||
|
||||
data_type: 'text'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 disabled
|
||||
|
||||
data_type: 'integer'
|
||||
default_value: 0
|
||||
is_nullable: 0
|
||||
|
||||
=head2 starttime
|
||||
|
||||
data_type: 'integer'
|
||||
is_nullable: 1
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
"id",
|
||||
{
|
||||
data_type => "integer",
|
||||
is_auto_increment => 1,
|
||||
is_foreign_key => 1,
|
||||
is_nullable => 0,
|
||||
},
|
||||
"priority",
|
||||
{ data_type => "integer", default_value => 0, is_nullable => 0 },
|
||||
"busy",
|
||||
{ data_type => "integer", default_value => 0, is_nullable => 0 },
|
||||
"locker",
|
||||
{ data_type => "text", default_value => "", is_nullable => 0 },
|
||||
"logfile",
|
||||
{ data_type => "text", is_nullable => 1 },
|
||||
"disabled",
|
||||
{ data_type => "integer", default_value => 0, is_nullable => 0 },
|
||||
"starttime",
|
||||
{ data_type => "integer", is_nullable => 1 },
|
||||
);
|
||||
|
||||
=head1 PRIMARY KEY
|
||||
|
||||
=over 4
|
||||
|
||||
=item * L</id>
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->set_primary_key("id");
|
||||
|
||||
=head1 RELATIONS
|
||||
|
||||
=head2 id
|
||||
|
||||
Type: belongs_to
|
||||
|
||||
Related object: L<Hydra::Schema::Builds>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->belongs_to("id", "Hydra::Schema::Builds", { id => "id" }, {});
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2011-12-05 14:15:43
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Uz7y9Ly+ADRrtrPfEk9lGA
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
1;
|
||||
@@ -151,10 +151,4 @@ __PACKAGE__->belongs_to("build", "Hydra::Schema::Builds", { id => "build" }, {})
|
||||
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2011-12-05 14:15:43
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:5H+OkGT0zQEWkAjU+OlBdg
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
"schedulingInfo",
|
||||
"Hydra::Schema::BuildSchedulingInfo",
|
||||
{ id => "build" },
|
||||
);
|
||||
|
||||
1;
|
||||
|
||||
@@ -130,6 +130,85 @@ __PACKAGE__->table("Builds");
|
||||
data_type: 'text'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 priority
|
||||
|
||||
data_type: 'integer'
|
||||
default_value: 0
|
||||
is_nullable: 0
|
||||
|
||||
=head2 busy
|
||||
|
||||
data_type: 'integer'
|
||||
default_value: 0
|
||||
is_nullable: 0
|
||||
|
||||
=head2 locker
|
||||
|
||||
data_type: 'text'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 logfile
|
||||
|
||||
data_type: 'text'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 disabled
|
||||
|
||||
data_type: 'integer'
|
||||
default_value: 0
|
||||
is_nullable: 0
|
||||
|
||||
=head2 starttime
|
||||
|
||||
data_type: 'integer'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 stoptime
|
||||
|
||||
data_type: 'integer'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 iscachedbuild
|
||||
|
||||
data_type: 'integer'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 buildstatus
|
||||
|
||||
data_type: 'integer'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 errormsg
|
||||
|
||||
data_type: 'text'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 logsize
|
||||
|
||||
data_type: 'bigint'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 size
|
||||
|
||||
data_type: 'bigint'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 closuresize
|
||||
|
||||
data_type: 'bigint'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 releasename
|
||||
|
||||
data_type: 'text'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 keep
|
||||
|
||||
data_type: 'integer'
|
||||
default_value: 0
|
||||
is_nullable: 0
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
@@ -173,6 +252,36 @@ __PACKAGE__->add_columns(
|
||||
{ data_type => "text", is_nullable => 1 },
|
||||
"nixexprpath",
|
||||
{ data_type => "text", is_nullable => 1 },
|
||||
"priority",
|
||||
{ data_type => "integer", default_value => 0, is_nullable => 0 },
|
||||
"busy",
|
||||
{ data_type => "integer", default_value => 0, is_nullable => 0 },
|
||||
"locker",
|
||||
{ data_type => "text", is_nullable => 1 },
|
||||
"logfile",
|
||||
{ data_type => "text", is_nullable => 1 },
|
||||
"disabled",
|
||||
{ data_type => "integer", default_value => 0, is_nullable => 0 },
|
||||
"starttime",
|
||||
{ data_type => "integer", is_nullable => 1 },
|
||||
"stoptime",
|
||||
{ data_type => "integer", is_nullable => 1 },
|
||||
"iscachedbuild",
|
||||
{ data_type => "integer", is_nullable => 1 },
|
||||
"buildstatus",
|
||||
{ data_type => "integer", is_nullable => 1 },
|
||||
"errormsg",
|
||||
{ data_type => "text", is_nullable => 1 },
|
||||
"logsize",
|
||||
{ data_type => "bigint", is_nullable => 1 },
|
||||
"size",
|
||||
{ data_type => "bigint", is_nullable => 1 },
|
||||
"closuresize",
|
||||
{ data_type => "bigint", is_nullable => 1 },
|
||||
"releasename",
|
||||
{ data_type => "text", is_nullable => 1 },
|
||||
"keep",
|
||||
{ data_type => "integer", default_value => 0, is_nullable => 0 },
|
||||
);
|
||||
|
||||
=head1 PRIMARY KEY
|
||||
@@ -234,36 +343,6 @@ __PACKAGE__->has_many(
|
||||
{},
|
||||
);
|
||||
|
||||
=head2 buildresultinfo
|
||||
|
||||
Type: might_have
|
||||
|
||||
Related object: L<Hydra::Schema::BuildResultInfo>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->might_have(
|
||||
"buildresultinfo",
|
||||
"Hydra::Schema::BuildResultInfo",
|
||||
{ "foreign.id" => "self.id" },
|
||||
{},
|
||||
);
|
||||
|
||||
=head2 buildschedulinginfo
|
||||
|
||||
Type: might_have
|
||||
|
||||
Related object: L<Hydra::Schema::BuildSchedulingInfo>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->might_have(
|
||||
"buildschedulinginfo",
|
||||
"Hydra::Schema::BuildSchedulingInfo",
|
||||
{ "foreign.id" => "self.id" },
|
||||
{},
|
||||
);
|
||||
|
||||
=head2 buildsteps
|
||||
|
||||
Type: has_many
|
||||
@@ -350,8 +429,8 @@ __PACKAGE__->has_many(
|
||||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2011-12-05 14:15:43
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:RRtBPTdD946kA5133+c4kw
|
||||
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2012-02-29 18:56:22
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:w16c86FRReLPdA8H0yTIRg
|
||||
|
||||
use Hydra::Helper::Nix;
|
||||
|
||||
@@ -369,18 +448,6 @@ __PACKAGE__->has_many(
|
||||
{ "foreign.build" => "self.id" },
|
||||
);
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
"schedulingInfo",
|
||||
"Hydra::Schema::BuildSchedulingInfo",
|
||||
{ id => "id" },
|
||||
);
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
"resultInfo",
|
||||
"Hydra::Schema::BuildResultInfo",
|
||||
{ id => "id" },
|
||||
);
|
||||
|
||||
__PACKAGE__->has_one(
|
||||
"actualBuildStep",
|
||||
"Hydra::Schema::BuildSteps",
|
||||
@@ -408,35 +475,16 @@ sub makeSource {
|
||||
sub makeQueries {
|
||||
my ($name, $constraint) = @_;
|
||||
|
||||
my $joinWithStatusChange =
|
||||
<<QUERY;
|
||||
natural join BuildResultInfo r
|
||||
left join Builds b on
|
||||
b.id =
|
||||
(select max(id)
|
||||
from builds c natural join buildresultinfo r2
|
||||
where
|
||||
x.project = c.project and x.jobset = c.jobset and x.job = c.job and x.system = c.system and
|
||||
x.id > c.id and
|
||||
((r.buildstatus = 0 and r2.buildstatus != 0) or
|
||||
(r.buildstatus != 0 and r2.buildstatus = 0)))
|
||||
QUERY
|
||||
|
||||
my $activeJobs = "(select distinct project, jobset, job, system from Builds where isCurrent = 1 $constraint)";
|
||||
|
||||
makeSource(
|
||||
"JobStatus$name",
|
||||
# Urgh, can't use "*" in the "select" here because of the status change join.
|
||||
<<QUERY
|
||||
select
|
||||
x.id, x.finished, x.timestamp, x.project, x.jobset, x.job, x.nixname,
|
||||
x.description, x.drvpath, x.outpath, x.system, x.longdescription,
|
||||
x.license, x.homepage, x.maintainers, x.isCurrent, x.nixExprInput,
|
||||
x.nixExprPath, x.maxsilent, x.timeout,
|
||||
b.id as statusChangeId, b.timestamp as statusChangeTime
|
||||
select x.*, b.id as statusChangeId, b.timestamp as statusChangeTime
|
||||
from
|
||||
(select
|
||||
(select max(id) from builds b
|
||||
(select max(b.id) from Builds b
|
||||
where
|
||||
project = activeJobs.project and jobset = activeJobs.jobset
|
||||
and job = activeJobs.job and system = activeJobs.system
|
||||
@@ -445,7 +493,15 @@ QUERY
|
||||
from $activeJobs as activeJobs
|
||||
) as latest
|
||||
join Builds x using (id)
|
||||
$joinWithStatusChange
|
||||
left join Builds b on
|
||||
b.id =
|
||||
(select max(c.id) from Builds c
|
||||
where
|
||||
c.finished = 1 and
|
||||
x.project = c.project and x.jobset = c.jobset and x.job = c.job and x.system = c.system and
|
||||
x.id > c.id and
|
||||
((x.buildStatus = 0 and c.buildStatus != 0) or
|
||||
(x.buildStatus != 0 and c.buildStatus = 0)))
|
||||
QUERY
|
||||
);
|
||||
|
||||
@@ -457,12 +513,11 @@ QUERY
|
||||
select *
|
||||
from
|
||||
(select
|
||||
(select max(id) from builds b
|
||||
(select max(b.id) from builds b
|
||||
where
|
||||
project = activeJobs.project and jobset = activeJobs.jobset
|
||||
and job = activeJobs.job and system = activeJobs.system
|
||||
and finished = 1
|
||||
and exists (select 1 from buildresultinfo where id = b.id and buildstatus = 0)
|
||||
and finished = 1 and buildstatus = 0
|
||||
) as id
|
||||
from $activeJobs as activeJobs
|
||||
) as latest
|
||||
|
||||
@@ -161,5 +161,4 @@ __PACKAGE__->has_many(
|
||||
|
||||
__PACKAGE__->many_to_many(builds => 'buildIds', 'build');
|
||||
|
||||
# You can replace this text with custom content, and it will be preserved on regeneration
|
||||
1;
|
||||
|
||||
@@ -26,31 +26,15 @@ __PACKAGE__->table("SchemaVersion");
|
||||
=head2 version
|
||||
|
||||
data_type: 'integer'
|
||||
is_auto_increment: 1
|
||||
is_nullable: 0
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
"version",
|
||||
{ data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
|
||||
);
|
||||
|
||||
=head1 PRIMARY KEY
|
||||
|
||||
=over 4
|
||||
|
||||
=item * L</version>
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->set_primary_key("version");
|
||||
__PACKAGE__->add_columns("version", { data_type => "integer", is_nullable => 0 });
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2011-12-05 14:15:43
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:F/jsSRq8pxR4mWq/N4qYGw
|
||||
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2012-02-29 00:47:18
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:LFD28W0GvvrOOylCM98SEQ
|
||||
|
||||
|
||||
# You can replace this text with custom code or comments, and it will be preserved on regeneration
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
[% project = build.project %]
|
||||
[% jobset = build.jobset %]
|
||||
[% job = build.job %]
|
||||
[% resultInfo = build.resultInfo %]
|
||||
|
||||
[% BLOCK renderBuildSteps %]
|
||||
|
||||
@@ -35,7 +34,7 @@
|
||||
[% INCLUDE renderDuration duration = step.stoptime - step.starttime %]
|
||||
[% ELSE %]
|
||||
[% IF build.finished %]
|
||||
[% INCLUDE renderDuration duration = resultInfo.stoptime - step.starttime %]
|
||||
[% INCLUDE renderDuration duration = build.stoptime - step.starttime %]
|
||||
[% ELSE %]
|
||||
[% INCLUDE renderDuration duration = curTime - step.starttime %]
|
||||
[% END %]
|
||||
@@ -70,7 +69,7 @@
|
||||
<h1>
|
||||
Job <tt>[% project.name %]:[% jobset.name %]:[% job.name %]</tt> build [% id %]
|
||||
[% IF !build.finished %]
|
||||
[% IF build.schedulingInfo.busy %]
|
||||
[% IF build.busy %]
|
||||
(currently building)
|
||||
[% ELSE %]
|
||||
(scheduled)
|
||||
@@ -106,10 +105,10 @@
|
||||
<th>Build ID:</th>
|
||||
<td>[% build.id %]</td>
|
||||
</tr>
|
||||
[% IF resultInfo.releasename %]
|
||||
[% IF build.releasename %]
|
||||
<tr>
|
||||
<th>Release name:</th>
|
||||
<td><tt>[% HTML.escape(resultInfo.releasename) %]</tt></td>
|
||||
<td><tt>[% HTML.escape(build.releasename) %]</tt></td>
|
||||
</tr>
|
||||
[% ELSE %]
|
||||
<tr>
|
||||
@@ -127,19 +126,19 @@
|
||||
<th>System:</th>
|
||||
<td><tt>[% build.system %]</tt></td>
|
||||
</tr>
|
||||
[% IF !build.schedulingInfo %]
|
||||
[% IF !build.finished %]
|
||||
<tr>
|
||||
<th>Duration:</th>
|
||||
<td>
|
||||
[% IF resultInfo.iscachedbuild %]
|
||||
[% IF build.iscachedbuild %]
|
||||
(cached[% IF cachedBuild %] from [% INCLUDE renderFullBuildLink build=cachedBuild %][% END %])
|
||||
[% ELSE %]
|
||||
[% INCLUDE renderDuration duration = resultInfo.stoptime - resultInfo.starttime %] <tt>finished at [% INCLUDE renderDateTime timestamp = resultInfo.stoptime %]</tt>
|
||||
[% INCLUDE renderDuration duration = build.stoptime - build.starttime %] <tt>finished at [% INCLUDE renderDateTime timestamp = build.stoptime %]</tt>
|
||||
[% END %]
|
||||
</td>
|
||||
</tr>
|
||||
[% END %]
|
||||
[% IF resultInfo.logfile %]
|
||||
[% IF build.logfile %]
|
||||
<tr>
|
||||
<th>Logfile:</th>
|
||||
<td>
|
||||
@@ -182,7 +181,7 @@
|
||||
[% END %]
|
||||
|
||||
[% IF build.finished %]
|
||||
[% IF build.buildsteps && resultInfo.buildstatus != 0 && resultInfo.buildstatus != 6 %]
|
||||
[% IF build.buildsteps && build.buildstatus != 0 && build.buildstatus != 6 %]
|
||||
[% INCLUDE renderBuildSteps type="Failed" %]
|
||||
[% END %]
|
||||
|
||||
@@ -214,11 +213,11 @@
|
||||
</table>
|
||||
[% END %]
|
||||
|
||||
[% IF resultInfo.errormsg && resultInfo.buildstatus != 5 %]
|
||||
[% IF build.errormsg && build.buildstatus != 5 %]
|
||||
|
||||
<h2 id="nix-error">Nix error output</h2>
|
||||
|
||||
<pre class="buildlog">[% HTML.escape(resultInfo.errormsg) -%]</pre>
|
||||
<pre class="buildlog">[% HTML.escape(build.errormsg) -%]</pre>
|
||||
[% END %]
|
||||
[% END %]
|
||||
[% IF logtext %]
|
||||
@@ -264,10 +263,10 @@
|
||||
<th>Nix name:</th>
|
||||
<td><tt>[% build.nixname %]</tt></td>
|
||||
</tr>
|
||||
[% IF resultInfo.releasename %]
|
||||
[% IF build.releasename %]
|
||||
<tr>
|
||||
<th>Release name:</th>
|
||||
<td><tt>[% HTML.escape(resultInfo.releasename) %]</tt></td>
|
||||
<td><tt>[% HTML.escape(build.releasename) %]</tt></td>
|
||||
</tr>
|
||||
[% END %]
|
||||
<tr>
|
||||
@@ -316,18 +315,20 @@
|
||||
[% END %]
|
||||
</td>
|
||||
</tr>
|
||||
[% IF pathHash %]
|
||||
<tr>
|
||||
<th>Output store path hash:</th>
|
||||
<td>
|
||||
<tt>[% pathHash %]</tt>
|
||||
</td>
|
||||
</tr>
|
||||
[% END %]
|
||||
<tr>
|
||||
<th>Time added:</th>
|
||||
<td>[% INCLUDE renderDateTime timestamp = build.timestamp %]</td>
|
||||
</tr>
|
||||
[% IF build.finished && resultInfo.buildstatus != 4 %]
|
||||
[% IF resultInfo.iscachedbuild && cachedBuild %]
|
||||
[% IF build.finished && build.buildstatus != 4 %]
|
||||
[% IF build.iscachedbuild && cachedBuild %]
|
||||
<tr>
|
||||
<th>Cached build:</th>
|
||||
<td>[% INCLUDE renderFullBuildLink build=cachedBuild %]</td>
|
||||
@@ -336,23 +337,23 @@
|
||||
|
||||
<tr>
|
||||
<th>Build started:</th>
|
||||
<td>[% IF resultInfo.starttime %][% INCLUDE renderDateTime timestamp = resultInfo.starttime %][% ELSE %]<em>(cached build)</em>[% END %]</td>
|
||||
<td>[% IF build.starttime %][% INCLUDE renderDateTime timestamp = build.starttime %][% ELSE %]<em>(cached build)</em>[% END %]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Build finished:</th>
|
||||
<td>[% IF resultInfo.stoptime %][% INCLUDE renderDateTime timestamp = resultInfo.stoptime %][% ELSE %]<em>(cached build)</em>[% END %]</td>
|
||||
<td>[% IF build.stoptime %][% INCLUDE renderDateTime timestamp = build.stoptime %][% ELSE %]<em>(cached build)</em>[% END %]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Duration:</th>
|
||||
<td>
|
||||
[% IF resultInfo.iscachedbuild %]
|
||||
[% IF build.iscachedbuild %]
|
||||
<em>(cached build)</em>
|
||||
[% ELSE %]
|
||||
[% INCLUDE renderDuration duration = resultInfo.stoptime - resultInfo.starttime %]
|
||||
[% INCLUDE renderDuration duration = build.stoptime - build.starttime %]
|
||||
[% END %]
|
||||
</td>
|
||||
</tr>
|
||||
[% IF resultInfo.logfile %]
|
||||
[% IF build.logfile %]
|
||||
<tr>
|
||||
<th>Logfile:</th>
|
||||
<td>
|
||||
@@ -366,7 +367,7 @@
|
||||
[% IF !build.finished %]
|
||||
<tr>
|
||||
<th>Priority:</th>
|
||||
<td>[% build.schedulingInfo.priority %]</td>
|
||||
<td>[% build.priority %]</td>
|
||||
</tr>
|
||||
[% END %]
|
||||
[% IF build.finished && build.buildproducts %]
|
||||
@@ -375,7 +376,7 @@
|
||||
<td>
|
||||
[% IF !available %]
|
||||
<em>Build output is no longer available</em>
|
||||
[% ELSIF resultInfo.keep %]
|
||||
[% ELSIF build.keep %]
|
||||
<em>Build output will be kept permanently</em>
|
||||
[% IF c.user_exists %]
|
||||
<form action="[% c.uri_for('/build' build.id 'keep' 0) %]" method="post" class="inline">
|
||||
@@ -485,10 +486,9 @@
|
||||
$(function() {
|
||||
var d = [];
|
||||
var ids = [];
|
||||
[% FOREACH prevbuild IN prevBuilds %][% IF prevbuild.resultInfo.starttime != 0 %]
|
||||
[% pbResultInfo = prevbuild.resultInfo %]
|
||||
d.push([[% pbResultInfo.starttime * 1000 %],[% prevbuild.get_column('actualBuildTime') %]]);
|
||||
ids[[% pbResultInfo.starttime * 1000 %]] = [% prevbuild.id %] ;
|
||||
[% FOREACH prevbuild IN prevBuilds %][% IF prevbuild.build.starttime != 0 %]
|
||||
d.push([[% prevbuild.starttime * 1000 %],[% prevbuild.get_column('actualBuildTime') %]]);
|
||||
ids[[% prevbuild.starttime * 1000 %]] = [% prevbuild.id %] ;
|
||||
[% END %][% END %]
|
||||
|
||||
var options = {
|
||||
@@ -558,9 +558,9 @@
|
||||
$(function() {
|
||||
var d = [];
|
||||
var ids = [];
|
||||
[% FOREACH prevbuild IN prevBuilds %][% IF prevbuild.resultInfo.size != 0 %]
|
||||
d.push([[% prevbuild.resultInfo.starttime * 1000 %],[% prevbuild.resultInfo.size / (1024*1024.0) %]]);
|
||||
ids[[% prevbuild.resultInfo.starttime * 1000 %]] = [% prevbuild.id %] ;
|
||||
[% FOREACH prevbuild IN prevBuilds %][% IF prevbuild.size != 0 %]
|
||||
d.push([[% prevbuild.starttime * 1000 %],[% prevbuild.size / (1024*1024.0) %]]);
|
||||
ids[[% prevbuild.starttime * 1000 %]] = [% prevbuild.id %] ;
|
||||
[% END %][% END %]
|
||||
|
||||
var options = {
|
||||
|
||||
@@ -93,20 +93,20 @@
|
||||
[%- FOREACH build IN builds -%]
|
||||
<tr class="clickable
|
||||
[%- IF showSchedulingInfo -%]
|
||||
[%- IF build.get_column('busy') %]runningBuild[% ELSIF build.get_column('disabled') == 1 || build.get_column('enabled') == 0 %]disabledBuild[% END -%]
|
||||
[%- IF build.busy %]runningBuild[% ELSIF build.disabled == 1 || build.get_column('enabled') == 0 %]disabledBuild[% END -%]
|
||||
[%- ELSE -%]
|
||||
[%- IF odd %] odd [% END; odd = !odd -%]
|
||||
[%- END %]"
|
||||
onclick="if(event.which == 2) return true; window.location = '[% c.uri_for('/build' build.id) %]'">
|
||||
[%- IF !hideResultInfo -%]
|
||||
<td>
|
||||
[%- INCLUDE renderBuildStatusIcon size=16 busy=(showSchedulingInfo ? 1 : 0) buildstatus=build.get_column('buildStatus') -%]
|
||||
[%- INCLUDE renderBuildStatusIcon size=16 busy=(showSchedulingInfo ? 1 : 0) buildstatus=build.buildstatus -%]
|
||||
</td>
|
||||
[%- END -%]
|
||||
<td><a href="[% c.uri_for('/build' build.id) %]">[% build.id %]</a></td>
|
||||
[%- IF showSchedulingInfo -%]
|
||||
<td>[% IF build.get_column('busy') %]<img src="/static/images/running.gif" alt="Running" />[% ELSIF build.get_column('disabled') == 1 || build.get_column('enabled') == 0 %]Disabled[% END %]</td>
|
||||
<td>[% build.get_column('priority') %]</td>
|
||||
<td>[% IF build.busy %]<img src="/static/images/running.gif" alt="Running" />[% ELSIF build.disabled == 1 || build.get_column('enabled') == 0 %]Disabled[% END %]</td>
|
||||
<td>[% build.priority %]</td>
|
||||
[%- END -%]
|
||||
<td>[%- INCLUDE renderFullJobNameOfBuild -%]</td>
|
||||
<td>[% !showSchedulingInfo and build.get_column('releasename') ? build.get_column('releasename') : build.nixname %]</td>
|
||||
@@ -203,8 +203,8 @@
|
||||
|
||||
[%- BLOCK renderBuildStatusIcon -%]
|
||||
[%- finished = build != undef ? build.finished : 1 -%]
|
||||
[%- busy = busy != undef ? busy : build.schedulingInfo.busy -%]
|
||||
[%- buildstatus = buildstatus != undef ? buildstatus : build.resultInfo.buildstatus -%]
|
||||
[%- busy = busy != undef ? busy : build.busy -%]
|
||||
[%- buildstatus = buildstatus != undef ? buildstatus : build.buildstatus -%]
|
||||
[%- IF finished -%]
|
||||
[%- IF buildstatus == 0 -%]
|
||||
<img src="/static/images/checkmark_[% size %].png" alt="Succeeded" />
|
||||
@@ -230,7 +230,7 @@
|
||||
|
||||
[% BLOCK renderStatus %]
|
||||
[% IF build.finished %]
|
||||
[% buildstatus = build.resultInfo.buildstatus %]
|
||||
[% buildstatus = build.buildstatus %]
|
||||
[% INCLUDE renderBuildStatusIcon size=16 %]
|
||||
[% IF buildstatus == 0 %]
|
||||
<strong>Success</strong>
|
||||
@@ -242,8 +242,6 @@
|
||||
<span class="error">Cancelled by user</span>
|
||||
[% ELSIF buildstatus == 5 %]
|
||||
<span class="error">Build inhibited because a dependency previously failed to build</span>
|
||||
[% failedDep = build.resultInfo.failedDep %]
|
||||
(namely, <a href="[% c.uri_for('/build' failedDep.build.id 'nixlog' failedDep.stepnr) %]"><tt>[% failedDep.outpath %]</tt></a>)
|
||||
[% ELSIF buildstatus == 6 %]
|
||||
<span class="error">Build failed (with result)</span>
|
||||
[% ELSE %]
|
||||
@@ -255,9 +253,9 @@
|
||||
<button id="restart" type="submit">Restart</button>
|
||||
</form>
|
||||
[% END %]
|
||||
[% ELSIF build.schedulingInfo.busy %]
|
||||
[% ELSIF build.busy %]
|
||||
<strong>Build in progress</strong>
|
||||
since [% INCLUDE renderDateTime timestamp = build.schedulingInfo.starttime %]
|
||||
since [% INCLUDE renderDateTime timestamp = build.starttime %]
|
||||
[% ELSE %]
|
||||
<strong>Scheduled to be built</strong>
|
||||
[% IF c.user_exists %]
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
[% SWITCH product.type %]
|
||||
|
||||
[% CASE "nix-build" %]
|
||||
[% IF build.resultInfo.buildstatus == 6 %]
|
||||
[% IF build.buildstatus == 6 %]
|
||||
[% filename = "${build.nixname}.closure.gz" %]
|
||||
[% uri = c.uri_for('/build' build.id 'nix' 'closure' filename ) %]
|
||||
<tr class="product">
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
[% IF j.build %]
|
||||
|
||||
[% IF j.build.resultInfo.buildstatus == 0 %]
|
||||
[% IF j.build.buildstatus == 0 %]
|
||||
|
||||
[% IF j.build.buildproducts %]
|
||||
[% p = jobNames.${j.job.job} > 1 ? "-${j.build.system}" : "";
|
||||
|
||||
@@ -3,6 +3,7 @@ EXTRA_DIST = \
|
||||
$(bin_SCRIPTS)
|
||||
|
||||
bin_SCRIPTS = \
|
||||
hydra-init \
|
||||
hydra-build \
|
||||
hydra-evaluator \
|
||||
hydra-queue-runner \
|
||||
|
||||
@@ -17,7 +17,7 @@ use Text::Table;
|
||||
use POSIX qw(strftime);
|
||||
use Net::Twitter::Lite;
|
||||
use Data::Dump qw(dump);
|
||||
use Switch;
|
||||
use feature qw/switch/;
|
||||
|
||||
STDOUT->autoflush();
|
||||
|
||||
@@ -34,9 +34,9 @@ sub sendTwitterNotification {
|
||||
my $addURL = defined $config{'base_uri'};
|
||||
|
||||
my $jobName = $build->project->name . ":" . $build->jobset->name . ":" . $build->job->name;
|
||||
my $status = $build->resultInfo->buildstatus == 0 ? "SUCCEEDED" : "FAILED";
|
||||
my $status = $build->buildstatus == 0 ? "SUCCEEDED" : "FAILED";
|
||||
my $system = $build->system;
|
||||
my $duration = ($build->resultInfo->stoptime - $build->resultInfo->starttime) . " seconds";
|
||||
my $duration = ($build->stoptime - $build->starttime) . " seconds";
|
||||
my $url = $config{'base_uri'}."/build/".$build->id ;
|
||||
|
||||
my $nt = Net::Twitter::Lite->new(
|
||||
@@ -61,11 +61,11 @@ sub statusDescription {
|
||||
my ($buildstatus) = @_;
|
||||
|
||||
my $status = "Failed";
|
||||
switch ($buildstatus) {
|
||||
case 0 { $status = "Success"; }
|
||||
case 1 { $status = "Failed with non-zero exit code"; }
|
||||
case 2 { $status = "Dependency failed"; }
|
||||
case 4 { $status = "Cancelled"; }
|
||||
given ($buildstatus) {
|
||||
when (0) { $status = "Success"; }
|
||||
when (1) { $status = "Failed with non-zero exit code"; }
|
||||
when (2) { $status = "Dependency failed"; }
|
||||
when (4) { $status = "Cancelled"; }
|
||||
}
|
||||
|
||||
return $status;
|
||||
@@ -74,7 +74,7 @@ sub statusDescription {
|
||||
sub sendEmailNotification {
|
||||
my ($build) = @_;
|
||||
|
||||
die unless defined $build->resultInfo;
|
||||
die unless $build->finished;
|
||||
|
||||
return if ! ( $build->jobset->enableemail && ($build->maintainers ne "" || $build->jobset->emailoverride ne "") );
|
||||
|
||||
@@ -87,19 +87,18 @@ sub sendEmailNotification {
|
||||
, job => $build->job->name
|
||||
, system => $build->system
|
||||
, finished => 1
|
||||
, id => { '!=', $build->id }
|
||||
}, { order_by => ["timestamp DESC"] }
|
||||
|
||||
, id => { '<', $build->id }
|
||||
}, { order_by => ["id DESC"] }
|
||||
);
|
||||
|
||||
# if there is a previous build with same buildstatus, do not send email
|
||||
if (defined $prevBuild && ($build->resultInfo->buildstatus == $prevBuild->resultInfo->buildstatus)) {
|
||||
if (defined $prevBuild && ($build->buildstatus == $prevBuild->buildstatus)) {
|
||||
return;
|
||||
}
|
||||
|
||||
# if buildstatus of this build or the previous one is aborted, do
|
||||
# not send email
|
||||
if ($build->resultInfo->buildstatus == 3 || (defined $prevBuild && ($prevBuild->resultInfo->buildstatus == 3))) {
|
||||
if ($build->buildstatus == 3 || (defined $prevBuild && ($prevBuild->buildstatus == 3))) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -110,7 +109,7 @@ sub sendEmailNotification {
|
||||
|
||||
my $jobName = $build->project->name . ":" . $build->jobset->name . ":" . $build->job->name;
|
||||
|
||||
my $status = statusDescription($build->resultInfo->buildstatus);
|
||||
my $status = statusDescription($build->buildstatus);
|
||||
|
||||
my $baseurl = hostname_long ;
|
||||
my $sender = $config{'notification_sender'} ||
|
||||
@@ -132,10 +131,10 @@ sub sendEmailNotification {
|
||||
[ "Time added:", showTime $build->timestamp ],
|
||||
);
|
||||
push @lines, (
|
||||
[ "Build started:", showTime $build->resultInfo->starttime ],
|
||||
[ "Build finished:", showTime $build->resultInfo->stoptime ],
|
||||
[ "Duration:", $build->resultInfo->stoptime - $build->resultInfo->starttime . "s" ],
|
||||
) if $build->resultInfo->starttime;
|
||||
[ "Build started:", showTime $build->starttime ],
|
||||
[ "Build finished:", showTime $build->stoptime ],
|
||||
[ "Duration:", $build->stoptime - $build->starttime . "s" ],
|
||||
) if $build->starttime;
|
||||
$infoTable->load(@lines);
|
||||
|
||||
my $inputsTable = Text::Table->new(
|
||||
@@ -157,18 +156,18 @@ sub sendEmailNotification {
|
||||
$inputsTable->load(@lines);
|
||||
|
||||
my $loglines = 50;
|
||||
my $logfile = $build->resultInfo->logfile;
|
||||
my $logfile = $build->logfile;
|
||||
my $logtext = defined $logfile && -e $logfile ? `tail -$loglines $logfile` : "No logfile available.\n";
|
||||
$logtext = removeAsciiEscapes($logtext);
|
||||
|
||||
my $body = "Hi,\n"
|
||||
. "\n"
|
||||
. "This is to let you know that Hydra build " . $build->id
|
||||
. " of job " . $jobName . " " . (defined $prevBuild ? "has changed from '" . statusDescription($prevBuild->resultInfo->buildstatus) . "' to '$status'" : "is '$status'" ) .".\n"
|
||||
. " of job " . $jobName . " " . (defined $prevBuild ? "has changed from '" . statusDescription($prevBuild->buildstatus) . "' to '$status'" : "is '$status'" ) .".\n"
|
||||
. "\n"
|
||||
. "Complete build information can be found on this page: "
|
||||
. "$selfURI/build/" . $build->id . "\n"
|
||||
. ($build->resultInfo->buildstatus != 0 ? "\nThe last $loglines lines of the build log are shown at the bottom of this email.\n" : "")
|
||||
. ($build->buildstatus != 0 ? "\nThe last $loglines lines of the build log are shown at the bottom of this email.\n" : "")
|
||||
. "\n"
|
||||
. "A summary of the build information follows:\n"
|
||||
. "\n"
|
||||
@@ -181,7 +180,7 @@ sub sendEmailNotification {
|
||||
. $inputsTable->body
|
||||
. "\n"
|
||||
. "Regards,\n\nThe Hydra build daemon.\n"
|
||||
. ($build->resultInfo->buildstatus != 0 ? "\n---\n$logtext" : "");
|
||||
. ($build->buildstatus != 0 ? "\n---\n$logtext" : "");
|
||||
|
||||
# stripping trailing spaces from lines
|
||||
$body =~ s/[\ ]+$//gm;
|
||||
@@ -192,7 +191,6 @@ sub sendEmailNotification {
|
||||
To => $to,
|
||||
From => "Hydra Build Daemon <$sender>",
|
||||
Subject => "Hydra job $jobName on " . $build->system . ", build " . $build->id . ": $status",
|
||||
|
||||
'X-Hydra-Instance' => $baseurl,
|
||||
'X-Hydra-Project' => $build->project->name,
|
||||
'X-Hydra-Jobset' => $build->jobset->name,
|
||||
@@ -398,16 +396,16 @@ sub doBuild {
|
||||
}
|
||||
|
||||
txn_do($db, sub {
|
||||
$build->update({finished => 1, timestamp => time});
|
||||
|
||||
my $releaseName = getReleaseName($outPath);
|
||||
|
||||
if ($buildStatus == 0 && -f "$outPath/nix-support/failed") {
|
||||
$buildStatus = 6;
|
||||
}
|
||||
$buildStatus = 6 if $buildStatus == 0 && -f "$outPath/nix-support/failed";
|
||||
|
||||
$db->resultset('BuildResultInfo')->create(
|
||||
{ id => $build->id
|
||||
$build->update(
|
||||
{ finished => 1
|
||||
, busy => 0
|
||||
, locker => ''
|
||||
, logfile => ''
|
||||
, timestamp => time # !!! Why change the timestamp?
|
||||
, iscachedbuild => $isCachedBuild
|
||||
, buildstatus => $buildStatus
|
||||
, starttime => $startTime
|
||||
@@ -423,8 +421,6 @@ sub doBuild {
|
||||
if ($buildStatus == 0 || $buildStatus == 6) {
|
||||
addBuildProducts($db, $build);
|
||||
}
|
||||
|
||||
$build->schedulingInfo->delete;
|
||||
});
|
||||
|
||||
sendEmailNotification $build;
|
||||
@@ -452,11 +448,11 @@ my $build;
|
||||
txn_do($db, sub {
|
||||
$build = $db->resultset('Builds')->find($buildId);
|
||||
die "build $buildId doesn't exist" unless defined $build;
|
||||
die "build $buildId already done" if defined $build->resultInfo;
|
||||
if ($build->schedulingInfo->busy != 0 && $build->schedulingInfo->locker != getppid) {
|
||||
die "build $buildId already done" if $build->finished;
|
||||
if ($build->busy != 0 && $build->locker != getppid) {
|
||||
die "build $buildId is already being built";
|
||||
}
|
||||
$build->schedulingInfo->update({busy => 1, locker => $$});
|
||||
$build->update({busy => 1, locker => $$});
|
||||
$build->buildsteps->search({busy => 1})->delete_all;
|
||||
$build->buildproducts->delete_all;
|
||||
});
|
||||
@@ -472,6 +468,6 @@ eval {
|
||||
if ($@) {
|
||||
warn $@;
|
||||
txn_do($db, sub {
|
||||
$build->schedulingInfo->update({busy => 0, locker => $$});
|
||||
$build->update({busy => 0, locker => $$});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -80,11 +80,12 @@ sub sendJobsetErrorNotification() {
|
||||
);
|
||||
$email->body_set($body);
|
||||
|
||||
print $email->as_string if $ENV{'HYDRA_MAIL_TEST'};
|
||||
print STDERR $email->as_string if $ENV{'HYDRA_MAIL_TEST'};
|
||||
|
||||
sendmail($email);
|
||||
}
|
||||
|
||||
|
||||
sub permute {
|
||||
my @list = @_;
|
||||
for (my $n = scalar @list - 1; $n > 0; $n--) {
|
||||
@@ -105,13 +106,12 @@ sub checkJobset {
|
||||
my $checkoutStop = time;
|
||||
|
||||
# Hash the arguments to hydra-eval-jobs and check the
|
||||
# JobsetInputHashes to see if we've already evaluated this set of
|
||||
# JobsetInputHashes to see if the previous evaluation had the same
|
||||
# inputs. If so, bail out.
|
||||
my @args = ($jobset->nixexprinput, $jobset->nixexprpath, inputsToArgs($inputInfo));
|
||||
my $argsHash = sha256_hex("@args");
|
||||
|
||||
if (scalar($jobset->jobsetevals->search({hash => $argsHash})) > 0) {
|
||||
print " already evaluated, skipping\n";
|
||||
if (getPrevJobsetEval($db, $jobset, 0)->hash eq $argsHash) {
|
||||
print STDERR " jobset is unchanged, skipping\n";
|
||||
txn_do($db, sub {
|
||||
$jobset->update({lastcheckedtime => time});
|
||||
});
|
||||
@@ -125,12 +125,19 @@ sub checkJobset {
|
||||
|
||||
txn_do($db, sub {
|
||||
|
||||
my $prevEval = getPrevJobsetEval($db, $jobset, 1);
|
||||
|
||||
# Clear the "current" flag on all builds. Since we're in a
|
||||
# transaction this will only become visible after the new
|
||||
# current builds have been added.
|
||||
$jobset->builds->search({iscurrent => 1})->update({iscurrent => 0});
|
||||
|
||||
# Schedule each successfully evaluated job.
|
||||
my %currentBuilds;
|
||||
my %buildIds;
|
||||
foreach my $job (permute @{$jobs->{job}}) {
|
||||
next if $job->{jobName} eq "";
|
||||
print "considering job " . $job->{jobName} . "\n";
|
||||
checkBuild($db, $project, $jobset, $inputInfo, $nixExprInput, $job, \%currentBuilds);
|
||||
print STDERR " considering job " . $project->name, ":", $jobset->name, ":", $job->{jobName} . "\n";
|
||||
checkBuild($db, $project, $jobset, $inputInfo, $nixExprInput, $job, \%buildIds, $prevEval);
|
||||
}
|
||||
|
||||
# Update the last checked times and error messages for each
|
||||
@@ -140,22 +147,11 @@ sub checkJobset {
|
||||
|
||||
$jobset->update({lastcheckedtime => time});
|
||||
|
||||
foreach my $job ($jobset->jobs->all) {
|
||||
if ($failedJobNames{$job->name}) {
|
||||
$job->update({errormsg => join '\n', @{$failedJobNames{$job->name}}});
|
||||
} else {
|
||||
$job->update({errormsg => undef});
|
||||
}
|
||||
}
|
||||
|
||||
# Clear the "current" flag on all builds that are no longer
|
||||
# current.
|
||||
foreach my $build ($jobset->builds->search({iscurrent => 1})) {
|
||||
$build->update({iscurrent => 0}) unless defined $currentBuilds{$build->id};
|
||||
}
|
||||
$_->update({ errormsg => $failedJobNames{$_->name} ? join '\n', @{$failedJobNames{$_->name}} : undef })
|
||||
foreach $jobset->jobs->all;
|
||||
|
||||
my $hasNewBuilds = 0;
|
||||
while (my ($id, $new) = each %currentBuilds) {
|
||||
while (my ($id, $new) = each %buildIds) {
|
||||
$hasNewBuilds = 1 if $new;
|
||||
}
|
||||
|
||||
@@ -168,13 +164,16 @@ sub checkJobset {
|
||||
});
|
||||
|
||||
if ($hasNewBuilds) {
|
||||
while (my ($id, $new) = each %currentBuilds) {
|
||||
while (my ($id, $new) = each %buildIds) {
|
||||
$ev->jobsetevalmembers->create({ build => $id, isnew => $new });
|
||||
}
|
||||
print STDERR " created new eval ", $ev->id, "\n";
|
||||
} else {
|
||||
print STDERR " created cached eval ", $ev->id, "\n";
|
||||
}
|
||||
});
|
||||
|
||||
# Store the errors messages for jobs that failed to evaluate.
|
||||
# Store the error messages for jobs that failed to evaluate.
|
||||
my $msg = "";
|
||||
foreach my $error (@{$jobs->{error}}) {
|
||||
my $bindings = "";
|
||||
@@ -197,7 +196,7 @@ sub checkJobset {
|
||||
sub checkJobsetWrapped {
|
||||
my ($project, $jobset) = @_;
|
||||
|
||||
print "considering jobset ", $jobset->name, " in ", $project->name, "\n";
|
||||
print STDERR "considering jobset ", $project->name, ":", $jobset->name, "\n";
|
||||
|
||||
eval {
|
||||
checkJobset($project, $jobset);
|
||||
@@ -205,7 +204,7 @@ sub checkJobsetWrapped {
|
||||
|
||||
if ($@) {
|
||||
my $msg = $@;
|
||||
print "error evaluating jobset ", $jobset->name, ": $msg";
|
||||
print STDERR "error evaluating jobset ", $jobset->name, ": $msg";
|
||||
txn_do($db, sub {
|
||||
$jobset->update({lastcheckedtime => time});
|
||||
setJobsetError($jobset, $msg);
|
||||
@@ -216,7 +215,7 @@ sub checkJobsetWrapped {
|
||||
|
||||
sub checkProjects {
|
||||
foreach my $project ($db->resultset('Projects')->search({enabled => 1})) {
|
||||
print "considering project ", $project->name, "\n";
|
||||
print STDERR "considering project ", $project->name, "\n";
|
||||
checkJobsetWrapped($project, $_)
|
||||
foreach $project->jobsets->search({enabled => 1});
|
||||
}
|
||||
@@ -237,7 +236,7 @@ while (1) {
|
||||
eval {
|
||||
checkProjects;
|
||||
};
|
||||
if ($@) { print "$@"; }
|
||||
print "sleeping...\n";
|
||||
if ($@) { print STDERR "$@"; }
|
||||
print STDERR "sleeping...\n";
|
||||
sleep 30;
|
||||
}
|
||||
|
||||
@@ -20,10 +20,9 @@ STDOUT->autoflush();
|
||||
sub unlockDeadBuilds {
|
||||
# Unlock builds whose building process has died.
|
||||
txn_do($db, sub {
|
||||
my @builds = $db->resultset('Builds')->search(
|
||||
{finished => 0, busy => 1}, {join => 'schedulingInfo'});
|
||||
my @builds = $db->resultset('Builds')->search({finished => 0, busy => 1});
|
||||
foreach my $build (@builds) {
|
||||
my $pid = $build->schedulingInfo->locker;
|
||||
my $pid = $build->locker;
|
||||
my $unlock = 0;
|
||||
if ($pid == $$) {
|
||||
# Work around sqlite locking timeouts: if the child
|
||||
@@ -32,7 +31,7 @@ sub unlockDeadBuilds {
|
||||
# So if after a minute it hasn't been updated,
|
||||
# unlock the build. !!! need a better fix for those
|
||||
# locking timeouts.
|
||||
if ($build->schedulingInfo->starttime + 60 < time) {
|
||||
if ($build->starttime + 60 < time) {
|
||||
$unlock = 1;
|
||||
}
|
||||
} elsif (kill(0, $pid) != 1) { # see if we can signal the process
|
||||
@@ -40,9 +39,9 @@ sub unlockDeadBuilds {
|
||||
}
|
||||
if ($unlock) {
|
||||
print "build ", $build->id, " pid $pid died, unlocking\n";
|
||||
$build->schedulingInfo->busy(0);
|
||||
$build->schedulingInfo->locker("");
|
||||
$build->schedulingInfo->update;
|
||||
$build->busy(0);
|
||||
$build->locker("");
|
||||
$build->update;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -64,7 +63,7 @@ sub findBuildDependencyInQueue {
|
||||
|
||||
($depBuild) = $db->resultset('Builds')->search(
|
||||
{ drvpath => [ @drvs ], finished => 0, busy => 0, enabled => 1, disabled => 0 },
|
||||
{ join => ['schedulingInfo', 'project'], rows => 1 } ) ;
|
||||
{ join => ['project'], rows => 1 } ) ;
|
||||
return $depBuild;
|
||||
}
|
||||
|
||||
@@ -79,7 +78,7 @@ sub checkBuilds {
|
||||
# Get the system types for the runnable builds.
|
||||
my @systemTypes = $db->resultset('Builds')->search(
|
||||
{ finished => 0, busy => 0, enabled => 1, disabled => 0 },
|
||||
{ join => ['schedulingInfo', 'project'], select => ['system'], as => ['system'], distinct => 1 });
|
||||
{ join => ['project'], select => ['system'], as => ['system'], distinct => 1 });
|
||||
|
||||
# For each system type, select up to the maximum number of
|
||||
# concurrent build for that system type. Choose the highest
|
||||
@@ -88,8 +87,7 @@ sub checkBuilds {
|
||||
# How many builds are already currently executing for this
|
||||
# system type?
|
||||
my $nrActive = $db->resultset('Builds')->search(
|
||||
{finished => 0, busy => 1, system => $system->system},
|
||||
{join => 'schedulingInfo'})->count;
|
||||
{finished => 0, busy => 1, system => $system->system})->count;
|
||||
|
||||
# How many extra builds can we start?
|
||||
(my $systemTypeInfo) = $db->resultset('SystemTypes')->search({system => $system->system});
|
||||
@@ -100,7 +98,7 @@ sub checkBuilds {
|
||||
# Select the highest-priority builds to start.
|
||||
my @builds = $extraAllowed == 0 ? () : $db->resultset('Builds')->search(
|
||||
{ finished => 0, busy => 0, system => $system->system, enabled => 1, disabled => 0 },
|
||||
{ join => ['schedulingInfo', 'project'], order_by => ["priority DESC", "timestamp"],
|
||||
{ join => ['project'], order_by => ["priority DESC", "timestamp"],
|
||||
rows => $extraAllowed });
|
||||
|
||||
print "system type `", $system->system,
|
||||
@@ -114,11 +112,11 @@ sub checkBuilds {
|
||||
my $logfile = getcwd . "/logs/" . $build->id;
|
||||
mkdir(dirname $logfile);
|
||||
unlink($logfile);
|
||||
$build->schedulingInfo->busy(1);
|
||||
$build->schedulingInfo->locker($$);
|
||||
$build->schedulingInfo->logfile($logfile);
|
||||
$build->schedulingInfo->starttime(time);
|
||||
$build->schedulingInfo->update;
|
||||
$build->busy(1);
|
||||
$build->locker($$);
|
||||
$build->logfile($logfile);
|
||||
$build->starttime(time);
|
||||
$build->update;
|
||||
push @buildsStarted, $build;
|
||||
}
|
||||
}
|
||||
@@ -130,7 +128,7 @@ sub checkBuilds {
|
||||
my $id = $build->id;
|
||||
print "starting build $id (", $build->project->name, ":", $build->jobset->name, ':', $build->job->name, ") on ", $build->system, "\n";
|
||||
eval {
|
||||
my $logfile = $build->schedulingInfo->logfile;
|
||||
my $logfile = $build->logfile;
|
||||
my $child = fork();
|
||||
die unless defined $child;
|
||||
if ($child == 0) {
|
||||
@@ -147,9 +145,9 @@ sub checkBuilds {
|
||||
if ($@) {
|
||||
warn $@;
|
||||
txn_do($db, sub {
|
||||
$build->schedulingInfo->busy(0);
|
||||
$build->schedulingInfo->locker($$);
|
||||
$build->schedulingInfo->update;
|
||||
$build->busy(0);
|
||||
$build->locker($$);
|
||||
$build->update;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,32 +13,64 @@ my $db = openHydraDB;
|
||||
|
||||
my %roots;
|
||||
|
||||
sub registerRoot {
|
||||
sub addRoot {
|
||||
my ($path) = @_;
|
||||
Hydra::Helper::Nix::registerRoot($path);
|
||||
registerRoot($path);
|
||||
$roots{$path} = 1;
|
||||
}
|
||||
|
||||
|
||||
my @columns = ( "id", "project", "jobset", "job", "system", "finished", "outpath", "drvpath", "timestamp" );
|
||||
|
||||
sub keepBuild {
|
||||
my ($build) = @_;
|
||||
print STDERR " keeping build ", $build->id, " (",
|
||||
$build->system, "; ",
|
||||
print STDERR " keeping ", ($build->finished ? "" : "scheduled "), "build ", $build->id, " (",
|
||||
$build->get_column('project'), ":", $build->get_column('jobset'), ":", $build->get_column('job'), "; ",
|
||||
$build->system, "; ",
|
||||
strftime("%Y-%m-%d %H:%M:%S", localtime($build->timestamp)), ")\n";
|
||||
if (isValidPath($build->outpath)) {
|
||||
registerRoot $build->outpath;
|
||||
addRoot $build->outpath;
|
||||
} else {
|
||||
print STDERR "warning: output ", $build->outpath, " has disappeared\n";
|
||||
print STDERR " warning: output ", $build->outpath, " has disappeared\n" if $build->finished;
|
||||
}
|
||||
if (!$build->finished) {
|
||||
if (isValidPath($build->drvpath)) {
|
||||
addRoot $build->drvpath;
|
||||
} else {
|
||||
print STDERR " warning: derivation ", $build->drvpath, " has disappeared\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Go over all projects.
|
||||
# Read the current GC roots. We need to do that here so that we don't
|
||||
# delete roots that were added while we were determining the desired
|
||||
# roots.
|
||||
print STDERR "*** reading current roots...\n";
|
||||
my $gcRootsDir = getGCRootsDir;
|
||||
opendir DIR, $gcRootsDir or die;
|
||||
my @roots = readdir DIR;
|
||||
closedir DIR;
|
||||
|
||||
foreach my $project ($db->resultset('Projects')->all) {
|
||||
|
||||
# Keep every build in every release of every project.
|
||||
print STDERR "*** looking for release members\n";
|
||||
keepBuild $_ foreach $db->resultset('Builds')->search_literal(
|
||||
"exists (select 1 from releasemembers where build = me.id)", { order_by => ["project", "jobset", "job", "id"] });
|
||||
|
||||
|
||||
# Keep all builds that have been marked as "keep".
|
||||
print STDERR "*** looking for kept builds\n";
|
||||
my @buildsToKeep = $db->resultset('Builds')->search(
|
||||
{ finished => 1, keep => 1 }, { order_by => ["project", "jobset", "job", "id"], columns => [ @columns ] });
|
||||
keepBuild $_ foreach @buildsToKeep;
|
||||
|
||||
|
||||
# Go over all projects.
|
||||
foreach my $project ($db->resultset('Projects')->search({}, { order_by => ["name"] })) {
|
||||
|
||||
# Go over all jobsets in this project.
|
||||
foreach my $jobset ($project->jobsets->all) {
|
||||
foreach my $jobset ($project->jobsets->search({}, { order_by => ["name" ]})) {
|
||||
my $keepnr = $jobset->keepnr;
|
||||
|
||||
# If the jobset has been disabled for more than one week, than
|
||||
@@ -53,29 +85,19 @@ foreach my $project ($db->resultset('Projects')->all) {
|
||||
next;
|
||||
}
|
||||
|
||||
# Go over all jobs in this jobset.
|
||||
foreach my $job ($jobset->jobs->all) {
|
||||
print STDERR "*** looking for builds to keep in job ",
|
||||
$project->name, ":", $job->jobset->name, ":", $job->name, "\n";
|
||||
print STDERR "*** looking for the $keepnr most recent successful builds of each job in jobset ",
|
||||
$project->name, ":", $jobset->name, "\n";
|
||||
|
||||
# Keep the N most recent successful builds for each job
|
||||
# and platform.
|
||||
# !!! Take time into account? E.g. don't delete builds
|
||||
# that are younger than N days.
|
||||
my @systems = $job->builds->search({ }, { select => ["system"], distinct => 1 })->all;
|
||||
foreach my $system (@systems) {
|
||||
my @recentBuilds = $job->builds->search(
|
||||
{ finished => 1
|
||||
, buildStatus => 0 # == success
|
||||
, system => $system->system
|
||||
},
|
||||
{ join => 'resultInfo'
|
||||
, order_by => 'me.id DESC'
|
||||
, rows => $keepnr
|
||||
});
|
||||
keepBuild $_ foreach @recentBuilds;
|
||||
}
|
||||
}
|
||||
keepBuild $_ foreach $jobset->builds->search(
|
||||
{ 'me.id' => { 'in' => \
|
||||
[ "select b2.id from Builds b2 join " .
|
||||
" (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)" .
|
||||
" , 0) 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"
|
||||
, [ '', $keepnr - 1 ], [ '', $project->name ], [ '', $jobset->name ], [ '', $project->name ], [ '', $jobset->name ] ] }
|
||||
},
|
||||
{ order_by => ["job", "system", "id"], columns => [ @columns ] });
|
||||
}
|
||||
|
||||
# Go over all views in this project.
|
||||
@@ -88,56 +110,35 @@ foreach my $project ($db->resultset('Projects')->all) {
|
||||
# Keep all builds belonging to the most recent successful view result.
|
||||
my $latest = getLatestSuccessfulViewResult($project, $primaryJob, $jobs);
|
||||
if (defined $latest) {
|
||||
print STDERR "keeping latest successful view result ", $latest->id, " (", $latest->get_column('releasename'), ")\n";
|
||||
print STDERR " keeping latest successful view result ", $latest->id, " (", $latest->get_column('releasename'), ")\n";
|
||||
my $result = getViewResult($latest, $jobs);
|
||||
keepBuild $_->{build} foreach @{$result->{jobs}};
|
||||
}
|
||||
}
|
||||
|
||||
# Keep every build in every release in this project.
|
||||
print STDERR "*** keeping releases in project ", $project->name, "\n"
|
||||
if scalar $project->releases > 0;
|
||||
foreach my $release ($project->releases->all) {
|
||||
print STDERR "keeping release ", $release->name, "\n";
|
||||
keepBuild $_->build foreach $release->releasemembers;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Keep all builds that have been marked as "keep".
|
||||
print STDERR "*** looking for kept builds\n";
|
||||
my @buildsToKeep = $db->resultset('Builds')->search({finished => 1, keep => 1}, {join => 'resultInfo'});
|
||||
keepBuild $_ foreach @buildsToKeep;
|
||||
|
||||
|
||||
# For scheduled builds, we register the derivation as a GC root.
|
||||
print STDERR "*** looking for scheduled builds\n";
|
||||
foreach my $build ($db->resultset('Builds')->search({finished => 0}, {join => 'schedulingInfo'})) {
|
||||
if (isValidPath($build->drvpath)) {
|
||||
print STDERR "keeping scheduled build ", $build->id, " (",
|
||||
strftime("%Y-%m-%d %H:%M:%S", localtime($build->timestamp)), ")\n";
|
||||
registerRoot $build->drvpath;
|
||||
registerRoot $build->outpath if -e $build->outpath;
|
||||
} else {
|
||||
print STDERR "warning: derivation ", $build->drvpath, " has disappeared\n";
|
||||
}
|
||||
}
|
||||
keepBuild $_ foreach $db->resultset('Builds')->search({ finished => 0 }, { columns => [ @columns ] });
|
||||
|
||||
|
||||
# Remove existing roots that are no longer wanted. !!! racy
|
||||
# Remove existing roots that are no longer wanted.
|
||||
print STDERR "*** removing unneeded GC roots\n";
|
||||
|
||||
my $gcRootsDir = getGCRootsDir;
|
||||
my $rootsKept = 0;
|
||||
my $rootsDeleted = 0;
|
||||
|
||||
opendir DIR, $gcRootsDir or die;
|
||||
|
||||
foreach my $link (readdir DIR) {
|
||||
next if !-l "$gcRootsDir/$link";
|
||||
my $path = readlink "$gcRootsDir/$link" or die;
|
||||
foreach my $link (@roots) {
|
||||
next if $link eq "." || $link eq "..";
|
||||
my $path = "/nix/store/$link";
|
||||
if (!defined $roots{$path}) {
|
||||
print STDERR "removing root $path\n";
|
||||
unlink "$gcRootsDir/$link" or die "cannot remove $gcRootsDir/$link";
|
||||
$rootsDeleted++;
|
||||
#unlink "$gcRootsDir/$link" or warn "cannot remove $gcRootsDir/$link";
|
||||
} else {
|
||||
$rootsKept++;
|
||||
}
|
||||
}
|
||||
|
||||
closedir DIR;
|
||||
print STDERR "kept $rootsKept roots, deleted $rootsDeleted roots\n";
|
||||
|
||||
@@ -114,11 +114,6 @@ create table Jobs (
|
||||
);
|
||||
|
||||
|
||||
-- This table contains all wbuilds, either scheduled or finished. For
|
||||
-- scheduled builds, additional info (such as the priority) can be
|
||||
-- found in the BuildSchedulingInfo table. For finished builds,
|
||||
-- additional info (such as the logs, build products, etc.) can be
|
||||
-- found in several tables, such as BuildResultInfo and BuildProducts.
|
||||
create table Builds (
|
||||
#ifdef POSTGRESQL
|
||||
id serial primary key not null,
|
||||
@@ -156,37 +151,22 @@ create table Builds (
|
||||
-- build.
|
||||
nixExprInput text,
|
||||
nixExprPath text,
|
||||
|
||||
foreign key (project) references Projects(name) on update cascade,
|
||||
foreign key (project, jobset) references Jobsets(project, name) on update cascade,
|
||||
foreign key (project, jobset, job) references Jobs(project, jobset, name) on update cascade
|
||||
);
|
||||
|
||||
|
||||
-- Info for a scheduled build.
|
||||
create table BuildSchedulingInfo (
|
||||
id integer primary key not null,
|
||||
|
||||
-- Information about scheduled builds.
|
||||
priority integer not null default 0,
|
||||
|
||||
busy integer not null default 0, -- true means someone is building this job now
|
||||
locker text not null default '', -- !!! hostname/pid of the process building this job?
|
||||
locker text, -- !!! hostname/pid of the process building this job?
|
||||
|
||||
logfile text, -- if busy, the path of the logfile
|
||||
|
||||
disabled integer not null default 0,
|
||||
disabled integer not null default 0, -- !!! boolean
|
||||
|
||||
startTime integer, -- if busy, time we started
|
||||
|
||||
foreign key (id) references Builds(id) on delete cascade
|
||||
);
|
||||
stopTime integer,
|
||||
|
||||
|
||||
-- Info for a finished build.
|
||||
create table BuildResultInfo (
|
||||
id integer primary key not null,
|
||||
|
||||
isCachedBuild integer not null, -- boolean
|
||||
-- Information about finished builds.
|
||||
isCachedBuild integer, -- boolean
|
||||
|
||||
-- Status codes:
|
||||
-- 0 = succeeded
|
||||
@@ -199,23 +179,17 @@ create table BuildResultInfo (
|
||||
|
||||
errorMsg text, -- error message in case of a Nix failure
|
||||
|
||||
startTime integer, -- in Unix time, 0 = used cached build result
|
||||
stopTime integer,
|
||||
|
||||
logfile text, -- the path of the logfile
|
||||
|
||||
logsize bigint not null default 0,
|
||||
size bigint not null default 0,
|
||||
closuresize bigint not null default 0,
|
||||
logSize bigint,
|
||||
size bigint,
|
||||
closureSize bigint,
|
||||
|
||||
releaseName text, -- e.g. "patchelf-0.5pre1234"
|
||||
|
||||
keep integer not null default 0, -- true means never garbage-collect the build output
|
||||
|
||||
failedDepBuild integer, -- obsolete
|
||||
failedDepStepNr integer, -- obsolete
|
||||
|
||||
foreign key (id) references Builds(id) on delete cascade
|
||||
foreign key (project) references Projects(name) on update cascade,
|
||||
foreign key (project, jobset) references Jobsets(project, name) on update cascade,
|
||||
foreign key (project, jobset, job) references Jobs(project, jobset, name) on update cascade
|
||||
);
|
||||
|
||||
|
||||
@@ -519,16 +493,18 @@ create table BuildMachineSystemTypes (
|
||||
|
||||
|
||||
-- Some indices.
|
||||
|
||||
create index IndexBuildInputsOnBuild on BuildInputs(build);
|
||||
create index IndexBuildInputsOnDependency on BuildInputs(dependency);
|
||||
create index IndexBuildProducstOnBuildAndType on BuildProducts(build, type);
|
||||
create index IndexBuildProductsOnBuild on BuildProducts(build);
|
||||
create index IndexBuildSchedulingInfoOnBuild on BuildSchedulingInfo(id); -- idem
|
||||
create index IndexBuildStepsOnBuild on BuildSteps(build);
|
||||
create index IndexBuildStepsOnBusy on BuildSteps(busy);
|
||||
create index IndexBuildStepsOnDrvpathTypeBusyStatus on BuildSteps(drvpath, type, busy, status);
|
||||
create index IndexBuildStepsOnOutpath on BuildSteps(outpath);
|
||||
create index IndexBuildStepsOnOutpathBuild on BuildSteps (outpath, build);
|
||||
create index IndexBuildsOnFinished on Builds(finished);
|
||||
create index IndexBuildsOnFinishedBusy on Builds(finished, busy);
|
||||
create index IndexBuildsOnIsCurrent on Builds(isCurrent);
|
||||
create index IndexBuildsOnJobsetIsCurrent on Builds(project, jobset, isCurrent);
|
||||
create index IndexBuildsOnJobIsCurrent on Builds(project, jobset, job, isCurrent);
|
||||
@@ -551,3 +527,10 @@ create index IndexJobsetInputAltsOnInput on JobsetInputAlts(project, jobset, inp
|
||||
create index IndexJobsetInputAltsOnJobset on JobsetInputAlts(project, jobset);
|
||||
create index IndexProjectsOnEnabled on Projects(enabled);
|
||||
create index IndexReleaseMembersOnBuild on ReleaseMembers(build);
|
||||
|
||||
-- For hydra-update-gc-roots.
|
||||
create index IndexBuildsOnKeep on Builds(keep);
|
||||
create index IndexMostRecentSuccessfulBuilds on Builds(project, jobset, job, system, finished, buildStatus, id desc);
|
||||
|
||||
-- To get the most recent eval for a jobset.
|
||||
create index IndexJobsetEvalsOnJobsetId on JobsetEvals(project, jobset, hasNewBuilds, id desc);
|
||||
|
||||
44
src/sql/upgrade-2.sql
Normal file
44
src/sql/upgrade-2.sql
Normal file
@@ -0,0 +1,44 @@
|
||||
alter table Builds
|
||||
add column priority integer not null default 0,
|
||||
add column busy integer not null default 0,
|
||||
add column locker text,
|
||||
add column logfile text,
|
||||
add column disabled integer not null default 0,
|
||||
add column startTime integer,
|
||||
add column stopTime integer,
|
||||
add column isCachedBuild integer,
|
||||
add column buildStatus integer,
|
||||
add column errorMsg text,
|
||||
add column logSize bigint,
|
||||
add column size bigint,
|
||||
add column closureSize bigint,
|
||||
add column releaseName text,
|
||||
add column keep integer not null default 0;
|
||||
|
||||
update Builds b set
|
||||
priority = (select priority from BuildSchedulingInfo s where s.id = b.id),
|
||||
busy = (select busy from BuildSchedulingInfo s where s.id = b.id),
|
||||
disabled = (select disabled from BuildSchedulingInfo s where s.id = b.id),
|
||||
locker = (select locker from BuildSchedulingInfo s where s.id = b.id),
|
||||
logfile = (select logfile from BuildSchedulingInfo s where s.id = b.id)
|
||||
where exists (select 1 from BuildSchedulingInfo s where s.id = b.id);
|
||||
|
||||
update Builds b set
|
||||
startTime = ((select startTime from BuildSchedulingInfo s where s.id = b.id) union (select startTime from BuildResultInfo r where r.id = b.id));
|
||||
|
||||
update Builds b set
|
||||
isCachedBuild = (select isCachedBuild from BuildResultInfo r where r.id = b.id),
|
||||
buildStatus = (select buildStatus from BuildResultInfo r where r.id = b.id),
|
||||
errorMsg = (select errorMsg from BuildResultInfo r where r.id = b.id),
|
||||
startTime = (select startTime from BuildResultInfo r where r.id = b.id),
|
||||
stopTime = (select stopTime from BuildResultInfo r where r.id = b.id),
|
||||
logfile = (select logfile from BuildResultInfo r where r.id = b.id),
|
||||
logSize = (select logsize from BuildResultInfo r where r.id = b.id),
|
||||
size = (select size from BuildResultInfo r where r.id = b.id),
|
||||
closureSize = (select closuresize from BuildResultInfo r where r.id = b.id),
|
||||
releaseName = (select releaseName from BuildResultInfo r where r.id = b.id),
|
||||
keep = (select keep from BuildResultInfo r where r.id = b.id)
|
||||
where exists (select 1 from BuildResultInfo r where r.id = b.id);
|
||||
|
||||
drop table BuildSchedulingInfo;
|
||||
drop table BuildResultInfo;
|
||||
2
src/sql/upgrade-3.sql
Normal file
2
src/sql/upgrade-3.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
create index IndexBuildsOnKeep on Builds(keep); -- used by hydra-update-gc-roots
|
||||
create index IndexMostRecentSuccessfulBuilds on Builds(project, jobset, job, system, finished, buildStatus, id desc); -- used by hydra-update-gc-roots
|
||||
1
src/sql/upgrade-4.sql
Normal file
1
src/sql/upgrade-4.sql
Normal file
@@ -0,0 +1 @@
|
||||
create index IndexJobsetEvalsOnJobsetId on JobsetEvals(project, jobset, hasNewBuilds, id desc);
|
||||
Reference in New Issue
Block a user