Skip to content

fix: Detect scoped @turbo/{platform} packages in local binary resolution#12386

Merged
anthonyshew merged 2 commits intovercel:mainfrom
EmojiPati:fix/scoped-platform-package-detection
Mar 18, 2026
Merged

fix: Detect scoped @turbo/{platform} packages in local binary resolution#12386
anthonyshew merged 2 commits intovercel:mainfrom
EmojiPati:fix/scoped-platform-package-detection

Conversation

@EmojiPati
Copy link
Copy Markdown
Contributor

@EmojiPati EmojiPati commented Mar 18, 2026

Summary

  • Fix generate_unplugged_path to detect scoped @turbo/{platform} packages in Berry PnP unplugged directories (was hardcoded to legacy turbo-{platform} only)
  • Fix ? operator inside the search loop that silently aborted the entire binary resolution when one search root had a malformed package.json
  • Restructure infer() to interleave scoped/legacy probing per search root, halving worst-case filesystem I/O during the migration period
  • Add 7 integration tests for infer() covering hoisted, unplugged, fallback, priority ordering, and error resilience

Context

The initial commit extracted try_find_with_package_path to support both @turbo/{platform} and legacy turbo-{platform} formats. A review found three issues:

  1. Unplugged bug: generate_unplugged_path hardcoded the legacy package name prefix for Berry's directory matching, so scoped packages in Berry PnP would silently fall back to global turbo.

  2. Early-return bug (pre-existing, amplified): The ? operator on PackageJson::load().ok()? returned None from the entire search function instead of continuing to the next search root. A broken package.json at one root killed discovery of valid installs at other roots.

  3. Doubled I/O: The or_else structure ran all 4 search strategies for scoped, then all 4 again for legacy. Restructuring to try both formats per root reuses each generate_*_path result and avoids redundant .yarnrc.yml parsing.

Testing

The infer() public API previously had zero test coverage. This adds tempdir-based integration tests that create mock package layouts and verify resolution:

  • test_infer_hoisted_scoped / test_infer_hoisted_legacy_fallback
  • test_infer_scoped_preferred_over_legacy — verifies priority ordering
  • test_infer_malformed_package_json_continues_search — verifies the ? fix
  • test_infer_unplugged_scoped / test_infer_unplugged_legacy — verifies the Berry fix
  • test_infer_empty_dir_returns_none

Run with cargo test -p turborepo-shim.

…ution

The per-platform npm packages were renamed from `turbo-{platform}`
to `@turbo/{platform}` in 2.8.18-canary.3, but the shim still
searched for the old unscoped names, causing it to fall back to the
global binary with a spurious warning.
@EmojiPati EmojiPati requested a review from a team as a code owner March 18, 2026 19:30
@EmojiPati EmojiPati requested review from tknickman and removed request for a team March 18, 2026 19:30
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Mar 18, 2026

Someone is attempting to deploy a commit to the Vercel Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Copy Markdown
Contributor

@vercel vercel Bot left a comment

Choose a reason for hiding this comment

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

Additional Suggestion:

The generate_unplugged_path function only matches legacy turbo-{platform} unplugged directory names, missing scoped @turbo/{platform} packages which Yarn Berry slugifies as @turbo-{platform}-npm-{version}-{hash}.

Fix on Vercel

- Fix generate_unplugged_path to support scoped package names in Berry
  PnP unplugged directories (was hardcoded to legacy format only)
- Fix early-return via ? operator that aborted the entire search when
  one root had a malformed package.json (now continues to next candidate)
- Restructure infer() to interleave scoped/legacy per search root,
  halving worst-case filesystem I/O during migration period
- Add 7 integration tests for infer() covering hoisted, unplugged,
  fallback, priority ordering, and malformed package.json resilience
- Replace tautological turbo_state tests with meaningful invariant check
- Restore removed comments explaining search ordering and design rationale
@anthonyshew anthonyshew changed the title fix: detect scoped @turbo/{platform} packages in local binary resolution fix: Detect scoped @turbo/{platform} packages in local binary resolution Mar 18, 2026
Copy link
Copy Markdown
Contributor

@anthonyshew anthonyshew left a comment

Choose a reason for hiding this comment

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

Oops, what a blunder by me! Thank you for calling this to my attention.

I reworked a little bit of yours to turn it into what we'll merge. Thank you!

@anthonyshew anthonyshew merged commit 9d977f1 into vercel:main Mar 18, 2026
53 of 65 checks passed
anthonyshew added a commit that referenced this pull request Mar 18, 2026
The Rust shim was updated in #12386 to resolve both @turbo/<platform>
and turbo-<platform> packages, but the Node shim (packages/turbo/bin/turbo)
still only resolves @turbo/* names. Users upgrading across the 2.8.17 →
2.8.18 scope rename boundary can have legacy turbo-<platform> binaries
in node_modules that the Node shim silently ignores.

Add binaryPaths/packageNames/tryResolve helpers so every resolution step
(direct, JIT install, emulated, diagnostics, lockfile check) tries the
scoped name first and falls back to the legacy unscoped name.

Closes #10618
anthonyshew added a commit that referenced this pull request Mar 18, 2026
## Summary

- The Node shim (`packages/turbo/bin/turbo`) only resolves
`@turbo/<platform>` packages. Users upgrading across the 2.8.17 → 2.8.18
scope rename boundary can have legacy `turbo-<platform>` binaries in
`node_modules` that the shim silently ignores, falling through to a slow
JIT `npm install` or failing entirely.
- Add `binaryPaths`/`packageNames`/`tryResolve` helpers so every
resolution step (direct, JIT install, emulated, diagnostics, lockfile
check) tries the scoped name first then falls back to the legacy
unscoped name.

This is the Node-side counterpart to #12386, which fixed the same gap in
the Rust shim.

## How to test

```bash
mkdir /tmp/turbo-test && cd /tmp/turbo-test && npm init -y

# Install last version with unscoped names
npm install turbo@2.8.17

# Swap in the patched shim
cp <repo>/packages/turbo/bin/turbo node_modules/turbo/bin/turbo

# Should resolve the legacy binary without JIT install
npx turbo --version
```

Closes #10618
github-actions Bot added a commit that referenced this pull request Mar 18, 2026
## Release v2.8.19

Versioned docs: https://v2-8-19.turborepo.dev

### Changes

- release(turborepo): 2.8.18 (#12378) (`0afc24f`)
- fix: Use previous stable tag for stable release notes (#12379)
(`3e2b9bb`)
- fix: Remove unnecessary git pull in update-examples workflow (#12380)
(`97367ec`)
- ci: Use blobless clones for release workflow checkouts (#12381)
(`84a5369`)
- release(turborepo): 2.8.19-canary.1 (#12383) (`f9517de`)
- feat: Deprecate `turbo-ignore` in favor of `turbo query affected`
(#12382) (`1b9f6cb`)
- release(turborepo): 2.8.19-canary.2 (#12384) (`1885f08`)
- fix: Tailored `turbo-ignore` deprecation notice for Vercel users
(#12385) (`4b2eae6`)
- release(turborepo): 2.8.19-canary.3 (#12387) (`ee90d8e`)
- chore: Upgrade Next.js to 16.2.0 in examples and apps (#12389)
(`2ff8fc9`)
- fix: Detect scoped `@turbo/{platform}` packages in local binary
resolution (#12386) (`9d977f1`)
- fix: Support legacy unscoped binary package names in Node shim
(#12391) (`7da779a`)

---------

Co-authored-by: Turbobot <turbobot@vercel.com>
github-actions Bot added a commit that referenced this pull request Mar 19, 2026
## Release v2.8.20-canary.1

Versioned docs: https://v2-8-20-canary-1.turborepo.dev

### Changes

- release(turborepo): 2.8.19-canary.3 (#12387) (`ee90d8e`)
- chore: Upgrade Next.js to 16.2.0 in examples and apps (#12389)
(`2ff8fc9`)
- fix: Detect scoped `@turbo/{platform}` packages in local binary
resolution (#12386) (`9d977f1`)
- fix: Support legacy unscoped binary package names in Node shim
(#12391) (`7da779a`)
- release(turborepo): 2.8.19 (#12392) (`66f2c36`)
- fix: Prevent `stdin` from being dropped for persistent tasks in stream
mode (#12394) (`106fa3e`)

Co-authored-by: Turbobot <turbobot@vercel.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.

2 participants