Releases
Commit messages drive release notes
Section titled “Commit messages drive release notes”GitHub release notes are produced mechanically by scripts/release-notes.mjs (pure git parsing —
no LLM). Every non-merge commit subject between two tags becomes one bullet, grouped by type, and
body lines starting with - become sub-bullets. CI enforces the shape on every PR (the
commit-lint job). Write every commit as if it will be read on the release page — because it will:
<type>(<scope>): <imperative summary that reads as a release-note bullet>
- one body bullet per distinct feature / fix / behavior change- keep each bullet to one line- type:
feat | fix | perf | refactor | docs | test | ci | build | chore | style | revert - Subject ≤ 120 chars and must stand alone.
- A squash-merged PR should enumerate each change as a
-body bullet — that’s where per-feature granularity comes from. - Trailers (
Co-Authored-By, …) go after a blank line and never leak into notes.
Preview the notes an upcoming tag would get:
node scripts/release-notes.mjs HEADTagged releases
Section titled “Tagged releases”Pushing a vX.Y.Z tag triggers .github/workflows/release.yml. The pipeline builds the app (with
the daemon sidecar embedded) for all platforms in parallel and signs + notarizes the macOS
bundle.
Notarization is decoupled from the expensive macOS runner so Apple’s notary queue can’t burn CI
minutes: the macOS legs sign and submit without waiting, then exit. A short notarize-wait job
catches the fast case; if Apple’s queue is slow, a scheduled notarize-poll workflow finalizes the
release whenever Apple completes. Finalizing staples the ticket into the artifacts, promotes the
GitHub release (with the generated notes), and mirrors updater artifacts for every platform.
The site (this docs site included) deploys separately via .github/workflows/deploy-site.yml, gated
by the SITE_DEPLOY_ENABLED repository variable.