diff --git a/.gitea/scripts/publish-releases.js b/.gitea/scripts/publish-releases.js index ba54567b9f..58db24a2b9 100644 --- a/.gitea/scripts/publish-releases.js +++ b/.gitea/scripts/publish-releases.js @@ -213,7 +213,17 @@ function existingHistory(body) { // --- main ----------------------------------------------------------------- -async function publish(ver) { +// The digest `:latest` currently serves. The release for that version is the +// repository's one full release; every other version keeps upstream's +// pre-release flag. Gitea defines a repository's "latest release" as its +// newest non-pre-release, and only that is shown on the repository home page +// and served at /releases/latest — with every RC flagged pre-release the way +// upstream flags them, the home page listed no release at all. +async function latestDigest() { + try { return (await imageFacts('latest')).digest; } catch { return ''; } +} + +async function publish(ver, current) { const tag = `dsh-v${ver}`; const tagLookup = await gitea('GET', `/repos/${REPO}/tags/${tag}`); if (tagLookup.status !== 200) { @@ -223,24 +233,27 @@ async function publish(ver) { const facts = await imageFacts(ver); const up = await upstreamRelease(ver); const title = up?.name ?? `v${ver}`; - const prerelease = up ? up.prerelease : true; + const isCurrent = current !== '' && facts.digest === current; + const prerelease = isCurrent ? false : (up ? up.prerelease : true); const existing = await gitea('GET', `/repos/${REPO}/releases/tags/${tag}`); if (existing.status === 200) { const prev = existingDigest(existing.json.body); const history = [prev, ...existingHistory(existing.json.body)].filter((d) => d && d !== facts.digest).slice(0, HISTORY_MAX); const body = renderBody(ver, facts, up, history); - // Same image, same title, same rendered text -> nothing to do. Comparing - // the text (not just the digest) lets a changed container note or edited - // upstream notes reach existing releases on the next run. + // Same image, same title, same flag, same rendered text -> nothing to do. + // Comparing the text (not just the digest) lets a changed container note + // or edited upstream notes reach existing releases on the next run. const norm = (s) => (s || '').replace(/\r\n/g, '\n').trim(); const sameImage = prev === facts.digest; - if (sameImage && existing.json.name === title && norm(existing.json.body) === norm(body)) { console.log(`${tag}: up to date`); return; } + const unchanged = sameImage && existing.json.name === title && existing.json.prerelease === prerelease + && norm(existing.json.body) === norm(body); + if (unchanged) { console.log(`${tag}: up to date`); return; } if (DRY_RUN) { console.log(`\n===== DRY RUN update ${tag} =====\n${title}\n\n${body}\n`); return; } const r = await gitea('PATCH', `/repos/${REPO}/releases/${existing.json.id}`, { name: title, body, prerelease, draft: false }); if (r.status !== 200) throw new Error(`${tag}: update HTTP ${r.status} ${r.text.slice(0, 200)}`); console.log(sameImage - ? `${tag}: updated (notes)` + ? `${tag}: updated (${existing.json.prerelease === prerelease ? 'notes' : isCurrent ? 'now the latest release' : 'back to pre-release'})` : `${tag}: updated (image rebuilt: ${prev.slice(0, 19)} -> ${facts.digest.slice(0, 19)})`); return; } @@ -258,6 +271,8 @@ async function publish(ver) { (async () => { const versions = await registryVersions(); console.log(`image versions: ${versions.join(', ') || '(none)'}`); + const current = await latestDigest(); + console.log(`:latest digest: ${current || '(none — every release keeps its upstream flag)'}`); // Oldest first, so the Releases page (newest created on top) reads in version order. - for (const v of versions) await publish(v); + for (const v of versions) await publish(v, current); })().catch((err) => { console.error(`::error::${err.message}`); process.exit(1); }); diff --git a/README.md b/README.md index 795a20217a..6c810a62c0 100644 --- a/README.md +++ b/README.md @@ -266,7 +266,9 @@ The [Releases](../../releases) page mirrors upstream's: one entry per version, o `dsh-v` tag, titled the same way, with upstream's own notes followed by a short container footer (digest, pnpm, base image, build). Version tags are rebuilt in place when *our* recipe changes — the dsh inside never does — and the release footer keeps the -previous digests. +previous digests. One deliberate difference: the release for the version `:latest` serves +is the repository's **Latest** release (Gitea lists only a non-pre-release on the home page +and at `/releases/latest`); every other version carries upstream's pre-release flag. Changing the **dsh version** is the deliberate part, and with `:latest` in the template that happens whenever you accept an update Unraid offers. Before you do: