Skip to content
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

[Feature] "yarn workspaces focus" option --immutable #1803

Closed
2 tasks done
jtbennett opened this issue Sep 4, 2020 · 19 comments
Closed
2 tasks done

[Feature] "yarn workspaces focus" option --immutable #1803

jtbennett opened this issue Sep 4, 2020 · 19 comments
Labels
enhancement New feature or request waiting for feedback Will autoclose in a while unless more data are provided

Comments

@jtbennett
Copy link

jtbennett commented Sep 4, 2020

  • I'd be willing to implement this feature
  • This feature can already be implemented through a plugin - the workspace-tools plugin in this repo.

Describe the user story

Enable using yarn workspaces focus while also guaranteeing that all direct and indirect dependencies are the exact versions specified in the yarn.lock file.

Consider a monorepo, using yarn workspaces, with two applications that will be deployed independently and a shared library that both apps depend on. The deployed version of App A must:

  • Include App A's dependencies
  • Include the shared library's dependencies
  • Exclude App B's dependencies that are not used by either App A or the shared library
  • Exclude all devDependencies

For [reasons], I use the node-modules linker, not PnP, and do not commit dependencies in the repo.

I can achieve all of that on my CI server with a script that looks like this:

yarn --immutable

# do things across all workspaces that require devDependencies
yarn workspaces foreach lint 
yarn workspaces foreach test
yarn workspaces foreach build

# get rid of devDependencies so we don't deploy them
# **but dependency versions may be updated!**
yarn workspaces focus --production app-a

However, as noted in the comment, there is no way with focus for me to guarantee that yarn won't retrieve a different version of some dependency from the registry.

Describe the solution you'd like

Add an --immutable option to yarn workspaces focus that does the same thing as the --immutable option on yarn install -- guarantees that only the exact versions in yarn.lock are installed. Example:

yarn workspaces focus --production --immutable app-a

Describe the drawbacks of your solution

Increased API surface area and additional logic to test/maintain in the future.

Describe alternatives you've considered

Modify the --production option to behave like install --immutable. The primary use case for --production is to eliminate devDependencies when deploying an application. I would expect most people using --production to also want the dependency versions to be guaranteed. But there may be other use cases, and making --immutable explicit keeps the focus API closer to install.

Additional context

I initially raised this in the comments for #1789, since that issue is also related to using focus when deploying an app. My comment there mentioned that am doing the build inside a docker container. However, I don't think that's really relevant. The same concern about dependency versions applies whether using docker or not.

A related nice-to-have would be to tell focus to behave like yarn v1's yarn --frozen-lockfile --production --offline. That is: "Don't bother checking the registry, use what you already have in cache."

@jtbennett jtbennett added the enhancement New feature or request label Sep 4, 2020
@arcanis
Copy link
Member

arcanis commented Sep 5, 2020

However, as noted in the comment, there is no way with focus for me to guarantee that yarn won't retrieve a different version of some dependency from the registry.

I don't understand this critical part. The focus command will remove the direct dependencies from the other workspaces. It won't downgrade or upgrade anything, so I don't see why you wouldn't be able to guarantee that.

@arcanis arcanis added the waiting for feedback Will autoclose in a while unless more data are provided label Sep 5, 2020
@jtbennett
Copy link
Author

The focus code calls project.install(), but does not pass the immutable option:

await project.install({cache, report, persistProject: false});

That's the same function used for any other install, so my assumption was that it could retrieve a newer version from the registry, (as long as it matches the relevant semver range).

But maybe I am misunderstanding the install behavior. Is it correct to say that project.install() will never modify a version if the dependency is already satisfiable by the lockfile version? Even when the dependency is not already cached?

@paul-soporan
Copy link
Member

If a dependency is locked inside the lockfile and resolver.shouldPersistResolution returns true (basically for all resolvers except the ones that hydrate files directly from the filesystem), the dependency will never be upgraded or downgraded. The immutable option checks if the lockfile was modified by the install, which can only happen if a dependency isn't already locked (aka modifying your package.json without running an install) or if it hydrates files from the filesystem that aren't in the state Yarn remembers.

@jtbennett
Copy link
Author

Great. Thanks for the explanation!

@zyf0330
Copy link

zyf0330 commented Dec 7, 2021

But I have tried that focus changed package version.
I change one package version in package.json, and yarn --immutable gives error The lockfile would have been modified by this install, which is explicitly forbidden, but yarn workspaces focus -A succeeds and actual package version is changed too.

@magnattic
Copy link

Any chance we can reopen this?

There is still a need for an immutable option when focus does not fail on changed package versions, but installs them.

@arcanis
Copy link
Member

arcanis commented Apr 12, 2022

Your whole project must be checked for the immutability check to have any value. One this check is done (typically on CI, at PR time), the flag isn't necessary anymore (and thus has no value in production settings, ie what focus is intended for).

@magnattic
Copy link

I think the problem here is that people want to install just a part of the whole workspace and still make sure that the packages are still the same that they have tested before. This is useful for example when building a docker container for deployment.

Having one big workspace is helpful for development, but during deployment we don't want to drag all workspace folders along just to make sure that nothing changed in one workspace folder.

#1223 seems to go into the same direction.

@Rugvip
Copy link

Rugvip commented Aug 19, 2022

Chiming in that we've also hit this issue as a blocker for us to migrate to Yarn 3. Being able to install only production dependencies in a docker image is important both for build speed, image size, and security, and that of course needs to be done as an immutable install. Tried working around it by adding enableImmutableInstalls: true to config, but that seems to be ignored.

I think perhaps it could be worth revisiting the decision to remove --production from yarn install? I also found it quite unfortunate to have to install the workspace plugin just for this purpose, even through you might not be using workspaces.

@billyjacoby
Copy link

Chiming in here also requesting support for passing immutable to focus!

In our case we don't want to install and build all packages for our react-native app when running our CI pipeline for our web deployments.

@arcanis
Copy link
Member

arcanis commented Sep 17, 2022

I don't quite understand your use case. Do you also run your linting on build, and only on the workspaces you deploy? Because that's essentially what --immutable does.

@billyjacoby
Copy link

I don't quite understand your use case. Do you also run your linting on build, and only on the workspaces you deploy? Because that's essentially what --immutable does.

Doesn’t immutable stop the process if the lock file changes? Maybe I misunderstood, but that’s what i’m trying to achieve.

@arcanis
Copy link
Member

arcanis commented Sep 17, 2022

Doesn’t immutable stop the process if the lock file changes? Maybe I misunderstood, but that’s what i’m trying to achieve.

Yes, and what I'm saying is that you should use it as a validation step before you even reach the deployment step. If you validate that the lockfile doesn't change at a PR CI level, there's no reason why it would change during deployment.

@billyjacoby
Copy link

Is the only way to “validate at a PR” level to use something like husky or is there a different yarn command that i’m not aware of that validates without installing? The latter would be ideal actually

@arcanis
Copy link
Member

arcanis commented Sep 17, 2022

I would guess you run some CI on your PRs, to check they pass the linting / type check / tests? That's where --immutable is meant to be used.

Note however that Yarn automatically enable --immutable when it detects the current env is a CI, so if you do any of what I described above you probably don't even have to change anything, unless you want to be explicit.

@billyjacoby
Copy link

right but all of those things happen after the yarn install which means that I would have already installed all of the packages for the react-native app.

Unfortunately my current setup doesn't allow for zer-installs so we have to run the install step before linting & testing

@JavaScriptBach
Copy link

Doesn’t immutable stop the process if the lock file changes? Maybe I misunderstood, but that’s what i’m trying to achieve.

Yes, and what I'm saying is that you should use it as a validation step before you even reach the deployment step. If you validate that the lockfile doesn't change at a PR CI level, there's no reason why it would change during deployment.

@arcanis in that case, would you consider adding a way to quickly validate the lockfile is in sync without installing all dependencies? Otherwise I would need to add the following checks in CI:

  1. In the "test" job, run yarn install --immutable to verify the lock file is up to date.
  2. In the "deploy" job, run yarn workspaces focus ... to install the subset of packages I want.

It seems like a lot of wasted work in 1) to install all dependencies just to verify a lockfile.

@rogeralmeida
Copy link

Yes, I'm facing the same issue and by reading this issue I suspect we are far from finding a plausible solution.

We have monorepo using workspaces with BackendApp (NestJS), FrontendApp (NextJS), and a SharedLib.
We deploy both BackendApp and FrontendApp on docker containers.

Our end goal is to build slim independent docker images for both backend and frontend with only production dependencies without introducing a dependency version we didn't test locally

At the moment trying to achieve that with a multi-stage Dockerfile looks like the following

  1. In the building stage
  • Copy over the whole monorepo to ensure yarn install --immutable succeed
  • Run yarn workspaces focus BackendApp to strip unrelated dependencies
  • Run BackendApp build command (nest build)
  1. In the runner stage
  • Copy the dist folder from the building stage
  • Copy the node_modules folder from the building stage 😭

That last step, although is not bringing FrontendApp dependencies, is terrible as it is bringing devDependencies 🤢
We cannot replace it with yarn install --immutable since that would require the package.json from modules not relevant (FrontendApp). 😢

Ideally the yarn workspaces focus --immutable BackendApp would behave like @jtbennett suggested:

  • Assume it is --production (nice to have)
  • Installs BackendApp dependencies
  • Check yarn.lock modifications for the BackendApp dependencies ONLY

@eddienubes
Copy link

Stumble across this issue as well.
yarn workspaces focus is in a desperate need of --immutable option!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request waiting for feedback Will autoclose in a while unless more data are provided
Projects
None yet
Development

No branches or pull requests

10 participants