yarnpkg/yarn

Lifecycle scripts for hosted git and local dependencies not run on install

rtsao opened this issue Β· 40 comments

rtsao commented

Do you want to request a feature or report a bug?
Bug

What is the current behavior?
When a hosted git dependency is installed, lifecycle scripts are not run (e.g. prepare)

If the current behavior is a bug, please provide the steps to reproduce.
Run yarn add user/repo or yarn add github:user/repo. No lifecycle scripts will be run.

What is the expected behavior?
prepare should be run on install (like with npm@5)

Please mention your node.js, yarn and operating system version.
Node.js 9.4.0
Yarn 1.3.2
MacOS 10.13

I've done some investigation as to why this broken. This is what I've found:

#3553 added the correct behavior, but only for git fetching, meaning the dependency was actually fetched via git.

Because Yarn recognizes certain git hosts, Yarn will install these dependencies via HTTP/tarball method instead of git (presumably for performance). So if this logic is triggered (which will happen with dependencies formatted like repo/user or github:repo/user, then the appropriate lifecycle scripts won't be run.

Related issues:

I'd be interested in making a PR to resolve this, but figured I'd make an issue first. I'm thinking that the logic for running lifecycle scripts in src/fetchers/git-fetcher.js should probably be moved into the more generic src/fetchers/base-fetcher, since per #3911 lifecycle scripts should be run for non-git dependencies as well.

@rtsao I think I just ran into this issue, do you know if there's any workaround for it? Otherwise I was just going to vendor the code I wanted from a Github repo

This bit me too. Here's my experience with this bug in case it helps anyone. My team used bower-away to migrate from bower to yarn, so all of our non-dev dependencies are under the @bower_components namespace. As we upgrade and add non-dev packages, we're trying to keep them all under @bower_components, for the sake of consistency.

These are the steps I took...

First try with the github repo and the namespace:

$ yarn add @bower_components/angular-ui-bootstrap@angular-ui/bootstrap#~2.5.6

$ ls node_modules/@bower_components/angular-ui-bootstrap/
CHANGELOG.md	    CONTRIBUTING.md  LICENSE	index.js       misc	     src
CODE_OF_CONDUCT.md  Gruntfile.js     README.md	karma.conf.js  package.json  template

The dist dir is missing due to this bug.

Now try with the github repo, but without the @bower_components namespace...

$ yarn add angular-ui/bootstrap#~2.5.6
$ ls node_modules/angular-ui-bootstrap/
CHANGELOG.md	    CONTRIBUTING.md  LICENSE	index.js       misc	     src
CODE_OF_CONDUCT.md  Gruntfile.js     README.md	karma.conf.js  package.json  template

The dist dir is still missing, again due to this bug.

Now try using package name, rather than github repo...

$ yarn add angular-ui-bootstrap#~2.5.6
$ ls node_modules/angular-ui-bootstrap/
CHANGELOG.md  LICENSE  README.md  dist	index.js  package.json	src  template

The dist dir is now there. However, I want it to be under @bower_components, since that's where all my other non-dev deps reside, so this doesn't work for me.

Try specifying package name instead of github repo, but using the namespace...

$ yarn add @bower_components/angular-ui-bootstrap@angular-ui-bootstrap#~2.5.6
error An unexpected error occurred: "http://npm-registry.my-company.com/@bower_components%2fangular-ui-bootstrap: Not found".

Error! Why doesn't this syntax work??? Is there no way to specify a package name along with a namespace??

I ended up working around this by installing using the package name without the @bower_components namespace, and then grabbing the resolved URL from yarn.lock, and running yarn add again using that:

$ yarn add @bower_components/angular-ui-bootstrap@http://npm-registry.my-company.com/angular-ui-bootstrap/-/angular-ui-bootstrap-2.5.6.tgz#23937322ec641a6fbee16498cc32452aa199e7c5

This works, but is obviously less than ideal, and I wasted over an hour on it.

Yarn maintainers, this bug has now been open for over 3 months. In addition, there are 7 related bugs, and 16 votes. Why has this not been addressed?

Aliases are very experimental and we don't really recommend using them - even if they work, they break the expected semantics in various ways, especially with regard to peer dependencies. I would recommend you to find a way to use the packages from npm without using the @bower_components namespace.

Yarn maintainers, this bug has now been open for over 3 months. In addition, there are 7 related bugs, and 16 votes. Why has this not been addressed?

Mostly because I missed this issue, sorry @rtsao, you did a good investigation! Moving this logic to the base fetcher could work, yes. It would have to be called only in some cases though, since we don't want it to happen for regular tarballs (which are expected to already have been packed).

Is this ever going to be addressed? I switch to Yarn because npm was falling behind on crucial features, but now it looks like the pendulum is swinging back again. This issue has been known in one ticket or another for probably 16 months now. Since at least version 0.23.x.

Yarn is a community project. If a feature is so problematic, we invite you to contribute to fix it. Right now nobody stepped in yet, and it isn't something we are facing ourselves, so it's lower priority for us than other fixes and features. Still, if you open a PR, we'll gladly review it πŸ™‚

Totally understand if that's the holdup and don't get me wrong, I'm very thankful for the hard work! I would be happy to contribute when I find the time... Always the excuse I know.... My question was meant to be a, "Why isn't this a higher priority?" and not a "Why isn't this fixed dammit!?"

Here's why I think this is a big issue:

Github has a billion forks people definitely want to use as they would use a normal package. I avoid forking things quite a lot because npm and yarn both didn't use to manage this. It's actually what got me to try Yarn in the first place. Though it didn't have it either, I stayed for those sweet, sweet yarn.lock files.

I'm not sure if npm has addressed this either, I'm just surprised it's not a higher priority. Not being able to readily use both Github and local forks as expected massively increases iteration time and I feel like it must be a single line somewhere.

Can you possibly point me towards the part(s) of the codebase that manage invoking post-install scripts?

Lifecycle scripts are executed here, which is called through here and here.

Why isn't this a higher priority?

Because no maintainer seem to have been suffering from this issue - it's not to say that the problem doesn't exist, but we all tend to work first on what affects us πŸ™‚ We do try to fix problems other people report when we get the chance, but time is scarce and some help is always appreciated.

Thanks very much @arcanis! I'll poke around in those files when I find some downtime and see if I can find a fix.

Is there a recommended workaround for this? I'm using URLs like git+https://github.com/user/project.git in my package.json and hitting the same problem where prepare is not executed.

3cp commented

My workaround is back to npm. I got too many private repos using prepare script, so I have to tolerant the slowness of npm, which is actually not too bad these days.

I just use a postinstall in the main package.json, that cds in any git submodule I might have, and runs yarn in there too. Silly, but at least it works, until yarn actually works as expected :D

I just hit a variant of this issue and it's a real pain.

I've got a library package that includes sources and a non-trivial build that I want to execute when the package is installed as a dependency. The runtime functionality supported by the package is not available to the dependent package until this build process completes.

I do not want the client (i.e. derived package that declares dependency on my lib package) to have any knowledge or direct coupling to the details of how the lib package is installed.

I believed that declaring a postinstall rule in my lib package was all that would be need to automatically execute my lib package build when it's installed.

But, unfortunately this lifecycle hook doesn't seem to ever get called iff the dependent package declares the dependency using one of the git syntax variants.

So this means that yarn install doesn't act consistently; the results are radically different based on how the package is installed.

There's been a PR (#6131) to fix this open since August 2018. Is anything going to happen?

I'm also having this issue, it would be great to see resolved. Has anyone found a workaround to force Yarn to use git+ssh dependencies as non hosted?

We're also having this issue and I wanted to add flavor/motivation as to why: code review.

Assume we have two repositories: package-foo and app-bar.

app-bar is an application we're working on that depends on a custom package we wrote package-foo but needs new functionality implemented in package-foo in order to implement a new feature in app-bar. We want to be able to submit to PRs at the same time (one for each repo) for our internal code review process.

The issue here is if a developer submits a PR for app-bar that relies on unpublished modifications to package-foo (because those changes themselves are undergoing code review in their repository) there's no way to indicate in app-bar/package.json that the changes rely on new functionality implemented in an unpublished commit of package-foo w/o using git URIs and this bug means this process doesn't work if package-foo is written in typescript or has any build process defined before it's require-able at runtime.

Our workaround in the meantime is that we don't submit a PR for app-bar until package-foo's PR is merged and published. Then we submit a PR with the actual npm version of the package referenced in app-bar. Which might be the "more correct" way to do this since we'd have to change them to version numbers before merging anyway.

Hi, this issue is wired but this is what worked in my case:

I have a GitHub repo, with this "package.json script" which runs babel (for some "jsx" transforms):
"scripts": { "prepare": "babel src --out-dir ." }

And I reference as a dependency in my project like this:
"@namespace/repo-name": "github:namespace/repo-name"

Now I can run yarn and it usually runs the "prepare" script correctly, but sometimes remove the yarn.lock file is necessary.

The approach from @zicrox seems working. But if I add my build directory to .gitignore, yarn will still skip the prepare script (I have no clue how these two are connected). Hopefully someone would confirm this.

@qinsoon I managed to get mine working by adding my dist folder to the include in package.json:

Slice of package.json:

  "main": "dist/index.js",
  "files": [
    "dist/"
  ],
  "scripts": {
    "clean": "rm -rf ./dist",
    "build": "tsc",

slice of .gitignore:

**/node_modules/
yarn-error.log
dist

Hopefully that's helpful...

I managed to get mine working by adding my dist folder to the include in package.json

This right here. Thanks @toshi38

Same issue for local dependency like file:/Users/user/something

Another one here who was confused on why I wasn't seeing any build files on the installed package.

njlr commented

In my case the prepare script was executed but the build artefacts were deleted by Yarn because they matched the .gitignore. I solved this by adding an empty .npmignore, which Yarn picks over the .gitignore.

OliDM commented

@njlr bless you for i was already starting to get a real headache before seeing your comment.

I came to this issue with a typescript package, I made a minimum working example here: https://github.com/richard-ejem/tspackage

See my SO question https://stackoverflow.com/q/61754026/1660584 (and my own answer to it) - it is weird that yarn keeps index.js in the dist folder, therefore the prepare script is not skipped, just its results are removed, except of index.js for unknown reason.

It also shows a difference in npm and yarn behavior, npm does not remove files created by prepare script at all.

I've added the empty .npmignore file but prepare output is still not present so I think that the prepare script is not being run... yarn version 1.12.3. npm install <my-git-repo> works fine.

I've added the empty .npmignore file but prepare output is still not present so I think that the prepare script is not being run... yarn version 1.12.3. npm install <my-git-repo> works fine.

That's pretty old version, have you tried current?

I've just upgraded to v1.22.5 and still no success.

Also present in v2

@KishanBagaria If you use the prepack script as documented, it'll work just fine in Yarn 2.

@arcanis I'm not going to pick a yarn specific solution; doesn't feel very futureproof, a solution that's compatible with other package managers is preferable. A perfect example for this would be if I'm running a build and launch of an up within a slim docker container running node; I'm not gonna bother having yarn as a dependency for that.

However, thanks for your assistance regardless!

I don't know what you mean, prepack has been a standard script for ten years. It's nowhere Yarn specific.

Ah, my bad!

There has been an open PR for this for 2 years! How is this still a problem? I've been using and loving yarn for years, but this one bit me and cost ALOT of time

This issue is fixed in Yarn 2+, and the fix will not be backported in 1.x. Please check the Migration guide and continue from there.

This issue is fixed in Yarn 2+, and the fix will not be backported in 1.x. Please check the Migration guide and continue from there.

Yep, and Yarn 2.0 is why I stopped using Yarn!

@arcanis I don't see prepare mentioned anywhere at https://yarnpkg.com/advanced/lifecycle-scripts. Is it supposed to be mentioned there?

@arcanis I don't see prepare mentioned anywhere at https://yarnpkg.com/advanced/lifecycle-scripts. Is it supposed to be mentioned there?

According to the Yarn Rulebook Yarn 2 uses prepack instead.

@arcanis I don't see prepare mentioned anywhere at https://yarnpkg.com/advanced/lifecycle-scripts. Is it supposed to be mentioned there?

According to the Yarn Rulebook Yarn 2 uses prepack instead.

Prepack is not the same. They’re run at different times.

Prepack is run before packing. Not after installing unlike prepare.

@arcanis I don't see prepare mentioned anywhere at https://yarnpkg.com/advanced/lifecycle-scripts. Is it supposed to be mentioned there?

According to the Yarn Rulebook Yarn 2 uses prepack instead.

Prepack is not the same. They’re run at different times.

Prepack is run before packing. Not after installing unlike prepare.

Yes, in npm prepack and prepare are run at different times.

But in Yarn 2 prepare is deprecated and never run (according to the Yarn Rulebook). Instead, it is prepack that is run both before packing and after installing (but only for Git dependencies, since Yarn first fetches the repository and builds it using yarn pack, according to the Lifecycle Scripts).

Any workarounds for v1 users?