yarnpkg / berry

📦🐈 Active development trunk for Yarn ⚒
https://yarnpkg.com
BSD 2-Clause "Simplified" License
7.44k stars 1.11k forks source link

[Bug?]: `yarn workspace … run` allows running binaries not listed in dependencies (`nodeLinker: node-modules`) #5042

Open bradleyayers opened 2 years ago

bradleyayers commented 2 years ago

Self-service

Describe the bug

When running binaries in a workspace, it's possible for yarn to incorrectly determine that the binary is available and execute. This can happen when a workspace script is executed via a root script, and that root package.json has a dependency that provides the binary being run.

I ran into this problem when using nx, and having a yarn nx entry point that we use to execute commands in other workspaces. The problem came about where we had a dependency on eslint in our root package.json, but had missed it in our workspace package.json, and encountered unexpected difference in behavior where yarn nx build lib worked, but yarn workspace lib build did not.

This seems to be because of $PATH inheritance via environment, where the parent yarn process adds binaries to a temp directory and puts it on $PATH, and then the child yarn inherits that and sees binaries that it shouldn't.

To reproduce

const fs = require('fs').promises;

await fs.writeFile('.yarnrc.yml', 'nodeLinker: node-modules');
await fs.mkdir('packages/lib', { recursive: true });
await fs.writeFile("packages/lib/package.json", `{
  "name": "lib",
  "scripts": {
    "env": "envinfo"
  }
}`);
await packageJsonAndInstall({
  "name": "root",
  "workspaces": [
    "packages/*"
  ],
  "scripts": {
    "libEnvViaRoot": "yarn workspace lib env"
  },
  "dependencies": {
    "envinfo": "^7"
  },
});

await expect(yarn(`workspace`, `lib`, `bin`, `envinfo`)).rejects.toThrow();
await expect(yarn(`bin`, `envinfo`)).resolves.toBeTruthy();

await expect(yarn(`workspace`, `lib`, `env`)).rejects.toThrow(`command not found: envinfo`);
// This incorrectly succeeds.
await expect(yarn(`libEnvViaRoot`)).rejects.toThrow(`command not found: envinfo`);

Environment

System:
  OS: macOS 12.6
  CPU: (10) arm64 Apple M1 Max
Binaries:
  Node: 16.14.0 - /private/var/folders/jq/3x3ddtjx3rb9nwy79zxfwnj00000gn/T/xfs-ef47ac84/node
  Yarn: 3.2.4 - /private/var/folders/jq/3x3ddtjx3rb9nwy79zxfwnj00000gn/T/xfs-ef47ac84/yarn
  npm: 8.3.1 - ~/.n/bin/npm
npmPackages:
  jest: ^26.6.3 => 26.6.3

Additional context

No response

yarnbot commented 2 years ago

This issue reproduces on master:

Error: expect(received).rejects.toThrow()

Received promise resolved instead of rejected
Resolved to value: "
  System:
    OS: Linux 5.15 Alpine Linux
    CPU: (2) x64 Intel(R) Xeon(R) Platinum 8272CL CPU @ 2.60GHz
    Memory: 4.97 GB / 6.78 GB
    Container: Yes
    Shell: 1.34.1 - /bin/ash
  Binaries:
    Node: 16.17.1 - /tmp/xfs-c757b612/node
    Yarn: 4.0.0-rc.27.dev - /tmp/xfs-c757b612/yarn
  Utilities:
    Make: 4.3 - /usr/bin/make
    GCC: 12.2.1 - /usr/bin/gcc
    Git: 2.34.5 - /usr/bin/git
  Languages:
    Bash: 5.1.16 - /bin/bash
    Perl: 5.36.0 - /usr/bin/perl
    Python: 2.7.18 - /usr/bin/python

"
    at expect (/github/workspace/.yarn/cache/expect-npm-24.8.0-8c7640c562-44ff9ab1e7.zip/node_modules/expect/build/index.js:138:15)
    at module.exports (evalmachine.<anonymous>:30:7)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async /github/workspace/.yarn/cache/@arcanis-sherlock-npm-2.0.3-558f52b79f-286d94b96d.zip/node_modules/@arcanis/sherlock/lib/executeRepro.js:57:13
    at async executeInTempDirectory (/github/workspace/.yarn/cache/@arcanis-sherlock-npm-2.0.3-558f52b79f-286d94b96d.zip/node_modules/@arcanis/sherlock/lib/executeRepro.js:18:16)
    at async executeRepro (/github/workspace/.yarn/cache/@arcanis-sherlock-npm-2.0.3-558f52b79f-286d94b96d.zip/node_modules/@arcanis/sherlock/lib/executeRepro.js:25:12)
    at async ExecCommand.execute (/github/workspace/.yarn/cache/@arcanis-sherlock-npm-2.0.3-558f52b79f-286d94b96d.zip/node_modules/@arcanis/sherlock/lib/commands/exec.js:26:38)
    at async ExecCommand.validateAndExecute (/github/workspace/.yarn/cache/clipanion-npm-2.0.0-rc.16-b9444aaf89-4061026d74.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-4061026d74.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-4061026d74.zip/node_modules/clipanion/lib/advanced/Cli.js:83:28)
larixer commented 2 years ago

@bradleyayers Thanks, if you have time and willing to help, you can clone berry repo, create the project that reproduces the error in any directory, then put .yarnrc.yml inside this project and set yarnPath: ../berry/scripts/run-yarn.js inside. After that you can console.log inside berry code and find out where the problem is, your changes into source code will have immediate effect because berry/scripts/run-yarn.js transpiles code on the fly. You can also ask questions on our discord at: https://discord.com/invite/yarnpkg

larixer commented 2 months ago

The issue reproduces for PnP as well, i.e. not a node-modules linker specifc