Releasing
Releases are built and published from a maintainer’s machine. GitHub Actions is manual-only (workflow_dispatch on both CI and Release), so no hosted minutes are spent. act runs the real workflow files locally: macOS jobs run natively on the host (-self-hosted), Ubuntu jobs in Docker.
Commands#
All from the repository root (package.json scripts, run with bun run <name>):
| Command | What it does |
|---|---|
bun run check | cargo fmt --all -- --check, cargo clippy --workspace --all-targets -- -D warnings, cargo test --workspace, cargo deny check, the skills check, then bun run desktop:check (svelte-check, prettier, bun test). The same gate as CI. |
bun run release <patch|minor|major|X.Y.Z> | Cut and publish a release (default bump: patch). |
bun run release:dry | scripts/release.sh --dry-run: prints every step, changes and pushes nothing. |
bun run release:act [vX.Y.Z] [--publish] | Run .github/workflows/release.yml through act. Default tag: v + the version in crates/atlas-cli/Cargo.toml. Without --publish it builds artifacts into .act/artifacts and publishes nothing. |
bun run ci:act | Run .github/workflows/ci.yml locally through act. |
scripts/release.sh also accepts --skip-checks (skip bun run check).
Prerequisites#
- On
main, working tree clean, in sync withorigin/main(the script checks;--dry-runrelaxes the clean/sync checks). ghlogged in with write access tocodeatlasdev/atlas(gh auth login); its token is used to publish.act(brew install act) and a running Docker (OrbStack or Docker Desktop) for the Ubuntupublishjob.- Rust toolchain pinned in
rust-toolchain.toml,bun,gh.scripts/setup.sh(run byrelease.sh,desktop:devanddesktop:build) verifies them and installs frontend deps,cargo-tauriandcargo-denywhen missing. Underact, theSwatinem/rust-cachesteps are skipped (if: ${{ !env.ACT }}): the localtarget/is the cache. .actrcis committed and configures act:macos-14→-self-hosted,ubuntu-latest→catthehacker/ubuntu:act-latest, container architecturelinux/arm64, artifact server path.act/artifacts(gitignored).
What bun run release patch does#
- Resume check. If
HEADis already a pushedvX.Y.Ztag whose GitHub Release does not exist (a previous publish failed), it only runs the publish step for that tag and exits. It never bumps again. - Preflight. Branch is
main; tree is clean;mainequalsorigin/main; the new tag exists neither locally nor onorigin;CHANGELOG.mdhas a## [X.Y.Z]section. - Checks.
bun run check(unless--skip-checks). - Bump. Every
crates/*/Cargo.toml(includingatlas-desktop;tauri.conf.jsonhas no version of its own and reads it from the crate) gets the same new version (taken fromatlas-cli’s current version);cargo update --workspace --offlinerefreshesCargo.lock. - Commit, tag, push. Commit
chore(release): vX.Y.Z, annotated tagvX.Y.Z(“Atlas vX.Y.Z”),git push --atomic origin main vX.Y.Z. - Build and publish via
scripts/act-release.sh vX.Y.Z --publish.
Version rules: patch/minor/major bump the current version; an explicit X.Y.Z (optionally with a -pre suffix such as -beta.1) is used as is. Tags containing beta or alpha are published as pre-releases.
What the release workflow does#
.github/workflows/release.yml, three jobs in order:
- Test (
macos-14):bun run check(the full gate: every workspace crate, deny, skills, desktop frontend). - Build (
macos-14):cargo build --release --target aarch64-apple-darwin -p atlas-cli, ad-hoccodesign(so Gatekeeper does not SIGKILL the binary), packageatlas-<version>-aarch64-apple-darwin.tar.gzplus a.sha256. Then Atlas Desktop:cargo tauri build --bundles app,dmg(ad-hoc signed throughbundle.macOS.signingIdentity: "-"), packaged asAtlas-<version>-aarch64-apple-darwin.app.tar.gz(whatatlas desktopandatlas updateinstall) andAtlas-<version>-aarch64-apple-darwin.dmg(manual download), each with a.sha256. - Publish (
ubuntu-latest): generatemanifest.jsonwithscripts/generate-manifest.py(itsdesktopanddesktop_dmgentries point at the app assets), then create the GitHub Release (softprops/action-gh-release) with the tarball, checksum and manifest. The body is the version’sCHANGELOG.mdsection followed by the install/update instructions.
Publishing is skipped under act unless ATLAS_PUBLISH=1 is set, which act-release.sh --publish does. The version comes from the tag name (GITHUB_REF_NAME), which the script passes to act through a synthetic event file.
Publishing with a token#
scripts/act-release.sh --publish needs a token with permission to create releases. It uses $GITHUB_TOKEN if set, otherwise gh auth token, and passes it to act as the GITHUB_TOKEN secret. It refuses to publish when the tag is not on origin, act is missing, Docker is not running, or no token is available. Without --publish nothing is uploaded.
If something fails#
- Checks or tests fail: nothing has changed yet; fix and rerun.
- Push succeeded but build or publish failed: rerun
bun run release patch; the resume check sees the pushed tag without a Release and only republishes. Or runbun run release:act vX.Y.Z --publishdirectly. - Want to rehearse:
bun run release:dry, thenbun run release:act(builds artifacts locally, publishes nothing). - Tag exists error: pick another version, or delete the tag only if no Release was created from it.
Verify a release#
gh release view vX.Y.Z --repo codeatlasdev/atlas
atlas update --check
curl -fsSL https://api.github.com/repos/codeatlasdev/atlas/releases/latest | grep tag_name # what users' installers see: no login
Users get the new version with atlas update, which downloads atlas-<version>-aarch64-apple-darwin.tar.gz from the release and then the matching Atlas-<version>-…app.tar.gz when Atlas Desktop is installed. Or they click Update CLI & app in the app. Check the app asset with atlas desktop --install --latest --no-open && atlas desktop --status.