Canonical release process documentation for OK Code.
Last updated: 2026-04-25
The next stable train ships one semver across desktop, CLI, and iOS surfaces:
- macOS arm64 and x64 desktop DMGs plus updater metadata
- Windows x64 NSIS installer
- Linux x64 AppImage
- iOS TestFlight build from the same release version/ref, dispatched separately
okcodesnpm package from the same tag
docs/release.md is the source of truth for release policy, release gates, and the platform matrix. Treat docs/releases/README.md and README release references as pointers only.
- iOS is TestFlight-only for this release train.
- Both macOS architectures are blocking for the main desktop release, and the published
latest-mac.ymlmanifest must contain arm64 and x64 payloads. - Android is non-blocking.
- Only macOS signing/notarization is mandatory. Windows signs when Azure Trusted Signing secrets are configured; otherwise the release publishes an unsigned Windows installer.
- Prereleases like
vX.Y.Z-rc.1are optional. Use them when we want soak time or extra validation, not as a mandatory gate. - Stable releases may ship directly as
vX.Y.Zwhen the change set is understood and approved. - Publish prereleases to npm with the
nextdist-tag. - Publish stable releases to npm with the
latestdist-tag.
Official release tags and follow-up mobile promotion now use two workflows:
release.ymlruns automatically on release tags for desktop artifacts, npm publish, GitHub Release publication, and finalize.release-ios.ymlis dispatched manually for the matching version/ref when we want the TestFlight upload.
release.yml job order:
preflightdesktop_buildpublish_clireleasefinalize
release-ios.yml job order:
preflightios_signing_preflightios_testflight
Desktop/CLI publishing must not be blocked by iOS signing availability or TestFlight upload retries.
Every RC and stable release must pass:
bun run fmt:check
bun run lint
bun run typecheck
bun run test
bun run --cwd apps/web test:browser
bun run test:desktop-smoke
bun run release:smokebun run lint is a zero-warning gate.
Run the comprehensive pre-release validator before cutting any RC or promoting to stable:
bun run release:validate <version>This checks documentation completeness, version alignment, git state, iOS project version, and optionally runs all quality gates. Use --skip-quality for a docs-only pass or --ci for CI pipelines.
Use the end-to-end release command for the normal desktop + CLI train:
bun run release:ship <version>This command runs local preflight, invokes release preparation, pushes the release tag, waits for release.yml, and verifies the published GitHub Release assets plus the merged macOS OTA manifest before returning success.
Blocking stable matrix:
| Surface | Runner | Artifact | Blocking |
|---|---|---|---|
| macOS arm64 | macos-14 |
signed/notarized DMG + updater metadata | yes |
| macOS x64 | macos-15-intel |
signed/notarized DMG + updater metadata | yes |
| Windows x64 | windows-2022 |
NSIS installer | yes |
| Linux x64 | ubuntu-24.04 |
AppImage | yes |
| iOS | macos-14 |
TestFlight upload | separate |
| CLI | ubuntu-24.04 |
npm publish | yes |
Optional manual rebuild lane:
| Surface | Workflow | Artifact |
|---|---|---|
| macOS x64 | release-intel-compat.yml |
Intel DMG rebuild |
- Build artifacts with
bun run dist:desktop:artifact. - Refuse macOS stable release builds unless signing and notarization secrets are present.
- Sign Windows builds when Azure Trusted Signing secrets are present; otherwise publish the unsigned installer.
- Publish both macOS arm64 and x64 DMG/ZIP payloads from the main
release.ymlworkflow. - Merge
latest-mac.ymlandlatest-mac-x64.ymlinto one publishedlatest-mac.ymlbefore creating the GitHub Release. - Validate packaged outputs before upload:
- macOS: both arch-specific DMGs exist and updater manifests are present
- Windows: installer exists
- Linux: AppImage exists
- Keep
bun run test:desktop-smokeandbun run release:smokegreen before tagging.
- Reuse the same release version as desktop and CLI.
- Build the mobile web bundle and sync Capacitor before archiving.
- Run a simulator build in CI before archive/upload.
- Upload the archive to TestFlight from the dedicated
release-ios.ymlworkflow. - Dispatch
release-ios.ymlwith the release version and matching tag/ref. Ifrefis left blank, it builds the workflow dispatch commit. - During RC soak, manually verify on:
- one current supported iPhone/iOS
- one older supported iPhone/iOS
Manual RC device checks:
- Pair the mobile companion with a desktop/server.
- Restore a saved pairing.
- Open a thread and send a follow-up.
- Approve an action and answer a user-input request.
- Background and foreground the app.
- Return to the thread from a notification tap.
- Build the CLI package from
apps/server. - Verify
npm pack. - Verify local
okcode --version,okcode --help, andokcode doctor --help. - Publish after desktop artifacts pass; do not wait on the separate iOS workflow.
- Verify the published package with
npx okcodes@<version> --version.
Before tagging:
- Ensure the main branch is green on the full gate set.
- Prepare:
CHANGELOG.mddocs/releases/vX.Y.Z.mddocs/releases/vX.Y.Z/assets.mddocs/releases/vX.Y.Z/rollout-checklist.md(version-specific rollout playbook)docs/releases/vX.Y.Z/soak-test-plan.md(version-specific soak test cases)
- Run
bun run release:validate <version>and fix any failures. - Confirm Apple signing/notarization and iOS distribution secrets:
APPLE_API_KEYAPPLE_API_KEY_IDAPPLE_API_ISSUERAPPLE_TEAM_IDIOS_PROVISIONING_PROFILEIOS_PROVISIONING_PROFILE_NAME
- Confirm Windows signing secrets.
- Confirm publish and finalize secrets:
NODE_AUTH_TOKENRELEASE_APP_IDRELEASE_APP_PRIVATE_KEY
The 48-hour RC soak must finish with:
- no Sev-1 or Sev-2 issues
- no crash-on-launch on blocking desktop platforms
- updater verification from the previous stable desktop build
- successful TestFlight install
- successful
npx okcodes@<rc-version> --version
If any blocker fails, cut a new RC and repeat the soak.
- The GitHub release includes desktop artifacts plus release notes and asset manifest.
- iOS is distributed through TestFlight by a separate
release-ios.ymldispatch against the release tag or exact release commit, not attached to the GitHub release. finalizeupdates version strings and pushes the post-release bump tomain.
- If
preflightfails, reproduce locally with the exact failing command before retriggering the workflow. - If
desktop_buildfails, inspect the target-specific signing secrets first. - If
ios_testflightfails, re-check provisioning, App Store Connect API key setup, the dispatched ref, and archive/export logs inrelease-ios.yml. - If
publish_clifails, do not continue the train. Fix the publish issue so the app and CLI do not drift.