Skip to content

Trigger lifecycle scripts for workspaces#6869

Open
tomasz-sodzawiczny wants to merge 4 commits intoyarnpkg:masterfrom
tomasz-sodzawiczny:workspaces-lifecycle-scripts
Open

Trigger lifecycle scripts for workspaces#6869
tomasz-sodzawiczny wants to merge 4 commits intoyarnpkg:masterfrom
tomasz-sodzawiczny:workspaces-lifecycle-scripts

Conversation

@tomasz-sodzawiczny
Copy link

Fixes #3911

All lifecycle scripts run during install (preinstall, install, postinstall, prepublish & prepare) are now run on the main directory and all workspaces.

@buildsize
Copy link

buildsize bot commented Jan 3, 2019

File name Previous Size New Size Change
yarn-[version].noarch.rpm 1.11 MB 1.11 MB 1.1 KB (0%)
yarn-[version].js 4.47 MB 4.47 MB 3.34 KB (0%)
yarn-legacy-[version].js 4.66 MB 4.67 MB 3.81 KB (0%)
yarn-v[version].tar.gz 1.12 MB 1.12 MB 13 bytes (0%)
yarn_[version]all.deb 815.4 KB 815.83 KB 442 bytes (0%)

@tomasz-sodzawiczny tomasz-sodzawiczny force-pushed the workspaces-lifecycle-scripts branch from 28ccf69 to 7ece7fb Compare January 3, 2019 02:05
@arcanis
Copy link
Member

arcanis commented Jan 4, 2019

I'm not too sure about this one ... I think there's a conceptual issue with regard to peer dependencies.

Let's say you have the following:

  • Workspace A has a peer dependency on B
  • Workspace C depends on A and B@1
  • Workspace D depends on B and B@2

What's the correct action? The way I see it, they are:

  1. The package manager might call the install script twice
  2. The package manager might call the install script once
  3. The package manager might never call the install script for workspaces
  4. The package manager might never call the install script for workspaces with peer dependencies

Both 1 and 2 are quite surprising and probably wrong. 3 is surprising as well, but has an entirely predictable behavior, which I would count as an improvement over the two others. The 4 is similar, but only affects workspaces that can trip on this behavior. While it would seems better at first glance, it makes the behavior confusing: from the end user perspective, I'm not sure they would understand why the install scripts run in a case but not every case.

I'd personally tend to pick option 3, but I clearly understand the use case so I'd be happy if we could find an intuitive way to make it work that I haven't think of!

@tomasz-sodzawiczny
Copy link
Author

@arcanis I am not sure I understand why I would ever run the scripts more than once - the scripts are run when the install script is run on the package itself, not when the package is installed as a dependency.

What am I missing? Can you clarify that?


I realise now I missed something else - the order of running the scripts matters: install, postinstall, prepublish and prepare are run with the assumption that my dependencies are already in place (in my node_modules/).

So when we have packages:

  • A → Z
  • B → Z
  • Z

The flow using the registry would be:

  1. Build and publish Z (Z.prepare runs here - once)
  2. Install A - download prepared Z into node_modules, run A.prepare
  3. Install B - download prepared Z into node_modules, run B.prepare

So I believe with workspaces we should run:

  1. Z.prepare
  2. A.prepare, B.prepare (in any order)

The only case left is when we have circular dependencies - but maybe in this case throwing a warning and not doing anything is a valid solution.

Does that make any sense?

@arcanis
Copy link
Member

arcanis commented Jan 4, 2019

I am not sure I understand why I would ever run the scripts more than once - the scripts are run when the install script is run on the package itself, not when the package is installed as a dependency.

It's not so simple. Imagine that in my previous example B is actually babel, and A uses the peer dependency in order to build itself. In this situation, C expects A to have been built against babel@1 while D expects A to have been built against babel@2. If you only run the install script once, then one of those affirmation will be false.

Of course it's a contrived example, but basically: the peer dependencies might be used in order to build the package. Since different peer dependencies can be provided to the same package, it follows that the package might need to be built against each set of dependencies.

@tomasz-sodzawiczny
Copy link
Author

tomasz-sodzawiczny commented Jan 4, 2019

the peer dependencies might be used in order to build the package.

Can they? We're talking about the scripts that run before the package is installed or even published - aren't the peerDependencies not present then by definition?


edit: Of course it is possible with workspaces, but this is IMO an invalid setup and workspaces don't protect you from it in any way - the same way you can technically require any of the dependencies of sibling workspaces.

@arcanis
Copy link
Member

arcanis commented Jan 4, 2019

That's true for preinstall but not for prepare or the others, which are run after the install. Even if to be fair the npm lifecycle hooks are a huge mess and it's not clear what's semantically correct and what isn't, even after reviewing the closest thing to a documentation they have.

@tomasz-sodzawiczny
Copy link
Author

Oh wow, thanks for pointing that out - you're right, install and postinstall are affected by the peer deps issue.

I did a quick check though, and it seems prepare & prepublish do actually behave as documented - they don't run when installed as a dependency, only on local yarn install and yarn publish.
You can try it out with this package: install-scripts-test-package@1.1.1 (have a look inside the package.json to see what it's doing).

So - we can do nothing about install/postinstall, but I think we can still run the others if we keep the right order. This should be "good enough" to solve the issue for most people, as it's mostly about the prepare/prepublish scripts.

@NathanielHill
Copy link

Can we restrict this to prepare and prepublish and get this merged?

@tomasz-sodzawiczny
Copy link
Author

@NathanielHill Sorry for not following up on this yet - I think prepare/prepublishstill need to get fired in topological order (dependencies first) and I didn't find time yet to dig into that.

Do you know if there is some easy way to do this? I'm guessing this order is already computed somewhere in yarn, but I don't know the codebase well enough.

@NathanielHill
Copy link

Haven't looked at yarn codebase at all. I'm currently solving this by explicitly calling the prepare scripts that I need from the root prepare script.

Tomasz Sodzawiczny added 3 commits February 3, 2019 23:48
* add test for not running `install`/`postinstall`
* add test for running `prepare` in correct order
@tomasz-sodzawiczny
Copy link
Author

@arcanis I updated the PR.

Now the behaviour of yarn install is as follows:

  • preinstall script is run on all workspaces (no specific order)
  • prepare andprepublish scripts are run on all workspaces in topological order
  • install and postinstall are not run on workspaces - due to undefined behaviour with peer deps

Do you think this is a valid solution now?
Do you have any suggestions for the code structure or some additional test to be added?

@itsMapleLeaf
Copy link

Gentle ping on this, still finding myself needing this every so often. Is there anything else needed for this to be merged?

@Gxmadix
Copy link

Gxmadix commented Apr 7, 2022

Hello are there news about merging this PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Workspaces don't run lifecycle scripts when linking local packages

5 participants