Previously, for scheduled builds, "timestamp" contained the time the build was added to the queue, while for finished builds, it was the time the build finished. Now it's always the former.
528 lines
19 KiB
Plaintext
528 lines
19 KiB
Plaintext
[% WRAPPER layout.tt title="Build $id of job $project.name:$jobset.name:$job.name" %]
|
|
[% PROCESS common.tt %]
|
|
[% PROCESS "product-list.tt" %]
|
|
[% USE HTML %]
|
|
[% USE Date %]
|
|
|
|
[% project = build.project %]
|
|
[% jobset = build.jobset %]
|
|
[% job = build.job %]
|
|
|
|
[% BLOCK renderOutputs %]
|
|
[% start=1; FOREACH output IN outputs %]
|
|
[% IF !start %],<br/>[% END; start=0; output.path %]
|
|
[% END %]
|
|
[% END %]
|
|
|
|
[% BLOCK renderBuildSteps %]
|
|
<table class="table table-striped table-condensed clickable-rows">
|
|
<thead>
|
|
<tr><th>Nr</th><th>What</th><th>Duration</th><th>Machine</th><th>Status</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
[% FOREACH step IN build.buildsteps %]
|
|
[% IF ( type == "All" ) || ( type == "Failed" && step.status != 0 ) || ( type == "Running" && step.busy == 1 ) %]
|
|
[% has_log = log_exists(step.drvpath);
|
|
log = c.uri_for('/build' build.id 'nixlog' step.stepnr); %]
|
|
<tr>
|
|
<td>[% step.stepnr %]</td>
|
|
<td>
|
|
[% IF step.type == 0 %]
|
|
Build of <tt>[% INCLUDE renderOutputs outputs=step.buildstepoutputs %]</tt>
|
|
[% ELSE %]
|
|
Substitution of <tt>[% INCLUDE renderOutputs outputs=step.buildstepoutputs %]</tt>
|
|
[% END %]
|
|
</td>
|
|
<td>
|
|
[% IF step.busy == 0;
|
|
INCLUDE renderDuration duration = step.stoptime - step.starttime;
|
|
ELSIF build.finished;
|
|
INCLUDE renderDuration duration = build.stoptime - step.starttime;
|
|
ELSE;
|
|
INCLUDE renderDuration duration = curTime - step.starttime;
|
|
END %]
|
|
</td>
|
|
<td>[% step.machine.split('@').1 %]</td>
|
|
<td>
|
|
[% IF step.busy == 1 %]
|
|
<strong>Building</strong>
|
|
[% ELSIF step.status == 0 %]
|
|
Succeeded
|
|
[% ELSIF step.status == 4 %]
|
|
<span class="error">Aborted</span>
|
|
[% ELSIF step.status == 7 %]
|
|
<span class="error">Timed out</span>
|
|
[% ELSIF step.status == 8 %]
|
|
<span class="error">Cached failure</span>
|
|
[% ELSE %]
|
|
<span class="error">Failed: [% HTML.escape(step.errormsg) %]</span>
|
|
[% END %]
|
|
[%%] [%+ IF has_log; INCLUDE renderLogLinks url=log inRow=1; END %]
|
|
</td>
|
|
</tr>
|
|
[% END %]
|
|
[% END %]
|
|
</tbody>
|
|
</table>
|
|
[% END %]
|
|
|
|
<ul class="nav nav-tabs">
|
|
<li class="active"><a href="#tabs-summary" data-toggle="tab">Summary</a></li>
|
|
<li><a href="#tabs-details" data-toggle="tab">Details</a></li>
|
|
<li><a href="#tabs-buildinputs" data-toggle="tab">Inputs</a></li>
|
|
[% IF build.buildsteps %]<li><a href="#tabs-buildsteps" data-toggle="tab">Build steps</a></li>[% END %]
|
|
[% IF build.dependents %]<li><a href="#tabs-usedby" data-toggle="tab">Used by</a></li>[% END%]
|
|
[% IF prevBuilds %]<li><a href="#tabs-history" data-toggle="tab">History chart</a></li>[% END %]
|
|
[% IF drvAvailable %]<li><a href="#tabs-build-deps" data-toggle="tab">Build dependencies</a></li>[% END %]
|
|
[% IF available %]<li><a href="#tabs-runtime-deps" data-toggle="tab">Runtime dependencies</a></li>[% END %]
|
|
</ul>
|
|
|
|
<div id="generic-tabs" class="tab-content">
|
|
|
|
<div id="tabs-summary" class="tab-pane active">
|
|
|
|
[% IF build.nixexprinput %]
|
|
[% WRAPPER makePopover title="Reproduce locally" classes="btn-info pull-right" placement="left" %]
|
|
[% url = c.uri_for('/build' build.id 'reproduce') %]
|
|
|
|
<p>You can reproduce this build on your own machine by
|
|
downloading <a [% HTML.attributes(href => url) %]>a script</a>
|
|
that checks out all inputs of the build and then invokes Nix
|
|
to perform the build. This script requires that you have Nix
|
|
on your system.</p>
|
|
|
|
<p>To download and execute the script from the command line,
|
|
run the following command:</p>
|
|
|
|
<pre>
|
|
<span class="shell-prompt">$ </span>bash <(curl <a [% HTML.attributes(href => url) %]>[% HTML.escape(url) %]</a>)
|
|
</pre>
|
|
|
|
[% END %]
|
|
[% END %]
|
|
|
|
<table>
|
|
<tr>
|
|
<td>
|
|
[% INCLUDE renderBuildStatusIcon size=128, build=build %]
|
|
</td>
|
|
<td>
|
|
<table class="info-table">
|
|
<tr>
|
|
<th>Build ID:</th>
|
|
<td>[% build.id %]</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Status:</th>
|
|
<td>[% INCLUDE renderStatus build=build icon=0 %]</td>
|
|
</tr>
|
|
<tr>
|
|
<th>System:</th>
|
|
<td><tt>[% build.system %]</tt></td>
|
|
</tr>
|
|
[% IF build.releasename %]
|
|
<tr>
|
|
<th>Release name:</th>
|
|
<td><tt>[% HTML.escape(build.releasename) %]</tt></td>
|
|
</tr>
|
|
[% ELSE %]
|
|
<tr>
|
|
<th>Nix name:</th>
|
|
<td><tt>[% build.nixname %]</tt></td>
|
|
</tr>
|
|
[% END %]
|
|
[% IF eval %]
|
|
<tr>
|
|
<th>Part of:</th>
|
|
<td>
|
|
<a href="[% c.uri_for(c.controller('JobsetEval').action_for('view'), [eval.id]) %]">evaluation [% eval.id %]</a>
|
|
[% IF nrEvals > 1 +%] (and <a href="[% c.uri_for('/build' build.id 'evals') %]">[% nrEvals - 1 %] others</a>)[% END %]
|
|
</td>
|
|
</tr>
|
|
[% END %]
|
|
[% IF build.iscachedbuild %]
|
|
<tr>
|
|
<th>Cached from:</th>
|
|
<td>[% INCLUDE renderFullBuildLink build=cachedBuild %]</td>
|
|
</tr>
|
|
[% END %]
|
|
[% IF build.finished %]
|
|
<tr>
|
|
<th>Duration:</th>
|
|
<td>[% actualBuild = build.iscachedbuild ? cachedBuild : build;
|
|
INCLUDE renderDuration duration = actualBuild.stoptime - actualBuild.starttime %];
|
|
finished at [% INCLUDE renderDateTime timestamp = actualBuild.stoptime %]</td>
|
|
</tr>
|
|
[% END %]
|
|
[% IF log_exists(build.drvpath) %]
|
|
<tr>
|
|
<th>Logfile:</th>
|
|
<td>
|
|
<a class="btn btn-mini" href="[% c.uri_for('/build' build.id 'log') %]">pretty</a>
|
|
<a class="btn btn-mini" href="[% c.uri_for('/build' build.id 'log' 'raw') %]">raw</a>
|
|
<a class="btn btn-mini" href="[% c.uri_for('/build' build.id 'log' 'tail-reload') %]">tail</a>
|
|
</td>
|
|
</tr>
|
|
[% END %]
|
|
</table>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
[% IF c.user_exists && available %]
|
|
<br/>
|
|
<form class="form-horizontal" action="[% c.uri_for('/build' build.id 'add-to-release') %]" method="post">
|
|
<div class="control-group">
|
|
<label class="control-label">Add to release</label>
|
|
<div class="controls">
|
|
<input type="text" class="input" name="name"></input>
|
|
<button type="submit" class="btn btn-success">Apply</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
[% END %]
|
|
|
|
[% IF build.buildproducts %]
|
|
|
|
<h3>Build products</h3>
|
|
|
|
[% IF !available %]
|
|
<p class="error">Note: this build is no longer available.</p>
|
|
[% END %]
|
|
|
|
[% INCLUDE renderProductList latestRoot=['/job' build.project.name build.jobset.name build.job.name 'latest'] %]
|
|
|
|
[% END %]
|
|
|
|
[% IF build.busy %]
|
|
<h3>Running build steps</h3>
|
|
[% INCLUDE renderBuildSteps type="Running" %]
|
|
[% END %]
|
|
|
|
[% IF build.finished %]
|
|
|
|
[% IF build.buildsteps && build.buildstatus != 0 && build.buildstatus != 6 %]
|
|
<h3>Failed build steps</h3>
|
|
[% INCLUDE renderBuildSteps type="Failed" %]
|
|
[% END %]
|
|
|
|
[% IF prevSuccessfulBuild %]
|
|
<h3>Changes</h3>
|
|
<table class="table table-striped table-condensed">
|
|
<thead>
|
|
<th>Last successful build [% INCLUDE renderDateTime timestamp = prevSuccessfulBuild.timestamp %]</th>
|
|
[% IF prevSuccessfulBuild && firstBrokenBuild && firstBrokenBuild.id != build.id %]
|
|
<th>First broken build [% INCLUDE renderDateTime timestamp = firstBrokenBuild.timestamp %]
|
|
<a class="btn btn-mini" href="[% c.uri_for(c.controller('API').action_for('logdiff') prevSuccessfulBuild.id firstBrokenBuild.id ) %]">log diff</a>
|
|
</th>
|
|
[% END %]
|
|
<th>This build [% INCLUDE renderDateTime timestamp = build.timestamp %]
|
|
<a class="btn btn-mini" href="[% c.uri_for(c.controller('API').action_for('logdiff') prevSuccessfulBuild.id build.id) %]">log diff</a>
|
|
</th>
|
|
</thead>
|
|
<tr>
|
|
<td valign="center">[% INCLUDE renderBuildStatusIcon build=prevSuccessfulBuild size=32 %] [% INCLUDE renderBuildLink build=prevSuccessfulBuild %]</td>
|
|
[% IF prevSuccessfulBuild && firstBrokenBuild && firstBrokenBuild.id != build.id %]
|
|
<td valign="center">[% INCLUDE renderBuildStatusIcon build=firstBrokenBuild size=32 %] [% INCLUDE renderBuildLink build=firstBrokenBuild %]</td>
|
|
[% END %]
|
|
<td>[% INCLUDE renderBuildStatusIcon build=build size=32 %] [% INCLUDE renderBuildLink build=build %]</td>
|
|
</tr>
|
|
<tr>
|
|
<td></td>
|
|
[% IF prevSuccessfulBuild && firstBrokenBuild && firstBrokenBuild.id != build.id %]
|
|
<td>[% INCLUDE renderInputDiff inputs1=prevSuccessfulBuild.inputs inputs2=firstBrokenBuild.inputs %]</td>
|
|
[% END %]
|
|
<td>[% INCLUDE renderInputDiff inputs1=prevSuccessfulBuild.inputs inputs2=build.inputs %]</td>
|
|
</tr>
|
|
</table>
|
|
[% END %]
|
|
|
|
[% IF build.errormsg && build.buildstatus != 5 %]
|
|
<h2 id="nix-error">Nix error output</h2>
|
|
<pre class="buildlog">[% HTML.escape(build.errormsg) %]</pre>
|
|
[% END %]
|
|
|
|
[% END %]
|
|
|
|
[% IF logtext %]
|
|
<h2>Log</h2>
|
|
<pre class="buildlog">[% HTML.escape(logtext) %]</pre>
|
|
[% END %]
|
|
|
|
</div>
|
|
|
|
<div id="tabs-details" class="tab-pane">
|
|
|
|
<table class="info-table">
|
|
[% IF build.nixexprinput %]
|
|
<tr>
|
|
<th>Nix expression:</th>
|
|
<td>file <tt>[% HTML.escape(build.nixexprpath) %]</tt> in input <tt>[% HTML.escape(build.nixexprinput) %]</tt></td>
|
|
</tr>
|
|
[% END %]
|
|
<tr>
|
|
<th>Nix name:</th>
|
|
<td><tt>[% build.nixname %]</tt></td>
|
|
</tr>
|
|
<tr>
|
|
<th>Short description:</th>
|
|
<td>[% IF build.description %][% HTML.escape(build.description) %][% ELSE %]<em>(not given)</em>[% END %]</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Long description:</th>
|
|
<td>[% IF build.longdescription %][% HTML.escape(build.longdescription) %][% ELSE %]<em>(not given)</em>[% END %]</td>
|
|
</tr>
|
|
<tr>
|
|
<th>License:</th>
|
|
<td>[% IF build.license %][% HTML.escape(build.license) %][% ELSE %]<em>(not given)</em>[% END %]</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Homepage:</th>
|
|
<td>[% IF build.homepage %]<a [% HTML.attributes(href => build.homepage) %]>[% HTML.escape(build.homepage) %]</a>[% ELSE %]<em>(not given)</em>[% END %]</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Maintainer(s):</th>
|
|
<td>[% IF build.maintainers %][% HTML.escape(build.maintainers) %][% ELSE %]<em>(not given)</em>[% END %]</td>
|
|
</tr>
|
|
<tr>
|
|
<th>System:</th>
|
|
<td><tt>[% build.system %]</tt></td>
|
|
</tr>
|
|
<tr>
|
|
<th>Derivation store path:</th>
|
|
<td><tt>[% build.drvpath %]</tt></td>
|
|
</tr>
|
|
<tr>
|
|
<th>Output store paths:</th>
|
|
<td><tt>[% INCLUDE renderOutputs outputs=build.buildoutputs %]</tt></td>
|
|
</tr>
|
|
<tr>
|
|
<th>Queued:</th>
|
|
<td>[% INCLUDE renderDateTime timestamp = build.timestamp %]</td>
|
|
</tr>
|
|
[% IF build.finished && !build.iscachedbuild %]
|
|
<tr>
|
|
<th>Build started:</th>
|
|
<td>[% INCLUDE renderDateTime timestamp = build.starttime %]</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Build finished:</th>
|
|
<td>[% INCLUDE renderDateTime timestamp = build.stoptime %]</td>
|
|
</tr>
|
|
[% END %]
|
|
[% IF !build.finished %]
|
|
<tr>
|
|
<th>Priority:</th>
|
|
<td>[% build.priority %]</td>
|
|
</tr>
|
|
[% END %]
|
|
[% IF build.finished && build.buildproducts %]
|
|
<tr>
|
|
<th>Availability:</th>
|
|
<td>
|
|
[% IF !available %]
|
|
<em>Build output is no longer available</em>
|
|
[% ELSIF build.keep %]
|
|
<em>Build output will be kept permanently</em>
|
|
[% ELSE %]
|
|
<em>Build output is available, but may be garbage-collected</em>
|
|
[% END %]
|
|
</td>
|
|
</tr>
|
|
[% END %]
|
|
</table>
|
|
</div>
|
|
|
|
<div id="tabs-buildinputs" class="tab-pane">
|
|
|
|
[% INCLUDE renderInputs inputs=build.inputs %]
|
|
|
|
[% IF prevBuild %]
|
|
<h3>Changes since previous [% INCLUDE renderBuildLink build=prevBuild %]</h3>
|
|
[% INCLUDE renderInputDiff inputs2=build.inputs inputs1=prevBuild.inputs %]
|
|
[% END %]
|
|
|
|
</div>
|
|
|
|
[% IF build.buildsteps %]
|
|
<div id="tabs-buildsteps" class="tab-pane">
|
|
[% INCLUDE renderBuildSteps type="All" %]
|
|
</div>
|
|
[% END %]
|
|
|
|
[% IF build.dependents %]
|
|
<div id="tabs-usedby" class="tab-pane">
|
|
|
|
<p>The following builds have used this build as an input:</p>
|
|
|
|
<table class="table table-condensed table-striped">
|
|
<thead>
|
|
<tr><th>Build</th><th>Input name</th><th>System</th><th>Timestamp</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
[% FOREACH input IN build.dependents %]
|
|
<tr>
|
|
<td>[% INCLUDE renderFullBuildLink build=input.build %]</td>
|
|
<td><tt>[% input.name %]</tt></td>
|
|
<td><tt>[% input.build.system %]</tt></td>
|
|
<td>[% INCLUDE renderDateTime timestamp = input.build.timestamp %]</td>
|
|
</tr>
|
|
[% END %]
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
[% END %]
|
|
|
|
[% IF prevBuilds %]
|
|
<div id="tabs-history" class="tab-pane">
|
|
<h3>Build time history (in seconds)</h3>
|
|
|
|
<div id="placeholder" style="width:800px;height:400px;"></div>
|
|
<div id="overview" style="margin-left:50px;margin-top:20px;width:600px;height:50px"></div>
|
|
|
|
<script src="/static/js/flot/jquery.flot.js" type="text/javascript"></script>
|
|
<script src="/static/js/flot/jquery.flot.selection.js" type="text/javascript"></script>
|
|
<script type="text/javascript">
|
|
$(function() {
|
|
var d = [];
|
|
var ids = [];
|
|
[% 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 = {
|
|
xaxis: { mode: "time" },
|
|
selection: { mode: "x" },
|
|
points: { show: true },
|
|
lines: { show: true },
|
|
grid: {
|
|
clickable: true,
|
|
hoverable: true,
|
|
hoverFill: '#444',
|
|
hoverRadius: 4,
|
|
},
|
|
};
|
|
|
|
|
|
var plot = $.plot($("#placeholder"), [d], options);
|
|
|
|
var overview = $.plot($("#overview"), [d], {
|
|
series: {
|
|
lines: { show: true, lineWidth: 1 },
|
|
shadowSize: 0
|
|
},
|
|
xaxis: { ticks: [], mode: "time" },
|
|
yaxis: { ticks: [], min: 0, autoscaleMargin: 0.1 },
|
|
selection: { mode: "x" }
|
|
});
|
|
|
|
// now connect the two
|
|
|
|
$("#placeholder").bind("plotselected", function (event, ranges) {
|
|
// do the zooming
|
|
plot = $.plot($("#placeholder"), [d],
|
|
$.extend(true, {}, options, {
|
|
xaxis: { min: ranges.xaxis.from, max: ranges.xaxis.to }
|
|
}));
|
|
|
|
// don't fire event on the overview to prevent eternal loop
|
|
overview.setSelection(ranges, true);
|
|
});
|
|
|
|
$("#overview").bind("plotselected", function (event, ranges) {
|
|
plot.setSelection(ranges);
|
|
});
|
|
|
|
$("#placeholder").bind("plotclick", function (e, pos, item) {
|
|
if (item) {
|
|
plot.highlight(item.series, item.datapoint);
|
|
buildid = ids[item.datapoint[0]];
|
|
window.location = "/build/"+buildid;
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
|
|
<h3>Store path size history (in MB)</h3>
|
|
|
|
<div id="placeholder-size" style="width:800px;height:400px;"></div>
|
|
<div id="overview-size" style="margin-left:50px;margin-top:20px;width:600px;height:50px"></div>
|
|
|
|
<script type="text/javascript">
|
|
$(function() {
|
|
var d = [];
|
|
var ids = [];
|
|
[% 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 = {
|
|
xaxis: { mode: "time" },
|
|
selection: { mode: "x" },
|
|
points: { show: true },
|
|
lines: { show: true },
|
|
grid: {
|
|
clickable: true,
|
|
hoverable: true,
|
|
hoverFill: '#444',
|
|
hoverRadius: 4,
|
|
},
|
|
};
|
|
|
|
var plot = $.plot($("#placeholder-size"), [d], options);
|
|
|
|
var overview = $.plot($("#overview-size"), [d], {
|
|
series: {
|
|
lines: { show: true, lineWidth: 1 },
|
|
shadowSize: 0
|
|
},
|
|
xaxis: { ticks: [], mode: "time" },
|
|
yaxis: { ticks: [], min: 0, autoscaleMargin: 0.1 },
|
|
selection: { mode: "x" }
|
|
});
|
|
|
|
// now connect the two
|
|
|
|
$("#placeholder-size").bind("plotselected", function (event, ranges) {
|
|
// do the zooming
|
|
plot = $.plot($("#placeholder-size"), [d],
|
|
$.extend(true, {}, options, {
|
|
xaxis: { min: ranges.xaxis.from, max: ranges.xaxis.to }
|
|
}));
|
|
|
|
// don't fire event on the overview to prevent eternal loop
|
|
overview.setSelection(ranges, true);
|
|
});
|
|
|
|
$("#overview-size").bind("plotselected", function (event, ranges) {
|
|
plot.setSelection(ranges);
|
|
});
|
|
|
|
$("#placeholder-size").bind("plotclick", function (e, pos, item) {
|
|
if (item) {
|
|
plot.highlight(item.series, item.datapoint);
|
|
buildid = ids[item.datapoint[0]];
|
|
window.location = "/build/"+buildid;
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
|
|
</div>
|
|
[% END %]
|
|
|
|
[% IF drvAvailable %]
|
|
[% INCLUDE makeLazyTab tabName="tabs-build-deps" uri=c.uri_for('/build' build.id 'build-deps') %]
|
|
[% END %]
|
|
|
|
[% IF available %]
|
|
[% INCLUDE makeLazyTab tabName="tabs-runtime-deps" uri=c.uri_for('/build' build.id 'runtime-deps') %]
|
|
[% END %]
|
|
|
|
</div>
|
|
|
|
|
|
[% END %]
|