Splux Linux

source-based Linux using SPS

splux-site · log · files · refs · tags · releases · people · languages · GitHub · commit

f8c400a


Pick live ISO tags numerically and use a circular SPS mark.

Date
Commitf8c400add25de0041118bf1a50fb12d2e0298524
GitHubmirror

Parents

Stat

 .github/workflows/pages.yml |   4 +-
 site/assets/favicon.png     | Bin 17163 -> 18330 bytes
 site/assets/live.js         | 204 ++++++++++++++++++++++++++++++++------------
 site/assets/sps.png         | Bin 17163 -> 16598 bytes
 site/assets/style.css       |   1 +
 site/download/index.html    |   4 +-
 site/index.html             |   4 +-
 site/install/index.html     |   1 +
 site/news/index.html        |   2 +-
 tools/build-site.sh         |  45 ++++++++--
 tools/subst.awk             |   1 +
 11 files changed, 201 insertions(+), 65 deletions(-)

Diff

diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml
index 0c57e41..9101d49 100644
--- a/.github/workflows/pages.yml
+++ b/.github/workflows/pages.yml
@@ -4,7 +4,7 @@ on:
   push:
     branches: [main]
   schedule:
-    - cron: "*/5 * * * *"
+    - cron: "*/15 * * * *"
   workflow_dispatch:
   repository_dispatch:
     types: [rebuild]
@@ -16,7 +16,7 @@ permissions:
 
 concurrency:
   group: pages
-  cancel-in-progress: true
+  cancel-in-progress: false
 
 jobs:
   build:
diff --git a/site/assets/favicon.png b/site/assets/favicon.png
index 53db258..d06f8bf 100644
Binary files a/site/assets/favicon.png and b/site/assets/favicon.png differ
diff --git a/site/assets/live.js b/site/assets/live.js
index f9caac3..520c63f 100644
--- a/site/assets/live.js
+++ b/site/assets/live.js
@@ -325,13 +325,83 @@
 			esc(shortSha(sha)) + "</a>";
 	}
 
+	// live-2026-08-31-9 sorts after live-2026-08-31-15 as a string.
+	function liveRank(tag) {
+		var m = /^live-(\d{4})-(\d{2})-(\d{2})(?:-(\d+))?$/.exec(String(tag || ""));
+		if (!m)
+			return null;
+		return [
+			parseInt(m[1], 10),
+			parseInt(m[2], 10),
+			parseInt(m[3], 10),
+			parseInt(m[4] || "0", 10)
+		];
+	}
+
+	function liveBetter(a, b) {
+		var ra = liveRank(a);
+		var rb = liveRank(b);
+		if (!ra)
+			return false;
+		if (!rb)
+			return true;
+		for (var i = 0; i < 4; i++) {
+			if (ra[i] > rb[i])
+				return true;
+			if (ra[i] < rb[i])
+				return false;
+		}
+		return false;
+	}
+
+	function pickLiveRelease(list) {
+		var best = null;
+		if (!list || !list.length)
+			return null;
+		for (var i = 0; i < list.length; i++) {
+			var r = list[i];
+			if (!r || r.draft)
+				continue;
+			var tag = r.tag_name || r.tag || "";
+			if (!liveRank(tag))
+				continue;
+			if (!best || liveBetter(tag, best.tag_name || best.tag || ""))
+				best = r;
+		}
+		return best;
+	}
+
+	var shownIsoTag = "";
+
+	function rewriteDownloads(tag) {
+		if (!tag)
+			return;
+		var encoded = encodeURIComponent(tag);
+		var prefix = "https://github.com/RobertFlexx/SPS/releases/";
+		var as = document.getElementsByTagName("a");
+		for (var i = 0; i < as.length; i++) {
+			var href = as[i].getAttribute("href") || "";
+			var m = href.match(/^https:\/\/github\.com\/RobertFlexx\/SPS\/releases\/(?:latest\/download|download\/[^/]+)\/([^/?#]+)$/);
+			if (m) {
+				as[i].setAttribute("href", prefix + "download/" + encoded + "/" + m[1]);
+				continue;
+			}
+			if (href === prefix + "latest" || href === prefix + "latest/" ||
+			    /^https:\/\/github\.com\/RobertFlexx\/SPS\/releases\/tag\/[^/]+\/?$/.test(href))
+				as[i].setAttribute("href", prefix + "tag/" + encoded);
+		}
+	}
+
 	function setIso(rel) {
 		if (!rel)
 			return;
-		var tag = rel.tag_name || rel.name || "";
-		var when = rel.published_at || "";
+		var tag = rel.tag_name || rel.tag || rel.name || "";
+		var when = rel.published_at || rel.published || rel.date || "";
+		var href = tag
+			? "https://github.com/RobertFlexx/SPS/releases/tag/" + encodeURIComponent(tag)
+			: "https://github.com/RobertFlexx/SPS/releases/latest";
 		if (isoEl && tag) {
-			isoEl.innerHTML = "<a href=\"https://github.com/RobertFlexx/SPS/releases/latest\">" +
+			isoEl.innerHTML = "<a href=\"" + esc(href) + "\">" +
 				esc(tag) + "</a> (<time datetime=\"" + esc(when) + "\">" +
 				esc(formatLocal(when)) + "</time>)";
 		}
@@ -339,10 +409,24 @@
 			tagEl.textContent = tag;
 		if (dateEl && when) {
 			dateEl.setAttribute("data-iso", when);
+			dateEl.setAttribute("datetime", when);
 			dateEl.textContent = formatLocal(when);
 		}
 	}
 
+	function applyRelease(rel) {
+		if (!rel)
+			return;
+		var tag = rel.tag_name || rel.tag || rel.name || "";
+		if (!liveRank(tag))
+			return;
+		if (shownIsoTag && liveBetter(shownIsoTag, tag))
+			return;
+		shownIsoTag = tag;
+		setIso(rel);
+		rewriteDownloads(tag);
+	}
+
 	function fromReleases(list) {
 		var items = [];
 		for (var i = 0; i < list.length; i++) {
@@ -411,58 +495,72 @@
 		location.href = newsHref(n, newsNest());
 	});
 
-	var req = [json(API + "SPS/releases?per_page=100")];
-	for (var r = 0; r < REPOS.length; r++)
-		req.push(json(API + REPOS[r].path + "/commits?per_page=100"));
-	if (feed)
-		req.push(json(dataRoot + "data/news.json").catch(function () { return []; }));
-
-	Promise.all(req).then(function (rows) {
-		var releases = rows[0] || [];
-		var extra = fromReleases(releases);
-		for (var i = 0; i < REPOS.length; i++)
-			extra = extra.concat(fromCommits(rows[i + 1] || [], REPOS[i].label));
-
-		var baked = [];
-		if (feed)
-			baked = fromJson(rows[REPOS.length + 1] || []);
-		if (!baked.length)
-			baked = readItems(feed).concat(readItems(brief));
-		var items = merge(baked, extra);
-
-		if (feed) {
-			var nest = newsNest();
-			var page = newsPage();
-			var pages = Math.max(1, Math.ceil(items.length / PER) || 1);
-			if (page > pages) {
-				location.replace(newsHref(pages, nest));
-				return;
-			}
-			paintPage(feed, items, page, PER);
-			paintPager(page, pages, nest, PER);
-		}
-		paint(brief, items, 8, true);
+	json(dataRoot + "data/release.json").then(function (info) {
+		if (!info || !info.tag)
+			return;
+		applyRelease({
+			tag_name: info.tag,
+			published_at: info.published_at || info.date || ""
+		});
+	}).catch(function () {});
 
-		var latest = null;
-		for (var j = 0; j < releases.length; j++) {
-			if (releases[j] && !releases[j].draft) {
-				latest = releases[j];
-				break;
+	function refreshCommitsAndNews(releases) {
+		var needCommits = !!(feed || brief || document.getElementById("live-sps"));
+		if (!needCommits)
+			return;
+		var extra = fromReleases(releases || []);
+		var commitReqs = [];
+		for (var r = 0; r < REPOS.length; r++)
+			commitReqs.push(json(API + REPOS[r].path + "/commits?per_page=100")
+				.catch(function () { return []; }));
+		var newsReq = feed
+			? json(dataRoot + "data/news.json").catch(function () { return []; })
+			: Promise.resolve([]);
+		Promise.all(commitReqs.concat([newsReq])).then(function (rows) {
+			var i;
+			for (i = 0; i < REPOS.length; i++)
+				extra = extra.concat(fromCommits(rows[i] || [], REPOS[i].label));
+			var baked = [];
+			if (feed)
+				baked = fromJson(rows[REPOS.length] || []);
+			if (!baked.length)
+				baked = readItems(feed).concat(readItems(brief));
+			var items = merge(baked, extra);
+
+			if (feed) {
+				var nest = newsNest();
+				var page = newsPage();
+				var pages = Math.max(1, Math.ceil(items.length / PER) || 1);
+				if (page > pages) {
+					location.replace(newsHref(pages, nest));
+					return;
+				}
+				paintPage(feed, items, page, PER);
+				paintPager(page, pages, nest, PER);
 			}
-		}
-		setIso(latest);
-
-		for (var k = 0; k < REPOS.length; k++) {
-			var commits = rows[k + 1] || [];
-			var sha = commits[0] && commits[0].sha;
-			if (REPOS[k].path === "SPS")
-				setSha("live-sps", "SPS", sha);
-			else if (REPOS[k].path === "sps-core")
-				setSha("live-core", "sps-core", sha);
-			else if (REPOS[k].path === "sps-extra")
-				setSha("live-extra", "sps-extra", sha);
-		}
+			paint(brief, items, 8, true);
+
+			for (var k = 0; k < REPOS.length; k++) {
+				var commits = rows[k] || [];
+				var sha = commits[0] && commits[0].sha;
+				if (REPOS[k].path === "SPS")
+					setSha("live-sps", "SPS", sha);
+				else if (REPOS[k].path === "sps-core")
+					setSha("live-core", "sps-core", sha);
+				else if (REPOS[k].path === "sps-extra")
+					setSha("live-extra", "sps-extra", sha);
+			}
+		}).catch(function () {
+			/* keep the HTML from the last site build */
+		});
+	}
+
+	json(API + "SPS/releases?per_page=100").then(function (releases) {
+		var rel = pickLiveRelease(releases);
+		if (rel)
+			applyRelease(rel);
+		refreshCommitsAndNews(releases);
 	}).catch(function () {
-		/* keep the HTML from the last site build */
+		refreshCommitsAndNews([]);
 	});
 })();
diff --git a/site/assets/sps.png b/site/assets/sps.png
index 53db258..88e88ed 100644
Binary files a/site/assets/sps.png and b/site/assets/sps.png differ
diff --git a/site/assets/style.css b/site/assets/style.css
index 2eaf4a5..8fee3fa 100644
--- a/site/assets/style.css
+++ b/site/assets/style.css
@@ -76,6 +76,7 @@ img.logo {
 	height: 96px;
 	flex-shrink: 0;
 	background: #000;
+	border-radius: 50%;
 }
 
 .identity h1 {
diff --git a/site/download/index.html b/site/download/index.html
index 326e186..2c45c8c 100644
--- a/site/download/index.html
+++ b/site/download/index.html
@@ -15,7 +15,7 @@
 <main id="main">
 <h2>Download</h2>
 <hr class="rule">
-<p>Images are hybrid ISOs from <a href="https://github.com/RobertFlexx/SPS">RobertFlexx/SPS</a>. The filenames below are stable. GitHub's <code>releases/latest/download</code> URLs always resolve to the newest published files of those names.</p>
+<p>Images are hybrid ISOs from <a href="https://github.com/RobertFlexx/SPS">RobertFlexx/SPS</a>. The filenames below are stable. This page names the current <code>live-*</code> ISO tag and links to that GitHub release.</p>
 <div class="dl-row"><span class="muted">Latest tag</span><span id="live-tag">live-2026-09-06-1</span></div>
 <div class="dl-row"><span class="muted">Released</span><span id="live-date">2026-09-06</span></div>
 <div class="dl-row"><span class="muted">Architecture</span><span>x86_64</span></div>
@@ -37,6 +37,6 @@ Plasma/Wayland live session plus the same installer path. Needs a KMS device (vi
 </main>
 @@FOOTER@@
 </div>
-<script src="../../../../assets/live.js" data-root="../../../../"></script>
+<script src="../../../../assets/live.js?v=4c4f12c-20261019130111" data-root="../../../../"></script>
 </body>
 </html>
diff --git a/site/index.html b/site/index.html
index 59b2d20..55b2761 100644
--- a/site/index.html
+++ b/site/index.html
@@ -26,7 +26,7 @@
 
 <h2 id="download"><span class="num">[002]</span> Download</h2>
 <hr class="rule">
-<p>Live images are published from the SPS repository. Asset names stay the same from release to release, so these links always point at the newest published files.</p>
+<p>Live images are published from the SPS repository. This page names the current <code>live-*</code> ISO tag and links to that GitHub release.</p>
 <div class="dl-row"><span class="muted">Latest tag</span><span id="live-tag">live-2026-09-06-1</span></div>
 <div class="dl-row"><span class="muted">Released</span><span id="live-date">2026-09-06</span></div>
 <div class="dl-row"><span class="muted">Architecture</span><span>x86_64</span></div>
@@ -111,6 +111,6 @@ this website
 </main>
 @@FOOTER@@
 </div>
-<script src="../../../../assets/live.js" data-root="../../../../"></script>
+<script src="../../../../assets/live.js?v=4c4f12c-20261019130111" data-root="../../../../"></script>
 </body>
 </html>
diff --git a/site/install/index.html b/site/install/index.html
index 7568113..8a8b0d0 100644
--- a/site/install/index.html
+++ b/site/install/index.html
@@ -40,5 +40,6 @@ setup --non-interactive --profile minimal --target /mnt --hostname darkstar</pre
 </main>
 @@FOOTER@@
 </div>
+<script src="../../../../assets/live.js?v=4c4f12c-20261019130111" data-root="../../../../"></script>
 </body>
 </html>
diff --git a/site/news/index.html b/site/news/index.html
index db885cb..575cdb8 100644
--- a/site/news/index.html
+++ b/site/news/index.html
@@ -28,6 +28,6 @@
 </main>
 @@FOOTER@@
 </div>
-<script src="../../../../assets/live.js" data-root="../../../../"></script>
+<script src="../../../../assets/live.js?v=4c4f12c-20261019130111" data-root="../../../../"></script>
 </body>
 </html>
diff --git a/tools/build-site.sh b/tools/build-site.sh
index 4399740..d13be05 100755
--- a/tools/build-site.sh
+++ b/tools/build-site.sh
@@ -165,20 +165,52 @@ BEGIN { print "[" }
 END { print "\n]" }
 ' "$tsv" >"$OUT/data/packages.json"
 
+# GitHub's "latest" flag can lag. Pick the highest live-YYYY-MM-DD[-N]
+# tag by numeric suffix. String sort ranks -9 above -15.
+pick_live_release() {
+	gh api "repos/RobertFlexx/SPS/releases?per_page=100" --jq '
+		def rank:
+			capture("^live-(?<y>[0-9]+)-(?<m>[0-9]+)-(?<d>[0-9]+)(?:-(?<n>[0-9]+))?$")
+			| [(.y|tonumber), (.m|tonumber), (.d|tonumber), ((.n // "0")|tonumber)];
+		[.[] | select(.draft == false) | select(.tag_name | test("^live-[0-9]{4}-[0-9]{2}-[0-9]{2}(-[0-9]+)?$"))]
+		| if length == 0 then empty else max_by(.tag_name | rank) end
+		| [.tag_name, .published_at] | @tsv
+	' 2>/dev/null
+}
+
 tag=unknown
 date=unknown
+published=
 if command -v gh >/dev/null 2>&1; then
-	tag=$(gh release view -R RobertFlexx/SPS --json tagName --jq .tagName 2>/dev/null) || tag=unknown
-	date=$(gh release view -R RobertFlexx/SPS --json publishedAt --jq .publishedAt 2>/dev/null) || date=unknown
-	date=${date%%T*}
+	picked=$(pick_live_release || true)
+	if [ -n "$picked" ]; then
+		tag=${picked%%	*}
+		published=${picked#*	}
+		date=${published%%T*}
+	fi
+	if [ "$tag" = unknown ] || [ -z "$tag" ]; then
+		tag=$(gh release view -R RobertFlexx/SPS --json tagName --jq .tagName 2>/dev/null) || tag=unknown
+		published=$(gh release view -R RobertFlexx/SPS --json publishedAt --jq .publishedAt 2>/dev/null) || published=
+		date=${published%%T*}
+	fi
 fi
 if [ "$tag" = unknown ] || [ -z "$tag" ]; then
 	tag=latest
 	date=see GitHub
+	published=
 fi
 generated=$(date -u '+%Y-%m-%d %H:%M UTC' 2>/dev/null || date)
 
-printf '%s\n' "{\"tag\":\"$tag\",\"date\":\"$date\",\"iso_tty\":\"$SPS_GIT/releases/latest/download/sps-live-tty.iso\"}" \
+iso_base=$SPS_GIT/releases/latest/download
+notes_url=$SPS_GIT/releases/latest
+case $tag in
+	latest|unknown) ;;
+	*)
+		iso_base=$SPS_GIT/releases/download/$tag
+		notes_url=$SPS_GIT/releases/tag/$tag
+		;;
+esac
+printf '%s\n' "{\"tag\":\"$tag\",\"date\":\"$date\",\"published_at\":\"$published\",\"iso_tty\":\"$iso_base/sps-live-tty.iso\",\"iso_slim\":\"$iso_base/sps-live-tty-slim.iso\",\"iso_plasma\":\"$iso_base/sps-live-plasma.iso\",\"sha256sums\":\"$iso_base/SHA256SUMS\"}" \
 	>"$OUT/data/release.json"
 
 short_sha() {
@@ -214,10 +246,11 @@ sps_sha=$(short_sha "$SPS")
 core_sha=$(short_sha "$CORE")
 extra_sha=$(short_sha "$EXTRA")
 site_sha=$(short_sha "$SITE")
+livesig=${site_sha}-$(date -u '+%Y%m%d%H%M%S' 2>/dev/null || date +%Y%m%d%H%M%S)
 
 cat >"$tmp/news-info.html" <<EOF
 <div class="info">
-<div class="dl-row"><span class="muted">Latest ISO</span><span id="live-iso"><a href="$SPS_GIT/releases/latest">$tag</a> ($date)</span></div>
+<div class="dl-row"><span class="muted">Latest ISO</span><span id="live-iso"><a href="$notes_url">$tag</a> ($date)</span></div>
 <div class="dl-row"><span class="muted">SPS</span><span id="live-sps"><a href="$SPS_GIT/commit/$sps_sha">$sps_sha</a></span></div>
 <div class="dl-row"><span class="muted">sps-core</span><span id="live-core"><a href="$CORE_GIT/commit/$core_sha">$core_sha</a></span></div>
 <div class="dl-row"><span class="muted">sps-extra</span><span id="live-extra"><a href="$EXTRA_GIT/commit/$extra_sha">$extra_sha</a></span></div>
@@ -249,6 +282,7 @@ subst() {
 		-v ncore="$ncore" \
 		-v nextra="$nextra" \
 		-v generated="$generated" \
+		-v livesig="$livesig" \
 		-v curlver="$curlver" \
 		"$src" >"$dest"
 }
@@ -329,6 +363,7 @@ do
 		-v ncore="$ncore" \
 		-v nextra="$nextra" \
 		-v generated="$generated" \
+		-v livesig="$livesig" \
 		-v curlver="$curlver" \
 		"$page" >"$tmp/page.html"
 	mv "$tmp/page.html" "$page"
diff --git a/tools/subst.awk b/tools/subst.awk
index e1890a0..1507da0 100644
--- a/tools/subst.awk
+++ b/tools/subst.awk
@@ -71,6 +71,7 @@ $0 == "@@PAGER@@" {
 	gsub(/119/, ncore)
 	gsub(/1355/, nextra)
 	gsub(/2026-10-19 13:01 UTC/, generated)
+	gsub(/4c4f12c-20261019130111/, livesig)
 	gsub(/8.21.0-2/, curlver)
 	gsub(//, pagetitle)
 	print