ops/dossiers-ngi: edition tree bar, 10min tip-skip rebuild, cleanup
This commit is contained in:
parent
e35205b6cd
commit
737d37a76d
22 changed files with 1458 additions and 173 deletions
|
|
@ -2,28 +2,50 @@
|
|||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.35rem 0.85rem;
|
||||
align-items: baseline;
|
||||
margin: -1.5rem -1.5rem 1rem;
|
||||
padding: 0.55rem 1.5rem;
|
||||
padding: 0.55rem 1.5rem 0.65rem;
|
||||
background: #10141a;
|
||||
border-bottom: 1px solid #243040;
|
||||
color: #c8d0d8;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
font-size: 12px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
.cascade-bar a {
|
||||
.cascade-tree-lines {
|
||||
white-space: pre;
|
||||
overflow-x: auto;
|
||||
}
|
||||
a.cascade-tree-row {
|
||||
display: block;
|
||||
color: #7ec8ff;
|
||||
text-decoration: none;
|
||||
padding: 0.05rem 0.15rem;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.cascade-bar a:hover { text-decoration: underline; }
|
||||
.cascade-bar a.is-active {
|
||||
color: #e8eef4;
|
||||
a.cascade-tree-row:hover {
|
||||
background: #1a2430;
|
||||
text-decoration: none;
|
||||
box-shadow: inset 0 -2px 0 #e8eef4;
|
||||
}
|
||||
.cascade-bar a.is-active:hover { text-decoration: none; }
|
||||
.cascade-bar code { color: inherit; }
|
||||
a.cascade-tree-row.is-active {
|
||||
color: #e8eef4;
|
||||
background: #1e2a38;
|
||||
box-shadow: inset 3px 0 0 #e8eef4;
|
||||
}
|
||||
a.cascade-tree-row.is-active:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.cascade-bar code { color: inherit; font-size: inherit; }
|
||||
.cascade-bar .tip {
|
||||
color: #8a96a3;
|
||||
font-size: 12px;
|
||||
}
|
||||
.cascade-bar .cascade-default {
|
||||
color: #6a9080;
|
||||
}
|
||||
.cascade-timing {
|
||||
margin-top: 0.35rem;
|
||||
color: #6a7888;
|
||||
font-size: 11px;
|
||||
}
|
||||
.cascade-bar .tip {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,5 @@
|
|||
{% $p := .Req.URL.Path %}
|
||||
<link rel="stylesheet" href="/_cascade/bar.css">
|
||||
<nav class="cascade-bar" aria-label="cascade">
|
||||
<a href="/" {% if or (eq $p "/") (eq $p "/index.html") %}class="is-active" aria-current="page"{% end %}>cascade</a>
|
||||
<a href="/main/" {% if or (eq $p "/main") (or (eq $p "/main/") (eq $p "/main/index.html")) %}class="is-active" aria-current="page"{% end %}><code>/main/</code> <span class="tip" data-tip="main">main @ d2fbd79</span></a>
|
||||
<a href="/feat-sm-content/" {% if or (eq $p "/feat-sm-content") (or (eq $p "/feat-sm-content/") (eq $p "/feat-sm-content/index.html")) %}class="is-active" aria-current="page"{% end %}><code>/feat-sm-content/</code> <span class="tip" data-tip="feat/sm-content">feat/sm-content @ 089ef1f</span></a>
|
||||
<a href="/feat-website-features/" {% if or (eq $p "/feat-website-features") (or (eq $p "/feat-website-features/") (eq $p "/feat-website-features/index.html")) %}class="is-active" aria-current="page"{% end %}><code>/feat-website-features/</code> <span class="tip" data-tip="feat/website-features">feat/website-features @ 53a3176</span></a>
|
||||
<a href="/feat-website-design/" {% if or (eq $p "/feat-website-design") (or (eq $p "/feat-website-design/") (eq $p "/feat-website-design/index.html")) %}class="is-active" aria-current="page"{% end %}><code>/feat-website-design/</code> <span class="tip" data-tip="feat/website-design">feat/website-design @ 2e26c80</span></a>
|
||||
<a href="/feat-website-views/" {% if or (eq $p "/feat-website-views") (or (eq $p "/feat-website-views/") (eq $p "/feat-website-views/index.html")) %}class="is-active" aria-current="page"{% end %}><code>/feat-website-views/</code> <span class="tip" data-tip="feat/website-views">feat/website-views @ bbafc38</span></a>
|
||||
<nav class="cascade-bar" aria-label="cascade builds" id="cascade-bar">
|
||||
<div class="cascade-tree" id="cascade-tree" role="tree">Loading builds…</div>
|
||||
</nav>
|
||||
<script src="/_cascade/bar.js" defer></script>
|
||||
|
|
|
|||
|
|
@ -1,24 +1,146 @@
|
|||
(function () {
|
||||
var nodes = document.querySelectorAll(".cascade-bar [data-tip]");
|
||||
if (!nodes.length) return;
|
||||
function cookieEdition() {
|
||||
var m = document.cookie.match(/(?:^|; )tdpd_edition=([^;]*)/);
|
||||
if (!m) return "flask";
|
||||
try {
|
||||
return decodeURIComponent(m[1]) || "flask";
|
||||
} catch (e) {
|
||||
return "flask";
|
||||
}
|
||||
}
|
||||
|
||||
function esc(s) {
|
||||
return String(s)
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """);
|
||||
}
|
||||
|
||||
function shortTime(iso) {
|
||||
if (!iso) return "";
|
||||
// keep local-looking wall clock from ISO when possible
|
||||
var m = String(iso).match(/T(\d{2}:\d{2})/);
|
||||
if (m) return m[1];
|
||||
return String(iso).slice(0, 16);
|
||||
}
|
||||
|
||||
function fmtBuildSec(sec) {
|
||||
if (sec == null || sec === "") return "";
|
||||
var n = Number(sec);
|
||||
if (!isFinite(n)) return "";
|
||||
if (n < 10) return n.toFixed(1) + "s";
|
||||
return Math.round(n) + "s";
|
||||
}
|
||||
|
||||
function markActive(root, edition) {
|
||||
var links = root.querySelectorAll("a[data-edition]");
|
||||
for (var i = 0; i < links.length; i++) {
|
||||
var a = links[i];
|
||||
var ed = a.getAttribute("data-edition");
|
||||
var on =
|
||||
ed === edition ||
|
||||
(edition === "flask" && a.getAttribute("data-default") === "1");
|
||||
if (on) {
|
||||
a.classList.add("is-active");
|
||||
a.setAttribute("aria-current", "page");
|
||||
} else {
|
||||
a.classList.remove("is-active");
|
||||
a.removeAttribute("aria-current");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderTree(data) {
|
||||
var mount = document.getElementById("cascade-tree");
|
||||
if (!mount) return;
|
||||
var list = (data && data.branches) || [];
|
||||
if (!list.length) {
|
||||
mount.textContent = "No branches in branch-status.json";
|
||||
return;
|
||||
}
|
||||
var defaultBranch = (data && data.default_branch) || "feat/website-views";
|
||||
var timing = (data && data.rebuild_timing) || null;
|
||||
var html = [];
|
||||
html.push('<div class="cascade-tree-lines" role="group">');
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
var row = list[i];
|
||||
var edition = row.edition || "";
|
||||
var branch = row.branch || "";
|
||||
var tip = row.tip || "";
|
||||
var prefix = row.prefix || "";
|
||||
var parent = row.parent || "";
|
||||
var tipTime = row.tip_time || "";
|
||||
var builtAt = row.built_at || "";
|
||||
var buildSec = row.build_seconds;
|
||||
var isDefault = branch === defaultBranch;
|
||||
var metaBits = [];
|
||||
if (tip) metaBits.push("@" + tip);
|
||||
if (tipTime) metaBits.push("tip " + shortTime(tipTime));
|
||||
if (buildSec != null && buildSec !== "")
|
||||
metaBits.push("build " + fmtBuildSec(buildSec));
|
||||
if (builtAt) metaBits.push("at " + shortTime(builtAt));
|
||||
var label =
|
||||
esc(prefix) +
|
||||
"<code>" +
|
||||
esc(branch) +
|
||||
"</code>" +
|
||||
(metaBits.length
|
||||
? '<span class="tip"> ' + esc(metaBits.join(" · ")) + "</span>"
|
||||
: "") +
|
||||
(isDefault ? '<span class="cascade-default"> · default</span>' : "");
|
||||
html.push(
|
||||
'<a class="cascade-tree-row" role="treeitem" href="/_cascade/use/' +
|
||||
esc(edition) +
|
||||
'" data-edition="' +
|
||||
esc(edition) +
|
||||
'"' +
|
||||
(parent ? ' data-parent="' + esc(parent) + '"' : "") +
|
||||
(isDefault ? ' data-default="1"' : "") +
|
||||
' title="Switch build to ' +
|
||||
esc(branch) +
|
||||
(tip ? " @ " + esc(tip) : "") +
|
||||
(tipTime ? " tip=" + esc(tipTime) : "") +
|
||||
(buildSec != null ? " build=" + esc(fmtBuildSec(buildSec)) : "") +
|
||||
'">' +
|
||||
label +
|
||||
"</a>"
|
||||
);
|
||||
}
|
||||
html.push("</div>");
|
||||
if (timing && timing.wall_seconds != null) {
|
||||
html.push(
|
||||
'<div class="cascade-timing">last full pass ~' +
|
||||
esc(String(timing.wall_seconds)) +
|
||||
"s · built " +
|
||||
esc(String(timing.built || 0)) +
|
||||
" / skipped " +
|
||||
esc(String(timing.skipped || 0)) +
|
||||
" / " +
|
||||
esc(String(timing.branches || list.length)) +
|
||||
"</div>"
|
||||
);
|
||||
}
|
||||
mount.innerHTML = html.join("");
|
||||
markActive(mount, cookieEdition());
|
||||
}
|
||||
|
||||
var edition = cookieEdition();
|
||||
var mount = document.getElementById("cascade-tree");
|
||||
fetch("/branch-status.json", { cache: "no-store" })
|
||||
.then(function (res) {
|
||||
if (!res.ok) throw new Error("status");
|
||||
return res.json();
|
||||
})
|
||||
.then(function (data) {
|
||||
var tips = {};
|
||||
var list = (data && data.branches) || [];
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
var row = list[i];
|
||||
if (row && row.branch && row.tip) tips[row.branch] = row.tip;
|
||||
}
|
||||
for (var j = 0; j < nodes.length; j++) {
|
||||
var el = nodes[j];
|
||||
var name = el.getAttribute("data-tip");
|
||||
var tip = tips[name];
|
||||
if (tip) el.textContent = name + " @ " + tip;
|
||||
}
|
||||
renderTree(data);
|
||||
})
|
||||
.catch(function () {});
|
||||
.catch(function () {
|
||||
if (mount) {
|
||||
mount.innerHTML =
|
||||
'<a class="cascade-tree-row" href="/_cascade/use/flask" data-edition="flask"><code>flask</code></a>' +
|
||||
'<span class="tip"> (branch-status unavailable)</span>';
|
||||
markActive(mount, edition);
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>edition pending</title>
|
||||
<style>
|
||||
:root { color-scheme: dark; }
|
||||
body { margin:0; padding:1.5rem; font-family: ui-monospace, monospace; background:#0b0d10; color:#c8d0d8; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{% include "_cascade/bar.html" . %}
|
||||
<h1>edition pending</h1>
|
||||
<p>Full freeze for this tip is not staged under <code>editions/</code> yet. Sticky bar still switches other builds.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,31 +1,124 @@
|
|||
{
|
||||
"generated": "2026-09-30T02:30:50+02:00",
|
||||
"host": "https://dossiers.ngi.hacktivism.ch",
|
||||
"generated": "2026-09-30T15:34:26+02:00",
|
||||
"host": "koopa",
|
||||
"default_branch": "feat/website-views",
|
||||
"default_edition": "flask",
|
||||
"branches": [
|
||||
{
|
||||
"branch": "feature/nl-net-injestion-injestor-update",
|
||||
"edition": "feature-nl-net-injestion-injestor-update",
|
||||
"tip": "ba68fe9",
|
||||
"tip_time": "2026-08-27T10:42:02+02:00",
|
||||
"parent": "",
|
||||
"prefix": "",
|
||||
"url": "/_cascade/use/feature-nl-net-injestion-injestor-update",
|
||||
"built_at": "2026-09-30T15:23:10+02:00",
|
||||
"build_seconds": 3.841
|
||||
},
|
||||
{
|
||||
"branch": "main",
|
||||
"edition": "main",
|
||||
"tip": "d2fbd79",
|
||||
"url": "https://dossiers.ngi.hacktivism.ch/main/"
|
||||
"tip_time": "2026-09-01T16:52:35+02:00",
|
||||
"parent": "",
|
||||
"prefix": "",
|
||||
"url": "/_cascade/use/main",
|
||||
"built_at": "2026-09-30T15:23:10+02:00",
|
||||
"build_seconds": 8.447
|
||||
},
|
||||
{
|
||||
"branch": "feat/sm-content",
|
||||
"edition": "feat-sm-content",
|
||||
"tip": "089ef1f",
|
||||
"url": "https://dossiers.ngi.hacktivism.ch/feat-sm-content/"
|
||||
"tip_time": "2026-09-28T15:52:12+02:00",
|
||||
"parent": "main",
|
||||
"prefix": "\u251c\u2500\u2500 ",
|
||||
"url": "/_cascade/use/feat-sm-content",
|
||||
"built_at": "2026-09-30T15:23:10+02:00",
|
||||
"build_seconds": 12.845
|
||||
},
|
||||
{
|
||||
"branch": "feat/website-features",
|
||||
"edition": "feat-website-features",
|
||||
"tip": "53a3176",
|
||||
"url": "https://dossiers.ngi.hacktivism.ch/feat-website-features/"
|
||||
"tip_time": "2026-09-28T19:38:22+02:00",
|
||||
"parent": "feat/sm-content",
|
||||
"prefix": "\u2502 \u2514\u2500\u2500 ",
|
||||
"url": "/_cascade/use/feat-website-features",
|
||||
"built_at": "2026-09-30T15:23:10+02:00",
|
||||
"build_seconds": 8.34
|
||||
},
|
||||
{
|
||||
"branch": "feat/website-design",
|
||||
"edition": "feat-website-design",
|
||||
"tip": "2e26c80",
|
||||
"url": "https://dossiers.ngi.hacktivism.ch/feat-website-design/"
|
||||
"tip_time": "2026-09-28T22:42:08+02:00",
|
||||
"parent": "feat/website-features",
|
||||
"prefix": "\u2502 \u2514\u2500\u2500 ",
|
||||
"url": "/_cascade/use/feat-website-design",
|
||||
"built_at": "2026-09-30T15:23:10+02:00",
|
||||
"build_seconds": 10.358
|
||||
},
|
||||
{
|
||||
"branch": "feat/website-views",
|
||||
"edition": "feat-website-views",
|
||||
"tip": "bbafc38",
|
||||
"url": "https://dossiers.ngi.hacktivism.ch/feat-website-views/"
|
||||
"tip_time": "2026-09-28T23:17:42+02:00",
|
||||
"parent": "feat/website-design",
|
||||
"prefix": "\u2502 \u2514\u2500\u2500 ",
|
||||
"url": "/_cascade/use/feat-website-views",
|
||||
"built_at": "2026-09-30T15:23:10+02:00",
|
||||
"build_seconds": 5.576
|
||||
},
|
||||
{
|
||||
"branch": "project_template",
|
||||
"edition": "project_template",
|
||||
"tip": "a97c263",
|
||||
"tip_time": "2026-09-28T15:03:35+02:00",
|
||||
"parent": "main",
|
||||
"prefix": "\u2514\u2500\u2500 ",
|
||||
"url": "/_cascade/use/project_template",
|
||||
"built_at": "2026-09-30T15:23:10+02:00",
|
||||
"build_seconds": 2.245
|
||||
}
|
||||
]
|
||||
],
|
||||
"rebuild": {
|
||||
"feat/sm-content": {
|
||||
"status": "skipped",
|
||||
"seconds": 0.077
|
||||
},
|
||||
"feat/website-design": {
|
||||
"status": "skipped",
|
||||
"seconds": 0.083
|
||||
},
|
||||
"feat/website-features": {
|
||||
"status": "skipped",
|
||||
"seconds": 0.091
|
||||
},
|
||||
"feat/website-views": {
|
||||
"status": "skipped",
|
||||
"seconds": 0.09
|
||||
},
|
||||
"feature/nl-net-injestion-injestor-update": {
|
||||
"status": "skipped",
|
||||
"seconds": 0.098
|
||||
},
|
||||
"main": {
|
||||
"status": "skipped",
|
||||
"seconds": 0.102
|
||||
},
|
||||
"project_template": {
|
||||
"status": "skipped",
|
||||
"seconds": 0.083
|
||||
}
|
||||
},
|
||||
"rebuild_timing": {
|
||||
"wall_seconds": 0.627,
|
||||
"branches": 7,
|
||||
"built": 0,
|
||||
"skipped": 7,
|
||||
"errors": 0,
|
||||
"built_seconds_sum": 0.0,
|
||||
"built_seconds_max": 0.0
|
||||
}
|
||||
}
|
||||
|
|
|
|||
51
sites/dossiers.ngi.hacktivism.ch/cascade/_cascade/bar.css
Normal file
51
sites/dossiers.ngi.hacktivism.ch/cascade/_cascade/bar.css
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
.cascade-bar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
margin: -1.5rem -1.5rem 1rem;
|
||||
padding: 0.55rem 1.5rem 0.65rem;
|
||||
background: #10141a;
|
||||
border-bottom: 1px solid #243040;
|
||||
color: #c8d0d8;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
font-size: 12px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
.cascade-tree-lines {
|
||||
white-space: pre;
|
||||
overflow-x: auto;
|
||||
}
|
||||
a.cascade-tree-row {
|
||||
display: block;
|
||||
color: #7ec8ff;
|
||||
text-decoration: none;
|
||||
padding: 0.05rem 0.15rem;
|
||||
border-radius: 2px;
|
||||
}
|
||||
a.cascade-tree-row:hover {
|
||||
background: #1a2430;
|
||||
text-decoration: none;
|
||||
}
|
||||
a.cascade-tree-row.is-active {
|
||||
color: #e8eef4;
|
||||
background: #1e2a38;
|
||||
box-shadow: inset 3px 0 0 #e8eef4;
|
||||
}
|
||||
a.cascade-tree-row.is-active:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.cascade-bar code { color: inherit; font-size: inherit; }
|
||||
.cascade-bar .tip {
|
||||
color: #8a96a3;
|
||||
}
|
||||
.cascade-bar .cascade-default {
|
||||
color: #6a9080;
|
||||
}
|
||||
.cascade-timing {
|
||||
margin-top: 0.35rem;
|
||||
color: #6a7888;
|
||||
font-size: 11px;
|
||||
}
|
||||
.cascade-bar .tip {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<link rel="stylesheet" href="/_cascade/bar.css">
|
||||
<nav class="cascade-bar" aria-label="cascade builds" id="cascade-bar">
|
||||
<div class="cascade-tree" id="cascade-tree" role="tree">Loading builds…</div>
|
||||
</nav>
|
||||
<script src="/_cascade/bar.js" defer></script>
|
||||
146
sites/dossiers.ngi.hacktivism.ch/cascade/_cascade/bar.js
Normal file
146
sites/dossiers.ngi.hacktivism.ch/cascade/_cascade/bar.js
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
(function () {
|
||||
function cookieEdition() {
|
||||
var m = document.cookie.match(/(?:^|; )tdpd_edition=([^;]*)/);
|
||||
if (!m) return "flask";
|
||||
try {
|
||||
return decodeURIComponent(m[1]) || "flask";
|
||||
} catch (e) {
|
||||
return "flask";
|
||||
}
|
||||
}
|
||||
|
||||
function esc(s) {
|
||||
return String(s)
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """);
|
||||
}
|
||||
|
||||
function shortTime(iso) {
|
||||
if (!iso) return "";
|
||||
// keep local-looking wall clock from ISO when possible
|
||||
var m = String(iso).match(/T(\d{2}:\d{2})/);
|
||||
if (m) return m[1];
|
||||
return String(iso).slice(0, 16);
|
||||
}
|
||||
|
||||
function fmtBuildSec(sec) {
|
||||
if (sec == null || sec === "") return "";
|
||||
var n = Number(sec);
|
||||
if (!isFinite(n)) return "";
|
||||
if (n < 10) return n.toFixed(1) + "s";
|
||||
return Math.round(n) + "s";
|
||||
}
|
||||
|
||||
function markActive(root, edition) {
|
||||
var links = root.querySelectorAll("a[data-edition]");
|
||||
for (var i = 0; i < links.length; i++) {
|
||||
var a = links[i];
|
||||
var ed = a.getAttribute("data-edition");
|
||||
var on =
|
||||
ed === edition ||
|
||||
(edition === "flask" && a.getAttribute("data-default") === "1");
|
||||
if (on) {
|
||||
a.classList.add("is-active");
|
||||
a.setAttribute("aria-current", "page");
|
||||
} else {
|
||||
a.classList.remove("is-active");
|
||||
a.removeAttribute("aria-current");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderTree(data) {
|
||||
var mount = document.getElementById("cascade-tree");
|
||||
if (!mount) return;
|
||||
var list = (data && data.branches) || [];
|
||||
if (!list.length) {
|
||||
mount.textContent = "No branches in branch-status.json";
|
||||
return;
|
||||
}
|
||||
var defaultBranch = (data && data.default_branch) || "feat/website-views";
|
||||
var timing = (data && data.rebuild_timing) || null;
|
||||
var html = [];
|
||||
html.push('<div class="cascade-tree-lines" role="group">');
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
var row = list[i];
|
||||
var edition = row.edition || "";
|
||||
var branch = row.branch || "";
|
||||
var tip = row.tip || "";
|
||||
var prefix = row.prefix || "";
|
||||
var parent = row.parent || "";
|
||||
var tipTime = row.tip_time || "";
|
||||
var builtAt = row.built_at || "";
|
||||
var buildSec = row.build_seconds;
|
||||
var isDefault = branch === defaultBranch;
|
||||
var metaBits = [];
|
||||
if (tip) metaBits.push("@" + tip);
|
||||
if (tipTime) metaBits.push("tip " + shortTime(tipTime));
|
||||
if (buildSec != null && buildSec !== "")
|
||||
metaBits.push("build " + fmtBuildSec(buildSec));
|
||||
if (builtAt) metaBits.push("at " + shortTime(builtAt));
|
||||
var label =
|
||||
esc(prefix) +
|
||||
"<code>" +
|
||||
esc(branch) +
|
||||
"</code>" +
|
||||
(metaBits.length
|
||||
? '<span class="tip"> ' + esc(metaBits.join(" · ")) + "</span>"
|
||||
: "") +
|
||||
(isDefault ? '<span class="cascade-default"> · default</span>' : "");
|
||||
html.push(
|
||||
'<a class="cascade-tree-row" role="treeitem" href="/_cascade/use/' +
|
||||
esc(edition) +
|
||||
'" data-edition="' +
|
||||
esc(edition) +
|
||||
'"' +
|
||||
(parent ? ' data-parent="' + esc(parent) + '"' : "") +
|
||||
(isDefault ? ' data-default="1"' : "") +
|
||||
' title="Switch build to ' +
|
||||
esc(branch) +
|
||||
(tip ? " @ " + esc(tip) : "") +
|
||||
(tipTime ? " tip=" + esc(tipTime) : "") +
|
||||
(buildSec != null ? " build=" + esc(fmtBuildSec(buildSec)) : "") +
|
||||
'">' +
|
||||
label +
|
||||
"</a>"
|
||||
);
|
||||
}
|
||||
html.push("</div>");
|
||||
if (timing && timing.wall_seconds != null) {
|
||||
html.push(
|
||||
'<div class="cascade-timing">last full pass ~' +
|
||||
esc(String(timing.wall_seconds)) +
|
||||
"s · built " +
|
||||
esc(String(timing.built || 0)) +
|
||||
" / skipped " +
|
||||
esc(String(timing.skipped || 0)) +
|
||||
" / " +
|
||||
esc(String(timing.branches || list.length)) +
|
||||
"</div>"
|
||||
);
|
||||
}
|
||||
mount.innerHTML = html.join("");
|
||||
markActive(mount, cookieEdition());
|
||||
}
|
||||
|
||||
var edition = cookieEdition();
|
||||
var mount = document.getElementById("cascade-tree");
|
||||
fetch("/branch-status.json", { cache: "no-store" })
|
||||
.then(function (res) {
|
||||
if (!res.ok) throw new Error("status");
|
||||
return res.json();
|
||||
})
|
||||
.then(function (data) {
|
||||
renderTree(data);
|
||||
})
|
||||
.catch(function () {
|
||||
if (mount) {
|
||||
mount.innerHTML =
|
||||
'<a class="cascade-tree-row" href="/_cascade/use/flask" data-edition="flask"><code>flask</code></a>' +
|
||||
'<span class="tip"> (branch-status unavailable)</span>';
|
||||
markActive(mount, edition);
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
|
@ -26,13 +26,14 @@ li { margin: 0.25rem 0; }
|
|||
<body>
|
||||
{% include "_cascade/bar.html" . %}
|
||||
<h1>cascade</h1>
|
||||
<p class="meta">Flask freeze lives at <a href="/"><code>/</code></a> (views). This ladder is tip stubs only.</p>
|
||||
<p class="meta">last-built: 2026-09-30T02:30:50+02:00</p>
|
||||
<ul class="plain">
|
||||
<li><a href="/main/"><code>/main/</code></a> <span class="dim">·</span> <code>main</code> @ <code>d2fbd79</code></li>
|
||||
<li><a href="/feat-sm-content/"><code>/feat-sm-content/</code></a> <span class="dim">·</span> <code>feat/sm-content</code> @ <code>089ef1f</code></li>
|
||||
<li><a href="/feat-website-features/"><code>/feat-website-features/</code></a> <span class="dim">·</span> <code>feat/website-features</code> @ <code>53a3176</code></li>
|
||||
<li><a href="/feat-website-design/"><code>/feat-website-design/</code></a> <span class="dim">·</span> <code>feat/website-design</code> @ <code>2e26c80</code></li>
|
||||
<li><a href="/feat-website-views/"><code>/feat-website-views/</code></a> <span class="dim">·</span> <code>feat/website-views</code> @ <code>bbafc38</code></li>
|
||||
<li><a href="/cascade/main/"><code>/main/</code></a> <span class="dim">·</span> <code>main</code> @ <code>d2fbd79</code></li>
|
||||
<li><a href="/cascade/feat-sm-content/"><code>/feat-sm-content/</code></a> <span class="dim">·</span> <code>feat/sm-content</code> @ <code>089ef1f</code></li>
|
||||
<li><a href="/cascade/feat-website-features/"><code>/feat-website-features/</code></a> <span class="dim">·</span> <code>feat/website-features</code> @ <code>53a3176</code></li>
|
||||
<li><a href="/cascade/feat-website-design/"><code>/feat-website-design/</code></a> <span class="dim">·</span> <code>feat/website-design</code> @ <code>2e26c80</code></li>
|
||||
<li><a href="/cascade/feat-website-views/"><code>/feat-website-views/</code></a> <span class="dim">·</span> <code>feat/website-views</code> @ <code>bbafc38</code></li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue