All successful, non-garbage-collected builds in the evaluation are passed in a attribute set. So if you declare a Hydra input named ‘foo’ of type ‘eval’, you get a set with members ‘foo.<jobname>’. For instance, if you passed a Nixpkgs eval as an input named ‘nixpkgs’, then you could get the Firefox build for x86_64-linux as ‘nixpkgs.firefox.x86_64-linux’. Inputs of type ‘eval’ can be specified in three ways: * As the number of the evaluation. * As a jobset identifier (‘<project>:<jobset>’), which will yield the latest finished evaluation of that jobset. Note that there is no guarantee that any job in that evaluation has succeeded, so it might not be very useful. * As a job identifier (‘<project>:<jobset>:<job>’), which will yield the latest finished evaluation of that jobset in which <job> succeeded. In conjunction with aggregate jobs, this allows you to make sure that the evaluation contains the desired builds.
202 lines
7.3 KiB
Perl
202 lines
7.3 KiB
Perl
package Hydra::Controller::JobsetEval;
|
||
|
||
use strict;
|
||
use warnings;
|
||
use base 'Hydra::Base::Controller::NixChannel';
|
||
use Hydra::Helper::Nix;
|
||
use Hydra::Helper::CatalystUtils;
|
||
|
||
|
||
sub eval : Chained('/') PathPart('eval') CaptureArgs(1) {
|
||
my ($self, $c, $evalId) = @_;
|
||
|
||
my $eval = $c->model('DB::JobsetEvals')->find($evalId)
|
||
or notFound($c, "Evaluation $evalId doesn't exist.");
|
||
|
||
$c->stash->{eval} = $eval;
|
||
$c->stash->{project} = $eval->project;
|
||
$c->stash->{jobset} = $eval->jobset;
|
||
}
|
||
|
||
|
||
sub view : Chained('eval') PathPart('') Args(0) {
|
||
my ($self, $c) = @_;
|
||
|
||
$c->stash->{template} = 'jobset-eval.tt';
|
||
|
||
my $eval = $c->stash->{eval};
|
||
|
||
$c->stash->{filter} = $c->request->params->{filter} // "";
|
||
my $filter = $c->stash->{filter} eq "" ? {} : { job => { ilike => "%" . $c->stash->{filter} . "%" } };
|
||
|
||
my $compare = $c->req->params->{compare};
|
||
my $eval2;
|
||
|
||
# Allow comparing this evaluation against the previous evaluation
|
||
# (default), an arbitrary evaluation, or the latest completed
|
||
# evaluation of another jobset.
|
||
if (defined $compare) {
|
||
if ($compare =~ /^\d+$/) {
|
||
$eval2 = $c->model('DB::JobsetEvals')->find($compare)
|
||
or notFound($c, "Evaluation $compare doesn't exist.");
|
||
} elsif ($compare =~ /^-(\d+)$/) {
|
||
my $t = int($1);
|
||
$eval2 = $c->stash->{jobset}->jobsetevals->find(
|
||
{ hasnewbuilds => 1, timestamp => {'<=', $eval->timestamp - $t} },
|
||
{ order_by => "timestamp desc", rows => 1});
|
||
} elsif (defined $compare && $compare =~ /^($jobsetNameRE)$/) {
|
||
my $j = $c->stash->{project}->jobsets->find({name => $compare})
|
||
or notFound($c, "Jobset $compare doesn't exist.");
|
||
$eval2 = getLatestFinishedEval($j);
|
||
} else {
|
||
notFound($c, "Unknown comparison source ‘$compare’.");
|
||
}
|
||
} else {
|
||
($eval2) = $eval->jobset->jobsetevals->search(
|
||
{ hasnewbuilds => 1, id => { '<', $eval->id } },
|
||
{ order_by => "id DESC", rows => 1 });
|
||
}
|
||
|
||
$c->stash->{otherEval} = $eval2 if defined $eval2;
|
||
|
||
sub cmpBuilds {
|
||
my ($a, $b) = @_;
|
||
return $a->get_column('job') cmp $b->get_column('job')
|
||
|| $a->get_column('system') cmp $b->get_column('system')
|
||
}
|
||
|
||
my @builds = $eval->builds->search($filter, { columns => [@buildListColumns] });
|
||
my @builds2 = defined $eval2 ? $eval2->builds->search($filter, { columns => [@buildListColumns] }) : ();
|
||
|
||
@builds = sort { cmpBuilds($a, $b) } @builds;
|
||
@builds2 = sort { cmpBuilds($a, $b) } @builds2;
|
||
|
||
$c->stash->{stillSucceed} = [];
|
||
$c->stash->{stillFail} = [];
|
||
$c->stash->{nowSucceed} = [];
|
||
$c->stash->{nowFail} = [];
|
||
$c->stash->{new} = [];
|
||
$c->stash->{removed} = [];
|
||
$c->stash->{unfinished} = [];
|
||
$c->stash->{aborted} = [];
|
||
|
||
my $n = 0;
|
||
foreach my $build (@builds) {
|
||
if ($build->finished != 0 && ($build->buildstatus == 3 || $build->buildstatus == 4)) {
|
||
push @{$c->stash->{aborted}}, $build;
|
||
next;
|
||
}
|
||
my $d;
|
||
my $found = 0;
|
||
while ($n < scalar(@builds2)) {
|
||
my $build2 = $builds2[$n];
|
||
my $d = cmpBuilds($build, $build2);
|
||
last if $d == -1;
|
||
if ($d == 0) {
|
||
$n++;
|
||
$found = 1;
|
||
if ($build->finished == 0 || $build2->finished == 0) {
|
||
push @{$c->stash->{unfinished}}, $build;
|
||
} elsif ($build->buildstatus == 0 && $build2->buildstatus == 0) {
|
||
push @{$c->stash->{stillSucceed}}, $build;
|
||
} elsif ($build->buildstatus != 0 && $build2->buildstatus != 0) {
|
||
push @{$c->stash->{stillFail}}, $build;
|
||
} elsif ($build->buildstatus == 0 && $build2->buildstatus != 0) {
|
||
push @{$c->stash->{nowSucceed}}, $build;
|
||
} elsif ($build->buildstatus != 0 && $build2->buildstatus == 0) {
|
||
push @{$c->stash->{nowFail}}, $build;
|
||
} else { die; }
|
||
last;
|
||
}
|
||
push @{$c->stash->{removed}}, { job => $build2->get_column('job'), system => $build2->get_column('system') };
|
||
$n++;
|
||
}
|
||
push @{$c->stash->{new}}, $build if !$found;
|
||
}
|
||
|
||
$c->stash->{full} = ($c->req->params->{full} || "0") eq "1";
|
||
}
|
||
|
||
|
||
sub release : Chained('eval') PathPart('release') Args(0) {
|
||
my ($self, $c) = @_;
|
||
my $eval = $c->stash->{eval};
|
||
|
||
requireProjectOwner($c, $c->stash->{project});
|
||
|
||
my @builds = $eval->builds;
|
||
|
||
my $releaseName;
|
||
$releaseName ||= $_->releasename foreach @builds;
|
||
|
||
error($c, "No build in this evaluation has a release name.")
|
||
unless defined $releaseName;
|
||
|
||
my $release;
|
||
|
||
txn_do($c->model('DB')->schema, sub {
|
||
|
||
$release = $c->stash->{project}->releases->create(
|
||
{ name => $releaseName
|
||
, timestamp => time
|
||
});
|
||
|
||
foreach my $build (@builds) {
|
||
$release->releasemembers->create(
|
||
{ build => $build->id
|
||
, description => $build->description
|
||
}) if $build->buildstatus == 0;
|
||
}
|
||
});
|
||
|
||
$c->res->redirect($c->uri_for($c->controller('Release')->action_for('view'),
|
||
[$c->stash->{project}->name, $release->name]));
|
||
}
|
||
|
||
|
||
sub cancel : Chained('eval') PathPart('cancel') Args(0) {
|
||
my ($self, $c) = @_;
|
||
requireProjectOwner($c, $c->stash->{eval}->project);
|
||
my $n = cancelBuilds($c->model('DB')->schema, $c->stash->{eval}->builds);
|
||
$c->flash->{successMsg} = "$n builds have been cancelled.";
|
||
$c->res->redirect($c->uri_for($c->controller('JobsetEval')->action_for('view'), $c->req->captures));
|
||
}
|
||
|
||
|
||
sub restart_aborted : Chained('eval') PathPart('restart-aborted') Args(0) {
|
||
my ($self, $c) = @_;
|
||
requireProjectOwner($c, $c->stash->{eval}->project);
|
||
my $builds = $c->stash->{eval}->builds->search({ finished => 1, buildstatus => { -in => [3, 4] } });
|
||
my $n = restartBuilds($c->model('DB')->schema, $builds);
|
||
$c->flash->{successMsg} = "$n builds have been restarted.";
|
||
$c->res->redirect($c->uri_for($c->controller('JobsetEval')->action_for('view'), $c->req->captures));
|
||
}
|
||
|
||
|
||
# Hydra::Base::Controller::NixChannel needs this.
|
||
sub nix : Chained('eval') PathPart('channel') CaptureArgs(0) {
|
||
my ($self, $c) = @_;
|
||
$c->stash->{channelName} = $c->stash->{project}->name . "-" . $c->stash->{jobset}->name . "-latest";
|
||
$c->stash->{channelBuilds} = $c->stash->{eval}->builds
|
||
->search_literal("exists (select 1 from buildproducts where build = build.id and type = 'nix-build')")
|
||
->search({ finished => 1, buildstatus => 0 },
|
||
{ columns => [@buildListColumns, 'drvpath', 'description', 'homepage']
|
||
, join => ["buildoutputs"]
|
||
, order_by => ["build.id", "buildoutputs.name"]
|
||
, '+select' => ['buildoutputs.path', 'buildoutputs.name'], '+as' => ['outpath', 'outname'] });
|
||
}
|
||
|
||
|
||
sub job : Chained('eval') PathPart('job') {
|
||
my ($self, $c, $job, @rest) = @_;
|
||
|
||
my $build = $c->stash->{eval}->builds->find({job => $job});
|
||
|
||
notFound($c, "This evaluation has no job with the specified name.") unless defined $build;
|
||
|
||
$c->res->redirect($c->uri_for($c->controller('Build')->action_for("build"), [$build->id], @rest));
|
||
}
|
||
|
||
|
||
1;
|