|
1 | | -# npm release CI + runbook (jawcode-style) |
| 1 | +# npm release CI + runbook (jawcode-style, Trusted Publishing / OIDC) |
2 | 2 |
|
3 | 3 | ## Why |
4 | 4 | `bun/npm install -g opencodex` install from the **npm registry**, not GitHub — so opencodex must be |
5 | | -**published to npm**. Modeled on jawcode's release flow (minus its monorepo/native-binary bits). |
| 5 | +published to npm. Modeled on jawcode's release flow, but using **npm Trusted Publishing (OIDC)** so CI |
| 6 | +publishes **without a long-lived `NPM_TOKEN` secret** (npm's recommended approach — short-lived OIDC |
| 7 | +credentials, provenance generated automatically, nothing to leak). |
6 | 8 |
|
7 | | -## How jawcode does it (reference) |
8 | | -`jawcode/.github/workflows/release.yml` is a **`workflow_dispatch`** job with `version` + `tag` |
9 | | -(latest/preview) + `dry-run` (default **true**) inputs: it verifies package.json matches the input, |
10 | | -then `npm publish --tag … --access public --provenance`, then a post-publish registry smoke. A local |
11 | | -`bun scripts/release.ts <version>` does preflight → bump → commit → push → dispatch → watch. |
| 9 | +## What shipped |
| 10 | +- `.github/workflows/release.yml` — `workflow_dispatch` (inputs: `version` / `tag` latest|preview / |
| 11 | + `dry-run` default true). Verifies `tag == package.json`, upgrades npm (OIDC needs **npm ≥ 11.5.1**), |
| 12 | + then **tokenless** `npm publish` via OIDC (`id-token: write`) — no `NPM_TOKEN`, no `--provenance` |
| 13 | + (automatic). Post-publish `npm view` smoke. |
| 14 | +- `scripts/release.ts` (+ `bun run release` / `release:watch`) — preflight → bump → commit → push → |
| 15 | + dispatch → watch. Not shipped in the tarball. |
12 | 16 |
|
13 | | -## What shipped (opencodex) |
14 | | -- **`.github/workflows/release.yml`** — `workflow_dispatch` with `version` / `tag` (latest|preview) / |
15 | | - `dry-run` (default true). Verifies the tag input == package.json, then `npm publish` (prepublishOnly |
16 | | - builds the GUI into gui/dist first) with `--provenance`, then `npm view` smoke on real publishes. |
17 | | - Replaces the old tag-push `publish-npm.yml`. |
18 | | -- **`scripts/release.ts`** (+ `bun run release` / `release:watch`) — single-package version of |
19 | | - jawcode's helper: clean-main + typecheck preflight → `npm version --no-git-tag-version` → commit → |
20 | | - push → `gh workflow run release.yml` (dry-run unless `--publish`) → watch. Not shipped in the tarball. |
21 | | -- Package already publish-ready (`files`, bin `#!/usr/bin/env bun`, `engines.bun`, `prepublishOnly`). |
| 17 | +## One-time setup (owner) — Trusted Publishing |
| 18 | +⚠️ A Trusted Publisher can only be configured **after** the package has ≥1 published version. So the |
| 19 | +very first publish of the new name is done locally; everything after is tokenless CI. |
22 | 20 |
|
23 | | -## One-time setup (owner) |
24 | | -1. npm **Automation** (or Granular all-packages Read+Write) token: npmjs.com → Access Tokens. |
25 | | -2. Add it as the repo secret **`NPM_TOKEN`**: GitHub → Settings → Secrets and variables → Actions. |
| 21 | +1. **First publish (local, one-time)** — claims `opencodex`, no token stored anywhere: |
| 22 | + ```bash |
| 23 | + npm login # browser auth |
| 24 | + npm version 0.1.0 --no-git-tag-version # set the first version |
| 25 | + git commit -am "release: v0.1.0" && git push origin main |
| 26 | + npm publish --access public # prepublishOnly builds the GUI first |
| 27 | + ``` |
| 28 | +2. **Configure the Trusted Publisher** — npmjs.com → the `opencodex` package → **Settings → Trusted |
| 29 | + Publisher → GitHub Actions**: |
| 30 | + - Organization or user: `lidge-jun` |
| 31 | + - Repository: `opencodex` |
| 32 | + - Workflow filename: `release.yml` |
| 33 | + - Environment name: *(leave blank)* |
| 34 | +3. Done — every future release publishes tokenlessly via OIDC. (No `NPM_TOKEN` secret anywhere.) |
26 | 35 |
|
27 | | -## Releasing |
28 | | -**Easiest (local helper):** |
| 36 | +## Releasing (after setup) |
29 | 37 | ```bash |
30 | | -bun run release 0.1.0 # preflight → bump → commit → push → DRY-RUN dispatch → watch |
31 | | -bun run release 0.1.0 --publish # …and actually publish (after the dry-run looks good) |
| 38 | +bun run release 0.2.0 # bump + commit + push + DRY-RUN dispatch + watch |
| 39 | +bun run release 0.2.0 --publish # …and actually publish (tokenless OIDC) |
32 | 40 | ``` |
33 | | -**Manual (GitHub UI):** bump `version` in package.json on main, commit/push, then Actions → **Release** |
34 | | -→ Run workflow → enter the version, pick the dist-tag, leave **dry-run on** first, re-run with dry-run |
35 | | -off to publish. |
| 41 | +Or: Actions → **Release** → Run workflow → version + dist-tag, dry-run on first, then off to publish. |
36 | 42 |
|
37 | | -After a real publish: `bun install -g opencodex` / `npm install -g opencodex` work (package page: |
38 | | -https://www.npmjs.com/package/opencodex). opencodex is bun-native, so installers still need **bun** on PATH. |
| 43 | +After a real publish, `bun install -g opencodex` / `npm install -g opencodex` (and `ocx update`) work. |
| 44 | +opencodex is bun-native, so installers still need **bun** on PATH. Page: https://www.npmjs.com/package/opencodex |
| 45 | + |
| 46 | +## Why not an automation token? |
| 47 | +npm now warns that classic automation/granular tokens for CI carry security risk (a leaked write token |
| 48 | +publishes arbitrary versions). Trusted Publishing replaces it with per-run OIDC credentials scoped to |
| 49 | +this exact repo+workflow — nothing long-lived to store or steal. |
0 commit comments