Skip to content

feat(release): publish to npm from the tag, with provenance - #157

Merged
arthurpanhku merged 1 commit into
mainfrom
feat/release-npm-publish
Aug 8, 2026
Merged

feat(release): publish to npm from the tag, with provenance#157
arthurpanhku merged 1 commit into
mainfrom
feat/release-npm-publish

Conversation

@arthurpanhku

Copy link
Copy Markdown
Owner

Summary

Publishing to npm was a manual step, and it was already missed once: v0.14.1 was tagged and had a GitHub Release, but was never published to npm, so npx dvalincode served 0.14.0 for a month until it happened to get checked during the 0.15.0 release.

That used to be an inconvenience. Now that the README leads with npx dvalincode dvalin ., a missed publish means the first command on the front page is broken — so the hand-off moves into the workflow.

Pushing a version tag now builds, produces the Evidence Pack, attests, publishes the GitHub Release, and publishes to npm.

Guards

Two, both of which map to things that actually happened this week:

  • Tag must match package.json. A mismatch fails loudly rather than publishing a version no release points at. Relevant because the version lives in two separate constants (package.json and src/version.ts) — src/version.ts was still on 0.14.1 during the 0.15.0 prep and would have shipped a CLI that misreported itself.
  • An already-published version is skipped with a notice, not a failure. Re-running a release must stay green. Moving the v0.15.0 tag today would have hit exactly this case, since 0.15.0 was already on the registry.

--provenance ties the published tarball to the workflow run, giving the npm artifact the same traceability the release archives get from the attest job. prepublishOnly still re-runs npm run build && npm run check before anything leaves the runner.

Every publishing job is tag-gated (startsWith(github.ref, 'refs/tags/')), so workflow_dispatch remains a safe dry run.

Testing

The two guard conditions were exercised against the live registry before wiring them in:

case result
npm view dvalincode@0.15.0 (published) found → publish skipped
npm view dvalincode@9.9.9 (absent) not found → publish proceeds
tag v0.15.0 vs package.json 0.15.0 match → proceeds
tag v0.99.0 vs package.json 0.15.0 mismatch → fails

Workflow YAML parses; all four jobs and their if gates confirmed.

The publish path itself cannot be exercised without the secret and a real tag — the first live run will be the next release.

Security and AI Governance

  • This change does not expand file, shell, network, model, or approval permissions.
  • If it changes agent behavior, prompts, policy, providers, audit logging, or release/build security, I updated the relevant governance evidence in docs/.
  • If it introduces a new model/provider/tool or new data flow, I completed docs/governance/AI-CHANGE-IMPACT-ASSESSMENT.md.

Second box: this is release security. The workflow header documented a flow where npm was a human's job to remember, and that comment is now corrected in the same commit; no separate evidence doc covers the release runbook.

Third box, deliberately unticked: this does add an outbound flow — the release pipeline now pushes a tarball to registry.npmjs.org — and grants id-token: write to the new job for provenance. It involves no model, provider, or agent tool, so the assessment template does not fit, but a reviewer should weigh those two facts on their own terms.

Notes — action required before this helps

Add an NPM_TOKEN repository secret (Settings → Secrets and variables → Actions). It must be an npm Automation token; Automation tokens bypass 2FA on publish, which a CI publish requires.

Until the secret exists, the npm job will fail on the next tag while everything else still succeeds.

Unrelated but urgent: the token intended for this was pasted in plaintext into a chat transcript. It should be revoked on npmjs.com and replaced with a freshly generated one before being added as a secret.

npm drifted from the git tags because publishing was a manual step: v0.14.1
was tagged and had a GitHub Release, but was never pushed to npm, so
`npx dvalincode` served 0.14.0 until someone happened to check. The README
now leads with `npx dvalincode dvalin .`, which makes that drift a
user-visible break rather than an inconvenience.

Pushing a version tag now publishes to npm as well. Two guards:

- The tag must match package.json, so a mismatched version fails loudly
  instead of publishing something no release points at.
- A version already on the registry is skipped with a notice rather than
  failing the build, so re-running a release (a moved tag, a retried job)
  stays green. Moving the v0.15.0 tag today would have hit exactly this.

`--provenance` ties the published tarball to the workflow run, giving the
npm artifact the same traceability the release archives get from the attest
job. `prepublishOnly` still re-runs the build and full check first.

Every publishing job is tag-gated, so `workflow_dispatch` remains a safe
dry run. The header comment is updated — it described a flow where npm was
someone's job to remember.

Requires an NPM_TOKEN repository secret.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@arthurpanhku arthurpanhku mentioned this pull request Aug 8, 2026
3 tasks
@arthurpanhku
arthurpanhku merged commit 0bc8ec7 into main Aug 8, 2026
12 checks passed
@arthurpanhku
arthurpanhku deleted the feat/release-npm-publish branch August 8, 2026 01:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant