fix(release): use trusted publishing npm for bundled packages (#10030)

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Paperclip publishes canary and stable packages through a shared
release script
> - GitHub Actions authenticates those publishes through npm trusted
publishing and an OIDC identity token
> - Bundled-dependency packages recently moved from pnpm publish to a
pinned npm CLI to preserve their bundled files
> - That pin selected npm 10, which cannot use trusted publishing, so
the first bundled package failed with `ENEEDAUTH`
> - This pull request keeps bundled-package packing on npm 10 while
routing actual publishing through the trusted-publishing-capable npm 11
version
> - The benefit is bundled packages keep their required npm packaging
behavior while canary and stable releases authenticate successfully

## Linked Issues or Issue Description

**What happened**

The canary release job failed while publishing
`@paperclipai/adapter-utils` with `ENEEDAUTH`. The package has bundled
dependencies, so the release helper selected pinned `npm@10.9.7`; the
workflow provides OIDC trusted publishing rather than an npm token, and
npm 10 cannot use that authentication path. Because this is the first
package attempted, the release exited before trying the remaining
packages.

**Expected behavior**

Bundled-dependency packages should publish with an npm CLI that both
preserves bundled dependencies and supports GitHub Actions trusted
publishing.

**Steps to reproduce**

Run the canary release workflow from master after PR #9980. The
`publish_canary` job reaches `@paperclipai/adapter-utils`, invokes `npx
npm@10.9.7 publish`, and fails with `ENEEDAUTH`.

**Deployment mode**

GitHub Actions canary and stable npm release workflows.

Refs #9980.

## What Changed

- Kept bundled-package dry-run packing on npm `10.9.7`, which
successfully produces the staged tarball.
- Routed bundled-package publishing through npm `11.16.0`, which
supports GitHub Actions trusted publishing.
- Split the pack and publish helpers so future npm changes cannot
silently couple the two compatibility requirements.
- Updated focused release and ACPX packaging tests to enforce both
versions and call paths.

## Verification

- `pnpm test:release-registry` — 67 passed locally.
- Initial all-npm-11 PR head: Canary Dry Run reproduced an npm-internal
crash during bundled `pack`.
- Current head `5b3961ed13dd26ed2d6b1096ea23fd91b32e4353`: Canary Dry
Run passed with split npm pack/publish helpers.
- All PR checks passed, including build, typecheck + release registry,
server/workspace suites, both e2e shards, security gates, and Greptile.
- Greptile reviewed the current head at 5/5 confidence with no blocking
issues.

## Risks

- Low risk: the change only separates the npm CLI used for
bundled-package packing from the CLI used for publishing.
- The versions remain explicitly pinned because npm 11.16.0 currently
crashes on the bundled pack payload, while npm 10.9.7 cannot perform
trusted publishing.
- Focused tests assert both pins and both helper call paths, and the
full Canary Dry Run passes on the current head.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.

## Model Used

- OpenAI Codex, exact model ID `gpt-5.6-sol`, high reasoning mode, with
repository, shell, GitHub CLI, and code-execution tools.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Dotta 2026-07-22 13:19:15 -05:00 committed by GitHub
parent e1e35881af
commit 47c38777d8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 19 additions and 12 deletions

View File

@ -34,7 +34,9 @@ test("bundled package staging materializes publishConfig entrypoints", () => {
});
test("bundled package dry runs preview without querying published versions", () => {
assert.match(releaseScript, /run_bundled_npm pack --pack-destination "\$publish_dir"/);
assert.match(releaseLib, /BUNDLED_NPM_VERSION="10\.9\.7"/);
assert.match(releaseLib, /npx --yes "npm@\$BUNDLED_NPM_VERSION"/);
assert.match(releaseScript, /run_bundled_npm_pack pack --pack-destination "\$publish_dir"/);
assert.match(releaseLib, /BUNDLED_NPM_PACK_VERSION="10\.9\.7"/);
assert.match(releaseLib, /BUNDLED_NPM_PUBLISH_VERSION="11\.16\.0"/);
assert.match(releaseLib, /npx --yes "npm@\$BUNDLED_NPM_PACK_VERSION"/);
assert.match(releaseLib, /npx --yes "npm@\$BUNDLED_NPM_PUBLISH_VERSION"/);
});

View File

@ -310,10 +310,15 @@ package_publish_tool() {
'
}
BUNDLED_NPM_VERSION="10.9.7"
BUNDLED_NPM_PACK_VERSION="10.9.7"
BUNDLED_NPM_PUBLISH_VERSION="11.16.0"
run_bundled_npm() {
npx --yes "npm@$BUNDLED_NPM_VERSION" "$@"
run_bundled_npm_pack() {
npx --yes "npm@$BUNDLED_NPM_PACK_VERSION" "$@"
}
run_bundled_npm_publish() {
npx --yes "npm@$BUNDLED_NPM_PUBLISH_VERSION" "$@"
}
run_package_publish() {
@ -323,9 +328,9 @@ run_package_publish() {
if [ "$publish_tool" = "npm" ]; then
if [ "$disable_provenance" = "true" ]; then
run_bundled_npm publish --tag "$dist_tag" --access public --provenance=false
run_bundled_npm_publish publish --tag "$dist_tag" --access public --provenance=false
else
run_bundled_npm publish --tag "$dist_tag" --access public
run_bundled_npm_publish publish --tag "$dist_tag" --access public
fi
return
fi

View File

@ -91,7 +91,7 @@ exit 1
set -euo pipefail
printf 'npx %s\n' "$*" >> "$FAKE_CALL_LOG"
[ "$1" = "--yes" ] && shift
[ "$1" = "npm@10.9.7" ] && shift
[ "$1" = "npm@11.16.0" ] && shift
exec npm "$@"
`,
);
@ -141,11 +141,11 @@ test("publish_package_to_npm returns after a successful pnpm publish", () => {
assert.doesNotMatch(result.calls, /--provenance=false/);
});
test("publish_package_to_npm uses npm for bundled dependencies", () => {
test("publish_package_to_npm uses trusted-publishing-capable npm for bundled dependencies", () => {
const result = runPublishHelper({ pnpmMode: "success", publishTool: "npm" });
assert.equal(result.status, 0);
assert.match(result.calls, /^npx --yes npm@10\.9\.7 publish --tag canary --access public$/m);
assert.match(result.calls, /^npx --yes npm@11\.16\.0 publish --tag canary --access public$/m);
assert.match(result.calls, /^npm publish --tag canary --access public$/m);
assert.doesNotMatch(result.calls, /^pnpm publish/m);
});

View File

@ -262,7 +262,7 @@ if [ "$dry_run" = true ]; then
publish_dir="$(mktemp -d "${TMPDIR:-/tmp}/paperclip-release-package.XXXXXX")"
node "$REPO_ROOT/scripts/prepare-bundled-package.mjs" "$REPO_ROOT/$pkg_dir" "$publish_dir"
cd "$publish_dir"
run_bundled_npm pack --pack-destination "$publish_dir" 2>&1 | tail -3
run_bundled_npm_pack pack --pack-destination "$publish_dir" 2>&1 | tail -3
rm -rf "$publish_dir"
else
pnpm publish --dry-run --no-git-checks --tag "$DIST_TAG" 2>&1 | tail -3