ci: add hardened npm publish pipeline (OIDC, provenance, pkg.pr.new) - #2
Conversation
Set up CI-only, tokenless publishing for the packages, following current npm supply-chain best practices: - OIDC Trusted Publishing via the npm CLI (bun publish supports neither OIDC nor provenance), with automatic Sigstore provenance. release.yml splits the Changesets version PR (no id-token) from publish, which is gated by a protected Environment and is the only job granted id-token. - An ordered publish script that rewrites the workspace: protocol to a concrete range before packing, is fail-closed on registry errors, and asserts the protocol never leaks into the tarball. Unit tested. - A clean-room install+run smoke gate: packs each package, installs them with their real peers outside the workspace, and exercises the public entry points, the bin, and the spawned worker file to catch breakage that only appears after a real install. - Continuous preview releases via pkg.pr.new (no npm token). - Actions pinned to commit SHAs with automated update PRs; installs run with --ignore-scripts plus a lockfile-integrity guard. - Add repository metadata and a LICENSE to each package (required for provenance), set the initial version, and add publint/attw gates. See docs/RELEASING.md for the one-time human setup. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Switch the test:type / typecheck scripts from tsc to tsgo (@typescript/native-preview), the native TypeScript compiler, for much faster type-checking. Added as a root devDependency so the binary resolves at the workspace root; the platform binary ships as an os/cpu-constrained optionalDependency, so it resolves in CI under --ignore-scripts. tsc is kept for .d.ts generation via rolldown-plugin-dts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ushironoko
left a comment
There was a problem hiding this comment.
[AI Rally - Reviewee]
Removed the fragile overrides: { "@tskm/compiler": "file:..." } entry from scripts/smoke.ts. npm validates override targets as semver/package specs, so a file: tarball override can fail (e.g. Invalid comparator) and block the pre-publish smoke gate. The override was unnecessary: packPackage rewrites @tskm/vite's @tskm/compiler: workspace:* to ^<compiler version>, and the top-level @tskm/compiler file: tarball installs that exact version, which satisfies the ^ range — npm dedupes to it without reaching the registry, so transitive resolution already works. Added a comment explaining why no override is needed. Verified with bun run typecheck (tsgo, clean) and bun run lint (biome, clean). Committed locally; not pushed.
Files modified:
scripts/smoke.ts
ushironoko
left a comment
There was a problem hiding this comment.
[AI Rally - Reviewer]
The prior blocking issue is addressed. Removing the overrides entry avoids npm's override-target validation path, and the smoke test now correctly relies on the packed @tskm/vite dependency rewrite plus the top-level @tskm/compiler file tarball satisfying the resulting range. I did not find any new blocking issues in the updated diff. The PR is ready to merge, assuming the documented one-time npm/GitHub setup is completed before relying on the automated release path.
The smoke consumer carried an `overrides` entry pinning @tskm/compiler to the local file: tarball. It is redundant: the packed @tskm/vite has `@tskm/compiler: workspace:*` rewritten to a caret range, and because the three packages are released in lockstep at the same version, the top-level @tskm/compiler file: tarball already satisfies that range — npm dedupes to it without reaching the registry. Removing the override keeps the consumer minimal; verified the clean-room smoke still packs, installs, imports, runs the bin, and finds the worker file. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
CI-only, tokenless npm publishing for the packages, following current npm supply-chain best practices (researched + adversarially verified, then plan-reviewed).
Why
First-time publish setup. Given recent npm supply-chain attacks, the aim is to keep no long-lived publish token in CI and to attest every release with provenance.
What's in here
Publishing
bun publishsupports neither yet).release.ymlsplit into two jobs:version(Changesets PR, noid-token) andpublish(gated by a protectedreleaseEnvironment, withid-token: writescoped to that job only).scripts/publish.ts: ordered publish (compiler → tskm → vite), rewritesworkspace:*to a concrete range before packing and restores it, is fail-closed on registry errors, and asserts the protocol never leaks into the tarball. Unit-tested (scripts/publish.test.ts).scripts/smoke.ts: clean-room gate — packs each package, installs them with their real peers outside the workspace, and exercises the public entry points, the bin, and the spawned worker file.preview.yml, no npm token).Hardening
--ignore-scriptsinstalls plus a lockfile-integrity guard in CI.repositorymetadata and aLICENSEadded per package (provenance requiresrepository);publint/attwgates; initial version set.Verification
lint, typecheck, build, the full test suite, the publish unit tests,
publint,attw, the publish dry-run, the clean-room smoke test, andactionlintall pass locally.Follow-up (human, before first release)
One-time setup is documented in
docs/RELEASING.md: npm scope, the bootstrap publish, Trusted Publisher config (workflowrelease.yml), the pkg.pr.new GitHub App, branch protection onmain, and thereleaseEnvironment with required reviewers.🤖 Generated with Claude Code