diff --git a/.github/workflows/release-fast.yml b/.github/workflows/release-fast.yml index c43ab7f..c8dcf49 100644 --- a/.github/workflows/release-fast.yml +++ b/.github/workflows/release-fast.yml @@ -6,7 +6,7 @@ on: reddb_version: description: "Pinned RedDB release tag to smoke-package with" required: true - default: "v1.11.0" + default: "v1.13.1" permissions: contents: read diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2d960ec..03dc799 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,11 +18,14 @@ concurrency: env: # RedDB sidecar pinned for reproducible builds — bump when adopting a newer reddb. - # v1.13.0-rc.12: persists collection contracts (declared_model) in the single-file - # artifact, so KV collections survive a server restart (reddb fix a8139467). v1.11.0 - # lost the model on reopen → "model mismatch: expected kv, got table" on every project - # reopen. Bump to v1.13.0 once reddb publishes that tag's release binaries. - REDDB_VERSION: v1.13.0-rc.12 + # v1.13.1: first stable to publish the fully *static* (musl) `red-linux--static` + # assets, which fetch-reddb.mjs now prefers. The dynamic `red-linux-x86_64` links the + # build host's glibc and fails to start on older distros ("GLIBC_2.39 not found") → the + # reddb sidecar crash-loops and the app hangs on a black/loading screen. The static build + # runs on any glibc. v1.13.x also persists collection contracts (declared_model) so KV + # collections survive a server restart (reddb fix a8139467); v1.11.0 lost the model on + # reopen → "model mismatch: expected kv, got table". + REDDB_VERSION: v1.13.1 # Run AppImage tooling (linuxdeploy) without FUSE, which CI runners lack. APPIMAGE_EXTRACT_AND_RUN: "1" # linuxdeploy's strip pass chokes on some shared objects under the no-FUSE extract path diff --git a/scripts/check-reddb-release-assets.mjs b/scripts/check-reddb-release-assets.mjs index 96c9ff3..9157b37 100644 --- a/scripts/check-reddb-release-assets.mjs +++ b/scripts/check-reddb-release-assets.mjs @@ -4,7 +4,9 @@ // The desktop app cannot be packaged for Linux/Windows unless the pinned RedDB // release exposes the prebuilt `red--` assets that fetch-reddb.mjs // downloads. Check that up front so release jobs fail before runner setup, -// dependency install, and Tauri packaging work. +// dependency install, and Tauri packaging work. Mirrors fetch-reddb.mjs's +// candidate logic: linux x86_64/aarch64 prefer the static (musl) `-static` asset +// and fall back to the glibc one, so the preflight passes iff the fetch will. // // REDDB_VERSION=v1.11.0 node scripts/check-reddb-release-assets.mjs \ // x86_64-unknown-linux-gnu x86_64-pc-windows-msvc @@ -12,7 +14,8 @@ import { env, exit } from "node:process"; const REPO = "reddb-io/reddb"; -function assetName(triple) { +// Candidate asset names for this triple in preference order (see fetch-reddb.mjs). +function assetCandidates(triple) { const [arch] = triple.split("-"); const os = triple.includes("linux") ? "linux" @@ -23,7 +26,10 @@ function assetName(triple) { : null; if (!os) throw new Error(`unsupported target triple: ${triple}`); const ext = os === "windows" ? ".exe" : ""; - return `red-${os}-${arch}${ext}`; + const glibc = `red-${os}-${arch}${ext}`; + const preferStatic = + os === "linux" && (arch === "x86_64" || arch === "aarch64"); + return preferStatic ? [`red-${os}-${arch}-static`, glibc] : [glibc]; } async function ghJson(path) { @@ -52,7 +58,6 @@ if (targets.length === 0) { exit(2); } -const required = targets.map(assetName); const tag = env.REDDB_VERSION; let release; @@ -69,13 +74,21 @@ try { } const available = new Set((release.assets ?? []).map((asset) => asset.name)); -const missing = required.filter((name) => !available.has(name)); +// Each target is satisfied if ANY of its candidates is published; record the one +// fetch-reddb.mjs would actually download (the first available, static-preferred). +const resolved = targets.map((triple) => { + const names = assetCandidates(triple); + return { triple, names, chosen: names.find((n) => available.has(n)) ?? null }; +}); +const missing = resolved.filter((r) => r.chosen === null); if (missing.length > 0) { console.error( `RedDB ${release.tag_name} is missing required release assets:` ); - for (const name of missing) console.error(` - ${name}`); + for (const { triple, names } of missing) { + console.error(` - ${triple} (need one of: ${names.join(", ")})`); + } console.error(""); console.error("Available assets:"); for (const name of [...available].sort()) console.error(` - ${name}`); @@ -83,5 +96,5 @@ if (missing.length > 0) { } console.log( - `RedDB ${release.tag_name} has required assets: ${required.join(", ")}` + `RedDB ${release.tag_name} has required assets: ${resolved.map((r) => r.chosen).join(", ")}` ); diff --git a/scripts/fetch-reddb.mjs b/scripts/fetch-reddb.mjs index 5883240..d3fc2cc 100755 --- a/scripts/fetch-reddb.mjs +++ b/scripts/fetch-reddb.mjs @@ -41,7 +41,13 @@ function hostTriple() { return `${a}-unknown-linux-gnu`; } -function assetName(triple) { +// Candidate asset names for this triple, in preference order. Linux x86_64/aarch64 ship a +// fully *static* (musl) `red` as `-static`; prefer it. The dynamic `red-linux-` +// links the build host's glibc, so it fails to start on any host with an older glibc +// ("GLIBC_2.xx not found") — which black-screens the desktop app (the reddb sidecar +// crash-loops, the UI waits on it forever). The static build runs on any glibc. Fall back to +// the glibc asset for older reddb releases that predate the static one. Mirrors install.sh. +function assetCandidates(triple) { const [arch] = triple.split("-"); const os = triple.includes("linux") ? "linux" @@ -52,7 +58,11 @@ function assetName(triple) { : null; if (!os) throw new Error(`Unsupported target triple: ${triple}`); const ext = os === "windows" ? ".exe" : ""; - return { name: `red-${os}-${arch}${ext}`, ext, os }; + const glibc = `red-${os}-${arch}${ext}`; + // The static variant is published only for the linux x86_64 / aarch64 sidecars. + const preferStatic = os === "linux" && (arch === "x86_64" || arch === "aarch64"); + const names = preferStatic ? [`red-${os}-${arch}-static`, glibc] : [glibc]; + return { names, ext, os }; } async function ghJson(url) { @@ -74,7 +84,7 @@ async function download(url) { } const triple = hostTriple(); -const { name, ext, os } = assetName(triple); +const { names, ext, os } = assetCandidates(triple); const tag = process.env.REDDB_VERSION || @@ -83,8 +93,27 @@ const tag = if (!tag) throw new Error("could not determine a reddb release tag"); const base = `https://github.com/${REPO}/releases/download/${tag}`; -console.log(`Fetching RedDB ${name} from ${REPO}@${tag} …`); -const bin = await download(`${base}/${name}`); + +// Download the first candidate that exists. Fall back to the next ONLY on a 404 (asset not +// published in this release) — a real network/auth error still propagates instead of being +// masked by a fallback that would also fail. +let name, bin; +for (let i = 0; i < names.length; i++) { + const candidate = names[i]; + console.log(`Fetching RedDB ${candidate} from ${REPO}@${tag} …`); + try { + bin = await download(`${base}/${candidate}`); + name = candidate; + break; + } catch (e) { + const last = i === names.length - 1; + if (String(e).includes("→ 404") && !last) { + console.log(` (${candidate} not published in ${tag} — trying ${names[i + 1]})`); + continue; + } + throw e; + } +} // Verify the published .sha256 sidecar when present (best-effort). Pull the hash out by // regex so we don't care about the format: ` file` (sha256sum), `SHA256(file)= `