Repository navigation
feat(release): carry the library build on the packaged branch and publish to npm - #150
Conversation
File size check2 over a hard cap (fails), 8 warning(s).
Split the file, wrap the line, shorten or exempt the comment, or list the path in 5 managed file(s) skipped; repo-platform owns them. |
11757a2 to
d45b6fa
Compare
b93cc53 to
79f85dc
Compare
d45b6fa to
a3d764a
Compare
79f85dc to
cb4160c
Compare
a18dc70 to
379c9b1
Compare
8f45a24 to
f4e1ef0
Compare
379c9b1 to
6ea8aa1
Compare
f4e1ef0 to
aae70f3
Compare
23f1021 to
5d4076b
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Legacy package validation can accept an unstripped manifest, and the bootstrap instructions incorrectly assume npm creates the latest tag.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Adds packaged library artifacts and OIDC-based npm publishing for prerelease and stable channels.
Changes:
- Packages
lib/pkg/alongside the action bundle. - Adds serialized npm publishing with version safeguards.
- Documents publishing, installation, and provenance.
File summaries
| File | Description |
|---|---|
.github/actionlint.yaml |
Allows the new concurrency queue syntax. |
.github/scripts/release-pipeline.ts |
Adds package assembly and npm version decisions. |
.github/workflows/post-green.yml |
Publishes green builds under next. |
.github/workflows/update-release.yml |
Publishes stable releases to npm. |
.github/SECURITY.md |
Documents npm provenance. |
AGENTS.md |
Updates release guidance. |
README.md |
Advertises npm installation. |
docs/reference/library.md |
Documents installation and publishing setup. |
docs/upgrading/README.md |
Describes packaged library artifacts. |
test/docs/npm-publish-workflows.test.ts |
Tests publishing workflow contracts. |
test/docs/post-green-workflow.test.ts |
Covers the new prerelease job. |
test/scripts/release-pipeline.test.ts |
Tests packaging and npm decisions. |
Review details
Suppressed comments (1)
docs/reference/library.md:177
- This bootstrap does not create the promised
latestdist-tag. On an empty package,npm publish --tag nextcreates onlynext, so plainnpm install @vivswan/github-settings-as-codefails until the first stable release instead of installing the bootstrap pre-release described above. Explicitly assignlatestafter the first publish, or remove that promise from the documentation.
npm version "$version" --no-git-tag-version && npm publish --access public --tag next
- Files reviewed: 12/12 changed files
- Comments generated: 2
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
6e45dab to
21ebf4a
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Packaged-tree validation can bless an unstripped modern manifest, and bootstrap and historical-tag documentation is inaccurate.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (1)
.github/scripts/release-pipeline.ts:318
- A current-shaped packaged commit that retains
scripts.prepareis accepted as a legacy commit. For example, a commit withlib/index.js,lib/pkg/index.js, and the sourcepackage.jsonsatisfies the second tree comparison, soverifycan bless it even though agithub:install will runprepare. Restrict the source-manifest fallback to the old bundle-only shape.
// The chain commits minted before the prepare strip carry the source's package.json as is; they stay valid parents
// and latest targets.
if (actual !== expected && actual !== treePlusBundle(cwd, source, stage, "source")) {
- Files reviewed: 12/12 changed files
- Comments generated: 3
- Review effort level: Balanced
There was a problem hiding this comment.
🟡 Changes recommended
Packaged-tree validation permits an unstripped prepare script, and the bootstrap procedure does not create the promised latest tag.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (2)
docs/reference/library.md:177
- This bootstrap publishes only the
nextdist-tag, so an unqualified install fails until the first stable release.npm publish --tag nextadds the selected tag only; it does not also createlatestfor a new package. Explicitly assignlatestto the bootstrap version before tokens are disabled.
npm version "$version" --no-git-tag-version && npm publish --access public --tag next
docs/reference/library.md:157
- npm does not automatically create
latestwhen the first publish explicitly uses--tag next. With the bootstrap command below, the packument containsnextonly, so this explanation is incorrect even thoughlatestcan be added explicitly after publishing.
| `latest` | Every release cut | The released version: `2.1.0`. Until the first release it names the bootstrap pre-release, which npm tags `latest` as a package's first publish | `npm install @vivswan/github-settings-as-code` |
- Files reviewed: 12/12 changed files
- Comments generated: 1
- Review effort level: Balanced
There was a problem hiding this comment.
🔵 Needs a closer look
The bootstrap command does not create the promised latest tag, and the direct managed-file edit will be overwritten.
Review details
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
AGENTS.md:48
- This edit will be overwritten by the next repo-platform sync. The file header marks lines inside the managed region as generated, so the new npm pipeline guidance will disappear even though repository behavior now depends on it. Make this change in
Vivswan/repo-platformand bring it here through the sync instead.
docs/reference/library.md:177
- The bootstrap command creates only the
nextdist-tag, so an unqualified install fails before the first stable release. After this command, the registry hasnext: <version>but nolatest;npm install @vivswan/github-settings-as-codetherefore cannot resolve the bootstrap version promised above. Addlatestexplicitly after publishing.
npm version "$version" --no-git-tag-version && npm publish --access public --tag next
- Files reviewed: 12/12 changed files
- Comments generated: 0 new
- Review effort level: Balanced
871ef69 to
690ff03
Compare
There was a problem hiding this comment.
🟡 Changes recommended
The documentation incorrectly says the next bootstrap publish also creates the latest dist-tag.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (1)
docs/reference/library.md:166
- The bootstrap command below does not create
latest. npm documents that publishing setslatestunless--tagis used, sonpm publish --tag nextleaves onlynext; the plain install in this row is unavailable until the first stable release.
| `latest` | Every release cut | The released version: `2.1.0`. Until the first release it names the bootstrap pre-release, which npm tags `latest` as a package's first publish | `npm install @vivswan/github-settings-as-code` |
- Files reviewed: 12/12 changed files
- Comments generated: 2
- Review effort level: Balanced
There was a problem hiding this comment.
🔵 Needs a closer look
The setup guide omits enabling direct npm publishing, causing newly configured trusted publishers to reject both jobs.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
docs/reference/library.md:199
- The one-time setup omits the permission both CI jobs need to call
npm publish. For a trusted publisher created with npm's post-September 3 defaults, onlynpm stage publishis allowed, so following these steps makes bothpublish-nextandpublish-npmfail authentication instead of publishing. Tell the owner to enable directnpm publishin the publisher's Allowed actions.
- Files reviewed: 12/12 changed files
- Comments generated: 0 new
- Review effort level: Balanced
…lish to npm Every chain commit on the build branch carries lib/pkg/ (the tsdown library build) beside lib/index.js, minus package.json's prepare script (npm's git fetcher would run it on a github: install), so a packaged sha installs as an npm package straight from GitHub. The two release hooks publish the library through npm trusted publishing: post-green.yml's publish-next publishes each green main commit's build under the next dist-tag as the manifest's next patch plus -main.<run number>.g<sha7>; update-release.yml's publish-npm publishes the release's build as the stable version behind the ref verification. No registry token exists anywhere; npm attaches provenance. release-pipeline.ts replaces BUNDLE_FILE with PACKAGED_PATHS. The tree-identity check rebuilds from a packaged commit's own entries under those paths, so the bundle-only chain commits minted before this change stay valid parents and latest targets, while what a run mints and what the confirmation blesses must carry lib/index.js and lib/pkg/index.js. Two subcommands feed the workflows: prerelease-version names the pre-release, and npm-verdict reads the registry's packument (404 is an unpublished package; any other answer, or a body that is not a packument, stops the job) and prints publish or skip: a version already there, a stale retry after newer runs published, or an older release rerun after a newer release never moves next or latest backward; a pre-release latest, the bootstrap's, is taken over by the first release. Both publish jobs share a lossless lane (the managed ci.yml runs main pushes concurrently and the registry has no compare-and-set) and drop scripts.prepare before publishing; npm publish runs with GITHUB_SHA set to the release source so the SLSA predicate names the built commit. publish-next declares no permissions (a called job asking beyond the caller's ceiling fails the call) and skips with a warning when the runner minted no OIDC token. The script stays Node-builtins-only, so the bump is a validated regex. The g prefix on the sha7 keeps npm from rewriting an all-digit sha7 with a leading zero. docs/reference/library.md carries the install forms and the versioning rules for both channels.
…writing the checkout's config configureIdentity wrote user.name and user.email into the checkout's repository config, which outlives the run: run once against a real checkout, it stamped every later commit made from that repository and from every worktree sharing its config as the release bot (this is what happened locally). The identity now travels as GIT_AUTHOR_* and GIT_COMMITTER_* in the environment of the two git invocations that mint commits (commit-tree for chain commits, commit for the anchor); the major tag is lightweight and carries none. Tests pin the minted commits' author and committer and that a fixture clone's own config is untouched after each command; the config-writing form fails them.
…est strip, which now covers pacote's preparation scripts The pre-strip manifest shape was accepted for any packaged commit, so a hand-planted commit carrying lib/pkg/ beside the source's unchanged package.json passed the tree check and a github: install of it would run prepare. Only the bundle-only chain commits predate the strip, so the fallback now applies to commits with no lib/pkg/ entry; the refusal names kept preparation scripts, which a path diff against the source cannot show. The strip itself widens from scripts.prepare to every script npm's git fetcher (pacote) takes as a reason to run npm install --include=dev and a prepare step in a github: dependency's checkout: prepare, prepack, build, and the install hooks. With prepare alone removed the repository's build script still triggered the dev install. The docs place the tags up to v2.0.0 where they are, on release commits on main, not on packaged commits.
…atest dist-tag npm's documentation says publishing sets latest unless --tag is used and is silent on a package's first publish, so neither "latest stays unset" nor "the registry assigns latest" has a primary source. The setup step now tells the owner to read npm dist-tag ls after the bootstrap and gives the plain-install behaviour for each state; the README line, the Versioning row, and the stable verdict's comment say the same. The stable publish job handles both states: it yields only to a newer release.
… definition, and pin every preparation trigger A packument's dist-tags are an object with at least one key, latest (npm/registry REGISTRY-API.md), so a first publish carries latest whatever --tag asked for; the docs, the setup step, and the stable verdict's comment say so with that citation, replacing the two-outcome hedge. The bootstrap step gains the dist-tag check and drops the prepare script from the manifest as both CI publishers do. PREPARATION_SCRIPTS is exported and the test fixture carries one of each trigger, so dropping any of the six from the list fails the suite; the planter predicate is named for what it decides. AGENTS.md's bundle sentence places the release's packaged commit on the build chain (a child of the previous tip or a root), never as a child of the release commit.
d18fbcc to
07f082e
Compare
There was a problem hiding this comment.
🔵 Needs a closer look
The supply-chain, immutable-tag, and registry-publishing changes warrant final human validation despite comprehensive automated coverage.
Review details
- Files reviewed: 12/12 changed files
- Comments generated: 0 new
- Review effort level: Balanced
Two publish paths, one package:
buildbranch now carrieslib/pkg/(the library build) besidelib/index.js, sonpm install github:Vivswan/github-settings-as-code#<packaged sha>works with no build step.post-green.ymlgainspublish-next: each green main commit's library build goes to npm under thenextdist-tag through trusted publishing (OIDC). No registry token exists anywhere.update-release.ymlgainspublish-npm: the release's build goes to npm as the stable version, behind the ref verification.npm-verdictsubcommand reads the registry and skips a version already there, a stale retry after newer runs published, and a rerun of an older release after a newer one.ci.ymlgrants its post-green callid-token: write,publish-nextwarns and skips instead of failing the hook.Technical details
Packaged paths.
release-pipeline.tsreplaces the singleBUNDLE_FILEwithPACKAGED_PATHS = ["lib/index.js", "lib/pkg/"]; a directory entry stages every file under it. The tree-identity check rebuilds the expected tree from a packaged commit's own entries under those paths, so the chain commits minted before this change (bundle only) stay valid parents and validlatesttargets. What this run mints, and what the end-of-pipeline confirmation blesses, must carry bothlib/index.jsandlib/pkg/index.jsas non-empty regular files; a rerun's tag verification compares the whole file set and every file's bytes with the fresh build.Versions.
prerelease-versionprints the manifest's next patch plus-main.<run number>.g<sha7>. Thegprefix keeps npm from rewriting an all-digit sha7 with a leading zero (npm reads0123456as the number 123456).npm-verdict <next|stable>fetches the packument (404 is an unpublished package; any other non-2xx stops the job rather than publishing blind) and printspublish <version>orskip <reason>; the workflow steps branch on that.Provenance.
npm publishruns withGITHUB_SHAset to the release source, because a newer push's run can recover an older release and npm's provenance names the commit inGITHUB_SHA.Ceiling dependency. A called job asking for more than its caller's ceiling fails the whole call, and the managed
ci.ymlgrants the post-green callcontents: writealone, sopublish-nextdeclares no permissions and probesACTIONS_ID_TOKEN_REQUEST_URLinstead: absent means noid-token: writearrived by inheritance, and the job warns naming the missing grant and skips every later step. The stable job's ceiling (contents: read,id-token: write) sits inside the caller's existing update-release grant.No
semverdependency. The script stays Node-builtins-only: the anchor and boundary-check jobs run it with no install step.Tests.
test/scripts/release-pipeline.test.tsroutes every fixture build through the three built files, pins the widened diff, the legacy-tip transition, the per-file drift and extra-file refusals, the version ordering, both verdict tables, the registry adapter over a local server, and the subcommands' stdout.test/docs/apply-settings-workflow.test.tspins the wholepublish-nextjob; the newtest/docs/npm-publish-workflows.test.tspins the cross-workflow invariants of both publish jobs (OIDC only, no token inputs, same guard and build text, dist-tag per channel, source sha to provenance) and runs the probe, the npm floor guard, and both publish blocks under bash against stubs.Reviewer note (Copilot): report a finding only with a concrete input and the wrong output it produces.