Repository navigation
feat(distribution): desktop shell, AppImage release artifact and Flathub manifest (#232) - #235
Merged
Merged
Conversation
…hub manifest (#232) Flathub only accepts sandboxed graphical desktop apps, so the blocker on #232 was the desktop wrapper itself. This adds it, publishes the AppImage it produces, and repacks that AppImage for Flathub. desktop/ holds a Tauri v2 shell: a window that owns one child process, the same standalone Next.js server every other channel ships. It picks a free loopback port, spawns node server.js from the bundled payload with STORAGE_PROVIDER=sqlite in the per-user data directory, waits for GET /api/db/health, signs the user in against the credentials the zero-config first run persisted, and shows the workspace. It restarts the sidecar with backoff if it dies and always takes it down with the window (SIGTERM then SIGKILL, plus PR_SET_PDEATHSIG so a killed shell cannot orphan the server). No shell plugin, so the webview has no command it could invoke. 38 unit tests cover the pure logic. scripts/build-desktop-appimage.sh stages the payload and the pinned Node runtime, bundles the AppImage, renames it to the release-asset convention and, with --smoke, boots the bundled server out of the extracted AppDir. packaging/flatpak/ holds the manifest template, metainfo, desktop entry and launcher for org.libredb.Studio, rendered by scripts/render-flatpak-manifest.mjs; scripts/build-flatpak-local.sh builds, lints and installs it from a locally built AppImage so the packaging is verifiable before anything is released. release-artifacts.yml gains a desktop-appimage job (x64 and arm64, native runners) and the AppImage becomes a required release asset, since the Flathub update checker resolves that exact name every release. flatpak-smoke.yml builds the AppImage and repacks it with flatpak-builder@v6 in the privileged Flathub container on both architectures. Neither publishes to Flathub. One shared-code change was unavoidable: login() marked the session cookie Secure whenever NODE_ENV=production, and libsoup - the cookie store behind WebKitGTK - discards a Secure cookie delivered over http instead of ignoring the flag the way Chromium does on localhost, so the handoff signed in and then bounced between /login and / forever. Secure is now omitted only for a request that arrived on a loopback host over plain http, where it protects nothing; a proxy that terminated TLS still gets it via x-forwarded-proto. Verified locally end to end: AppImage smoke, appstreamcli validate, flatpak-builder-lint on manifest and repo, flatpak run org.libredb.Studio into the workspace with no password prompt, a PostgreSQL connection over TCP from inside the sandbox, and a query returning rows in the results grid.
14 tasks
auth.ts now imports headers() as well as cookies() to decide the session cookie's Secure flag. mock.module replaces the whole module, so a mock that returns only cookies makes the import fail at link time with "Export named 'headers' not found" - which is what the per-file coverage run (tests/run-core.sh) reported for tests/unit/lib/auth-jwt-config.test.ts. The aggregate `bun run test` did not catch it because mock.module is process-wide: the auth.test.ts mock, which does export headers, won the shared process. Verified each file in isolation and through the full coverage gate (161/161 core files, 100% lines).
…#232) The AppImage is its own user-facing channel - release CI builds it per arch and publish-release requires the assets - so it needs a row in channels.yaml and in the install tables, not just a mention under the Flathub section. status stays pending until the first release that actually ships it (0.9.59 predates the job).
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Closes the packaging work in #232: a real desktop application, the AppImage release
artifact it produces, and the Flathub manifest that repacks it.
Flathub only accepts sandboxed graphical desktop apps, so the blocker on #232 was the
desktop wrapper itself (the #115 spike's go recommendation). This PR implements the Linux
half of that recommendation and the Flathub packaging on top of it.
What lands
desktop/- Tauri v2 shell (Rust, 36 unit tests). A window that owns one childprocess: the same standalone Next.js server every other channel ships. It picks a free
loopback port, spawns
node server.jsfrom the bundled payload withSTORAGE_PROVIDER=sqlitepointed at the per-user data directory, waits forGET /api/db/health, signs the user in, and shows the workspace. It restarts the sidecarwith backoff if it dies and always takes it down with the window (SIGTERM then SIGKILL,
plus
PR_SET_PDEATHSIGso even a killed shell cannot orphan the server). No shell plugin,so the webview has no command it could invoke.
scripts/build-desktop-appimage.shstages the payload plus the pinned,checksum-verified Node runtime, bundles the AppImage, renames it to the repo's asset
convention (
libredb-studio-desktop-<version>-linux-<arch>.AppImage+.sha256), and with--smokeboots the bundled server out of the extracted AppDir and requires a 200 from thehealth endpoint - a display-free proof that the bundle is complete.
packaging/flatpak/holds the manifest template, AppStream metainfo, desktop entry andlauncher for
org.libredb.Studio, rendered byscripts/render-flatpak-manifest.mjs(samepattern as the Homebrew formula renderer, unit tested).
scripts/build-flatpak-local.shbuilds, lints and installs it from a locally builtAppImage, so the packaging is verifiable before anything is released.
CI.
release-artifacts.ymlgains adesktop-appimagejob (x64 + arm64, nativerunners) and the AppImage becomes a required release asset - the Flathub update checker
resolves that exact name on every release. A new
flatpak-smoke.ymlbuilds the AppImage andrepacks it with
flatpak-builder@v6in the privileged Flathub container on botharchitectures, path-filtered. It publishes nothing: no
flat-manager, no Flathubcredential.
Decisions worth reviewing
MIT-licensed release artifact, already built and smoke-tested per version, so Flathub
ships the same bytes as every other channel and there is one build pipeline. This is
accepted practice (Beekeeper Studio, Signal, Element) but Flathub's written guidance still
prefers source builds, and
flatpak-cargo-generatormakes a from-source Tauri buildrealistic - so a reviewer may ask. The submission PR should say this up front;
packaging/flatpak/README.mdrecords the reasoning. This supersedes the spike doc's"offline build with vendored cargo/npm sources" note, which is corrected there.
one-shot password per launch as the spike proposed. The wrapper leaves
JWT_SECRETandADMIN_PASSWORDunset, lets the server generate and persist them inauth-bootstrap.json(mode 0600) next to the storage database, then has the webview POST them to the existing
/api/auth/login. No new secret-handling code, no server change, sessions surviverestarts, and the bootstrap file stays the documented fallback if the handoff ever fails.
The wrapper clears inherited
ADMIN_PASSWORD/AUTH_BOOTSTRAP/ storage variables so adeveloper's shell cannot break that contract.
org.gnome.Platform//50, notorg.freedesktop.Platform. Only the GNOME runtime shipswebkit2gtk-4.1. The manifest's runtime version, the CI container image tag and the SDKin the local instructions must move together.
payload - and drops the GTK/WebKitGTK stack linuxdeploy bundled. Mixing a bundled
libwebkit2gtkwith the runtime's helper processes crashes the webview.Secureflag.login()marked the cookieSecurewheneverNODE_ENV=production, and libsoup - thecookie store behind WebKitGTK - discards a
Securecookie delivered over http instead ofignoring the flag the way Chromium does on localhost. The handoff therefore logged in
successfully and then bounced between
/loginand/forever.Secureis now omitted forexactly one case: a request that arrived on a loopback host over plain http, where the flag
protects nothing because the traffic never leaves the machine. A proxy that terminated TLS
and forwarded to loopback still gets
Secureviax-forwarded-proto; every otherdeployment is unchanged. Eight new tests cover the matrix.
desktop/had to join the payload prune list. Next.js output tracing sweeps the reporoot, so a local build with a Rust
target/present produced a 1.1 GB release tarballbefore the deny-list caught up (
scripts/lib/prune-standalone-payload.sh, issue Trim the standalone payload: Next output tracing pulls repo-root extras #124).and Turbopack resolves the externalized database drivers through hashed symlinks under
payload/.next/node_modules- so the bundle looked fine, the server logged "Ready", andevery request died with
Failed to load external module ssh2-<hash>. The build scriptmaterializes those symlinks and the smoke test now asserts the directory exists. Same for
the Alpine build of sharp's libvips, which makes linuxdeploy fail hard on
libc.musl-x86_64.so.1.productNamealso stays space-free so the manifest's buildcommands need no quoting.
no credentials, and a release without it would strand the Flathub channel.
Not in this PR
The Flathub submission PR against
flathub/flathuband the domain-verification token athttps://libredb.org/.well-known/org.flathub.VerifiedApps.txtare manual steps by design -the token only exists after submission. The checklist is in
packaging/flatpak/README.mdand #232 stays open for it. macOS
.dmg, brew cask, the Microsoft Store and the Tauriupdater remain out of scope (all need paid signing identities).