|
| 1 | +# Publishing `artifacts-viewer` |
| 2 | + |
| 3 | +Versions and changelogs are managed by pnpm's native release tooling (`pnpm change` + |
| 4 | +`pnpm version -r`). Publishing to npm is done by GitHub Actions via OIDC trusted |
| 5 | +publishing — no token is ever held locally or in repository secrets. |
| 6 | + |
| 7 | +Never hand-edit `version` in `packages/artifacts-viewer/package.json`, and never |
| 8 | +hand-write `CHANGELOG.md`. Both are generated. |
| 9 | + |
| 10 | +--- |
| 11 | + |
| 12 | +## 1. Record a change intent (during development) |
| 13 | + |
| 14 | +Every change that affects the published package needs an intent. Record it in the |
| 15 | +same commit as the code change: |
| 16 | + |
| 17 | +```sh |
| 18 | +pnpm change |
| 19 | +``` |
| 20 | + |
| 21 | +This prompts for the affected packages, the bump type, and a summary that becomes the |
| 22 | +changelog entry. It writes a file such as `.changeset/calm-cats-resolve.md`. |
| 23 | + |
| 24 | +Non-interactive equivalent: |
| 25 | + |
| 26 | +```sh |
| 27 | +pnpm change --bump patch --summary "Fix raw path encoding for nested files" artifacts-viewer |
| 28 | +``` |
| 29 | + |
| 30 | +Bump types are `patch`, `minor`, `major`, or `none`. Use `none` to explicitly record |
| 31 | +that a change needs no release. |
| 32 | + |
| 33 | +Changes limited to documentation, CI, or private workspace apps do not need an intent. |
| 34 | + |
| 35 | +Commit the generated markdown file alongside the code: |
| 36 | + |
| 37 | +```sh |
| 38 | +git add .changeset/ packages/ |
| 39 | +git commit -m "fix: encode raw path segments individually" |
| 40 | +``` |
| 41 | + |
| 42 | +## 2. Preview the release plan |
| 43 | + |
| 44 | +At any point, see what the pending intents would produce: |
| 45 | + |
| 46 | +```sh |
| 47 | +pnpm change status |
| 48 | +pnpm version -r --dry-run |
| 49 | +``` |
| 50 | + |
| 51 | +`change status` lists the pending intent files and the resulting version bumps. |
| 52 | +`version -r --dry-run` prints the same plan without touching the working tree. |
| 53 | + |
| 54 | +## 3. Apply the release |
| 55 | + |
| 56 | +The working tree must be clean. |
| 57 | + |
| 58 | +```sh |
| 59 | +pnpm version -r |
| 60 | +``` |
| 61 | + |
| 62 | +This consumes every pending intent and: |
| 63 | + |
| 64 | +- bumps `version` in `packages/artifacts-viewer/package.json` |
| 65 | +- propagates bumps to any workspace dependents through `workspace:` ranges |
| 66 | +- writes `packages/artifacts-viewer/CHANGELOG.md` (we set |
| 67 | + `versioning.changelog.storage: repository` in `pnpm-workspace.yaml`) |
| 68 | +- records the consumed intents in `.changeset/ledger.yaml` |
| 69 | + |
| 70 | +It deliberately does **not** create a git commit or tag. pnpm skips those in recursive |
| 71 | +mode because a workspace release can bump several packages to different versions. |
| 72 | + |
| 73 | +Verify the workspace still passes before committing: |
| 74 | + |
| 75 | +```sh |
| 76 | +vp run ready |
| 77 | +``` |
| 78 | + |
| 79 | +## 4. Commit and tag |
| 80 | + |
| 81 | +Read the new version back out rather than typing it, so the tag can never disagree |
| 82 | +with the manifest: |
| 83 | + |
| 84 | +```sh |
| 85 | +VERSION=$(node -p "require('./packages/artifacts-viewer/package.json').version") |
| 86 | + |
| 87 | +git add . |
| 88 | +git commit -m "chore(release): artifacts-viewer@${VERSION}" |
| 89 | +git tag -a "v${VERSION}" -m "artifacts-viewer@${VERSION}" |
| 90 | +git push origin main --follow-tags |
| 91 | +``` |
| 92 | + |
| 93 | +The tag **must** be exactly `v<version>`. `publish.yml` compares the two and fails the |
| 94 | +release if they differ. |
| 95 | + |
| 96 | +## 5. Create the GitHub release |
| 97 | + |
| 98 | +```sh |
| 99 | +gh release create "v${VERSION}" --title "v${VERSION}" --generate-notes |
| 100 | +``` |
| 101 | + |
| 102 | +Publishing to npm triggers on the `release: published` event, so creating the release |
| 103 | +is what starts the publish. |
| 104 | + |
| 105 | +## 6. Watch the publish |
| 106 | + |
| 107 | +```sh |
| 108 | +gh run watch |
| 109 | +``` |
| 110 | + |
| 111 | +`.github/workflows/publish.yml` then: |
| 112 | + |
| 113 | +1. runs `vp install --frozen-lockfile` |
| 114 | +2. validates that the release tag equals `v<package.json version>`, failing otherwise |
| 115 | +3. runs `vp run ready` |
| 116 | +4. runs `pnpm pack` then `npm publish <tarball>` from `packages/artifacts-viewer` |
| 117 | + (`pnpm pack` rewrites `catalog:` ranges; plain `npm publish` does not — |
| 118 | + that is how 0.0.3 shipped broken). pnpm is installed via `pnpm/action-setup` |
| 119 | + |
| 120 | +Authentication is OIDC trusted publishing (`permissions: id-token: write`). There is no |
| 121 | +`NODE_AUTH_TOKEN`. npm generates an SLSA provenance attestation automatically. |
| 122 | + |
| 123 | +Confirm the result. Run this **outside** the repository — the root manifest's |
| 124 | +`devEngines` requires pnpm, so npm commands fail with `EBADDEVENGINES` from inside it: |
| 125 | + |
| 126 | +```sh |
| 127 | +(cd /tmp && npm view artifacts-viewer dist-tags) |
| 128 | +``` |
| 129 | + |
| 130 | +--- |
| 131 | + |
| 132 | +## Trusted publishing configuration |
| 133 | + |
| 134 | +Configured on npmjs.com for the package: |
| 135 | + |
| 136 | +| Field | Value | |
| 137 | +| ----------------- | ------------------ | |
| 138 | +| Organization | `mdhruvil` | |
| 139 | +| Repository | `artifacts-viewer` | |
| 140 | +| Workflow filename | `publish.yml` | |
| 141 | +| Environment | _(empty)_ | |
| 142 | + |
| 143 | +The binding is to that workflow filename. Renaming `publish.yml`, or moving the |
| 144 | +`npm publish` step into a different workflow file, breaks publishing until the trusted |
| 145 | +publisher is reconfigured. |
| 146 | + |
| 147 | +## Things that will bite you |
| 148 | + |
| 149 | +- **Do not add `publishConfig.provenance: true`.** Provenance requires OIDC, so the flag |
| 150 | + would break any local publish. CI generates provenance automatically without it. |
| 151 | +- **Avoid publishing from your laptop.** `pnpm publish` rewrites the workspace root |
| 152 | + `package.json` and strips `"private": true`. If you ever must publish locally, check |
| 153 | + `git diff` afterwards and revert the root manifest. |
| 154 | +- **The first publish already happened.** `0.0.0` was a placeholder published by hand to |
| 155 | + bootstrap trusted publishing, and it permanently occupies the `placeholder` dist-tag. |
| 156 | + Every release from `0.0.1` onward goes through CI. |
| 157 | +- **Intents are consumed, not reused.** Once `pnpm version -r` runs, the intent files are |
| 158 | + deleted and recorded in `.changeset/ledger.yaml`. Do not restore them by hand. |
| 159 | + |
| 160 | +## Quick reference |
| 161 | + |
| 162 | +```sh |
| 163 | +# during development, per change |
| 164 | +pnpm change |
| 165 | + |
| 166 | +# at release time |
| 167 | +pnpm version -r |
| 168 | +vp run ready |
| 169 | +VERSION=$(node -p "require('./packages/artifacts-viewer/package.json').version") |
| 170 | +git add . && git commit -m "chore(release): artifacts-viewer@${VERSION}" |
| 171 | +git tag -a "v${VERSION}" -m "artifacts-viewer@${VERSION}" |
| 172 | +git push origin main --follow-tags |
| 173 | +gh release create "v${VERSION}" --title "v${VERSION}" --generate-notes |
| 174 | +``` |
0 commit comments