From 8a26cdca8c0d75c7481bbfb61aad2adfa0ed499d Mon Sep 17 00:00:00 2001 From: enki Date: Fri, 10 Jul 2026 20:37:27 -0700 Subject: [PATCH] =?UTF-8?q?desktop:=20fix=20auto-update=20feed=20=E2=80=94?= =?UTF-8?q?=20Codeberg=20has=20no=20/releases/latest/download=20route?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Forgejo/Codeberg 404s the GitHub-style latest/download path, so the feed baked into app-update.yml never worked (1.0.0/1.0.1 never saw updates). Point it at a rolling release on the fixed tag "updates" instead, and make release-codeberg.mjs sync latest.yml + the Windows installer to that release on every ship (channel files always replaced; curl retries added). Co-Authored-By: Claude Fable 5 --- apps/desktop/electron-builder.ts | 8 +++-- apps/desktop/scripts/release-codeberg.mjs | 37 ++++++++++++++++------- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/apps/desktop/electron-builder.ts b/apps/desktop/electron-builder.ts index 44637e6654..3516f18848 100644 --- a/apps/desktop/electron-builder.ts +++ b/apps/desktop/electron-builder.ts @@ -130,11 +130,13 @@ const config: Omit, "electronFuses"> & { }, // Codeberg release feed for electron-updater. electron-builder bakes this into // app-update.yml and generates the channel files (latest.yml / latest-linux.yml). - // "generic" = download-only (upload is handled by scripts/release-codeberg.mjs), - // pointed at Forgejo's stable "latest release" download path. + // "generic" = download-only (upload is handled by scripts/release-codeberg.mjs). + // Forgejo/Codeberg has NO GitHub-style /releases/latest/download/ route (404s), + // so the feed is a rolling release on the fixed tag "updates" that + // release-codeberg.mjs re-points at every ship. publish: { provider: "generic", - url: "https://codeberg.org/Enkisu/Blap/releases/latest/download/", + url: "https://codeberg.org/Enkisu/Blap/releases/download/updates/", channel: "latest", }, linux: { diff --git a/apps/desktop/scripts/release-codeberg.mjs b/apps/desktop/scripts/release-codeberg.mjs index 26c618981c..0f07064791 100644 --- a/apps/desktop/scripts/release-codeberg.mjs +++ b/apps/desktop/scripts/release-codeberg.mjs @@ -54,17 +54,17 @@ async function api(path, init = {}) { return res; } -async function getOrCreateRelease() { - let res = await api(`/repos/${OWNER}/${REPO}/releases/tags/${encodeURIComponent(tag)}`); +async function getOrCreateRelease(tagName, { prerelease = false } = {}) { + let res = await api(`/repos/${OWNER}/${REPO}/releases/tags/${encodeURIComponent(tagName)}`); if (res.status === 404) { res = await api(`/repos/${OWNER}/${REPO}/releases`, { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ tag_name: tag, name: tag, draft: false, prerelease: false }), + body: JSON.stringify({ tag_name: tagName, name: tagName, draft: false, prerelease }), }); - console.log(`Created release ${tag}`); + console.log(`Created release ${tagName}`); } else { - console.log(`Reusing existing release ${tag}`); + console.log(`Reusing existing release ${tagName}`); } return res.json(); } @@ -79,7 +79,21 @@ async function main() { process.exit(1); } - const release = await getOrCreateRelease(); + const release = await getOrCreateRelease(tag); + await syncAssets(release, files); + + // Forgejo/Codeberg has NO GitHub-style /releases/latest/download/ route, so + // app-update.yml points at a rolling release on the fixed tag "updates" — + // re-point its channel file + Windows installer at the build we just shipped. + const updaterFiles = files.filter((f) => f === "latest.yml" || /\.(exe|exe\.blockmap)$/.test(f)); + const updates = await getOrCreateRelease("updates", { prerelease: true }); + await syncAssets(updates, updaterFiles); + + const webBase = API.replace(/\/api\/v1\/?$/, ""); + console.log(`\nDone. Updater feed: ${webBase}/${OWNER}/${REPO}/releases/download/updates/latest.yml`); +} + +async function syncAssets(release, files) { const existing = new Map((release.assets ?? []).map((a) => [a.name, a])); for (const file of files) { @@ -88,8 +102,9 @@ async function main() { const size = statSync(filePath).size; const already = existing.get(name); // Resume: skip assets already uploaded at the same size (lets a re-run - // finish where it left off instead of re-uploading everything). - if (already && already.size === size) { + // finish where it left off instead of re-uploading everything). Channel + // files are tiny and change meaning per build — always replace those. + if (already && already.size === size && !CHANNEL_FILES.has(name)) { console.log(`Skipping ${name} (already uploaded)`); continue; } @@ -104,6 +119,9 @@ async function main() { [ "-sS", "--fail-with-body", + "--retry", + "4", + "--retry-all-errors", "-X", "POST", "-H", @@ -116,9 +134,6 @@ async function main() { ); console.log("done"); } - - const webBase = API.replace(/\/api\/v1\/?$/, ""); - console.log(`\nDone. Updater feed: ${webBase}/${OWNER}/${REPO}/releases/latest/download/latest.yml`); } await main();