Skip to content

Commit c069710

Browse files
MajorTalclaude
andcommitted
docs(publish): add pre-publish tarball smoke test + codify npm before-bypass
Pre-publish: pack each tarball, install in /tmp, and verify the entry points actually resolve before running `npm publish`. `npm test` runs against source, not a packed tarball, so it misses bugs like monorepo-relative imports that escape the `files` allowlist. v1.40.1 shipped broken because this check didn't exist. Post-publish install step: add `--prefer-online` (so freshly-published versions resolve instead of the local cache) and `--before=9999-12-31` to bypass the user's global `before` supply-chain guard for scratch installs. Note explicitly: keep the global config intact — do not suggest `npm config delete before`. The user keeps it on purpose as a mitigation against post-compromise dependency updates. The post-publish step now also runs `run402 allowance status` as a real end-to-end smoke test, so a broken tarball surfaces immediately. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent cf9fdc6 commit c069710

1 file changed

Lines changed: 45 additions & 3 deletions

File tree

.claude/commands/publish.md

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,44 @@ After updating all three package.json files, run `npm install --package-lock-onl
2121

2222
Stage and commit: `git add package.json cli/package.json sdk/package.json package-lock.json && git commit -m "chore: bump version to <new_version>"`
2323

24+
## Pre-publish tarball smoke test
25+
26+
`npm test` runs against the source tree, not a packed tarball, so it misses bugs like monorepo-relative imports that escape the `files` allowlist. Pack each tarball, install it in a scratch dir, and verify the entry points actually load before publishing. v1.40.1 shipped broken because this step didn't exist — every `run402` command threw `ERR_MODULE_NOT_FOUND`.
27+
28+
**About `--before=9999-12-31`:** the user's global npm has a `before` date pinned as a supply-chain mitigation (blocks installing packages published after that date, in case a dependency is compromised). Scratch installs in `/tmp` can safely bypass it per-invocation with `--before=9999-12-31`. Do **not** suggest the user remove the global config — they want it.
29+
30+
Run these in sequence. If any check fails, stop and fix the root cause. Do **not** `npm publish`.
31+
32+
```
33+
SMOKE=/tmp/smoke-<new_version> && rm -rf $SMOKE && mkdir $SMOKE
34+
npm pack --pack-destination $SMOKE
35+
(cd cli && npm pack --pack-destination $SMOKE)
36+
(cd sdk && npm pack --pack-destination $SMOKE)
37+
```
38+
39+
1. **CLI** — extract, install, check `--version`:
40+
```
41+
mkdir $SMOKE/cli && tar xzf $SMOKE/run402-<new_version>.tgz -C $SMOKE/cli
42+
(cd $SMOKE/cli/package && npm install --omit=dev --before=9999-12-31)
43+
node $SMOKE/cli/package/cli.mjs --version
44+
```
45+
Expect exit 0 and the new version string.
46+
47+
2. **MCP** — extract, install, verify the SDK import resolves (don't boot the stdio server — it won't exit):
48+
```
49+
mkdir $SMOKE/mcp && tar xzf $SMOKE/run402-mcp-<new_version>.tgz -C $SMOKE/mcp
50+
(cd $SMOKE/mcp/package && npm install --omit=dev --before=9999-12-31)
51+
(cd $SMOKE/mcp/package && node -e "import('./dist/sdk.js').then(m => console.log('OK', typeof m.getSdk)).catch(e => { console.error('FAIL', e.message); process.exit(1) })")
52+
```
53+
54+
3. **SDK** — extract, install, verify both entry points:
55+
```
56+
mkdir $SMOKE/sdk && tar xzf $SMOKE/run402-sdk-<new_version>.tgz -C $SMOKE/sdk
57+
(cd $SMOKE/sdk/package && npm install --omit=dev --before=9999-12-31)
58+
(cd $SMOKE/sdk/package && node -e "import('./dist/node/index.js').then(m => console.log('OK node', typeof m.run402)).catch(e => { console.error('FAIL', e.message); process.exit(1) })")
59+
(cd $SMOKE/sdk/package && node -e "import('./dist/index.js').then(m => console.log('OK iso', typeof m.Run402)).catch(e => { console.error('FAIL', e.message); process.exit(1) })")
60+
```
61+
2462
## Publish
2563

2664
1. **MCP server** (`run402-mcp`):
@@ -55,11 +93,15 @@ Stage and commit: `git add package.json cli/package.json sdk/package.json packag
5593
gh workflow run deploy-site.yml -R kychee-com/run402-private
5694
```
5795
(Or `gh api repos/kychee-com/run402-private/dispatches -f event_type=public-docs-updated` if you want the trigger to show up in the audit log as a `repository_dispatch`.)
58-
6. **Install the new version locally** so `run402` on the command line uses the just-published version:
96+
6. **Install the new version locally and smoke-test it** so `run402` on the command line uses the just-published version — and so a broken publish gets caught immediately, not when the user next runs a command:
5997
```
60-
npm install -g run402@<new_version>
98+
npm install -g run402@<new_version> --prefer-online --before=9999-12-31
99+
run402 --version
100+
run402 allowance status
61101
```
62-
Verify with `run402 --version` and confirm it matches the new version.
102+
- `--prefer-online` forces npm to hit the registry instead of a stale local cache (the new version can otherwise appear missing for a minute after publish).
103+
- `--before=9999-12-31` bypasses the user's global `before` supply-chain guard for this one install. Keep the global config intact — do not run `npm config delete before`.
104+
- Expect `run402 --version` to print the new version, and `run402 allowance status` to return valid JSON with the user's wallet info. If either fails with `ERR_MODULE_NOT_FOUND` or similar, the published tarball is broken — tell the user loudly and prepare a hotfix version immediately.
63105
7. Print a summary of what was published, including the new version and npm URLs:
64106
- https://www.npmjs.com/package/run402-mcp
65107
- https://www.npmjs.com/package/run402

0 commit comments

Comments
 (0)