fb649be700
Docker / Docker Buildx (push) Has been cancelled
Build Debian package / Build package (release) Has been cancelled
Build and Deploy / prepare (release) Has been cancelled
Build and Deploy / Trigger Pro pipeline (release) Has been cancelled
Build and Deploy / Windows arm64 (release) Has been cancelled
Build and Deploy / Windows x64 (release) Has been cancelled
Build and Deploy / macOS (release) Has been cancelled
Build and Deploy / Linux amd64 (sqlcipher static) (release) Has been cancelled
Build and Deploy / Linux arm64 (sqlcipher static) (release) Has been cancelled
Build and Deploy / ${{ needs.prepare.outputs.deploy == 'true' && 'Deploy' || 'Deploy (dry-run)' }} (release) Has been cancelled
Build and Deploy / Deploy builds to ESS (release) Has been cancelled
Deploy release / Deploy to Cloudflare Pages (release) Has been cancelled
Include deb/flatpak/pacman/tar.gz (not just the Windows exe) so the release page has every platform's download for the Linux update-ping to link to. Filter by version so leftover artifacts from older builds in dist/ aren't uploaded. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
102 lines
4.1 KiB
JavaScript
102 lines
4.1 KiB
JavaScript
#!/usr/bin/env node
|
|
/*
|
|
* Upload built desktop artifacts to a Codeberg (Forgejo) release so electron-updater
|
|
* (generic provider -> .../releases/latest/download/) can find them.
|
|
*
|
|
* Run AFTER `pnpm run build` from apps/desktop:
|
|
* CODEBERG_TOKEN=xxxx node scripts/release-codeberg.mjs [tag]
|
|
*
|
|
* - owner/repo are fixed to Enkisu/Blap (must match the electron-builder publish url).
|
|
* - tag defaults to "v<version>" from package.json.
|
|
* - Uploads latest.yml / latest-linux.yml + installers (.exe/.AppImage) + blockmaps.
|
|
* - Replaces assets of the same name so re-runs are idempotent.
|
|
* - The release is created non-draft / non-prerelease so Forgejo's "latest" alias
|
|
* resolves to it (that's what the updater feed URL points at).
|
|
*/
|
|
import { readFile, readdir } from "node:fs/promises";
|
|
import { basename, join } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const OWNER = "Enkisu";
|
|
const REPO = "Blap";
|
|
const API = "https://codeberg.org/api/v1";
|
|
|
|
const token = process.env.CODEBERG_TOKEN;
|
|
if (!token) {
|
|
console.error("CODEBERG_TOKEN env var is required (a Codeberg access token with repo scope).");
|
|
process.exit(1);
|
|
}
|
|
|
|
const here = fileURLToPath(new URL(".", import.meta.url));
|
|
const pkg = JSON.parse(await readFile(join(here, "..", "package.json"), "utf8"));
|
|
const tag = process.argv[2] ?? `v${pkg.version}`;
|
|
const distDir = join(here, "..", "dist");
|
|
|
|
const authHeaders = { Authorization: `token ${token}` };
|
|
|
|
// Channel files electron-updater needs + the installers themselves.
|
|
// electron-updater channel files (always the current build) — uploaded regardless.
|
|
const CHANNEL_FILES = new Set(["latest.yml", "latest-linux.yml"]);
|
|
// Installers for this version. The version filter avoids grabbing leftover
|
|
// artifacts from previous builds sitting in dist/.
|
|
const INSTALLER_EXT = /\.(exe|exe\.blockmap|deb|flatpak|pacman|tar\.gz)$/;
|
|
|
|
async function api(path, init = {}) {
|
|
const res = await fetch(`${API}${path}`, { ...init, headers: { ...authHeaders, ...(init.headers ?? {}) } });
|
|
if (!res.ok && res.status !== 404) {
|
|
throw new Error(`${init.method ?? "GET"} ${path} -> ${res.status} ${await res.text()}`);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
async function getOrCreateRelease() {
|
|
let res = await api(`/repos/${OWNER}/${REPO}/releases/tags/${encodeURIComponent(tag)}`);
|
|
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 }),
|
|
});
|
|
console.log(`Created release ${tag}`);
|
|
} else {
|
|
console.log(`Reusing existing release ${tag}`);
|
|
}
|
|
return res.json();
|
|
}
|
|
|
|
async function main() {
|
|
const version = pkg.version;
|
|
const files = (await readdir(distDir)).filter(
|
|
(f) => CHANNEL_FILES.has(f) || (f.includes(version) && INSTALLER_EXT.test(f)),
|
|
);
|
|
if (files.length === 0) {
|
|
console.error(`No matching artifacts in ${distDir}. Run \`pnpm run build\` first.`);
|
|
process.exit(1);
|
|
}
|
|
|
|
const release = await getOrCreateRelease();
|
|
const existing = new Map((release.assets ?? []).map((a) => [a.name, a.id]));
|
|
|
|
for (const file of files) {
|
|
const name = basename(file);
|
|
if (existing.has(name)) {
|
|
await api(`/repos/${OWNER}/${REPO}/releases/${release.id}/assets/${existing.get(name)}`, {
|
|
method: "DELETE",
|
|
});
|
|
}
|
|
const data = await readFile(join(distDir, file));
|
|
const form = new FormData();
|
|
form.append("attachment", new Blob([data]), name);
|
|
const res = await fetch(
|
|
`${API}/repos/${OWNER}/${REPO}/releases/${release.id}/assets?name=${encodeURIComponent(name)}`,
|
|
{ method: "POST", headers: authHeaders, body: form },
|
|
);
|
|
if (!res.ok) throw new Error(`upload ${name} -> ${res.status} ${await res.text()}`);
|
|
console.log(`Uploaded ${name}`);
|
|
}
|
|
|
|
console.log(`\nDone. Updater feed: https://codeberg.org/${OWNER}/${REPO}/releases/latest/download/latest.yml`);
|
|
}
|
|
|
|
await main();
|