Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug?]: Yarn fails to handle when cacheFolder is a symlink farm #3514

Closed
1 task
stevenxu-db opened this issue Sep 30, 2021 · 4 comments · Fixed by #6603
Closed
1 task

[Bug?]: Yarn fails to handle when cacheFolder is a symlink farm #3514

stevenxu-db opened this issue Sep 30, 2021 · 4 comments · Fixed by #6603
Labels
bug Something isn't working reproducible This issue can be successfully reproduced

Comments

@stevenxu-db
Copy link
Contributor

stevenxu-db commented Sep 30, 2021

Self-service

  • I'd be willing to implement a fix

Describe the bug

When cacheFolder points to a folder containing symlinks to zips rather than actual zips, Yarn fails to handle it as we expect. The failure happens regardless of whether --immutable-cache is used. Our main use case is with nodeLinker: node-modules, but a similar error also appears to affect PnP. When the cacheFolder is a symlink farm, yarn fails with the following error:

➤ YN0001: │ Error: While persisting /private/var/folders/4y/p9httzjd7px11pkmsfvlbdym0000gp/T/tmp-77720ZcZ4v1xoAlgh/symlink-farm/jquery-npm-3.6.0-ca7872bdbb-8fd5fef4aa.zip/node_modules/jquery/ -> /private/var/folders/4y/p9httzjd7px11pkmsfvlbdym0000gp/T/tmp-77720ZcZ4v1xoAlgh/node_modules/jquery ENOTDIR: not a directory, scandir '/private/var/folders/4y/p9httzjd7px11pkmsfvlbdym0000gp/T/tmp-77720ZcZ4v1xoAlgh/symlink-farm/jquery-npm-3.6.0-ca7872bdbb-8fd5fef4aa.zip/node_modules/jquery'

Here is the additional failure case affecting PnP. This doesn't affect us right now, but our goal is to migrate to PnP in time, and this would impact our ability to move:

await yarn('init');
await yarn('add', '[email protected]');

const { execSync } = require('child_process');
execSync('mkdir symlink-farm');
execSync('find .yarn/cache/* -exec ln -s ../{} symlink-farm/ \\;');
execSync('rm -rf node_modules');

await yarn('config', 'set', 'cacheFolder', 'symlink-farm');
await expect(yarn()).resolves.not.toThrow();
await expect(yarn('node', '-e', 'console.log(require("jquery"));')).resolves.toContain('[Function (anonymous)]');

Reported error:

Error: Required package missing from disk. If you keep your packages inside your repository then restarting the Node process may be enough. Otherwise, try to run an install first.

Missing package: jquery@npm:3.6.0
Expected package location: /private/var/folders/4y/p9httzjd7px11pkmsfvlbdym0000gp/T/tmp-77838wPTUvBCK43Kr/symlink-farm/jquery-npm-3.6.0-ca7872bdbb-8fd5fef4aa.zip/node_modules/jquery/

Require stack:
- /private/var/folders/4y/p9httzjd7px11pkmsfvlbdym0000gp/T/tmp-77838wPTUvBCK43Kr/[eval]
    at Function.external_module_.Module._resolveFilename (/private/var/folders/4y/p9httzjd7px11pkmsfvlbdym0000gp/T/tmp-77838wPTUvBCK43Kr/.pnp.cjs:9684:55)
    at Function.external_module_.Module._load (/private/var/folders/4y/p9httzjd7px11pkmsfvlbdym0000gp/T/tmp-77838wPTUvBCK43Kr/.pnp.cjs:9483:48)
    at Module.require (internal/modules/cjs/loader.js:952:19)
    at require (internal/modules/cjs/helpers.js:88:18)
    at [eval]:1:13
    at Script.runInThisContext (vm.js:133:18)
    at Object.runInThisContext (vm.js:310:38)
    at internal/process/execution.js:77:19
    at [eval]-wrapper:6:22
    at evalScript (internal/process/execution.js:76:60)

To reproduce

await yarn('init');
await yarn('config', 'set', 'nodeLinker', 'node-modules');
await yarn('add', '[email protected]');

const { execSync } = require('child_process');
execSync('mkdir symlink-farm');
execSync('find .yarn/cache/* -exec ln -s ../{} symlink-farm/ \\;');
execSync('rm -rf node_modules');

await yarn('config', 'set', 'cacheFolder', 'symlink-farm');
await expect(yarn()).resolves.not.toThrow(); // --immutable-cache makes no difference here

Environment

System:
    OS: macOS 11.5.2
    CPU: (16) x64 Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
  Binaries:
    Node: 14.16.1 - /private/var/folders/4y/p9httzjd7px11pkmsfvlbdym0000gp/T/xfs-1a58a4dc/node
    Yarn: 3.1.0-rc.8.dev - /private/var/folders/4y/p9httzjd7px11pkmsfvlbdym0000gp/T/xfs-1a58a4dc/yarn
    npm: 6.14.12 - ~/.nvm/version/npm

Additional context

We use Bazel (though not rules_nodejs). For each build action, Bazel naturally provides a sandboxed symlink farm because it doesn't guarantee that the folder the real zips are in is sandboxed. We'd like to point Yarn to this symlink farm instead of the real folder and we'd also like to avoid having to dereference the links and pay the cost of writing it to disk. For us, this only impacts install for now since we're using nodeLinker: node-modules, and after the install, the cacheFolder has no impact. But this will become increasingly important for us in order to migrate to PnP, otherwise we have to dereference the symlink farm at the beginning of every action.

@stevenxu-db stevenxu-db added the bug Something isn't working label Sep 30, 2021
@yarnbot yarnbot added the reproducible This issue can be successfully reproduced label Sep 30, 2021
@yarnbot

This comment has been minimized.

@yarnbot

This comment has been minimized.

@yarnbot
Copy link
Collaborator

yarnbot commented Sep 30, 2021

This issue reproduces on master:

Error: expect(received).resolves.not.toThrow()

Received promise rejected instead of resolved
Rejected to value: [Error: Command failed: /usr/bin/node /github/workspace/scripts/actions/../run-yarn.js

➤ YN0000: ┌ Resolution step
::group::Resolution step
::endgroup::
➤ YN0000: └ Completed
➤ YN0000: ┌ Fetch step
::group::Fetch step
➤ YN0013: │ 3 packages were already cached
::endgroup::
➤ YN0000: └ Completed
➤ YN0000: ┌ Link step
::group::Link step
➤ YN0001: │ Error: While persisting /tmp/tmp-182ZkHHgdoxFCP/symlink-farm/jquery-npm-3.6.0-ca7872bdbb-8fd5fef4aa.zip/node_modules/jquery/ -> /tmp/tmp-182ZkHHgdoxFCP/node_modules/jquery ENOTDIR: not a directory, scandir '/tmp/tmp-182ZkHHgdoxFCP/symlink-farm/jquery-npm-3.6.0-ca7872bdbb-8fd5fef4aa.zip/node_modules/jquery'
::endgroup::
➤ YN0000: └ Completed
➤ YN0000: Failed with errors in 0s 95ms
]
    at expect (/github/workspace/.yarn/cache/expect-npm-24.8.0-8c7640c562-0c0da74930.zip/node_modules/expect/build/index.js:138:15)
    at module.exports (evalmachine.<anonymous>:12:7)
    at processTicksAndRejections (internal/process/task_queues.js:95:5)
    at async /github/workspace/.yarn/cache/@arcanis-sherlock-npm-2.0.2-91650a2501-627bee24a7.zip/node_modules/@arcanis/sherlock/lib/executeRepro.js:56:13
    at async executeInTempDirectory (/github/workspace/.yarn/cache/@arcanis-sherlock-npm-2.0.2-91650a2501-627bee24a7.zip/node_modules/@arcanis/sherlock/lib/executeRepro.js:17:16)
    at async Object.executeRepro (/github/workspace/.yarn/cache/@arcanis-sherlock-npm-2.0.2-91650a2501-627bee24a7.zip/node_modules/@arcanis/sherlock/lib/executeRepro.js:24:12)
    at async ExecCommand.execute (/github/workspace/.yarn/cache/@arcanis-sherlock-npm-2.0.2-91650a2501-627bee24a7.zip/node_modules/@arcanis/sherlock/lib/commands/exec.js:25:38)
    at async ExecCommand.validateAndExecute (/github/workspace/.yarn/cache/clipanion-npm-2.0.0-rc.16-b9444aaf89-91cf93ba72.zip/node_modules/clipanion/lib/advanced/Command.js:161:26)
    at async Cli.run (/github/workspace/.yarn/cache/clipanion-npm-2.0.0-rc.16-b9444aaf89-91cf93ba72.zip/node_modules/clipanion/lib/advanced/Cli.js:74:24)
    at async Cli.runExit (/github/workspace/.yarn/cache/clipanion-npm-2.0.0-rc.16-b9444aaf89-91cf93ba72.zip/node_modules/clipanion/lib/advanced/Cli.js:83:28)

adrian-gierakowski added a commit to rhinofi/yarn-plugin-nixify that referenced this issue Jan 28, 2022
When used with pnp, we can avoid copying deps to the
main project, so that rebuilds are faster and less
spaces is used when only project source changes
(but not deps).

To make this work, we need to patch .pnp.cjs, since
unfortunately symlinks don't work as packageLocations cannot be (see: yarnpkg/berry#3514). We also cannot use
absolute paths, so we end up using relative paths to store locations
containing each deps. This also works for unplugged deps.
adrian-gierakowski added a commit to rhinofi/yarn-plugin-nixify that referenced this issue Jan 28, 2022
When used with pnp, we can avoid copying deps to the
main project, so that rebuilds are faster and less
spaces is used when only project source changes
(but not deps).

To make this work, we need to patch .pnp.cjs, since
unfortunately symlinks don't work as packageLocations cannot be (see: yarnpkg/berry#3514). We also cannot use
absolute paths, so we end up using relative paths to store locations
containing each deps. This also works for unplugged deps.
@thatsmydoing
Copy link
Contributor

It seems that this used to be supported but the functionality was removed in 22ef17a for performance reasons. I'm not quite sure removing the symlink resolution actually makes things any faster in the common case (when it's not a symlink) so maybe we can bring it back?

github-merge-queue bot pushed a commit that referenced this issue Nov 18, 2024
This is the same as #5474 but
allows edits by maintainer

**What's the problem this PR addresses?**
<!-- Describe the rationale of your PR. -->
Yarn shouldn't care if zips in the cache are symlinks or not. The only
reason it doesn't work is that finding the mount point skips zips if
they're not regular files. This used to be supported until it was
removed for "performance" reasons in
#1474. The bulk of the logic in the
linked PR involves resolving the real path of the symlink but I don't
think that's needed for this?

<!-- Link all issues that it closes. (Closes/Resolves #xxxx.) -->
Resolves #3514
Closes #5474 (supersedes it)

**How did you fix it?**
I switched the check to use `stat` from `lstat`.

**Checklist**
<!--- Don't worry if you miss something, chores are automatically
tested. -->
<!--- This checklist exists to help you remember doing the chores when
you submit a PR. -->
<!--- Put an `x` in all the boxes that apply. -->
- [X] I have read the [Contributing
Guide](https://yarnpkg.com/advanced/contributing).

<!-- See
https://yarnpkg.com/advanced/contributing#preparing-your-pr-to-be-released
for more details. -->
<!-- Check with `yarn version check` and fix with `yarn version check
-i` -->
- [X] I have set the packages that need to be released for my changes to
be effective.

<!-- The "Testing chores" workflow validates that your PR follows our
guidelines. -->
<!-- If it doesn't pass, click on it to see details as to what your PR
might be missing. -->
- [x] I will check that all automated PR checks pass before the PR gets
reviewed.

---------

Co-authored-by: merceyz <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working reproducible This issue can be successfully reproduced
Projects
None yet
3 participants