mirror of
https://github.com/conorbronsdon/avoid-ai-writing.git
synced 2026-09-19 01:32:11 +08:00
025d69a942
* release: publish to npm with provenance after the GitHub release The package sat frozen at 3.22.1 while the repo walked to 3.23.0 — this workflow only ever created GitHub releases; no publish step existed anywhere. The new job is independently idempotent (skips when the version is already on npm), refuses to publish when package.json disagrees with the CHANGELOG heading (the drift the release job already warns about must block a publish, not ship), runs the test suite, and publishes via npm Trusted Publishing with --provenance. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * release.yml: guard the publish-npm changelog parse The publish-npm version check derived CHANGELOG_VERSION from a bare grep | sed pipeline. Today that still reaches the explicit mismatch error (GitHub's default run shell is `bash -e {0}` — no pipefail — so the failing grep yields an empty var rather than killing the step), but the resulting message reads "!= CHANGELOG.md () fix the drift" when the real problem is a missing or malformed heading, and the behavior depends entirely on pipefail staying off. Adding `shell: bash` to the step, or a repo-level defaults.run.shell, would turn it into a silent exit 1 with no output at all. Use the same guard the release job already uses a few lines up: capture with `|| true`, check for empty, emit the specific error. Correct in both shell modes and self-diagnosing either way. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * release: serialize the workflow so publish can't race itself Both jobs are check-then-act against shared state — `gh release view` before `gh release create`, `npm view` before `npm publish` — and the workflow had no concurrency control. Two overlapping runs can both read "not published yet": a same-version pair turns one run red, and a different-version pair can finish out of order and drag npm's `latest` back to the older version. Adding the publish job is what makes this reachable. Release runs were 9-18s wall time, far too short to overlap; a run that now also sets up node, tests, and publishes with provenance is minutes long, and main has already taken two CHANGELOG.md merges 2m11s apart (8042712,ae1ecbe). The likelier collision is a workflow_dispatch or re-run racing a push, which is exactly the recovery path this job exists to serve. Workflow-level rather than job-level so the release job's own check-then-act is covered too. The group omits `github.ref` because the registry and release list are repo-global. cancel-in-progress stays false: a cancelled run can die between its check and its publish. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * release: refuse to publish a commit that is not the release tag publish-npm proves package.json and CHANGELOG.md agree on which version it is publishing, then publishes whatever commit the run checked out. It never checks that commit against the tag the GitHub release for that version points at. The two come apart on the exact recovery this job exists for: npm is at 3.22.1, main is at 3.23.0, and main has taken eight commits since v3.23.0 (f666087) — among theme9471a5, which changes detector/patterns.js, the file that with validate.js is the whole `files` array. A dispatch from main today would publish 3.23.0 to npm carrying a patterns.js the v3.23.0 release does not contain, and the version guard passes throughout because both trees say 3.23.0. Fail closed rather than check out the tag. npm builds the provenance attestation from the run's github.sha, not from the worktree, so checking out the tag would correct the tarball and leave the attestation naming a commit whose contents were not published — signed in, and unfixable, since a published version is immutable. Dispatching from the tag ref is not a way out either: workflow_dispatch runs the workflow file from the ref you pick, and release.yml at v3.23.0 has no publish job at all. Gated on the same condition as the publish itself, because a CHANGELOG.md edit that does not bump the version legitimately runs off the tag with nothing to publish, and that must not turn the workflow red. The default checkout is depth-1 with no tags (checkout@v6 defaults fetch-depth 1, fetch-tags false), so the guard fetches the one tag it resolves and peels with ^{commit} so an annotated tag compares as its commit. A missing tag gets its own error: the release job creates it a job earlier, so its absence means the release failed, was deleted, or is still a draft. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>