Skip to content

Merge v4-beta to v4 (2026-10-20) - #220

Merged
mike-ainsel merged 4 commits into
v4from
merge-2026-10-20
Oct 20, 2026
Merged

mike-ainsel merged 4 commits into
v4from
merge-2026-10-20

Conversation

@mike-ainsel

@mike-ainsel mike-ainsel commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

Merge v4-beta into v4

RetriggerView in GreptileConfidence Score: 1/5

Fix the release-blocking lookup, registry, and Node runtime changes before merging.

Fix All in Claude CodeFindings

  1. P1 Normal version bumps get rejected ▶
  2. P1 npmjs preparation gets skipped ▶
  3. P1 Publish scripts change runtime ▶
  4. P1 Custom registries stop working ▶
  5. P2 Trust recovery lacks coverage ▶
Fix with agent prompt
### Issue 1
actions/node/npm-split-publish/src/registry.ts:59-60
`versionState` treats every `E404` from `npm view name@version version` as a missing package. npm also returns `E404` when the package exists but that exact version does not. The new gate in `packWorkspace` therefore rejects normal npmjs releases with instructions to publish the first version by hand.

After a version lookup returns `E404`, query the package without the version before returning `new-package`. Add a test that distinguishes these two failures.

### Issue 2
actions/node/npm-split-publish/src/repository.ts:116-124
`prepareRepository` ignores scope settings in `.npmrc`. If a caller uses `npmrc-config` to send a package to npmjs while leaving `registry-url` at its GitHub Packages default, this function returns `other-registry`. It never adds the missing `repository` field, so the following npmjs upload can still fail the provenance check this action was added to fix.

Resolve the package's registry from npm's effective settings, as `resolveRegistry` already does.

### Issue 3
actions/node/prepare-trusted-publishing/action.yaml:53-57
This action can switch Node from the caller's chosen version to Node 24 after dependencies have been installed and the package has been built. Callers then run `npm publish` lifecycle scripts or `publish-script-name` against those same dependencies. If a script loads a native module built for Node 20, it will fail under Node 24. This also affects GitHub Packages uploads, which do not need npm trusted publishing.

Choose the publishing Node version before installing dependencies, or keep the caller's runtime for non-npmjs uploads.

### Issue 4
.github/workflows/node-matrix-pnpm.yaml:945-951
Replacing `pnpm -r publish` with `publish-tarballs` removes custom-registry support here and in `node-docker-simple-fast-pnpm.yaml`. If a caller supplies a custom registry through `npmrc-config`, packing still accepts it, but `npmrcFor` rejects every host except npmjs and GitHub Packages. The release fails after its prepublish scripts have already run.

Preserve that upload path, or reject the unsupported configuration before packing and document the changed input contract.

### Issue 5
scripts/npm-trust-setup.sh:88-91
The new `EOTP` recovery depends on a terminal call succeeding and a second captured call returning usable JSON. The existing test covers listing failure, but not that successful recovery sequence. A broken retry could therefore pass the tests and leave operators unable to finish setup.

Make terminal access replaceable in tests, or use a test terminal. Check that the retry preserves returned configurations and failure status.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

This PR moves two pnpm workflows to separate packing and uploading steps. It also adds preparation for direct npm trusted publishing and browser approval recovery to the trust setup script.

  • Workspace releases pack packages before publishing their tarballs.
  • Direct npm publish jobs prepare for trusted publishing.
  • The trust setup script can retry npm lookups after terminal approval.

Important touched terms:

  • VersionState: New type describing whether a package version is published, new, or belongs to a missing package. packWorkspace now uses it to stop first-time npmjs uploads.
  • PrepareResult: New type describing whether preparation skipped another registry or private repository, added metadata, or found matching metadata.
  • RepositoryState: Existing type describing missing, matching, or mismatched repository metadata. The new ensureRepository also uses it.
  • PackageJson: Existing type for package fields used by publishing. The new preparation code reads its repository and publishConfig fields; its shape is unchanged.
  • ManifestEntry: Existing type for one packed package and its upload settings. The README now documents its shared file format; its shape is unchanged.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Release ready] --> B{Publishing path}
  B -->|pnpm workflows| C[Check package versions]
  C --> D[Run prepublish scripts and pack]
  D --> E[Write manifest and tarballs]
  E --> F[Upload tarballs with npm]
  B -->|Direct npm workflows| G[Prepare Node and npm]
  G --> H[Check or add repository metadata]
  H --> I[Run npm publish or custom script]
Loading

Reviews (1) · Last reviewed commit: "MILAB-7081: trusted publishing for node-..." · Reviewed by Greptile

…ishing

MILAB-7081: npm trusted publishing (OIDC) for node-simple-pnpm
…e-docker-simple-fast-pnpm, node-simple, java-gradle (#219)

* MILAB-7081: trusted publishing for the remaining shared node workflows

npm no longer accepts 2FA-bypass tokens for direct publish. These workflows
already publish from GitHub-hosted runners with id-token: write, so they
need no split:

- node-matrix-pnpm, node-docker-simple-fast-pnpm: pnpm -r publish via
  changesets/action is replaced by pack-unpublished + publish-tarballs in
  the same job. node-docker-simple-fast-pnpm keeps changesets/action for
  the version PR and publishes only when no changesets are pending.
- node-matrix, node-simple, java-gradle (blocks/node/build-and-publish):
  new actions/node/prepare-trusted-publishing before `npm publish`: npm
  11.5.1+ and, in public repositories, a package.json `repository` naming
  the repository (npm auto-enables provenance; the registry rejects it
  otherwise). The token stays as the fallback npm uses if OIDC fails.
- npm-trust-setup.sh: npm prompts for 2FA only on a terminal; on EOTP the
  list call is re-run on /dev/tty so the browser approval appears.

* MILAB-7081: address independent review of the shared-workflow change

- pack refuses packages registry.npmjs.org does not have yet, before any
  script runs: without a token their first version needs a manual publish
  (npm only accepts a trusted publisher for an existing package). Avoids a
  partial release that keeps failing at upload.
- prepare-trusted-publishing: switch to Node 24 when the job's Node is older
  than 22.14 (npm documents 22.14+ for trusted publishing); leave package.json
  alone for registries other than npmjs (publishConfig.registry wins), via a
  tested prepareRepository.
- README: manifest.jsonl documented as a public contract (pframes-rs writes
  it); new-package rule.
@mike-ainsel
mike-ainsel merged commit 3fb8a4e into v4 Oct 20, 2026
2 checks passed
Comment on lines 59 to +60
if (/\bE404\b/.test(result.stderr + result.stdout)) {
return false;
return 'new-package';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Normal version bumps get rejected

versionState treats every E404 from npm view name@version version as a missing package. npm also returns E404 when the package exists but that exact version does not. The new gate in packWorkspace therefore rejects normal npmjs releases with instructions to publish the first version by hand.

After a version lookup returns E404, query the package without the version before returning new-package. Add a test that distinguishes these two failures.

Prompt To Fix With AI
This is a comment left during a code review.
Path: actions/node/npm-split-publish/src/registry.ts
Line: 59-60

Comment:
**Normal version bumps get rejected**

`versionState` treats every `E404` from `npm view name@version version` as a missing package. npm also returns `E404` when the package exists but that exact version does not. The new gate in `packWorkspace` therefore rejects normal npmjs releases with instructions to publish the first version by hand.

After a version lookup returns `E404`, query the package without the version before returning `new-package`. Add a test that distinguishes these two failures.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code

Comment on lines +116 to +124
const manifest = JSON.parse(fs.readFileSync(path.join(options.pkgDir, 'package.json'), 'utf8')) as PackageJson;
const registry = manifest.publishConfig?.registry || options.registry || 'https://registry.npmjs.org/';
let host = '';
try {
host = new URL(registry).hostname;
} catch {
/* not a URL: treated as another registry */
}
if (host !== 'registry.npmjs.org') return 'other-registry';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 npmjs preparation gets skipped

prepareRepository ignores scope settings in .npmrc. If a caller uses npmrc-config to send a package to npmjs while leaving registry-url at its GitHub Packages default, this function returns other-registry. It never adds the missing repository field, so the following npmjs upload can still fail the provenance check this action was added to fix.

Resolve the package's registry from npm's effective settings, as resolveRegistry already does.

Prompt To Fix With AI
This is a comment left during a code review.
Path: actions/node/npm-split-publish/src/repository.ts
Line: 116-124

Comment:
**npmjs preparation gets skipped**

`prepareRepository` ignores scope settings in `.npmrc`. If a caller uses `npmrc-config` to send a package to npmjs while leaving `registry-url` at its GitHub Packages default, this function returns `other-registry`. It never adds the missing `repository` field, so the following npmjs upload can still fail the provenance check this action was added to fix.

Resolve the package's registry from npm's effective settings, as `resolveRegistry` already does.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code

Comment on lines +53 to +57
- name: Node for trusted publishing
if: steps.node.outputs.too-old == 'true'
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Publish scripts change runtime

This action can switch Node from the caller's chosen version to Node 24 after dependencies have been installed and the package has been built. Callers then run npm publish lifecycle scripts or publish-script-name against those same dependencies. If a script loads a native module built for Node 20, it will fail under Node 24. This also affects GitHub Packages uploads, which do not need npm trusted publishing.

Choose the publishing Node version before installing dependencies, or keep the caller's runtime for non-npmjs uploads.

Prompt To Fix With AI
This is a comment left during a code review.
Path: actions/node/prepare-trusted-publishing/action.yaml
Line: 53-57

Comment:
**Publish scripts change runtime**

This action can switch Node from the caller's chosen version to Node 24 after dependencies have been installed and the package has been built. Callers then run `npm publish` lifecycle scripts or `publish-script-name` against those same dependencies. If a script loads a native module built for Node 20, it will fail under Node 24. This also affects GitHub Packages uploads, which do not need npm trusted publishing.

Choose the publishing Node version before installing dependencies, or keep the caller's runtime for non-npmjs uploads.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code

Comment on lines +945 to +951
- name: Publish npm packages
if: steps.npm-pack.outputs.count != '' && steps.npm-pack.outputs.count != '0'
uses: milaboratory/github-ci/actions/node/publish-tarballs@v4
with:
publish: pnpm -r publish --no-git-checks --access public
dir: ${{ steps.npm-pack.outputs.dir }}
access-public: ${{ inputs.publish-to-public }}
npm-auth-token: ${{ secrets.GITHUB_TOKEN }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Custom registries stop working

Replacing pnpm -r publish with publish-tarballs removes custom-registry support here and in node-docker-simple-fast-pnpm.yaml. If a caller supplies a custom registry through npmrc-config, packing still accepts it, but npmrcFor rejects every host except npmjs and GitHub Packages. The release fails after its prepublish scripts have already run.

Preserve that upload path, or reject the unsupported configuration before packing and document the changed input contract.

Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/node-matrix-pnpm.yaml
Line: 945-951

Comment:
**Custom registries stop working**

Replacing `pnpm -r publish` with `publish-tarballs` removes custom-registry support here and in `node-docker-simple-fast-pnpm.yaml`. If a caller supplies a custom registry through `npmrc-config`, packing still accepts it, but `npmrcFor` rejects every host except npmjs and GitHub Packages. The release fails after its prepublish scripts have already run.

Preserve that upload path, or reject the unsupported configuration before packing and document the changed input contract.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code

Comment on lines +88 to +91
if [ "${rc}" -ne 0 ] && grep -q 'EOTP' "${err}" && authenticate "${pkg}"; then
# Approved in the browser; the 2FA skip window covers the retry.
rc=0
out="$(npm trust list "${pkg}" --json 2>"${err}")" || rc=$?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Trust recovery lacks coverage

The new EOTP recovery depends on a terminal call succeeding and a second captured call returning usable JSON. The existing test covers listing failure, but not that successful recovery sequence. A broken retry could therefore pass the tests and leave operators unable to finish setup.

Make terminal access replaceable in tests, or use a test terminal. Check that the retry preserves returned configurations and failure status.

Prompt To Fix With AI
This is a comment left during a code review.
Path: scripts/npm-trust-setup.sh
Line: 88-91

Comment:
**Trust recovery lacks coverage**

The new `EOTP` recovery depends on a terminal call succeeding and a second captured call returning usable JSON. The existing test covers listing failure, but not that successful recovery sequence. A broken retry could therefore pass the tests and leave operators unable to finish setup.

Make terminal access replaceable in tests, or use a test terminal. Check that the retry preserves returned configurations and failure status.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code

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