berry: [Bug] Cannot run script without installing dependencies even if the script is trying to `install`
Describe the bug
Cannot run script defined in package.json
that actually runs yarn install
before explicitly running yarn install
, as long as there is a dependency.
e.g., in package.json
"scripts": {
"install-alias": "yarn install",
"setup": "yarn install && yarn run afterInstall",
"afterInstall": "blah blah",
},
dependencies {
"some-package": "1"
}
- Yarn version 2.4.1
If I run yarn run install-alias
or yarn run setup
, this error appears
Usage Error: Couldn't find the node_modules state file - running an install might help (findPackageLocation)
Googling the error message lead me to this https://github.com/yarnpkg/berry/blob/master/packages/plugin-node-modules/sources/NodeModulesLinker.ts#L43, and a little tracing shows that its trying to verify that all dependencies of the calling package.json
are installed before it is willing to run the script.
This seems overly cautious since I should be able to run scripts that doesn’t involve any dependencies before installing packages, or, in my case, wants to run some scripts immediately after install and have them as one command.
I have a workaround that moves all the scripts into its own workspace and keep the top level package.json
dependency free, but it makes much more sense for those scripts to live at the top level.
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Comments: 16 (3 by maintainers)
Commits related to this issue
- Don't use scripts before install, see yarnpkg/berry#2701 — committed to iotaledger/iota-wiki by jlvandenhout 2 years ago
- Don't use scripts before install, see yarnpkg/berry#2701 — committed to iota-community/communitydriven-blog by jlvandenhout 2 years ago
- Don't use scripts before install, see yarnpkg/berry#2701 — committed to iotaledger/iota-wiki by jlvandenhout 2 years ago
This seems like a crazy degradation in dev experience. We have a react native project with multiple different target platforms and multiple different installation scripts depending on precisely what you’re trying to do. i.e. an iOS engineer would run a different yarn install alias that would include ios dependencies and an android engineer would not need (and wouldn’t even be capable of it if they’re developing on Windows). So a generic postinstall script does not work for us.
+1 for reverting this behaviour.
Yarn will fail even if I am trying to execute
yarn install
alias, or if I try to run somenode script.js
script, and the reason “why?” is because it is not aware what I am actually trying to run. It just assumes that any script frompackage.json
requires full dependency tree to be installed. Since it can’t really understand what I am trying to run in my script, I do not feel that this is a good thing to take away control from user on whether they can run certain script or not. If yarn knew better, that’s another story, but it doesn’t, hence I, as a user, should have full control over it IMO, and if I run some script and it fails because of missing dependencies in$PATH
or whatnot, well, this is my fault then, but I am willing to take that responsibility. Right now it just causes frustration, because I know that I can run some script, but yarn refuses to, because it thinks it knows better, and there is no good way around as well, like there is noyarn sudo-run
.While this is indeed sort of a solution, I do not think this is a good one. First of all, I would need to give up a clear and convenient way of using some scripts via
package.json
"scripts":
in favor of some really specific way (hooks) to do it in a specific package manager (yarn@2 and higher). This fact alone IMO is enough for user to not want to use a hooks solution. It’s not some fancy thing I want my package manager to do, it is not some project specific thing that I would like to make a plugin for, it is just running scripts frompackage.json
. Another point, is that right now there is now way throughyarn
API to detect that it will bail with an “install error” (the only hook that runs before the said error issetupScriptEnvironment
), so to achieve desirable behavior (i.e. run script without needing to install first), you would need to add tricky code that will use some pseudo-evidence thatyarn
will bail with an error (like lack of.yarn-state.yml
for example, or generating a new one). And even if I am wrong and just haven’t found it in a docs, or if it will be added later, it forces you to add some complex solution for what was before a trivial task (from a user standpoint). Every time I updateyarn
, I would need to go though changelog very carefully because public API for classes likeProject
orConfiguration
IMO more likely to change, than let’s sayenableGlobalCache
flag, there is a high chance of making an error when adding or maintaining such a custom plugin and furthermore it adds unwanted complexity to user’s project infrastructure. And while it is acceptable to have those cons when developing such solution for a project-specific problem, I do not think this is one of them.Running into this issue as well. The policy in our project is that all necessary CLI commands must be documented by putting them into a package.json script and this includes
npm install
and should also includeyarn install
. It’s quite unexpected that the install script must be run usingnpm
instead ofyarn
, this causes quite a bit of friction during onboarding.@wjmao88 and future yarn’ers: I wrote a little plugin that generates an empty state file if none is present upon execution of scripts before
yarn install
. The empty state file removes the error upon startup and thus allows for customized commands to run. I know it is probably not ideal (state restoration might be impacted by this?), but in my case it can serve as a workaround just fine. You can specify which commands you want the state file generation to be triggered upon in theALLOWED_SCRIPTS
array..yarn/override-state/plugin-override-state.js
Also add this to your
plugins:
section in your.yarnrc.yml
:I understand this might be a little frustrating, but our position is simple: we must fulfill the contract, or fail early. For
package.json
, it means that your dependencies must be in the$PATH
when you execute a script. If we can’t fulfill this contract, then we must fail early.With that being said, the best workaround I can suggest is to implement your proxy connection by configuring a hook. We don’t have a generic
beforeAllInstalled
(PR welcome!), but thevalidateProject
hook should be interchangeable for this purpose.I encountered this error when I upgraded to yarn@3.2.0 from 3.0.0. My use case was to use the workspace-tools to generate github releases for each package in the monorepo in CI:
This has stopped working now since I didn’t install node modules before running this shell script. Adding an unnecessary
yarn install
to my pipeline adds another 30s to the build (even with cache).It makes sense to throw this error for “run” commands since those are coming from
package.json
and it is reasonable to expect node_modules/packages to be present for them, but for “exec” where the aim is to “Execute a shell script” it shouldn’t be thrown.I think this issue makes it difficult to use yarn as a “project manager” and forces us to download packages where it’s not needed. I hope it can be reconsidered.
I don’t think this issue should be closed, having
scripts
inpackage.json
that do not require a priorinstall
is a perfectly valid construct.For example, we use this to initialize proxy connections to our private registry. This is now broken after upgrading to yarn v2. Running
install
will also fail in this case, because the proxy connection is not there yet, thus retrieving some of the packages will fail.This change in behavior now requires us to create separate shell scripts, which is against the intention of scripts defined in
package.json
: i.e. one central place to bundle all project-related commands.Because of this behaviour, I am unable to bootstrap my project cleanly by using yarn scripts right now.
My project involves compiling rust source code to wasm by using wasm-pack. The wasm-pack generated packages is part of the yarn workspace, and is a dependency of other modules in that workspace. Unfortunately, because of how wasm-pack works, the
package.json
manifest files are not present before the package is built. With that,yarn
installation fails, as it complains about missing dependencies. Unfortunately, my scriptyarn wasm
, which is supposed to generate those, cannot be run until yarn detects a valid install. Which cannot happen because there are no built packages… so I can’t bootstrap my project.I also tried a
preinstall
script to run that wasm build process, butyarn
doesn’t runpreinstall
before attemtping an install, as the name would suggest. That seems to be just a completely broken feature right now.My current solution is to use
npm run wasm
before first installation, or invoke the necessarywasm-pack build
manually. Unfortunately, there is no nice way to do that automatically right now. I would love thepreinstall
script to do what it says on the tin.That is still a huge problem in Yarn v4. I hope it can be reconsidered in the future.
I know that, and I’m fine with that if there are other ways to automate an repository setup that needs to be done before first install. Though to be honest, why is it that way? It seems that the only reason for this behaviour is that the scipts cannot be run before install in general, which this whole issue is about. And in my opinion it’s a mis-feature.
Same issue here which causes a painful workaround on our side.