Releases happen on tag push. The release.yml workflow on macos-14 builds both binaries, creates a GitHub Release with provenance attestations, and publishes two npm packages with sigstore-signed provenance via npm Trusted Publishing.
The workflow uses no long-lived secrets β auth is via GitHub OIDC for both attestations and npm.
These steps must be done once before the first real release.
npm Trusted Publishing requires the package to already exist on the registry. Publish placeholder 0.0.1 versions interactively from your machine:
npm login # personal account; uses 2FA. Required.
# Platform package β empty bin/ is fine, this is a placeholder.
( cd npm/darwin-arm64 && npm publish --access public --dry-run ) # sanity-check first
( cd npm/darwin-arm64 && npm publish --access public )
( cd npm/wrapper && npm publish --access public --dry-run )
( cd npm/wrapper && npm publish --access public )After this you'll have @tashian/tsm@0.0.0 and @tashian/tsm-darwin-arm64@0.0.0 on the registry. They're harmless placeholders.
For each of the two packages:
- Go to
https://www.npmjs.com/package/@tashian/<pkg>/access - Scroll to "Trusted Publisher" β "Add trusted publisher"
- Set:
- Publisher: GitHub Actions
- Organization or user:
tashian - Repository:
tsm - Workflow filename:
release.yml - Environment: (leave blank)
Once both packages have a trusted publisher configured, the workflow can publish without an NPM_TOKEN secret.
Trusted Publishing requires 2FA on the publishing account. Check at https://www.npmjs.com/settings/<user>/profile.
# 1. Make sure main is green and you're at the commit you want to release.
git switch main
git pull
gh run list --branch main --limit 1 # confirm CI is green
# 2. Tag and push. The workflow runs automatically.
VERSION=0.1.0
git tag -a "v${VERSION}" -m "tsm v${VERSION}"
git push origin "v${VERSION}"
# 3. Watch the run.
gh run watchThe workflow:
- Builds
tsm(Go) andtsmd(Swift, ad-hoc signed byswift build) - Tarballs both into
tsm_<version>_darwin_arm64.tar.gzwithchecksums.txt - Attests build provenance via
actions/attest-build-provenance(sigstore, public transparency log) - Creates a GitHub Release with auto-generated notes from PRs since the previous tag
- Publishes
@tashian/tsm-darwin-arm64@<version>first (so the wrapper'soptionalDependenciesresolve) - Publishes
@tashian/tsm@<version>second - Both npm publishes carry provenance attestations linking back to this exact workflow run
After an upgrade, the running tsmd keeps serving the old code until it idle-quits (all sessions past TTL + 30 minutes without an RPC), at which point the next tsm command spawns a fresh daemon from the new binary. Users who want the upgrade applied immediately can tsm daemon stop.
Before tagging, you can sanity-check the build and packaging on your laptop:
just release-dryrun 0.0.0-dev
ls -lh dist/release/
# Inspect the tarball.
tar -tzvf dist/release/tsm_0.0.0-dev_darwin_arm64.tar.gzLocal builds do not mint provenance attestations β those require the workflow's OIDC identity. Use the dry-run only to verify packaging.
# GitHub Artifact Attestation on the release tarball
gh attestation verify tsm_0.1.0_darwin_arm64.tar.gz --repo tashian/tsm
# npm package provenance
npm view @tashian/tsm@0.1.0
npm audit signatures # in a project that depends on it"OIDC token has insufficient scope" β id-token: write is missing from the workflow's permissions: block. Should already be set; don't remove it.
"npm error code E403" on publish β Trusted Publishing isn't fully configured for the package, or the workflow filename in the npm settings doesn't match release.yml. Re-check step 2 above.
Wrapper installs but tsm: command not found β optionalDependencies was skipped. The user installed with --no-optional, or has an unsupported platform. The shim prints a clear error in that case; if it doesn't, the platform key in npm/wrapper/shim.js may need extending.
Daemon won't start ("tsmd not found") β TSM_TSMD_BIN is being unset somewhere, or the platform package didn't install. Check npm ls @tashian/tsm-darwin-arm64.