From a0c3921edd0507972f1262d77263a77c7df5d2da Mon Sep 17 00:00:00 2001 From: christian Date: Thu, 24 Sep 2026 13:27:13 -0400 Subject: [PATCH] Rank old builds by version, not created_at, when pruning the update package MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gitea returns an empty created_at for generic package files, so sorting by it left every build equally old and the "keep one previous for rollback" rule retained whichever the listing happened to return first — the OLDEST build, with the newest deleted. Rank by the version in the filename instead, numeric segment by segment so 1.4.34 outranks 1.4.9, and fall back to created_at only when a name carries no version. Co-Authored-By: Claude Opus 5 --- tools/publish-update.cjs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tools/publish-update.cjs b/tools/publish-update.cjs index 03e551c..e25435a 100644 --- a/tools/publish-update.cjs +++ b/tools/publish-update.cjs @@ -185,9 +185,24 @@ async function giteaPrune(baseUrl, token, keepFile, keepPrevious = 1) { let files; try { files = JSON.parse(res.body); } catch (_) { return; } if (!Array.isArray(files)) return; + // Rank by the version in the filename, not by created_at: this server returns that field empty, + // which would leave every build "equally old" and quietly retain the OLDEST one as the rollback + // copy instead of the newest. + const verOf = (name) => { + const m = String(name).match(/(\d+(?:\.\d+)+)/); + return m ? m[1].split('.').map(n => parseInt(n, 10) || 0) : []; + }; + const newerFirst = (a, b) => { + const pa = verOf(a.name), pb = verOf(b.name); + for (let i = 0; i < Math.max(pa.length, pb.length); i++) { + const x = pa[i] || 0, y = pb[i] || 0; + if (x !== y) return y - x; + } + return new Date(b.created_at || 0) - new Date(a.created_at || 0); + }; const stale = files .filter(f => f && typeof f.name === 'string' && f.name.toLowerCase().endsWith('.exe') && f.name !== keepFile) - .sort((a, b) => new Date(b.created_at || 0) - new Date(a.created_at || 0)) + .sort(newerFirst) .slice(keepPrevious); for (const f of stale) { const del = await giteaRequest('DELETE', baseUrl.replace(/\/+$/, '') + '/' + encodeURIComponent(f.name), token, null);