Repository navigation
fix: prioritize CLI flags over publishConfig settings - #7321
Merged
Merged
Conversation
Contributor
|
Welcome back! The new behavior is going to need tests. |
Contributor
Author
I added the tests |
wraithgar
reviewed
Apr 9, 2024
wraithgar
approved these changes
Apr 9, 2024
2 tasks done
Merged
bugdiver
added a commit
to get-enlace/enlace
that referenced
this pull request
Aug 23, 2026
--registry didn't work either — confirmed on the second real attempt, still tried GitHub Packages. This is a known npm bug (npm/cli#6400): publishConfig.registry can override even an explicit --registry flag. A fix landed upstream (npm/cli#7321) but this runner's npm version doesn't have it. Removing publishConfig.registry transiently before publish instead (npm pkg delete), reverted via git checkout right after, same pattern as enlace-js's dependency-pin revert.
inkeep-oss-sync Bot
pushed a commit
to inkeep/open-knowledge
that referenced
this pull request
Oct 5, 2026
* Publish OK's npm package from a job that runs no package code release.yml ran as one job. It installed the workspace, built it, ran the Changesets reader and then published, while holding id-token: write and a contents: write token that its checkout left in .git/config. Any installed package could mint the OIDC token npm Trusted Publishing accepts, or push with the write token. The workflow now runs three jobs. build holds only a read-only GITHUB_TOKEN. It installs, builds, computes the beta, overrides the versions, runs the cli's prepublishOnly, packs the cli with pnpm pack and uploads the tarball as the npm-package artifact. release installs nothing and holds contents: write. It checks out the workflow revision, resolves the beta from git itself and refuses one the build job packed differently, runs the reader gate, tags, creates the draft prerelease and dispatches desktop-release, handing the token to git for the one tag push. publish checks out nothing and holds only id-token: write. It installs npm 11 and downloads the tarball. It refuses a tarball that is not @inkeep/open-knowledge at the expected version, or whose publishConfig sets more than access, skips a version already on npm as changeset publish did, and runs npm publish on the tarball. pnpm pack is the packer pnpm publish uses, so the published bytes do not change. On a local build of the cli at 0.81.5-beta.0, the tarball changeset publish uploaded, the pnpm pack output and the tarball npm 11.21.0 uploaded from it all hash to sha256 33bd6d59. npm publish runs lifecycle scripts only for a directory (lib/commands/publish.js lines 100 and 223), and the trusted-publisher entry names the repository and the workflow file, not a job. A scratch repository showed that the build job cannot request an OIDC token and the publish job can, that the artifact arrives byte-identical, and that npm publish of the tarball fires none of seven planted lifecycle scripts. Triggers, the concurrency group, tags, the GitHub Release, the desktop-release dispatch and its payload, and every GITHUB_OUTPUT key are unchanged. New tests in release-cascade-shape.test.mjs pin the split, the values the credential jobs take from the build job, the release job's refusal and the publish step's behaviour. They also plant 13 mutations, each of which must introduce a violation the real workflow does not have. All 27 fail against the workflow at fe86e6d64a. Test replacements, per the OK test-deletion policy: - share-contract-reader-gates-shape.test.mjs drops "restores the local composite action and its inputs from the workflow revision". Kind: asserts something the product no longer promises. The restore step is gone because the job that runs the gate now checks out the workflow revision whole. It was meant to keep the stable path's reader gate on current machinery rather than the stable tag's. "runs the gate in the one job that checks out the workflow revision rather than the stable tag" pins that, and fails against fe86e6d64a. - scripts/check-ok-release-workflow.test.mjs drops "publishes via `pnpm exec changeset publish --tag` with provenance intact". Kind: asserts something the product no longer promises, since changeset publish would run workspace code in the job that holds the OIDC permission. It was meant to keep an explicit --tag and NPM_CONFIG_PROVENANCE on the publish step. "publishes the packed tarball via `npm publish --tag` with provenance intact" pins both, also refuses changeset publish there, and fails against fe86e6d64a. - Three tests change expected values only. The entry-point and Changesets-reader sweeps name release.yml#build instead of release.yml#release, and the release lane's reader-gate condition reads needs.build.outputs.run_beta. * Describe OK release recovery for the three-job release.yml release.yml now runs as build, release and publish, so RELEASES.md explains how to recover each failure without a new cut. When publish fails, "Re-run failed jobs" re-runs publish alone and publishes the tarball build packed. That works for 30 days, the artifact's retention and GitHub's re-run window. When release fails before it pushes the tag, the same re-run resolves the same beta and carries on. When it fails after the tag, the re-run refuses with "Re-run all jobs", and a full re-run cuts the next beta, leaving the pushed tag orphaned as a re-fire of the single job did. The publish-stable recovery section now offers the re-run before re-firing the whole workflow. The beta cadence steps also listed the npm publish before the desktop-release dispatch, and the workflow runs them the other way round. They now match it, and say that the prerelease is created as a draft. * Read the previous OK beta Release in a job that can see drafts Splitting release.yml gave the build job a read-only token, and compute-next-beta.mjs ran gh release list and gh release view with it. GitHub lists draft Releases only to callers with push access, and every beta Release stays a draft until desktop-release publishes it. So a beta cut while the previous one was still a draft diffed against the beta before it: the notes repeated the draft's changesets, and a push with no new changeset cut a beta instead of skipping. Since 2026-09-04 that window was hit in 26 of 154 consecutive betas. A new read-releases job runs first. It checks out nothing, installs nothing and holds contents: write, the narrowest permission that sees a draft. It runs the script's exact gh release list and gh release view queries and records their exit status and output as job outputs. build hands those to compute-next-beta.mjs, which now takes its gh results from a replay of that recording and no longer runs gh. The job always runs and gates only its step on the beta path, because a skipped job also skips every job that needs it through another, which would stop release and publish on a stable dispatch. Behaviour matches the single-job workflow. On a copy of the cli's real changesets, the old script with a write token and the new script fed by the job's own shell produced byte-identical output in six cases: the previous beta a draft with changesets left, a draft that consumed them all, a failed list, a failed view, a body without a marker and a garbled marker. A scratch repository showed the job's queries picking a real draft and matching the old path, while contents: read and read-all both missed it. New tests cover the warn-and-bootstrap cases, the replay, a CLI run that fails if the script calls gh, the job's shape and permission, the output names build reads, and the job's shell replayed through the workflow's own expressions in seven cases. All of them fail against 293eb12712. One existing test changes an expected value only: the release.yml job list now starts with read-releases. * Close the review findings on the OK npm publish split Review on #5571 raised four minor items and six to consider. This commit fixes nine and leaves one, narrowing the read-releases replay, as it was, so the warn lines and the stderr a failed gh list leaves stay byte-identical. The publish job now passes --provenance and --registry on the npm command line. npm lets a tarball's publishConfig override any option not given there (lib/commands/publish.js, npm/cli#7321), so provenance from an environment variable could be switched off from inside the tarball. npm unpacks with the first path component stripped (pacote strip: 1), so any dir/package.json could become the manifest it reads. The job now refuses a tarball unless every file is under package/ with exactly one package/package.json and no empty, . or .. segment, and it prints a refused manifest JSON-encoded so a newline in it cannot start a workflow command. It installs npm 11.21.0 exactly, and the reason it is not npm 12 is one UPSTREAM marker naming npm/cli#9722. The download-artifact pin comment now says v4.3.0, which is the tag that SHA resolves to. A recorded read-releases status that is missing, empty or not a number now fails the compute step with an error naming the handoff, instead of reading as a successful empty list and bootstrapping the cut. A real exit 0 or 1 behaves as before. The release job checks that the build job's base_version and version are well-formed before any step prints them. A new test runs the tag step's shell against a recording git and checks, with real git, that its one-command helper answers with GH_TOKEN and ignores a helper configured beforehand. Prose that still described the single job is trimmed or fixed in RELEASES.md, the release.yml header and promote-stable.yml, and the carried comment blocks in the new jobs are gone. The RELEASES.md row for release.yml is back to one line and links a new section on the four jobs and what each may hold. Test replacements, per the OK test-deletion policy: - scripts/check-ok-release-workflow.test.mjs keeps "publishes the packed tarball via npm publish --tag with provenance intact" but replaces its NPM_CONFIG_PROVENANCE assertion with one that requires --provenance and --registry on the publish command. Kind: asserts something the product no longer promises; provenance no longer comes from the environment. Moving it back to the env var turns the new assertion red. - release-cascade-shape.test.mjs renames "the step publishes with provenance and names the package it checks" to one that requires those flags on the only npm publish line and no NPM_CONFIG_PROVENANCE, for the same reason. Other changes there are expected values only: the refusal messages now quote the manifest as JSON, the publish argv carries the two flags, the exempted npm install names 11.21.0, and the release job's read list includes the new validation step. * Read the OK publish manifest with npm's own pacote The publish job checked the packed tarball's manifest with tar. GNU tar and bsdtar stop reading at a lone zero block, where node-tar, which npm uses, reads on. A tarball with a second package/package.json behind one zero block passed the check with the first manifest and published the second. The step now reads the manifest with the pacote bundled in the pinned npm, making the same pacote.manifest call that npm publish makes for a tarball (lib/commands/publish.js:298-302 in npm 11.21.0), so the manifest it checks is the one npm publishes. The tar layout check and the tar read are gone. A pacote error is printed JSON-encoded, and the step refuses. The tests build tarballs byte by byte, the lone zero block included, and run the step against the real pacote of the npm that runs them, with the proxies pointed at a dead port. Three rows pin fullReadJson: npm publishes a v-prefixed version, build metadata and a padded name in cleaned form, and the step checks that form. Also here: the release job's Resolve step drops the empty and bare-semver checks that the validation step before it makes unreachable, the npm install step is named for its exact pin, and two comments are deleted. Tests removed or replaced, under the OK test-deletion policy: - "the step refuses a file outside package/" is deleted. Kind: it asserts something the product no longer promises. The layout check it pinned is removed, and RELEASES.md now promises that the checked manifest is the one npm reads. Proof: the old step refuses its input and the new step publishes it, since the manifest npm reads is the expected one. It was meant to stop another directory's package.json from becoming the manifest. "a second manifest that npm would read instead" still covers that, and goes red when the read reverts to tar. - "the checked manifest twice" is replaced by "a later package/package.json that differs". Kind: it asserts something the product no longer promises. An identical duplicate entry is the same manifest npm publishes, and the new step publishes it. It was meant to stop a duplicate entry from changing the manifest; the replacement covers a duplicate that differs. - The newline test no longer asserts the JSON-encoded name in stdout, because pacote now refuses that name before the step's own check. It runs for name, version and publishConfig, and asserts that neither stream has a line starting ::warning:: and that one starts ::error::. - The Resolve test compared the build and release steps whole. It now compares them from MAX_N=-1 on and asserts that the release copy prints no ::error::, since that copy no longer carries the checks. GitOrigin-RevId: ec3369964b58a1aa82c52f43d1ba73ff110807b6
inkeep-oss-sync Bot
pushed a commit
to inkeep/open-knowledge
that referenced
this pull request
Oct 5, 2026
* Publish OK's npm package from a job that runs no package code release.yml ran as one job. It installed the workspace, built it, ran the Changesets reader and then published, while holding id-token: write and a contents: write token that its checkout left in .git/config. Any installed package could mint the OIDC token npm Trusted Publishing accepts, or push with the write token. The workflow now runs three jobs. build holds only a read-only GITHUB_TOKEN. It installs, builds, computes the beta, overrides the versions, runs the cli's prepublishOnly, packs the cli with pnpm pack and uploads the tarball as the npm-package artifact. release installs nothing and holds contents: write. It checks out the workflow revision, resolves the beta from git itself and refuses one the build job packed differently, runs the reader gate, tags, creates the draft prerelease and dispatches desktop-release, handing the token to git for the one tag push. publish checks out nothing and holds only id-token: write. It installs npm 11 and downloads the tarball. It refuses a tarball that is not @inkeep/open-knowledge at the expected version, or whose publishConfig sets more than access, skips a version already on npm as changeset publish did, and runs npm publish on the tarball. pnpm pack is the packer pnpm publish uses, so the published bytes do not change. On a local build of the cli at 0.81.5-beta.0, the tarball changeset publish uploaded, the pnpm pack output and the tarball npm 11.21.0 uploaded from it all hash to sha256 33bd6d59. npm publish runs lifecycle scripts only for a directory (lib/commands/publish.js lines 100 and 223), and the trusted-publisher entry names the repository and the workflow file, not a job. A scratch repository showed that the build job cannot request an OIDC token and the publish job can, that the artifact arrives byte-identical, and that npm publish of the tarball fires none of seven planted lifecycle scripts. Triggers, the concurrency group, tags, the GitHub Release, the desktop-release dispatch and its payload, and every GITHUB_OUTPUT key are unchanged. New tests in release-cascade-shape.test.mjs pin the split, the values the credential jobs take from the build job, the release job's refusal and the publish step's behaviour. They also plant 13 mutations, each of which must introduce a violation the real workflow does not have. All 27 fail against the workflow at fe86e6d64a. Test replacements, per the OK test-deletion policy: - share-contract-reader-gates-shape.test.mjs drops "restores the local composite action and its inputs from the workflow revision". Kind: asserts something the product no longer promises. The restore step is gone because the job that runs the gate now checks out the workflow revision whole. It was meant to keep the stable path's reader gate on current machinery rather than the stable tag's. "runs the gate in the one job that checks out the workflow revision rather than the stable tag" pins that, and fails against fe86e6d64a. - scripts/check-ok-release-workflow.test.mjs drops "publishes via `pnpm exec changeset publish --tag` with provenance intact". Kind: asserts something the product no longer promises, since changeset publish would run workspace code in the job that holds the OIDC permission. It was meant to keep an explicit --tag and NPM_CONFIG_PROVENANCE on the publish step. "publishes the packed tarball via `npm publish --tag` with provenance intact" pins both, also refuses changeset publish there, and fails against fe86e6d64a. - Three tests change expected values only. The entry-point and Changesets-reader sweeps name release.yml#build instead of release.yml#release, and the release lane's reader-gate condition reads needs.build.outputs.run_beta. * Describe OK release recovery for the three-job release.yml release.yml now runs as build, release and publish, so RELEASES.md explains how to recover each failure without a new cut. When publish fails, "Re-run failed jobs" re-runs publish alone and publishes the tarball build packed. That works for 30 days, the artifact's retention and GitHub's re-run window. When release fails before it pushes the tag, the same re-run resolves the same beta and carries on. When it fails after the tag, the re-run refuses with "Re-run all jobs", and a full re-run cuts the next beta, leaving the pushed tag orphaned as a re-fire of the single job did. The publish-stable recovery section now offers the re-run before re-firing the whole workflow. The beta cadence steps also listed the npm publish before the desktop-release dispatch, and the workflow runs them the other way round. They now match it, and say that the prerelease is created as a draft. * Read the previous OK beta Release in a job that can see drafts Splitting release.yml gave the build job a read-only token, and compute-next-beta.mjs ran gh release list and gh release view with it. GitHub lists draft Releases only to callers with push access, and every beta Release stays a draft until desktop-release publishes it. So a beta cut while the previous one was still a draft diffed against the beta before it: the notes repeated the draft's changesets, and a push with no new changeset cut a beta instead of skipping. Since 2026-09-04 that window was hit in 26 of 154 consecutive betas. A new read-releases job runs first. It checks out nothing, installs nothing and holds contents: write, the narrowest permission that sees a draft. It runs the script's exact gh release list and gh release view queries and records their exit status and output as job outputs. build hands those to compute-next-beta.mjs, which now takes its gh results from a replay of that recording and no longer runs gh. The job always runs and gates only its step on the beta path, because a skipped job also skips every job that needs it through another, which would stop release and publish on a stable dispatch. Behaviour matches the single-job workflow. On a copy of the cli's real changesets, the old script with a write token and the new script fed by the job's own shell produced byte-identical output in six cases: the previous beta a draft with changesets left, a draft that consumed them all, a failed list, a failed view, a body without a marker and a garbled marker. A scratch repository showed the job's queries picking a real draft and matching the old path, while contents: read and read-all both missed it. New tests cover the warn-and-bootstrap cases, the replay, a CLI run that fails if the script calls gh, the job's shape and permission, the output names build reads, and the job's shell replayed through the workflow's own expressions in seven cases. All of them fail against 293eb12712. One existing test changes an expected value only: the release.yml job list now starts with read-releases. * Close the review findings on the OK npm publish split Review on #5571 raised four minor items and six to consider. This commit fixes nine and leaves one, narrowing the read-releases replay, as it was, so the warn lines and the stderr a failed gh list leaves stay byte-identical. The publish job now passes --provenance and --registry on the npm command line. npm lets a tarball's publishConfig override any option not given there (lib/commands/publish.js, npm/cli#7321), so provenance from an environment variable could be switched off from inside the tarball. npm unpacks with the first path component stripped (pacote strip: 1), so any dir/package.json could become the manifest it reads. The job now refuses a tarball unless every file is under package/ with exactly one package/package.json and no empty, . or .. segment, and it prints a refused manifest JSON-encoded so a newline in it cannot start a workflow command. It installs npm 11.21.0 exactly, and the reason it is not npm 12 is one UPSTREAM marker naming npm/cli#9722. The download-artifact pin comment now says v4.3.0, which is the tag that SHA resolves to. A recorded read-releases status that is missing, empty or not a number now fails the compute step with an error naming the handoff, instead of reading as a successful empty list and bootstrapping the cut. A real exit 0 or 1 behaves as before. The release job checks that the build job's base_version and version are well-formed before any step prints them. A new test runs the tag step's shell against a recording git and checks, with real git, that its one-command helper answers with GH_TOKEN and ignores a helper configured beforehand. Prose that still described the single job is trimmed or fixed in RELEASES.md, the release.yml header and promote-stable.yml, and the carried comment blocks in the new jobs are gone. The RELEASES.md row for release.yml is back to one line and links a new section on the four jobs and what each may hold. Test replacements, per the OK test-deletion policy: - scripts/check-ok-release-workflow.test.mjs keeps "publishes the packed tarball via npm publish --tag with provenance intact" but replaces its NPM_CONFIG_PROVENANCE assertion with one that requires --provenance and --registry on the publish command. Kind: asserts something the product no longer promises; provenance no longer comes from the environment. Moving it back to the env var turns the new assertion red. - release-cascade-shape.test.mjs renames "the step publishes with provenance and names the package it checks" to one that requires those flags on the only npm publish line and no NPM_CONFIG_PROVENANCE, for the same reason. Other changes there are expected values only: the refusal messages now quote the manifest as JSON, the publish argv carries the two flags, the exempted npm install names 11.21.0, and the release job's read list includes the new validation step. * Read the OK publish manifest with npm's own pacote The publish job checked the packed tarball's manifest with tar. GNU tar and bsdtar stop reading at a lone zero block, where node-tar, which npm uses, reads on. A tarball with a second package/package.json behind one zero block passed the check with the first manifest and published the second. The step now reads the manifest with the pacote bundled in the pinned npm, making the same pacote.manifest call that npm publish makes for a tarball (lib/commands/publish.js:298-302 in npm 11.21.0), so the manifest it checks is the one npm publishes. The tar layout check and the tar read are gone. A pacote error is printed JSON-encoded, and the step refuses. The tests build tarballs byte by byte, the lone zero block included, and run the step against the real pacote of the npm that runs them, with the proxies pointed at a dead port. Three rows pin fullReadJson: npm publishes a v-prefixed version, build metadata and a padded name in cleaned form, and the step checks that form. Also here: the release job's Resolve step drops the empty and bare-semver checks that the validation step before it makes unreachable, the npm install step is named for its exact pin, and two comments are deleted. Tests removed or replaced, under the OK test-deletion policy: - "the step refuses a file outside package/" is deleted. Kind: it asserts something the product no longer promises. The layout check it pinned is removed, and RELEASES.md now promises that the checked manifest is the one npm reads. Proof: the old step refuses its input and the new step publishes it, since the manifest npm reads is the expected one. It was meant to stop another directory's package.json from becoming the manifest. "a second manifest that npm would read instead" still covers that, and goes red when the read reverts to tar. - "the checked manifest twice" is replaced by "a later package/package.json that differs". Kind: it asserts something the product no longer promises. An identical duplicate entry is the same manifest npm publishes, and the new step publishes it. It was meant to stop a duplicate entry from changing the manifest; the replacement covers a duplicate that differs. - The newline test no longer asserts the JSON-encoded name in stdout, because pacote now refuses that name before the step's own check. It runs for name, version and publishConfig, and asserts that neither stream has a line starting ::warning:: and that one starts ::error::. - The Resolve test compared the build and release steps whole. It now compares them from MAX_N=-1 on and asserts that the release copy prints no ::error::, since that copy no longer carries the checks. GitOrigin-RevId: ec3369964b58a1aa82c52f43d1ba73ff110807b6
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR addresses an issue where CLI flags were not taking precedence over publishConfig settings. To ensure CLI flags have higher priority, properties from the publishConfig object that also exist in CLI flags are filtered out.
Related to #6400