The original code would return standard "Please come back later" page when there are only fetch errors on a newly setup declarative project. The problem is that there are two types of errors: standard errors and fetch errors. Each is acompanied by a corresponding field for time of occurence. Standard errors use 'errortime', while fetch errors have 'lastchecktime' set to the time of the error. Unfortunately, jobset.tt file was only using 'errortime' for displaying the time. This would result in the following errors in logs: Couldn't render template "date error - bad time/date string: expects 'hⓂ️s dⓂ️y' got: '' This change includes using 'lastchecktime' when rendering the error times.
225 lines
7.1 KiB
Plaintext
225 lines
7.1 KiB
Plaintext
[% WRAPPER layout.tt title="Jobset $project.name:$jobset.name" %]
|
|
[% PROCESS common.tt %]
|
|
[% USE format %]
|
|
|
|
|
|
[% BLOCK renderJobsetInput %]
|
|
<tr class="input [% extraClass %]" [% IF id %]id="[% id %]"[% END %]>
|
|
<td>
|
|
<tt>[% HTML.escape(input.name) %]</tt>
|
|
</td>
|
|
<td>
|
|
[% INCLUDE renderSelection curValue=input.type param="$baseName-type" options=inputTypes %]
|
|
</td>
|
|
<td class="inputalts" id="[% baseName %]">
|
|
[% FOREACH alt IN input.search_related('jobsetinputalts', {}, { order_by => 'altnr' }) %]
|
|
<tt class="inputalt">
|
|
[% IF input.type == "string" %]
|
|
"<span class="keep-whitespace">[% HTML.escape(alt.value) %]</span>"
|
|
[% ELSE %]
|
|
[% HTML.escape(alt.value) %]
|
|
[% END %]
|
|
</tt>
|
|
[% END %]
|
|
</td>
|
|
</tr>
|
|
[% END %]
|
|
|
|
|
|
[% BLOCK renderJobsetInputs %]
|
|
<h3>Inputs</h3>
|
|
<table class="table table-striped table-condensed">
|
|
<thead>
|
|
<tr><th>Input name</th><th>Type</th><th>Values</th></tr>
|
|
</thead>
|
|
<tbody class="inputs">
|
|
[% FOREACH input IN jobset.jobsetinputs %]
|
|
[% INCLUDE renderJobsetInput input=input baseName="input-$input.name" %]
|
|
[% END %]
|
|
</tbody>
|
|
</table>
|
|
[% END %]
|
|
|
|
|
|
<ul class="nav nav-tabs">
|
|
[% IF c.user_exists %]
|
|
<li class="dropdown">
|
|
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
|
Actions
|
|
<b class="caret"></b>
|
|
</a>
|
|
<ul class="dropdown-menu">
|
|
[% UNLESS project.declfile %]
|
|
[% INCLUDE menuItem title="Edit configuration" icon="icon-edit" uri=c.uri_for(c.controller('Jobset').action_for('edit'), c.req.captures) %]
|
|
<!--
|
|
[% INCLUDE menuItem title="Delete this jobset" icon="icon-trash" uri="javascript:deleteJobset()" %]
|
|
-->
|
|
[% INCLUDE menuItem title="Clone this jobset" uri=c.uri_for(c.controller('Jobset').action_for('edit'), c.req.captures, { cloneJobset => 1 }) %]
|
|
[% END %]
|
|
[% INCLUDE menuItem title="Evaluate this jobset" uri="javascript:confirmEvaluateJobset()" %]
|
|
</ul>
|
|
</li>
|
|
[% END %]
|
|
|
|
<li class="active"><a href="#tabs-evaluations" data-toggle="tab">Evaluations</a></li>
|
|
[% IF jobset.errormsg || jobset.fetcherrormsg %]
|
|
<li><a href="#tabs-errors" data-toggle="tab"><span class="text-warning">Evaluation errors</span></a></li>
|
|
[% END %]
|
|
<li><a href="#tabs-jobs" data-toggle="tab">Jobs</a></li>
|
|
<li><a href="#tabs-configuration" data-toggle="tab">Configuration</a></li>
|
|
<li><a href="#tabs-links" data-toggle="tab">Links</a></li>
|
|
<li><a href="#tabs-channels" data-toggle="tab">Channels</a></li>
|
|
</ul>
|
|
|
|
<div id="generic-tabs" class="tab-content">
|
|
|
|
<div id="tabs-evaluations" class="tab-pane active">
|
|
|
|
<table class="info-table">
|
|
<tr>
|
|
<th>Last checked:</th>
|
|
<td>
|
|
[% IF jobset.lastcheckedtime %]
|
|
[% INCLUDE renderDateTime timestamp = jobset.lastcheckedtime %], [% IF jobset.errormsg || jobset.fetcherrormsg %]<em class="text-warning">with errors!</em>[% ELSE %]<em>no errors</em>[% END %]
|
|
[% ELSE %]
|
|
<em>never</em>
|
|
[% END %]
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Last evaluation:</th>
|
|
<td>
|
|
[% IF latestEval %]
|
|
[% INCLUDE renderDateTime timestamp = latestEval.timestamp %]
|
|
[% ELSE %]
|
|
<em>never</em>
|
|
[% END %]
|
|
</td>
|
|
</tr>
|
|
[% IF jobset.starttime %]
|
|
<tr>
|
|
<th>Evaluation running since:</th>
|
|
<td>[% INCLUDE renderRelativeDate timestamp = jobset.starttime %]</td>
|
|
</tr>
|
|
[% ELSIF jobset.triggertime %]
|
|
<tr>
|
|
<th>Evaluation pending since:</th>
|
|
<td>[% INCLUDE renderRelativeDate timestamp = jobset.triggertime %]</td>
|
|
</tr>
|
|
[% END %]
|
|
</table>
|
|
|
|
<br/>
|
|
|
|
[% IF evals.size() > 0 %]
|
|
[% INCLUDE renderEvals linkToAll=c.uri_for(c.controller('Jobset').action_for('evals'), [project.name, jobset.name]) %]
|
|
[% END %]
|
|
|
|
</div>
|
|
|
|
[% IF jobset.errormsg || jobset.fetcherrormsg %]
|
|
<div id="tabs-errors" class="tab-pane">
|
|
<p>Errors occurred at [% INCLUDE renderDateTime timestamp=(jobset.errortime || jobset.lastcheckedtime) %].</p>
|
|
<pre class="alert alert-error">[% HTML.escape(jobset.fetcherrormsg || jobset.errormsg) %]</pre>
|
|
</div>
|
|
[% END %]
|
|
|
|
<div id="tabs-configuration" class="tab-pane">
|
|
|
|
<table class="info-table">
|
|
<tr>
|
|
<th>State:</th>
|
|
<td>[% IF jobset.enabled == 0; "Disabled"; ELSIF jobset.enabled == 1; "Enabled"; ELSIF jobset.enabled == 2; "One-shot"; ELSIF jobset.enabled == 3; "One-at-a-time"; END %]</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Description:</th>
|
|
<td>[% HTML.escape(jobset.description) %]</td>
|
|
</tr>
|
|
[% IF jobset.type == 1 %]
|
|
<tr>
|
|
<th>Flake URI:</th>
|
|
<td>
|
|
<tt>[% HTML.escape(jobset.flake) %]</tt>
|
|
</td>
|
|
</tr>
|
|
[% END %]
|
|
[% IF jobset.type == 0 %]
|
|
<tr>
|
|
<th>Nix expression:</th>
|
|
<td>
|
|
<tt>[% HTML.escape(jobset.nixexprpath) %]</tt> in input
|
|
<tt>[% HTML.escape(jobset.nixexprinput) %]</tt>
|
|
</td>
|
|
</tr>
|
|
[% END %]
|
|
<tr>
|
|
<th>Check interval:</th>
|
|
<td>[% jobset.checkinterval || "<em>disabled</em>" %]</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Scheduling shares:</th>
|
|
<td>[% jobset.schedulingshares %] [% IF totalShares %] ([% f = format("%.2f"); f(jobset.schedulingshares / totalShares * 100) %]% out of [% totalShares %] shares)[% END %]</td>
|
|
</tr>
|
|
[% IF emailNotification %]
|
|
<tr>
|
|
<th>Enable email notification:</th>
|
|
<td>[% jobset.enableemail ? "Yes" : "No" %]</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Email override:</th>
|
|
<td>[% HTML.escape(jobset.emailoverride) %]</td>
|
|
</tr>
|
|
[% END %]
|
|
<tr>
|
|
<th>Number of evaluations to keep:</th>
|
|
<td>[% jobset.keepnr %]</td>
|
|
</tr>
|
|
</table>
|
|
|
|
[% IF jobset.type == 0 %]
|
|
[% INCLUDE renderJobsetInputs %]
|
|
[% END %]
|
|
</div>
|
|
|
|
[% INCLUDE makeLazyTab tabName="tabs-jobs" uri=c.uri_for('/jobset' project.name jobset.name "jobs-tab") %]
|
|
|
|
<div id="tabs-links" class="tab-pane">
|
|
<ul>
|
|
<li><a href="[% c.uri_for(c.controller('Jobset').action_for('latest_eval'), c.req.captures) %]">Latest finished evaluation</a></li>
|
|
</ul>
|
|
</div>
|
|
|
|
[% INCLUDE makeLazyTab tabName="tabs-channels" uri=c.uri_for('/jobset' project.name jobset.name "channels-tab") %]
|
|
|
|
</div>
|
|
|
|
<script>
|
|
function confirmEvaluateJobset() {
|
|
bootbox.confirm(
|
|
'Are you sure you want to force evaluation of this jobset?',
|
|
function(c) {
|
|
if (!c) return;
|
|
requestJSON({
|
|
url: "[% HTML.escape(c.uri_for('/api/push', { jobsets = project.name _ ':' _ jobset.name, force = "1" })) %]",
|
|
success: function(data) {
|
|
bootbox.alert("The jobset has been scheduled for evaluation.");
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
function deleteJobset() {
|
|
bootbox.confirm(
|
|
'Are you sure you want to delete this jobset?',
|
|
function(c) {
|
|
if (!c) return;
|
|
redirectJSON({
|
|
url: "[% c.uri_for(c.controller('Jobset').action_for('jobset'), c.req.captures) %]",
|
|
type: 'DELETE'
|
|
});
|
|
});
|
|
};
|
|
</script>
|
|
|
|
[% END %]
|