Skip to content

Release Process

Karthik P edited this page Oct 25, 2026 · 5 revisions

Releases are cut by pushing a vX.Y.Z tag. The release workflow then:

  1. checks that the tag matches the versions in Cargo.toml and js/package.json, that the tagged commit is on main, and that CHANGELOG.md has a ## [X.Y.Z] - date section;
  2. builds the wheels, the sdist, the npm package and the crate, and checks each one;
  3. publishes the Python packages to TestPyPI, after a manual approval;
  4. installs the release from TestPyPI with uv on Linux, macOS and Windows;
  5. publishes to PyPI and crates.io, and stages the npm package for your approval;
  6. installs the release from PyPI with uv on all three systems;
  7. creates the GitHub release, with the changelog section as its notes and the packages attached.

Running the workflow by hand from the Actions tab does steps 1–4 only. That is the dry run.

One-time setup

GitHub environments

Repository → Settings → Environments → New environment. Create four environments:

Environment Required reviewers Deployment branches and tags
testpypi yourself branch main, tag v*
pypi optional tag v*
npm optional tag v*
crates-io optional tag v*

For each: tick Required reviewers if wanted and add yourself. Under Deployment branches and tags, choose Selected branches and tags and add the rules in the table. Then Save protection rules.

PyPI and TestPyPI (trusted publishing)

Both publishers are set up. To set one up for a project that does not exist yet, PyPI accepts a pending publisher. On https://pypi.org/manage/account/publishing/:

  1. Under Add a new pending publisher, choose the GitHub tab.
  2. Fill in:
    • PyPI Project Name: workclock
    • Owner: Karthik-beta
    • Repository name: workclock
    • Workflow name: release.yml
    • Environment name: pypi
  3. Click Add.

Repeat on https://test.pypi.org/manage/account/publishing/ (a separate account) with the environment name testpypi.

npm (trusted publishing)

The release job runs npm stage publish with no token. npm checks that the upload comes from this repository's release.yml in the npm environment, adds provenance, and holds the version in staging until a maintainer approves it.

On npmjs.com, a new trusted publisher expires if nothing publishes through it within two days, so add it on the day of the release, before you push the tag:

  1. Open https://www.npmjs.com/package/workclock and click Settings.
  2. Under Trusted Publisher, choose GitHub Actions.
  3. Fill in:
    • Organization or user: Karthik-beta
    • Repository: workclock
    • Workflow filename: release.yml
    • Environment name: npm
    • Allowed actions: leave npm stage publish on, and leave npm publish and npm dist-tag off. The release then always waits for your approval.
  4. Save. npm does not check the settings until the first publish.

To stop tokens from publishing workclock, open Settings → Publishing access, choose Require two-factor authentication and disallow tokens and click Update Package Settings. Trusted publishers keep working. Then delete the NPM_TOKEN secret from the npm environment and the token itself under your avatar menu → Access Tokens.

crates.io (trusted publishing)

The release job gets a 30-minute publish token from rust-lang/crates-io-auth-action, which revokes it when the job ends.

  1. Sign in to https://crates.io and open https://crates.io/crates/workclock/settings.
  2. Under Trusted Publishing, click Add and choose GitHub.
  3. Fill in:
    • Repository owner: Karthik-beta
    • Repository name: workclock
    • Workflow filename: release.yml
    • Environment: crates-io
  4. Save.
  5. Tick Require trusted publishing for all new versions. API tokens can then no longer publish workclock.
  6. Delete the CARGO_REGISTRY_TOKEN secret from the crates-io environment, and revoke the token under Account Settings → API Tokens.

Cutting a release

  1. In a PR, move the Unreleased entries in CHANGELOG.md under the new version and date, bump the version in Cargo.toml and js/package.json, refresh Cargo.lock, js/package-lock.json and website/package-lock.json, and update the compare links at the foot of CHANGELOG.md. Merge it.

  2. Run the dry run (Actions → Release → Run workflow on main), approve the testpypi deployment and wait for it to pass.

  3. If npm has no trusted publisher yet, or it expired, add it (see above).

  4. Tag and push the release:

    git tag -a vX.Y.Z -m "workclock X.Y.Z"
    git push origin vX.Y.Z
  5. Approve the testpypi deployment, and any others that need approval.

  6. When the npm job has finished, approve the staged version. Either:

    • on npmjs.com, open the package's Staged Packages tab, check the version and file count against the npm-package job's output, and click Approve; or
    • from a terminal signed in with npm login, run npm stage list workclock, then npm stage approve <stage-id>.

    Both ask for your two-factor code. Until then the version is not on npm, although the GitHub release already lists it.

  7. Check that npm view workclock version prints the new version.

Clone this wiki locally