# NPM Release

> Bump @oxide/api to a new omicron release tag locally, then (after the user pushes, merges, and CI publishes) hand off the npm dist-tag command.

- Skill: `oxidecomputer/npm-release` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add oxidecomputer/npm-release`
- Raw SKILL.md: https://api.skillmd.com/api/skills/oxidecomputer/npm-release/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: oxidecomputer (https://skillmd.com/u/oxidecomputer)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/oxidecomputer/npm-release

---


# npm-release: Bump @oxide/api to a new omicron release

This skill handles the full release cycle for the `@oxide/api` npm package when
a new omicron release tag appears (e.g., `rel/v19/rc0`).

## Inputs

Ask the user for:

- **Omicron release tag** (e.g., `rel/v19/rc0`). If not provided, look up the
  latest `rel/` tag on oxidecomputer/omicron via
  `gh-api-read repos/oxidecomputer/omicron/tags --jq '.[].name'` and pick the
  newest one. Confirm with the user before proceeding.
- **New `@oxide/api` version**: Always a minor bump. Read the current version
  from `oxide-api/package.json` and bump minor (e.g., `0.5.1` → `0.6.0`).

## Steps

### Phase 1: Prepare the bump

1. **Resolve the omicron tag to a commit SHA.** Omicron release tags are
   annotated tags, so you need to dereference to the underlying commit:
   ```
   gh-api-read repos/oxidecomputer/omicron/git/ref/tags/<tag> --jq '.object.sha'
   ```
   That gives you the tag object SHA. Then:
   ```
   gh-api-read repos/oxidecomputer/omicron/git/tags/<tag-object-sha> --jq '.object.sha'
   ```
   That gives you the commit SHA. Write it to `OMICRON_VERSION`.

2. **Update `OMICRON_VERSION`** with the commit SHA (just the SHA, one line,
   trailing newline).

3. **Bump version in `oxide-api/package.json`** to the agreed version. Then
   run `cd oxide-api && npm install --package-lock-only` so `package-lock.json`
   is updated to match.

4. **Update the versions table in `oxide-api/README.md`.** Add a row for the
   new release at the top of the table with the system version, `@oxide/api`
   version, and npm dist-tag.

5. **Regenerate the API client** by running `./tools/gen.sh`. This downloads
   the OpenAPI spec from the omicron commit and regenerates `oxide-api/src/`.

6. **Update test snapshots.** In `oxide-openapi-gen-ts/`, run:
   ```
   npm ci && npm test run -- --update
   ```
   This regenerates the snapshots in
   `oxide-openapi-gen-ts/src/__snapshots__/`.

7. **Run CI checks locally** to make sure everything passes:
   - `cd oxide-api && npm ci && npm run tsc`
   - `cd oxide-openapi-gen-ts && npm run tsc && npm run lint && npm run fmt:check`

### Phase 2: Hand off (do not push)

8. **Describe the commit and stop.** Set the commit description with
   `jj desc -m 'Bump API to <tag> (<version>)'` and leave the change in the
   working copy for the user to review.

   Do **not** push a branch, and do **not** open a PR. No `jj git push`, no
   `gh pr create`. The user pushes and opens the PR themselves. Report what
   changed (the files touched and any notable spec changes) and stop there.

   Note for when the PR does exist: the `update-api-spec` workflow runs on push
   to non-main branches and may create an "Autogenerate config update" commit —
   that is expected. The `validate` workflow must pass. The user reviews and
   merges; never merge the PR yourself.

### Phase 3: Publish (automatic) and dist-tag (manual)

9. **Approve and wait for the automatic publish.** Merging the PR triggers the
    `Release` workflow on main, which runs validation and then waits for a
    required reviewer to approve the `release` environment deployment. Tell
    the user to approve it (or ask another required reviewer to approve it) in
    the Actions run UI. The workflow then publishes any package version not
    already on the registry via npm trusted publishing — no manual
    `npm publish`. Watch it with
    `gh run watch $(gh run list --workflow=release.yml --limit 1 --json databaseId --jq '.[0].databaseId')`
    and confirm with `npm view @oxide/api version`, which should show the new
    version.

10. **Add npm dist-tag.** Tag the published version with the release name so
    consumers can pin to it:
    ```
    npm dist-tag add @oxide/api@<version> rel<N>
    ```
    where `<N>` is the system release number (e.g., `19` for `rel/v19/rc0`).
    Note: bare version numbers like `v10` are not allowed by npm because they
    parse as semver — use the `rel` prefix.

    Do **not** run this yourself. It requires interactive npm auth (a browser
    one-time-password flow) that won't work from an automated tool call, and
    CI can't do it either — the trusted-publishing OIDC credential only works
    for `npm publish`, not `dist-tag`. Present the exact command with
    `<version>` and `<N>` already substituted and let the user run it.

### Notes

- The `@oxide/openapi-gen-ts` package version is bumped separately and less
  frequently — do not bump it as part of this flow unless the user asks.
- The version scheme for `@oxide/api` roughly follows omicron releases with
  minor bumps for new releases and patch bumps for fixes. See issue #245 for
  ongoing discussion.
- If the gen script fails to download the spec, verify the commit SHA is
  correct and that `openapi/nexus/nexus-latest.json` exists at that commit.

