forked from lix-project/hydra
Try harder to find build logs
Due to the fixed-output derivation hashing scheme, there can be multiple derivations of the same output path. But build logs are indexed by derivation path. Thus, we may not be able to find the log of a build or build step using its derivation. So as a fallback, Hydra now looks for other derivations with the same output paths.
This commit is contained in:
parent
559a98cfee
commit
fd7e37ef89
|
@ -35,18 +35,18 @@ sub buildChain :Chained('/') :PathPart('build') :CaptureArgs(1) {
|
||||||
|
|
||||||
|
|
||||||
sub findBuildStepByOutPath {
|
sub findBuildStepByOutPath {
|
||||||
my ($self, $c, $path, $status) = @_;
|
my ($self, $c, $path) = @_;
|
||||||
return $c->model('DB::BuildSteps')->search(
|
return $c->model('DB::BuildSteps')->search(
|
||||||
{ path => $path, busy => 0, status => $status },
|
{ path => $path, busy => 0 },
|
||||||
{ join => ["buildstepoutputs"], order_by => ["stopTime"], limit => 1 })->single;
|
{ join => ["buildstepoutputs"], order_by => ["status", "stopTime"], rows => 1 })->single;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub findBuildStepByDrvPath {
|
sub findBuildStepByDrvPath {
|
||||||
my ($self, $c, $drvPath, $status) = @_;
|
my ($self, $c, $drvPath) = @_;
|
||||||
return $c->model('DB::BuildSteps')->search(
|
return $c->model('DB::BuildSteps')->search(
|
||||||
{ drvpath => $drvPath, busy => 0, status => $status },
|
{ drvpath => $drvPath, busy => 0 },
|
||||||
{ order_by => ["stopTime"], limit => 1 })->single;
|
{ order_by => ["status", "stopTime"], rows => 1 })->single;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -68,8 +68,7 @@ sub build_GET {
|
||||||
|
|
||||||
if ($build->finished && $build->iscachedbuild) {
|
if ($build->finished && $build->iscachedbuild) {
|
||||||
my $path = ($build->buildoutputs)[0]->path or die;
|
my $path = ($build->buildoutputs)[0]->path or die;
|
||||||
my $cachedBuildStep = findBuildStepByOutPath($self, $c, $path,
|
my $cachedBuildStep = findBuildStepByOutPath($self, $c, $path);
|
||||||
$build->buildstatus == 0 || $build->buildstatus == 6 ? 0 : 1);
|
|
||||||
$c->stash->{cachedBuild} = $cachedBuildStep->build if defined $cachedBuildStep;
|
$c->stash->{cachedBuild} = $cachedBuildStep->build if defined $cachedBuildStep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,9 +94,9 @@ sub build_GET {
|
||||||
|
|
||||||
# Get the first eval of which this build was a part.
|
# Get the first eval of which this build was a part.
|
||||||
($c->stash->{nrEvals}) = $c->stash->{build}->jobsetevals->search({ hasnewbuilds => 1 })->count;
|
($c->stash->{nrEvals}) = $c->stash->{build}->jobsetevals->search({ hasnewbuilds => 1 })->count;
|
||||||
($c->stash->{eval}) = $c->stash->{build}->jobsetevals->search(
|
$c->stash->{eval} = $c->stash->{build}->jobsetevals->search(
|
||||||
{ hasnewbuilds => 1},
|
{ hasnewbuilds => 1},
|
||||||
{ limit => 1, order_by => ["id"] });
|
{ rows => 1, order_by => ["id"] })->single;
|
||||||
$self->status_ok(
|
$self->status_ok(
|
||||||
$c,
|
$c,
|
||||||
entity => $c->model('DB::Builds')->find($build->id,{
|
entity => $c->model('DB::Builds')->find($build->id,{
|
||||||
|
@ -132,26 +131,27 @@ sub view_nixlog : Chained('buildChain') PathPart('nixlog') {
|
||||||
|
|
||||||
$c->stash->{step} = $step;
|
$c->stash->{step} = $step;
|
||||||
|
|
||||||
showLog($c, $step->drvpath, $mode);
|
showLog($c, $mode, $step->drvpath, map { $_->path } $step->buildstepoutputs->all);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub view_log : Chained('buildChain') PathPart('log') {
|
sub view_log : Chained('buildChain') PathPart('log') {
|
||||||
my ($self, $c, $mode) = @_;
|
my ($self, $c, $mode) = @_;
|
||||||
showLog($c, $c->stash->{build}->drvpath, $mode);
|
showLog($c, $mode, $c->stash->{build}->drvpath, map { $_->path } $c->stash->{build}->buildoutputs->all);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub showLog {
|
sub showLog {
|
||||||
my ($c, $drvPath, $mode) = @_;
|
my ($c, $mode, $drvPath, @outPaths) = @_;
|
||||||
|
|
||||||
my $logPath = getDrvLogPath($drvPath);
|
my $logPath = findLog($c, $drvPath, @outPaths);
|
||||||
|
print STDERR "log = $logPath\n";
|
||||||
|
|
||||||
notFound($c, "The build log of derivation ‘$drvPath’ is not available.") unless defined $logPath;
|
notFound($c, "The build log of derivation ‘$drvPath’ is not available.") unless defined $logPath;
|
||||||
|
|
||||||
if (!$mode) {
|
if (!$mode) {
|
||||||
# !!! quick hack
|
# !!! quick hack
|
||||||
my $pipeline = "nix-store -l $drvPath"
|
my $pipeline = ($logPath =~ /.bz2$/ ? "bzip2 -d < $logPath" : "cat $logPath")
|
||||||
. " | nix-log2xml | xsltproc " . $c->path_to("xsl/mark-errors.xsl") . " -"
|
. " | nix-log2xml | xsltproc " . $c->path_to("xsl/mark-errors.xsl") . " -"
|
||||||
. " | xsltproc " . $c->path_to("xsl/log2html.xsl") . " - | tail -n +2";
|
. " | xsltproc " . $c->path_to("xsl/log2html.xsl") . " - | tail -n +2";
|
||||||
$c->stash->{template} = 'log.tt';
|
$c->stash->{template} = 'log.tt';
|
||||||
|
@ -159,7 +159,7 @@ sub showLog {
|
||||||
}
|
}
|
||||||
|
|
||||||
elsif ($mode eq "raw") {
|
elsif ($mode eq "raw") {
|
||||||
$c->stash->{'plain'} = { data => (scalar logContents($drvPath)) || " " };
|
$c->stash->{'plain'} = { data => (scalar logContents($logPath)) || " " };
|
||||||
$c->forward('Hydra::View::Plain');
|
$c->forward('Hydra::View::Plain');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,12 +169,12 @@ sub showLog {
|
||||||
$c->stash->{url} = $url;
|
$c->stash->{url} = $url;
|
||||||
$c->stash->{reload} = !$c->stash->{build}->finished && $c->stash->{build}->busy;
|
$c->stash->{reload} = !$c->stash->{build}->finished && $c->stash->{build}->busy;
|
||||||
$c->stash->{title} = "";
|
$c->stash->{title} = "";
|
||||||
$c->stash->{contents} = (scalar logContents($drvPath, 50)) || " ";
|
$c->stash->{contents} = (scalar logContents($logPath, 50)) || " ";
|
||||||
$c->stash->{template} = 'plain-reload.tt';
|
$c->stash->{template} = 'plain-reload.tt';
|
||||||
}
|
}
|
||||||
|
|
||||||
elsif ($mode eq "tail") {
|
elsif ($mode eq "tail") {
|
||||||
$c->stash->{'plain'} = { data => (scalar logContents($drvPath, 50)) || " " };
|
$c->stash->{'plain'} = { data => (scalar logContents($logPath, 50)) || " " };
|
||||||
$c->forward('Hydra::View::Plain');
|
$c->forward('Hydra::View::Plain');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,8 +361,8 @@ sub getDependencyGraph {
|
||||||
{ path => $path
|
{ path => $path
|
||||||
, name => $name
|
, name => $name
|
||||||
, buildStep => $runtime
|
, buildStep => $runtime
|
||||||
? findBuildStepByOutPath($self, $c, $path, 0)
|
? findBuildStepByOutPath($self, $c, $path)
|
||||||
: findBuildStepByDrvPath($self, $c, $path, 0)
|
: findBuildStepByDrvPath($self, $c, $path)
|
||||||
};
|
};
|
||||||
$$done{$path} = $node;
|
$$done{$path} = $node;
|
||||||
my @refs;
|
my @refs;
|
||||||
|
|
|
@ -16,7 +16,7 @@ our @EXPORT = qw(
|
||||||
getPrimaryBuildsForView
|
getPrimaryBuildsForView
|
||||||
getPrimaryBuildTotal
|
getPrimaryBuildTotal
|
||||||
getViewResult getLatestSuccessfulViewResult
|
getViewResult getLatestSuccessfulViewResult
|
||||||
jobsetOverview removeAsciiEscapes getDrvLogPath logContents
|
jobsetOverview removeAsciiEscapes getDrvLogPath findLog logContents
|
||||||
getMainOutput
|
getMainOutput
|
||||||
getEvals getMachines
|
getEvals getMachines
|
||||||
pathIsInsidePrefix
|
pathIsInsidePrefix
|
||||||
|
@ -264,10 +264,36 @@ sub getDrvLogPath {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Find the log of the derivation denoted by $drvPath. It it doesn't
|
||||||
|
# exist, try other derivations that produced its outputs (@outPaths).
|
||||||
|
sub findLog {
|
||||||
|
my ($c, $drvPath, @outPaths) = @_;
|
||||||
|
|
||||||
|
if (defined $drvPath) {
|
||||||
|
my $logPath = getDrvLogPath($drvPath);
|
||||||
|
return $logPath if defined $logPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undef if scalar @outPaths == 0;
|
||||||
|
|
||||||
|
my @steps = $c->model('DB::BuildSteps')->search(
|
||||||
|
{ path => { -in => [@outPaths] } },
|
||||||
|
{ select => ["drvpath"]
|
||||||
|
, distinct => 1
|
||||||
|
, join => "buildstepoutputs"
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach my $step (@steps) {
|
||||||
|
my $logPath = getDrvLogPath($step->drvpath);
|
||||||
|
return $logPath if defined $logPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
sub logContents {
|
sub logContents {
|
||||||
my ($drvPath, $tail) = @_;
|
my ($logPath, $tail) = @_;
|
||||||
my $logPath = getDrvLogPath($drvPath);
|
|
||||||
die unless defined $logPath;
|
|
||||||
my $cmd;
|
my $cmd;
|
||||||
if ($logPath =~ /.bz2$/) {
|
if ($logPath =~ /.bz2$/) {
|
||||||
$cmd = "bzip2 -d < $logPath";
|
$cmd = "bzip2 -d < $logPath";
|
||||||
|
|
|
@ -8,7 +8,7 @@ __PACKAGE__->config(
|
||||||
TEMPLATE_EXTENSION => '.tt',
|
TEMPLATE_EXTENSION => '.tt',
|
||||||
PRE_CHOMP => 1,
|
PRE_CHOMP => 1,
|
||||||
POST_CHOMP => 1,
|
POST_CHOMP => 1,
|
||||||
expose_methods => [qw/log_exists ellipsize/]);
|
expose_methods => [qw/log_exists buildLogExists buildStepLogExists/]);
|
||||||
|
|
||||||
sub log_exists {
|
sub log_exists {
|
||||||
my ($self, $c, $drvPath) = @_;
|
my ($self, $c, $drvPath) = @_;
|
||||||
|
@ -16,9 +16,16 @@ sub log_exists {
|
||||||
return defined $x;
|
return defined $x;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub ellipsize {
|
sub buildLogExists {
|
||||||
my ($self, $c, $s, $n) = @_;
|
my ($self, $c, $build) = @_;
|
||||||
return length $s <= $n ? $s : substr($s, 0, $n - 3) . "...";
|
my @outPaths = map { $_->path } $build->buildoutputs->all;
|
||||||
|
return defined findLog($c, $build->drvpath, @outPaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub buildStepLogExists {
|
||||||
|
my ($self, $c, $step) = @_;
|
||||||
|
my @outPaths = map { $_->path } $step->buildstepoutputs->all;
|
||||||
|
return defined findLog($c, $step->drvpath, @outPaths);
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
[% FOREACH step IN build.buildsteps %]
|
[% FOREACH step IN build.buildsteps %]
|
||||||
[% IF ( type == "All" ) || ( type == "Failed" && step.status != 0 ) || ( type == "Running" && step.busy == 1 ) %]
|
[% IF ( type == "All" ) || ( type == "Failed" && step.status != 0 ) || ( type == "Running" && step.busy == 1 ) %]
|
||||||
[% has_log = log_exists(step.drvpath);
|
[% has_log = buildStepLogExists(step);
|
||||||
log = c.uri_for('/build' build.id 'nixlog' step.stepnr); %]
|
log = c.uri_for('/build' build.id 'nixlog' step.stepnr); %]
|
||||||
<tr>
|
<tr>
|
||||||
<td>[% step.stepnr %]</td>
|
<td>[% step.stepnr %]</td>
|
||||||
|
@ -177,7 +177,7 @@
|
||||||
finished at [% INCLUDE renderDateTime timestamp = actualBuild.stoptime %]</td>
|
finished at [% INCLUDE renderDateTime timestamp = actualBuild.stoptime %]</td>
|
||||||
</tr>
|
</tr>
|
||||||
[% END %]
|
[% END %]
|
||||||
[% IF !isAggregate && log_exists(build.drvpath) %]
|
[% IF !isAggregate && buildLogExists(build) %]
|
||||||
<tr>
|
<tr>
|
||||||
<th>Logfile:</th>
|
<th>Logfile:</th>
|
||||||
<td>
|
<td>
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
<span id="[% done.${node.path} %]"><span class="dep-tree-line">
|
<span id="[% done.${node.path} %]"><span class="dep-tree-line">
|
||||||
[% IF node.buildStep %]
|
[% IF node.buildStep %]
|
||||||
<a href="[% c.uri_for('/build' node.buildStep.get_column('build')) %]"><tt>[% node.name %]</tt></a> [%
|
<a href="[% c.uri_for('/build' node.buildStep.get_column('build')) %]"><tt>[% node.name %]</tt></a> [%
|
||||||
IF log_exists(node.buildStep.drvpath);
|
IF buildStepLogExists(node.buildStep);
|
||||||
INCLUDE renderLogLinks url=c.uri_for('/build' node.buildStep.get_column('build') 'nixlog' node.buildStep.stepnr);
|
INCLUDE renderLogLinks url=c.uri_for('/build' node.buildStep.get_column('build') 'nixlog' node.buildStep.stepnr);
|
||||||
END %]
|
END %]
|
||||||
[% ELSE %]
|
[% ELSE %]
|
||||||
|
|
Loading…
Reference in a new issue