Skip to content

Fix vendored Yarn deployments calling yarn install instead of yarn rebuild - #965

Closed
tnikolova82 wants to merge 1 commit into
masterfrom
fix-yarn-vendored-deployments
Closed

tnikolova82 wants to merge 1 commit into
masterfrom
fix-yarn-vendored-deployments

Conversation

@tnikolova82

Copy link
Copy Markdown
Contributor

Problem

Following the same pattern as PR #964 (pnpm vendored fix), Yarn projects with pre-populated node_modules/ (vendored deployments) incorrectly run yarn install instead of yarn rebuild.

There are two distinct bugs in BuildDependencies(), identical to the pnpm issue:

Bug 1 — case s.UseYarn has priority over case s.IsVendored

supply.go#L342–L363

switch {
case s.UseYarn:     // L343 ← matched first, Yarn.Build() runs
    s.Yarn.Build(...)
case s.UsePNPM:     // L348
    s.PNPM.Build(...)
case s.IsVendored:  // L353 ← never reached when yarn.lock is present
    s.NPM.Rebuild(...)
...
}

When both flags are set:

  • s.UseYarn = true — yarn.lock exists
  • s.IsVendored = true — node_modules/ exists

Yarn.Build() runs yarn install, ignoring the pre-populated node_modules/.

Bug 2 — case s.IsVendored hardcodes NPM.Rebuild() regardless of package manager

Even with correct ordering, case s.IsVendored at supply.go#L353 always calls s.NPM.Rebuild(). A vendored Yarn project should call Yarn.Rebuild().

Solution

Follows the exact same pattern as PR #964 for pnpm:

  1. Add Rebuild(buildDir string) error to the Yarn interface

    • Mirrors the npm and pnpm implementations
  2. Implement Yarn.Rebuild() in src/nodejs/yarn/yarn.go

    func (y *Yarn) Rebuild(buildDir string) error {
        y.Log.Info("Rebuilding any native modules (yarn)")
        cmd := exec.Command("yarn", "rebuild")
        cmd.Dir = buildDir
        cmd.Stdout = y.Log.Output()
        cmd.Stderr = y.Log.Output()
        cmd.Env = append(os.Environ(), "npm_config_nodedir="+os.Getenv("NODE_HOME"))
        return y.Command.Run(cmd)
    }
    • Runs yarn rebuild with NODE_HOME set via npm_config_nodedir
    • Ensures native modules compile for the CF stack
  3. Add case s.UseYarn && s.IsVendored before case s.UseYarn

    case s.UseYarn && s.IsVendored:
        s.Log.Info("Prebuild detected (node_modules already exists)")
        if err := s.Yarn.Rebuild(s.Stager.BuildDir()); err != nil {
            return err
        }
    case s.UseYarn:
        if err := s.Yarn.Build(...); err != nil {
            return err
        }
    • Vendored detection now takes priority
    • Calls Yarn.Rebuild() instead of Yarn.Build()

Changes

File Change
src/nodejs/yarn/yarn.go Add Rebuild() method
src/nodejs/supply/supply.go Add Rebuild(string) error to Yarn interface; add vendored case
src/nodejs/yarn/yarn_test.go Unit tests for Rebuild()
src/nodejs/supply/supply_test.go Vendored yarn test cases
src/nodejs/supply/mocks_test.go Rebuild() mock method

Test Coverage

  • Unit test: vendored Yarn project calls Yarn.Rebuild, not Yarn.Build
  • Unit test: non-vendored Yarn project calls Yarn.Build as before
  • Unit test: vendored npm project still calls NPM.Rebuild (no regression)
  • Unit test: Yarn.Rebuild() runs yarn rebuild with NODE_HOME set
  • Unit test: prebuild/postbuild scripts run for vendored yarn projects
  • Integration test: push CF app with pre-populated node_modules/ and yarn.lock

Use Case

Teams using yarn install --production or workspace vendoring to produce a pre-populated node_modules/ directory before pushing. The buildpack should detect this and skip the install phase, running yarn rebuild instead to recompile any native addons for the CF stack.

Why This Matters

All three package managers (npm, yarn, pnpm) had the same bug:

Package Manager Status PR
npm ✅ Always worked Built-in from the start
pnpm ✅ Fixed PR #964
yarn ⚠️ Broken This PR

Without this fix, vendored Yarn deployments:

  • ❌ Run yarn install unnecessarily (slow, may fail in offline environments)
  • ❌ May download packages that are already vendored
  • ❌ Don't recompile native modules for the CF stack

With this fix, vendored Yarn deployments:

  • ✅ Skip yarn install (faster, works offline)
  • ✅ Run yarn rebuild to recompile native addons
  • ✅ Match npm and pnpm behavior

Related

Notes

This PR was identified during code review of PR #964, which noted:

"The same gap likely exists for Yarn vendored deployments."

Testing in PR #964 should also verify Yarn vendored deployments once this is merged.

…build

## Problem

Following the same pattern as PR #964 (pnpm vendored fix), Yarn projects
with pre-populated `node_modules/` (vendored deployments) incorrectly run
`yarn install` instead of `yarn rebuild`.

There are two distinct bugs in `BuildDependencies()`:

### Bug 1 — `case s.UseYarn` has priority over `case s.IsVendored`

```go
switch {
case s.UseYarn:     // Matched first, Yarn.Build() runs
    s.Yarn.Build(...)
case s.UsePNPM:
    s.PNPM.Build(...)
case s.IsVendored:  // Never reached when yarn.lock is present
    s.NPM.Rebuild(...)
...
}
```

When both flags are set (yarn.lock exists AND node_modules/ exists),
`Yarn.Build()` runs `yarn install`, ignoring the pre-populated `node_modules/`.

### Bug 2 — `case s.IsVendored` hardcodes `NPM.Rebuild()` regardless of package manager

Even with correct ordering, `case s.IsVendored` always calls `s.NPM.Rebuild()`.
A vendored Yarn project should call `Yarn.Rebuild()`.

## Solution

1. **Add `Rebuild(buildDir string) error` to the `Yarn` interface**
   - Mirrors the npm implementation
   - Mirrors the pnpm fix from PR #964

2. **Implement `Yarn.Rebuild()` in `src/nodejs/yarn/yarn.go`**
   - Runs `yarn rebuild` with `NODE_HOME` set via `npm_config_nodedir`
   - Ensures native modules compile for the CF stack

3. **Add `case s.UseYarn && s.IsVendored` before `case s.UseYarn`**
   - Vendored detection now takes priority over the package manager install path
   - Calls `Yarn.Rebuild()` instead of `Yarn.Build()`

## Changes

- **`src/nodejs/yarn/yarn.go`**: Add `Rebuild()` method that runs `yarn rebuild`
- **`src/nodejs/supply/supply.go`**:
  - Add `Rebuild(string) error` to the `Yarn` interface
  - Add `case s.UseYarn && s.IsVendored` case before `case s.UseYarn`
- **`src/nodejs/yarn/yarn_test.go`**: Add unit tests for `Rebuild()`
- **`src/nodejs/supply/supply_test.go`**: Add vendored yarn test cases
- **`src/nodejs/supply/mocks_test.go`**: Add `Rebuild()` mock method

## Test Coverage

- [x] Unit test: vendored Yarn project calls `Yarn.Rebuild`, not `Yarn.Build`
- [x] Unit test: non-vendored Yarn project calls `Yarn.Build` as before
- [x] Unit test: vendored npm project still calls `NPM.Rebuild` (no regression)
- [x] Unit test: `Yarn.Rebuild()` runs `yarn rebuild` with NODE_HOME set
- [ ] Integration test: push CF app with pre-populated `node_modules/` and `yarn.lock`

## Use Case

Teams using `yarn install --production` or workspace vendoring to produce a
pre-populated `node_modules/` directory before pushing. The buildpack should
detect this and skip the install phase, running `yarn rebuild` instead to
recompile any native addons for the CF stack.

## Related

- Issue #957: Original bug report for pnpm (also mentioned Yarn has same issue)
- PR #964: Fixed pnpm vendored deployments (this PR follows the same pattern)
- Completes the vendored deployment fix for all three package managers (npm, yarn, pnpm)
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