42fa9554 Andy McCall 1 month ago ↶
1
{% extends "base.html" %}
2
{% import "repo_tabs.html" as m %}
a0d4bbfb Andy McCall 1 month ago ↶
3
{% block title %}Run #{{ run.number }} · Flow · {{ repo.owner }}/{{ repo.name }}{% endblock %}
4
{% block bodyattrs %} data-repo="{{ repo.url }}"{% if run.live() %} data-flow-live="{{ repo.url }}/flow/{{ run.number }}/live"{% endif %}{% endblock %}
42fa9554 Andy McCall 1 month ago ↶
5
{% block tabs %}{% call m::tabs(repo, "flow") %}{% endblock %}
6
{% block mainclass %} class="wide"{% endblock %}
7
{% block content %}
8
< div class = " head " >
a0d4bbfb Andy McCall 1 month ago ↶
9
< h1 > < span class = " flow-glyph st-{{ run.status }} " data-run-glyph > {{ crate::web::flow::glyph(run.status) }}</ span > Run #{{ run.number }} < small data-run-status > {{ run.status }}</ small > </ h1 >
4f692533 Andy McCall 1 month ago ↶
10
< p class = " small mute " > {% if run.bookmark.is_empty() %}< span class = " tag " title = " Run on one change, not a bookmark" > change</ span > {% else %}< span class = " lbl lbl-bm " > {{ run.bookmark }}</ span > {% endif %} at < a href = " {{ repo.url }}/tree/{{ run.heads }}" title = " state {{ run.state }}" > < code > {{ run.state|short }}</ code > </ a > · {{ run.started_by() }} < time title = " {{ run.created_ms|when_i }}" > {{ run.created_ms|ago_i }}</ time > · {{ duration }}{% if !run.pipeline_hash.is_empty() %} · pipeline < code title = " {{ run.pipeline_hash }}" > {{ run.pipeline_hash|short }}</ code > {% endif %} · < a href = " {{ repo.url }}/file/{{ run.heads }}/.flow/pipeline.kdl" > .flow/pipeline.kdl</ a > · < a href = " {{ repo.url }}/flow" > all runs</ a > </ p >
1edac4b6 Andy McCall 1 month ago ↶
11
{% if can_rerun %}< form class = " inline " method = " post" action = " {{ repo.url }}/flow/{{ run.number }}/rerun" > < input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" > < button type = " submit" class = " small " > RE-RUN</ button > </ form > {% endif %}
745d225d Andy McCall 1 month ago ↶
12
{% if can_cancel %}< form class = " inline " method = " post" action = " {{ repo.url }}/flow/{{ run.number }}/cancel" onsubmit = " return confirm ( ' Cancel run #{{ run.number }}? Jobs still running are stopped.' ) " > < input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" > < button type = " submit" class = " small danger-btn " > CANCEL</ button > </ form > {% endif %}
42fa9554 Andy McCall 1 month ago ↶
13
</ div >
bf3d0754 Andy McCall 1 month ago ↶
14
{% if let Some(q) = queue %}
15
< section class = " panel " > < div class = " strip " > Waiting for a runner: {{ q.place().unwrap_or_default() }}.{% if !q.running.is_empty() %} Running now: {{ q.running.join(", ") }}.{% endif %}{% if !q.ahead.is_empty() %} Queued ahead: {{ q.ahead.join(", ") }}.{% endif %}{% if q.hidden > 0 %} Also ahead or running: {{ q.hidden }} job{% if q.hidden != 1 %}s{% endif %} in private repositories.{% endif %}</ div > </ section >
16
{% endif %}
42fa9554 Andy McCall 1 month ago ↶
17
{% if run.status == "error" %}
18
< section class = " panel " > < div class = " strip bad " > The pipeline is not valid, so nothing ran.</ div > < pre class = " snippet " > {{ run.error }}</ pre > < p class = " small mute " > Run < code > flow check</ code > locally to see the same problems.</ p > </ section >
19
{% else %}
417184c7 Andy McCall 20 days ago ↶
20
{% if !approvals.is_empty() %}< section class = " panel approvals " aria-label = " Approvals" >
21
< h2 > Approvals</ h2 >
22
< ul class = " plain " > {% for a in approvals %}< li class = " approval st-{{ a.state }} " > < strong > {{ a.job }}</ strong >
23
{% if a.state == "waiting" %}waits for approval by someone with the < em > {{ a.by }}</ em > role{% if a.others_only %}, other than {{ run.actor }}{% endif %}.
24
{% if a.may_decide() %}< form class = " inline " method = " post" action = " {{ repo.url }}/flow/{{ run.number }}/jobs/{{ a.job }}/decide" > < input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" > < button type = " submit" name = " action" value = " approve" class = " small " > APPROVE</ button > < button type = " submit" name = " action" value = " reject" class = " small danger-btn " > REJECT</ button > </ form >
25
{% else %}< span class = " small mute " > {{ a.why_not() }}</ span > {% endif %}
26
{% else if a.state == "approved" %}approved by {{ a.who }} < time title = " {{ a.when_ms|when }}" > {{ a.when_ms|ago }}</ time > .
27
{% else if a.state == "rejected" %}rejected by {{ a.who }} < time title = " {{ a.when_ms|when }}" > {{ a.when_ms|ago }}</ time > .
28
{% else %}not approved in time.{% endif %}</ li > {% endfor %}</ ul >
29
</ section > {% endif %}
42fa9554 Andy McCall 1 month ago ↶
30
< section class = " panel flow-graph-box " >
a0d4bbfb Andy McCall 1 month ago ↶
31
< svg class = " flow-graph " viewBox = " 0 0 {{ graph.width }} {{ graph.height }}" width = " {{ graph.width }}" height = " {{ graph.height }}" role = " img" aria-label = " Job graph of run {{ run.number }}" >
42fa9554 Andy McCall 1 month ago ↶
32
{% for e in graph.edges %}< path class = " edge st-{{ e.status }} " d = " {{ e.d }}" /> {% endfor %}
33
{% for n in graph.nodes %}
34
< a href = " #job-{{ n.j.id }}" data-job-node = " {{ n.j.id }}" class = " node st-{{ n.j.status }} " >
35
< rect x = " {{ n.x }}" y = " {{ n.y }}" width = " {{ crate::web::flow::NODE_W }}" height = " {{ crate::web::flow::NODE_H }}" rx = " 6" />
36
< text class = " g " x = " {{ n.x + 18 }}" y = " {{ n.y + 30 }}" text-anchor = " middle" data-glyph > {{ crate::web::flow::glyph(n.j.status) }}</ text >
37
< text class = " name " x = " {{ n.x + 34 }}" y = " {{ n.y + 21 }}" > {{ n.j.name }}</ text >
38
< text class = " meta " x = " {{ n.x + 34 }}" y = " {{ n.y + 37 }}" data-meta > {{ n.j.status }} · {{ n.duration }}</ text >
39
</ a >
40
{% endfor %}
41
</ svg >
42
</ section >
43
{% for l in logs %}
67040b15 Andy McCall 25 days ago ↶
44
< details class = " panel flow-job st-{{ l.j.status }} " id = " job-{{ l.j.id }} " data-job = " {{ l.j.id }}" {% if l.j.status = = "failed" || l.j.status = = "error" || l.j.status = = "running" || (l.j.status == "queued" && !l.j.waiting.is_empty()) %} open {% endif %}>
501d8b26 Andy McCall 25 days ago ↶
45
< summary > < span class = " flow-glyph st-{{ l.j.status }} " data-glyph > {{ crate::web::flow::glyph(l.j.status) }}</ span > < strong > {{ l.j.name }}</ strong > < span class = " small mute " data-meta > {{ l.j.status }}{% if let Some(c) = l.j.exit_code %} · exit {{ c }}{% endif %} · {{ l.duration }}{% if !l.j.runner.is_empty() %} · on < span {% if !l.j.placed.is_empty() %} title = " Placed there: {{ l.j.placed }}" {% endif %}> {{ l.j.runner }}</ span > {% endif %}</ span > {% if !l.j.receipt_attest.is_empty() %} < span class = " small mute " > · < a href = " {{ repo.url }}/receipts/{{ l.j.receipt_attest }}" title = " Receipt {{ l.j.receipt_hash }}, kept in the repository; check it with oxy receipt verify" > signed receipt</ a > by {{ l.j.receipt_signer }}</ span > {% else if !l.j.receipt_note.is_empty() %} < span class = " small warn " title = " {{ l.j.receipt_note }}" > · receipt not kept: {{ l.j.receipt_note }}</ span > {% else if !l.j.receipt_hash.is_empty() %} < span class = " small mute " > · unsigned receipt < code title = " {{ l.j.receipt_hash }}" > {{ l.j.receipt_hash|short }}</ code > </ span > {% endif %}{% if !l.artifacts.is_empty() %} < span class = " small mute " > · {{ l.artifacts.len()|plural("artifact", "artifacts") }}</ span > {% endif %}{% if let Some(t) = l.tests %} < span class = " small{% if t.broken() > 0 %} warn{% endif %} " > · < a href = " #tests-{{ l.j.id }}" > {{ t.summary() }}</ a > </ span > {% endif %}{% if let Some((cid, pct)) = l.coverage %} < span class = " small " > · < a href = " {{ repo.url }}/coverage?report={{ cid }}" > coverage {{ pct }}</ a > </ span > {% endif %}</ summary >
e8fa5369 Andy McCall 22 days ago ↶
46
{% if can_rerun & & l.j.done() %}< form class = " tiny inline job-rerun " method = " post" action = " {{ repo.url }}/flow/{{ run.number }}/jobs/{{ l.j.name }}/rerun" > < input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" > < button type = " submit" class = " small " title = " Run {{ l.j.name }} again in this run, with the jobs that depend on it; what it depends on keeps its results" > RE-RUN THIS JOB</ button > </ form > {% endif %}
42fa9554 Andy McCall 1 month ago ↶
47
{% if !l.j.error.is_empty() %}< div class = " strip bad " > {{ l.j.error }}</ div > {% endif %}
6f52a041 Andy McCall 1 month ago ↶
48
{% if !l.artifacts.is_empty() %}< ul class = " flow-artifacts small " > {% for a in l.artifacts %}< li > < a href = " {{ repo.url }}/flow/{{ run.number }}/artifacts/{{ l.j.name }}/{{ a.name }}" > {{ a.name }}</ a > < span class = " mute " > {{ a.path }} · {{ a.size|bytes }} · blob < code title = " {{ a.blob }}" > {{ a.blob|short }}</ code > </ span > {% if l.j.status != "passed" %} < span class = " warn " title = " Kept with when & quot; always& quot; : the job did not pass, so its receipt does not name this file" > · from a job that did not pass, not in its receipt</ span > {% endif %}{% if !releases.is_empty() & & l.j.status == "passed" %} < form class = " tiny inline " method = " post" action = " {{ repo.url }}/flow/{{ run.number }}/artifacts/{{ l.j.name }}/{{ a.name }}/attach" > < input type = " hidden" name = " _csrf" value = " {{ shell.csrf }}" > < select name = " release" aria-label = " Release to attach {{ a.name }} to" > {% for r in releases %}< option value = " {{ r.id }}" > {{ r.tag }}</ option > {% endfor %}</ select > < button > Attach to release</ button > </ form > {% endif %}</ li > {% endfor %}</ ul > {% endif %}
0b203bbf Andy McCall 1 month ago ↶
49
{% if let Some(t) = l.tests %}< div class = " flow-tests " id = " tests-{{ l.j.id }} " >
50
< p class = " small " > < span class = " tests-count ok " > {{ t.passed }} passed</ span > {% if t.failed > 0 %} · < span class = " tests-count bad " > {{ t.failed }} failed</ span > {% endif %}{% if t.errors > 0 %} · < span class = " tests-count bad " > {{ t.errors }} errored</ span > {% endif %}{% if t.skipped > 0 %} · < span class = " tests-count mute " > {{ t.skipped }} skipped</ span > {% endif %} < span class = " mute " > · {{ crate::flow::human_ms(*t.time_ms) }} of test time</ span > </ p >
51
{% for f in l.failures %}< details class = " tests-failure " >
52
< summary > < span class = " bad " > {% if f.status == "error" %}!{% else %}✕{% endif %}</ span > < code > {{ f.title() }}</ code > {% if !f.message.is_empty() %} < span class = " small mute " > {{ f.message }}</ span > {% endif %}</ summary >
53
{% if !f.output.is_empty() %}< pre class = " flow-log " > {{ f.output }}</ pre > {% endif %}
54
</ details > {% endfor %}
55
{% if t.broken() > l.failures.len() as i64 %}< p class = " small mute " > and {{ t.broken() - l.failures.len() as i64 }} more failures; the junit artifact has them all.</ p > {% endif %}
56
</ div > {% endif %}
42fa9554 Andy McCall 1 month ago ↶
57
< pre class = " flow-log " data-log data-seq = " {{ l.last_seq }}" > {% for line in l.lines %}< span class = " ln s-{{ line.stream }} " > < span class = " t " > {{ line.t / 1000 }}.{{ "{:01}"|format(line.t % 1000 / 100) }}s</ span > {{ line.text }}</ span >
58
{% endfor %}</ pre >
501d8b26 Andy McCall 25 days ago ↶
59
{% if !l.j.placed.is_empty() %}< p class = " small mute pad " > Ran on {{ l.j.runner }}: {{ l.j.placed }}.</ p > {% endif %}
4d2e44d7 Andy McCall 1 month ago ↶
60
{% if l.j.status == "queued" & & !l.j.waiting.is_empty() %}< p class = " small warn pad " data-waiting > Waiting: {{ l.j.waiting }}.</ p > {% else if l.lines.is_empty() & & l.j.status == "queued" %}< p class = " small mute pad " > Waiting for a runner{% if !l.j.depends_on().is_empty() %} and for {{ l.j.depends_on().join(", ") }}{% endif %}.</ p > {% endif %}
42fa9554 Andy McCall 1 month ago ↶
61
{% if l.lines.is_empty() & & l.j.status == "skipped" %}< p class = " small mute pad " > Skipped: a job it depends on did not pass.</ p > {% endif %}
62
</ details >
63
{% endfor %}
64
{% endif %}
65
{% endblock %}