-
Notifications
You must be signed in to change notification settings - Fork 2.7k
link bins of transitive deps to top level #3310
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
Conversation
Getting this error on Appveyor:
These tests pass on my machine; OSX w/ Node 6.3.0. If someone could help debug or even figure out how to reproduce, I would appreciate it! I tried using the same node version and test command that appveyor uses:
|
I'm getting errors on my pull request, too (#3308)...they're not related to anything I altered (as far as I can tell) |
In this case it is failing in my test that I added, and I did use calls to The same test passes on the Travis build (that build fails due to an out-of-memory error though, which I doubt is my fault). Must be some environment different between Travis and Appveyor? Just realized Appveyor is running Windows builds. That's probably the difference. Nothing in the Node docs for fs.readlink indicate that it doesn't work on Windows though... ugh, I'm going to have to turn on my Windows laptop 😞 uhhhh if I run the same branch on my Surface Pro 3 i7 Win10, I get 60 failing tests and it takes an astonishing 647 seconds. 😢 sigh ; oh, right, they aren't actually symlinks when on Windows. It outputs a shell script with the same name instead. I'll probably have to have my unit test verify on Windows by looking for a string in the shell script instead of checking a link... working up a fix... |
Alright, current status of this PR: I fixed my unit tests to verify what directories the bin links point to so that they work on Windows and unix/mac.
|
Did some analysis on various combanitions of dependency installs using npm. Scenario: Transitive dependency having bin script
npm behavior: [email protected] is symlinked in node_modeules/.bin
Scenario: Transitive dependency having version that is overridden by newer version as the direct dependency
npm behavior: [email protected] is symlinked in node_modeules/.bin and [email protected] is symlinked to node_modules/sample-dep-eslint-3.10.0/node_modules/.bin
Scenario: Transitive dependency having version that is overridden by older version as the direct dependency
npm behavior: [email protected] is symlinked in node_modeules/.bin and [email protected] is symlinked to node_modules/sample-dep-eslint-3.10.0/node_modules/.bin
Scenario: Transitive dependency having version that is overridden by newer version as the dev dependency
npm behavior: [email protected] is symlinked in node_modeules/.bin and [email protected] dependency for sample-dep-eslint-3.10.0 module is ignored. This same behavior exists when dev dependency specifies an older version.
Scenario: Transitive dependency having version that is overridden by older version as the dev dependency
npm behavior: [email protected] is symlinked in node_modeules/.bin and [email protected] dependency for sample-dep-eslint-3.10.0 module is ignored.
Scenario: Transitive dependency having version that is overridden by newer version as the optional dependency
npm behavior: [email protected] is symlinked in node_modeules/.bin and [email protected] is symlinked to node_modules/sample-dep-eslint-3.10.0/node_modules/.bin. This behavior is same as direct dependency.
Scenario: Transitive dependency having version that is overridden by older version as the optional dependency
npm behavior: [email protected] is symlinked in node_modeules/.bin and [email protected] is symlinked to node_modules/sample-dep-eslint-3.10.0/node_modules/.bin. This behavior is same as direct dependency.
Scenario: Transitive dependency having version that is conflicting with another transitive dependency version
npm behavior: [email protected] is symlinked in node_modeules/.bin and [email protected] is symlinked to node_modules/sample-dep-eslint-3.19.0/node_modules/.bin. Here it seems like npm add the modules in alphabatical order and transitive deps of first dependency is installed at top level.
Scenario: Transitive dependency having version that is conflicting with another dev transitive dependency version
|
@snagi this is awesome work! thank you! It's probably worth copying this information back to the issue #2874 too, since there are other details there. If you are willing to tackle it, I think it would be really worth it to add some of these cases to the unit tests of this PR. I only added one relatively simple one. The one that I know will be different is when 2 transitive deps both depend on different versions. NPM seemed to pick up whichever was "first", and my implementation here will pick the one with the greatest version (which IMO is "better" and I think we should leave it this way instead of the NPM way). Thanks so much for the help looking into this issue! |
There could be an issue in npm as well about how it handles duplicate transitive dependencies -
npm randomly (or in alphabatical order of direct deps) picks a version for transitive dep, while flattening the tree. This does not matter for the execution of js code as other transitive version is install correctly in the parent dependency. But for bin script, it might be an older version. Logic in yarn is to install the transient dependency version, that is most dependent by other modules, at root/top level.
In this regard symlinking the latest version of transitive dependency might not be appropriate as it would be linking to a different version instead of one installed at top level. May be we should symlink only those modules/versions which are installed at root level in flattree.
Better approach would be to identify and flag the packages which are being hoisted at top level in package-hoister.js. I am still trying to get my head around how the tree is being built as part of package-hoister.js, so will probably comment later on where we could flag that bit. |
There is some difference in how npm and yarn handles different versions of transitive dependencies. There are some pros with both yarn apporach (which ever is most dependent on goes at top level) and using latest version at top level. I am happy to contribute these tests to your PR, will work on this tomorrow. |
I had the thought to make a However I'm pretty sure that somewhere it does get a count of how many other packages depend on each package, as part of another improvement that put the most-used packages closest tot he root level do avoid more duplication (and save HD space). Part of me doesn't really mind if the root-level bin isn't exactly the same as NPM's, because if another package has install scripts that do care, they will be run in the context of the nested bin link anyway, so should be using the correct version. But if we can sort it out to work the same as NPM then let's do it 👍 |
@snagi taking a look at the code again this morning; In reply to your comment:
Up in the PR notes I mention that it makes these transitive links first based on highest-version, but after that, they are overwritten for top-level direct dependencies. It just happens at a different spot in the code (because I left that part of the original implementation alone). |
…mments. Changed bin linking to match this behavior
Finally got some time to come back to this...
The one test scenario I ignored was:
I ignored this one because to me it seemed like an NPM bug. It didn't make sense to me that a transient dependency would be linked in its nested directory if there was a conflicting Local OSX
Appveyor : node_version=6 seems to be failing due to:
but that seems to be a common problem. The node_version=4 job passes. Any additional feedback anyone? If not, I'd love to see this get merged. @snagi @bestander |
Great work, @rally25rs! |
Awesome @snagi, @bestander, @rally25rs! Have been waiting to get this sorted out. Thanks a lot. |
Since Yarn 0.25 (yarnpkg/yarn#3310), Yarn transitively symlinks binaries into node_modules/.bin. (Previously, it would only sync top-level binaries--i.e., binaries that we directly depend on.) The new behavior results in adding all sorts of junk to the path--notably, a JavaScript which reimplementation that shadows the system-provided which. Ew. Avoid putting node_modules/.bin on the PATH by using full paths to the binaries within, or using `yarn run` if within pkg/ui.
Since Yarn 0.25 (yarnpkg/yarn#3310), Yarn transitively symlinks binaries into node_modules/.bin. (Previously, it would only sync top-level binaries--i.e., binaries that we directly depend on.) The new behavior results in adding all sorts of junk to the path--notably, a JavaScript which reimplementation that shadows the system-provided which. Ew. Avoid putting node_modules/.bin on the PATH by using full paths to the binaries within, or using `yarn run` if within pkg/ui.
Summary
Add transitive dependency bin symlinks to top-level
node_modules/.bin
to be more consistent with NPM behavior.Fixes #2874
Replaces #3272
package-linker
now iterates over the flat/hoisted tree and finds the newest version of each package. Each of those packages gets its bins linked at the top-level.After that, during
PackageLinker.save()
each direct dependency has its bins linked (this was existing behavior, so unchanged) which will overwrite any existing bin links from transitive deps. This ensures that direct dependencies always take priority over the transitive ones.Test plan
Stopped skipping a previously removed test, and added an additional test in
__tests__/commands/install/integration.js