Desktop Release Handoff
Audience: maintainers preparing native installer bundles for anolis-workbench.
Overview
Desktop packaging is split into two explicit layers:
- Python sidecar freeze (
scripts/freeze_server.py) -> onefile executable. - Tauri bundle build (
desktop/src-tauri) -> platform installers.
The desktop wrapper is a shell only: frontend still talks directly to http://127.0.0.1:3010 over localhost HTTP/SSE.
Required Inputs
- The release version, bumped in every file listed in
version-locations.txt. - Rust + Node toolchains available in CI runners.
The workflow builds the frontend itself (npm ci && npm run build in frontend/) in every freeze and bundle job, so no prebuilt assets are needed.
Version alignment rule:
- The
validatejob's "Validate version alignment across all manifests" step inrelease.ymlfails unless the requested version is valid semver, its tag (v<version>) does not exist yet, and the version exactly matches all of:pyproject.toml(project.version)desktop/package.json(version)frontend/package.json(version)desktop/src-tauri/tauri.conf.json(version)desktop/src-tauri/Cargo.toml(package.version)
Workflow
Use:
.github/workflows/release.yml(run manually,workflow_dispatch, with theversioninput and an optionalprereleaseflag). Desktop and PyPI releases share this one workflow.
Desktop job flow:
validate— semver, tag, and cross-file version alignment (above).ci— the reusableci.ymlgate.freeze-server— freeze the sidecar per target: Linux x64, Windows x64, macOS arm64, macOS x64.package-desktop— stage the frozen sidecar into the TauriexternalBinpath and runtauri buildper target (.msi,.deb+.AppImage,.dmg).sbom— Syft CycloneDX SBOMs for the Python, Node and Rust trees. This job iscontinue-on-error: true, which keeps a failed SBOM from failing the run.attestneeds it, though, and GitHub documents that a failed job skips the jobs that need it without saying whethercontinue-on-errorchanges that. Treat a failedsbomas possibly blockingattestand everything after it, PyPI included, and check the SBOM assets (below).attest— normalize asset names and attest build provenance for every file the release attaches: the wheel and sdist, the installers, the SBOMs andmetrics.json. It needsvalidate,build-python,package-desktop,sbomandmetrics, andpublish-pypineeds it, so nothing is published until every asset is built and attested.create-release— push thev<version>tag and create the GitHub Release with the attested assets. It needsvalidate,smoke-testandattest.
Sidecar Freeze Guards
freeze_server.py enforces:
- Frontend dist presence check.
- Minimum executable size threshold check.
- Executable smoke checks (
--help,--version) unless explicitly skipped.
Manual Operations
- Verify desktop app identity in
desktop/src-tauri/tauri.conf.json:identifier: org.feastorg.anolis-workbenchproductName: Anolis Workbench
- Confirm port
3010is documented as reserved in user-facing release notes. - Confirm release assets include at minimum:
- Windows
.msi - Linux
.AppImageand.deb - macOS
.dmg(aarch64 and x64) - CycloneDX SBOM JSON files
- Windows
- Verify any downloaded asset's provenance:
gh attestation verify <asset> -R anolishq/anolis-workbench.
Notes
- macOS is built in the same
package-desktopmatrix as Windows and Linux, so a failed macOS leg fails that job andcreate-releasedoes not run. publish-pypineedsattest, so a desktop or attestation failure stops the run before the PyPI publish. A failure after it (smoke-test,create-release) can still leave the version on PyPI with no tag and no GitHub Release. Re-running the workflow for the same version recovers — no tag exists yet, sovalidatepasses, and the publish step usesskip-existing: true.- Re-running only the failed jobs of the same run also recovers, without rebuilding: GitHub re-runs the failed jobs and the ones they gated. 0.15.0 went out this way after
smoke-testfailed because PyPI's index had not yet listed the new version.
