Every Electrobun release has one exact version and one v<version> tag. Its
core and optional CEF archives plus electrobun-artifacts.json, six mirrored
paired-Hutch archives plus hutch-artifacts.json, single dependency-free npm
bootstrap, Kitchen release fixture, and template catalog metadata move together.
Updater packaging is a cross-repository contract. When an Electrobun release
depends on Hutch metadata or artifact-layout changes, publish Hutch first and
update the exact // @hutch pin in package/hutch.config.ts. The final release
check must run against that pinned Hutch release. A sibling Hutch checkout is a
development override only and must be selected explicitly with
ELECTROBUN_UPDATER_E2E_HUTCH (and HUTCH_ENGINE_BINARY when needed).
From a clean main checkout, run the release task in package:
hutch push:betaFor a complete pre-release pass on each desktop VM, run this from package:
hutch test:vmIt builds the package and every Kitchen main-process backend (Cottontail, Bun,
Zig, Rust, Go, Odin) against both the system webview (without bundled CEF) and
CEF, then runs each variant's automated suite one at a time. It then runs
test:unit (unit tests plus the desktop-only native tests), Kitchen's
test:tooling, the complete install/update/uninstall lifecycle, the native
updater's archive/patch/HTTP tests, and finally
check:release. Every stage
runs even if an earlier stage fails (Kitchen launches are skipped, and
reported, only when the build stage failed, so stale builds are never tested);
the task prints a combined failure summary and exits nonzero at the end.
Each Kitchen launch runs under a watchdog (--timeout): an app that deadlocks
or never finishes is killed with its CEF helpers and reported as a failed stage
instead of hanging the VM run.
Kitchen tests declare hard renderer requirements explicitly. A requested
renderer: "cef" is not itself a requirement: renderer-neutral tests preserve
that request so system-only builds exercise Electrobun's CEF-to-system fallback.
Only tests whose stated purpose and assertions require actual CEF are skipped
when CEF is not bundled.
Release CI runs the updater lifecycle through test:vm on macOS, Windows, and
Linux, on both x64 and ARM64. The release job depends on that entire matrix, so
a lifecycle failure blocks publication. The local check:release and push:*
preflight checks do not run the full lifecycle themselves. When working on
installer, updater, or uninstaller code, run hutch test:updater-lifecycle from
package/ to exercise it locally. It builds four releases, installs the first,
and verifies an app two versions behind follows a two-patch chain using the
canonical update.json with tar integrity fields. It then serves the next
update at the same manifest URL without those optional fields and with no
patch. That verifies older-manifest compatibility and full-archive fallback
before uninstalling. Each build must emit exactly one update manifest, and
runtime checks must never request update-v2.json. It uses
real native UI and may display installer or uninstaller windows while running.
The lifecycle restores a retained tar and removes the standalone manager and uninstall manifest to test migration from an older installation layout. The app still runs the current updater; this simulation does not verify an actual released old updater binary. Before promoting the streaming updater to stable, test a real previously released installation updating to the beta, followed by another update with the new updater, on each supported operating system.
Before the first 2.0 release, also smoke-test one real v1.18.1 installation
updating to the 2.0 release candidate. Keep app.name, app.identifier, and
the release base URL unchanged for that bridge release, and verify that the
updated app preserves its data/profile root and can update and uninstall again.
The task runs the release checks, advances the version with npm's prerelease
semantics, updates every synchronized identity, commits v<version>, creates
that tag, and pushes it. For example, a beta bump from 2.0.0 produces
v2.0.1-beta.0.
The single release workflow then:
- builds the per-platform core archives and Kitchen artifacts;
- downloads and verifies the six archives from the independently published,
exactly paired Hutch release, mirrors them into the Electrobun release, and
creates
electrobun-artifacts.jsonandhutch-artifacts.json; every indexed archive is bound to its immutable GitHub Release URL, byte size, and SHA-256 digest before the draft release is verified and finalized; - tests and publishes the single dependency-free
npm/electrobunpackage with npm'sbetadist-tag; - on macOS x64/ARM64, Linux x64/ARM64, and Windows x64/ARM64, upgrades an isolated real v1.18.1 npm project to the exact public release, verifies the one-package layout, downloads its paired Hutch from that version's public GitHub Release, and proves the warm cache works offline; and
- only after that acceptance matrix passes, publishes the Kitchen artifacts
to R2, stamps the exact release version into each staged template's
hutch.config.ts, and then advances the matching beta template catalog; - runs template acceptance on macOS x64/ARM64, Windows x64/ARM64, and Linux x64/ARM64.
From an isolated Hutch home it resolves the published release's paired Hutch,
uses
hutch electrobun initto download and install the publichello-worldtemplate, checks the generated pins, builds it, and observes native startup. A separate CI fixture goes throughinitusing a loopback catalog and the real published devkit/runtime. It verifies the install hook, SQL/SQLite capability detection, an explicit hashing capability override, and a system webview RPC round trip before exiting successfully. Linux runs under Xvfb.
Template acceptance tests the published channel, so it runs after publication:
a failure marks the release workflow red but does not roll back public artifacts.
It does not replace the full template QA dashboard or test:vm (including CEF).
Every command has a deadline; reports and stage logs are retained as CI artifacts.
To run the same acceptance test locally against a published release:
node scripts/accept-published-template.mjs --version 2.0.2 --platform macos-arm64 --output /tmp/template-acceptanceUse macos-x64, windows-x64, windows-arm64, linux-x64, or linux-arm64
for the other supported hosts.
The exact version must still be current in its stable/beta template catalog;
the harness fails rather than silently testing a different release. A desktop
session (or Xvfb on Linux) is required. The private fixture lives under
scripts/fixtures/template-acceptance and is not listed in the public catalog.
Stable tags use the same path and publish the npm package under latest plus
the stable template catalog. The npm package remains only a small command that
downloads, verifies, caches, and invokes its paired Hutch archive from the
Electrobun GitHub Release; it contains no Electrobun runtime or SDK and has no
platform-specific npm packages.
Installing the npm beta still does not implicitly select beta templates:
npx electrobun init # stable templates
npx electrobun init --beta # beta templates