Skip to content

fix(@pnpm/exe): preserve relative symlinks when packaging dist/#11399

Merged
zkochan merged 2 commits into
pnpm:mainfrom
comp615:fix/exe-build-verbatim-symlinks
Apr 30, 2026
Merged

fix(@pnpm/exe): preserve relative symlinks when packaging dist/#11399
zkochan merged 2 commits into
pnpm:mainfrom
comp615:fix/exe-build-verbatim-symlinks

Conversation

@comp615

@comp615 comp615 commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Closes #11398.

Problem

The v11 standalone executable release tarballs (pnpm-{darwin,linux}-{x64,arm64}.tar.gz) ship 4 broken absolute symlinks under dist/node_modules/.bin/:

dist/node_modules/.bin/node-gyp    -> /home/runner/work/pnpm/pnpm/pnpm/dist/node_modules/node-gyp/bin/node-gyp.js
dist/node_modules/.bin/node-which  -> /home/runner/work/pnpm/pnpm/pnpm/dist/node_modules/which/bin/which.js
dist/node_modules/.bin/nopt        -> /home/runner/work/pnpm/pnpm/pnpm/dist/node_modules/nopt/bin/nopt.js
dist/node_modules/.bin/semver      -> /home/runner/work/pnpm/pnpm/pnpm/dist/node_modules/semver/bin/semver.js

The /home/runner/work/pnpm/pnpm/... paths leak the GitHub Actions runner's filesystem layout into a public artifact, and the symlinks are dangling on every user's machine. Lenient extractors (get.pnpm.io/install.sh, npm, corepack, asdf, etc.) silently extract them as dangling — pnpm itself never traverses these paths at runtime, so the bug has been invisible. Strict extractors that validate symlink targets (hermit is the one that surfaced this) refuse to extract the tarball entirely.

See #11398 for the full investigation and reproducer.

Root cause

pnpm/artifacts/exe/scripts/build-artifacts.ts copies the repo's dist/ into the @pnpm/exe package directory with:

fs.cpSync(distSrc, distDest, { recursive: true })

Node's fs.cpSync defaults to verbatimSymlinks: false, which resolves relative symlinks into absolute paths at the source filesystem location during the copy. So a relative link in the pnpm repo (dist/node_modules/.bin/node-gyp -> ../node-gyp/bin/node-gyp.js) gets rewritten to an absolute /home/runner/work/pnpm/pnpm/pnpm/dist/... link in the destination directory that subsequently gets archived. See nodejs/node#41693 for the underlying Node behavior.

Fix

Add verbatimSymlinks: true to the fs.cpSync options. This preserves relative symlinks verbatim so the archived links stay valid at any extraction location.

-fs.cpSync(distSrc, distDest, { recursive: true })
+fs.cpSync(distSrc, distDest, { recursive: true, verbatimSymlinks: true })

Testing

The build script runs in CI on every release, so the next release will demonstrate the fix. Locally I can't easily exercise the full release pipeline, but the change is a one-line option flag well-documented in the Node.js docs.

🤖 Generated with Amp

The standalone executable build copies dist/ via fs.cpSync(...) without
verbatimSymlinks: true, which causes Node to resolve relative symlinks
into absolute paths at the source filesystem location. On the GitHub
Actions runner this rewrites .bin symlinks to /home/runner/work/pnpm/...
targets that ship verbatim in the release tarballs.

Adding verbatimSymlinks: true preserves the relative symlink targets so
the archived links remain valid at any extraction location.

Fixes pnpm#11398.

🤖 Generated with [Amp](https://ampcode.com)

Amp-Thread-ID: https://ampcode.com/threads/T-019dda79-b947-742f-8711-b6f83bcda9ff
Co-authored-by: Amp <amp@ampcode.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Fixes @pnpm/exe artifact packaging so symlinks under dist/node_modules/.bin/ remain relative when dist/ is copied into the package directory, preventing release tarballs from containing broken build-host absolute symlinks (per #11398).

Changes:

  • Pass verbatimSymlinks: true to fs.cpSync in the exe artifact build script to preserve symlink targets verbatim.
  • Add a changeset to publish a patch release of @pnpm/exe documenting the fix.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
pnpm/artifacts/exe/scripts/build-artifacts.ts Preserves relative symlinks when copying dist/ into @pnpm/exe for packaging.
.changeset/exe-build-verbatim-symlinks.md Adds a patch changeset describing the symlink preservation fix and its impact.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@zkochan

zkochan commented Apr 29, 2026

Copy link
Copy Markdown
Member

add unextractable to cspell.json

@comp615

comp615 commented Apr 30, 2026

Copy link
Copy Markdown
Contributor Author

Done in 7152521 — added unextractable between undollar and uninstallation to keep alphabetical order. Thanks for the quick review!

🤖

@zkochan zkochan merged commit 3b12eb2 into pnpm:main Apr 30, 2026
7 of 8 checks passed
@welcome

welcome Bot commented Apr 30, 2026

Copy link
Copy Markdown

Congrats on merging your first pull request! 🎉🎉🎉

zkochan pushed a commit that referenced this pull request Apr 30, 2026
* fix(@pnpm/exe): preserve relative symlinks when packaging dist/

The standalone executable build copies dist/ via fs.cpSync(...) without
verbatimSymlinks: true, which causes Node to resolve relative symlinks
into absolute paths at the source filesystem location. On the GitHub
Actions runner this rewrites .bin symlinks to /home/runner/work/pnpm/...
targets that ship verbatim in the release tarballs.

Adding verbatimSymlinks: true preserves the relative symlink targets so
the archived links remain valid at any extraction location.

Fixes #11398.

🤖 Generated with [Amp](https://ampcode.com)

Amp-Thread-ID: https://ampcode.com/threads/T-019dda79-b947-742f-8711-b6f83bcda9ff
Co-authored-by: Amp <amp@ampcode.com>

* chore: add 'unextractable' to cspell.json

Per #11399 (comment)

🤖 Generated with [Amp](https://ampcode.com)

Amp-Thread-ID: https://ampcode.com/threads/T-019dda79-b947-742f-8711-b6f83bcda9ff
Co-authored-by: Amp <amp@ampcode.com>

---------

Co-authored-by: Amp <amp@ampcode.com>
zkochan pushed a commit that referenced this pull request Apr 30, 2026
…rballs) (#11408)

#11399 fixed the fs.cpSync call in pnpm/artifacts/exe/scripts/build-artifacts.ts,
which controls the dist/ shipped inside the npm-published @pnpm/exe package.

But the GitHub release tarballs (pnpm-{darwin,linux}-{x64,arm64}.tar.gz) are
produced by a different script — __utils__/scripts/src/copy-artifacts.ts, run
via 'pn copy-artifacts' in the release workflow. That script has the same
fs.cpSync(...) call without verbatimSymlinks: true, so the broken absolute
symlinks under dist/node_modules/.bin/ pointing at /home/runner/work/pnpm/
pnpm/... still made it into the v11.0.2 GitHub release tarballs.

Apply the same one-line fix to that script so the next release ships clean
relative symlinks.

Follow-up to #11398.

🤖 Generated with [Amp](https://ampcode.com)

Amp-Thread-ID: https://ampcode.com/threads/T-019dda79-b947-742f-8711-b6f83bcda9ff

Co-authored-by: Amp <amp@ampcode.com>
zkochan pushed a commit that referenced this pull request Apr 30, 2026
…rballs) (#11408)

#11399 fixed the fs.cpSync call in pnpm/artifacts/exe/scripts/build-artifacts.ts,
which controls the dist/ shipped inside the npm-published @pnpm/exe package.

But the GitHub release tarballs (pnpm-{darwin,linux}-{x64,arm64}.tar.gz) are
produced by a different script — __utils__/scripts/src/copy-artifacts.ts, run
via 'pn copy-artifacts' in the release workflow. That script has the same
fs.cpSync(...) call without verbatimSymlinks: true, so the broken absolute
symlinks under dist/node_modules/.bin/ pointing at /home/runner/work/pnpm/
pnpm/... still made it into the v11.0.2 GitHub release tarballs.

Apply the same one-line fix to that script so the next release ships clean
relative symlinks.

Follow-up to #11398.

🤖 Generated with [Amp](https://ampcode.com)

Amp-Thread-ID: https://ampcode.com/threads/T-019dda79-b947-742f-8711-b6f83bcda9ff

Co-authored-by: Amp <amp@ampcode.com>
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.

v11 release tarballs contain broken absolute symlinks pointing at the GitHub Actions runner filesystem

3 participants