Files
blap/apps/desktop/scripts/release-codeberg.mjs
T
enki 5b9d457704 Blap reskin: social UI, fixes, and update/release pipeline
Fork of element-web v1.12.22 reskinned as "Blap", a Discord-style social
Matrix client. Visual fork only: calls stay on the Element Call widget.

UI / reskin:
- yap CSS overlay (_yap.pcss) wired into the dark theme @layer; fonts, surfaces
- spaces rail icon-only with the collapse mechanism removed
- YapRoomList with manual drag-and-drop ordering (works in Home; drop-to-bottom)
- Discord-style inline message timestamps next to the sender name
- image lightbox toolbar visibility; themed context menus + toasts
- bridges panel + call facepile
- login screen rebuilt to the Blap design (centered card, accent buttons)
- accent (purple) primary buttons app-wide via Compound token overrides

Desktop / distribution:
- updater.ts switched Squirrel -> electron-updater (NSIS); Codeberg generic feed
- scripts/release-codeberg.mjs uploads artifacts to the Codeberg release
- Blap build variant (apps/desktop/blap/build.json, appId lol.utn.blap)

Calls (visual-only via widget API):
- "join with camera off" device setting + Voice settings toggle

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 21:37:49 -07:00

95 lines
3.7 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.
const WANTED = [/^latest\.yml$/, /^latest-linux\.yml$/, /\.exe$/, /\.exe\.blockmap$/, /\.AppImage$/, /\.AppImage\.blockmap$/];
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 files = (await readdir(distDir)).filter((f) => WANTED.some((re) => re.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();