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

Workspaces: lock file per workspace #5428

Closed
netanelgilad opened this issue Feb 28, 2018 · 61 comments
Closed

Workspaces: lock file per workspace #5428

netanelgilad opened this issue Feb 28, 2018 · 61 comments
Assignees
Labels
cat-feature fixed-in-modern This issue has been fixed / implemented in Yarn 2+. triaged

Comments

@netanelgilad
Copy link

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

What is the current behavior?
Using yarn workspaces for a monorepo which includes a top level node module creates only a single yarn.lock at the root of the monorepo, with no yarn.lock that is specific for the top level node module.

What is the expected behavior?
I want to use yarn workspaces to manage a monorepo that includes both apps (top level node modules) and libraries. But having only a single yarn.lock file at the root of the monorepo prevents me from packaging my app into a docker image for example. I would love a way to get a yarn.lock file for chosen workspaces that need to have one of their own, because that workspace may later be used outside of the monorepo.

An example:
If I have a monorepo with 2 workspaces: workspace-a and workspace-b. workspace-a uses some of the exported modules from workspace-b. If I want to package workspace-a into a docker image (or any other way of packaging that workspace by itself, without the whole monorepo), I don't have a yarn.lock for it. That means that when I'll move the files of workspace-a into a different environment apart from the monorepo, I'll be missing a yarn.lock file and when installing dependencies, I'll lose all the advantages of a lock file (knowing I'm installing the same dependencies used in development).

I'm quite surprised I couldn't find an issue about this. Am I the only one who wants to work with monorepos that way? Maybe I'm missing something? My current workaround is using lerna without hoisting at all, so I'll have a lock file per package.
The recently released nohoist feature also doesn't seem to help (though I hoped), as it doesn't create a different yarn.lock per workspace.
This issue is somewhat related to this comment on another issue. Thought that it may deserve an issue of its own.

Please mention your node.js, yarn and operating system version.
node 8.9.3, yarn 1.5.1, OSX 10.13.3

@connectdotz
Copy link
Contributor

based on yarn blog:

When you publish a package that contains a yarn.lock, any user of that library will not be affected by it. When you install dependencies in your application or library, only your own yarn.lock file is respected. Lockfiles within your dependencies will be ignored.

it doesn't seem necessary to bundle yarn.lock when publishing the individual package... it is more a development artifact for the whole repo, therefore, no need to put it in each package.

@malbertSC
Copy link

@connectdotz it may not be needed for a library or published package but for building a docker container you're going to want to deploy somewhere it definitely would be.

@connectdotz
Copy link
Contributor

sure... but wouldn't the development docker container just have the whole repo and therefore the yarn.lock anyway? I can see we use docker containers to test our monorepo project for different OS or platforms, in which case we just deploy the whole repo and its yarn.lock. Can you give me an example use case that you need to deploy individual packages from the monorepo project into docker containers during the development cycle, so I can get a more concrete understanding...

@netanelgilad
Copy link
Author

So for us, we don't want to package the whole monorepo into the resulting docker container. We are using docker in production and those images should be as light as possible. Our mono repo is quite big and contains multiple microservices that share code between them using library packages (and some of the libraries are relevant for some of the microservices, but not all). So when we package a microservice, we want the image to contain the files of that microservice and any other dependencies as proper dependencies - downloaded from our private registry, and built for the arch of the docker image.

So I think the main consideration here is to keep our docker images as light as possible, and packaging the whole monorepo doesn't fit our needs. Also, when we run "yarn" inside the image of the microservice, we don't want to have symlinks there, just a normal dependency.

The solution here doesn't have to be creating a yarn.lock file per workspace, it could also be a yarn command, that helps in the process of packaging a given workspace, generating a yarn.lock file for a workspace on demand, etc..

Hope it helped clearify the use case.. 🍻

@connectdotz
Copy link
Contributor

@netanelgilad thanks for details, it does help to clarify that your use case is more about publishing individual packages, for production or development, to docker containers. Please join the discussion in #4521 so we can start to consolidate them.

@Pajn
Copy link

Pajn commented Mar 11, 2018

While I can se the use of individual lock files, they are not necessary. If you run docker from the root of the repo with the -f flag pointing to the individual files you'll have the whole repo as context and can copy in the package.json and yarn.lock from the root.

You only need the package.json for the packages you will build in the image and yarn will only install packages for those package.json files you have copied in even thou the yarn.lock includes much more.

EDIT: With that said. it causes docker cache to not be used for package changes in any package even though it is not included in the build

@constfun
Copy link

#4206 is related/duplicate, and the use-case described there is exactly the problem we're facing:

Let's say we have ten different packages. We want all of them to live in their own repository so that people can work on them independently if they want, but we also want to be able to link them together if we need to. To do this, we have a mega repository with a submodule for each package, and a package.json that references each of these submodule as a workspace.

@the-spyke
Copy link
Contributor

I have similar issues with workspaces. My project is a web-app which depends on many local packages:

web-app/
|--node_modules/
|--packages/
|  |--controls/
|  |  |--src/
|  |  |--package.json
|  |--utils/
|     |--src/
|     |--package.json
|--src/
|--package.json
|--yarn.lock

Workspace packages controls and utils aren't published and are used by paths. The issue is that I need to release controls package (yarn pack) and I wan't to build/test it on its own. That mean's I want to do yarn install inside web-app/packages/constols/. With workspaces it will use top-level web-app/yarn.lock file together with top-level web-app/node-modules/. So, It installs all packages instead of a subset, specified in web-app/packages/controls/package.json. But I need to check that my package has all required dependencies in it's own package.json and doesn't work by luck of filling missing deps from other workspaces.

There are 2 possible solutions:

  1. If it isn't root, use root's yarn.lock, but install only packages specified in local package.json.
  2. Do not search for top-level configs, but .yarnrc/.npmrc.

@intellix
Copy link

Also struggling with this. We've got an Angular CLI project alongside our API so they're in the same repository and trying to push the frontend to Heroku.

We're using a buildpack which tells Heroku to jump up to the frontend repository first: https://github.com/lstoll/heroku-buildpack-monorepo

Problem is, there's no yarn.lock inside that nohoist package so Heroku just installs with npm and we end up with all new packages rather than the locked ones

@johannes-scharlach
Copy link

You can just use the global yarn.lock file with the individual packages. I've recently approached my Dockerfile like this:

WORKDIR /app
ENV NODE_ENV=production

ADD yarn.lock /app/
ADD package.json /app/

# Only copy the packages that I need
ADD packages/my-first-package /app/packages/my-first-package
ADD packages/my-second-package /app/packages/my-second-package

RUN cd /app && yarn install --frozen-lockfile

This will install only dependencies that are actually in use by the two packages I copied and not by anyone else.

I have a build process where first I'd like to create a release artifact from one package and then not have any of its dependencies installed. This is fairly easy with Docker's multistage build

  1. Add only yarn.lock, package.json and the UI package to docker
  2. run yarn install --frozen-lockfile
  3. run the build process
  4. start a new stage and add yarn.lock, package.json and the necessary runtime packages/workspace folders
  5. Do a COPY --from=<stage> for the built artifact
  6. run yarn install --frozen-lockfile and expose a RUN command.

And you'll end up with a small container that only contains the dependencies specified in your yarn.lock file and needed in production.

@netanelgilad
Copy link
Author

@johannes-scharlach
I've ended up using an almost identical method to what you are describing. multistage build is a good tip :).

@connectdotz I think that from my end, this issue could be closed and we can keep working on issue #4521. Since the main yarn.lock file could work with a subset of the packages, it seems like a yarn.lock per workspace is not necessary (even though I still think this could be a better dev workflow 😉 ). But the issue in #4521 is still important, because in the solution we got to here, we need to mention every dependency workspace in the Dockerfile even though yarn should know the interdependencies and how "vendor" a given workspace.

@borekb
Copy link

borekb commented Oct 22, 2018

I spent the last couple of days trying to convert our monorepo to first Lerna and then Yarn workspaces. Yarn worked generally more reliably and it's really close to what we need, especially with the recent introduction of yarn workspaces run <script> and other nice things like wsrun.

However, the single yarn.lock is a pain point:

What would you think about Yarn workspaces being just a tiny core – a declaration of packages without any specific functionality. For example:

  • If you wanted to run some script across your workspaces, you'd do yarn workspaces run <script>.
  • If you wanted a single lock file and hoisting (are the two necessarily tied together?), this would be your root package.json:
    "workspaces": {
        "packages": ["packages/*"],
        "hoistDependencies": true
    }
  • If you wanted to migrate your current lockfiles to a hoisted structure, you'd run yarn workspaces hoist-dependencies.

Etc. These are just examples and in practice, some features would probably be opt-out instead of opt-in (for example, people expect a single yarn.lock and hoisting by now) but the general idea is that workspaces would be a lightweight foundation for repo-wide tasks.

What do you think?

@Gudahtt
Copy link
Member

Gudahtt commented Oct 22, 2018

I believe the problem this feature request is addressing is the same as in #4521 . A command to do essentially what @johannes-scharlach describes would certainly be more feasible than a lockfile per workspace.

There is also an RFC open right now for nested workspaces, which sounds similar to this feature request though I believe it's solving a different problem.

@arcanis
Copy link
Member

arcanis commented Oct 23, 2018

What would you think about Yarn workspaces being just a tiny core – a declaration of packages without any specific functionality

Workspaces won't drastically change, I think we're satisfied with their current interface.

If you wanted to run some script across your workspaces, you'd do yarn workspaces run <script>

That's already possible (v1.10, #6244).

If you wanted to migrate your current lockfiles to a hoisted structure, you'd run yarn workspaces hoist-dependencies.

Since we won't change the workspace interface it would be the opposite (dehoistDependencies).

What I don't like about this is that it takes a technical behavior (hoisting) and tries to turn it into a semantical behavior. You should focus on the user story and then figure out the implementation rather than the opposite.

In this case, I think your use case ("Installing dependencies in specific package only") would be better solved by extending yarn --focus.

@borekb
Copy link

borekb commented Oct 23, 2018

I guess the core question is whether hoisting and a single yarn.lock file are strictly necessary for workspaces. I mean, is is what truly defines them or is it "just" the first feature they historically got?

For example, in our use case, the best hypothetical behavior of workspaces would be:

  • Hoist node_modules at development time for efficiency.
  • Keep local yarn.lock files for build (we build specific packages in Docker, something that other people mentioned in this thread as well) and also so that packages can lock their specific versions. See also Migrate lock file to workspace root #6563.
  • Run scripts via yarn workspaces run <script> even if you don't need (or must avoid) hoisting.

Hoisting can be disabled with nohoist, run can be "disabled" by just not using the command but it's not possible to "disable" the single yarn.lock file, and I'm not sure if it's such a core feature that it cannot be disabled or if it just hasn't been requested enough yet :)

@spion-h4
Copy link

I think the best way to solve this would be to have yarn install --app-mode package@version

That way, you could simply copy the workspace lockfiles when publishing your app at a certain version, and install in app-mode will respect the bundled lockfile.

Yarn doesn't have to install the entire lockfile; it should easily be able to extract only the part of the dependency graph relevant to that package.

@spion-h4
Copy link

spion-h4 commented Oct 31, 2018

In fact this might be fairly easy to do manually even now:

  • download the package zip from the registry directly (yarn has no equivalent, npm does: npm pack package@version)
  • extract the gzip into node_modules/package
  • cd into node_modules/package
  • run yarn install --production from there (it will respect the bundled lockfile)

edit: unfortunately this is all wrong, as the workspace lockfile does not include the versions of packages within the workspace, which might be dependencies of the app package. There would need to be something more involved than copying when creating app lockfiles from workspace lockfiles.

@samuela
Copy link

samuela commented Feb 12, 2019

I'm not exactly sure if separate lockfiles is the answer, but I have a similar problem. I have a monorepo set up with a CLI and a backend. The CLI requires a few packages that are platform-specific and only work on desktop machines with a particular setup. On the other hand I need to be able to build my api into a docker image, which is fundamentally impossible in the current implementation of workspaces.

@kristian
Copy link

Very similar use case than @samuela here! This one would be massively helpful!

@loopmode
Copy link

My use-case might seem laughable compared to the other, "real" ones. But I have a monorepo for some utils - in ths case react hooks - inside packages/*.

I have a second workspace next to packages/*, and that is local/*. This is actually on gitignore, and the idea is that developers in the company may do whatever they like in there, for example put create-react-app apps in there and test the hooks during development.

Now, although the local/* packages are on gitignore, the root yarn.lock is simply bloated and polluted - and checked into git - because of the local workspaces.

What I would wish for is a way to specify that some workspaces shall use some specific lockfiles, e.g. some mapping like so:

  "workspaces": {
    "packages": [
      "packages/*",
      "local/*"
    ],
    "lockfiles": {
      "local/*": "./local.yarn.lock"
    }
  }

Or even a way to specify "do not put anything from this workspace into the lockfile at all".

But yeah, mine is not a serious use-case in the first place :)

@ux-engineer
Copy link

I'm not exactly sure if separate lockfiles is the answer, but I have a similar problem. I have a monorepo set up with a CLI and a backend. The CLI requires a few packages that are platform-specific and only work on desktop machines with a particular setup. On the other hand I need to be able to build my api into a docker image, which is fundamentally impossible in the current implementation of workspaces.

You nailed it – as I see it, one of the very core benefits of yarn.lock file is for creating frozen-dep production builds! Did the creators of Yarn forget that?

@loopmode
Copy link

Maybe it's also worth moving this discussion to npm itself, which supports workspaces too starting with v7.0..

@GrayStrider
Copy link

GrayStrider commented Jun 19, 2020

I've been researching related topics and would like to add some clarification to my comment above (mostly aimed at less experienced developers I suppose, since the issues I was facing were to some extent due to my failure in understanding the importance of lockfile).

"locking all dependencies to specific version" is called pinning; unfortunately, it will not prevent things from potentially breaking if sub-dependency updates (final paragraphs of the article here), which I haven't considered. That is exactly what lockfile meant to prevent from happening.

I've wasted more than enough hours on breaking updates in the past on multiple occasions; I will experiment with using lockfile in monorepo, since I'd rather deal with merge conflicts and organizational headache, than invisible bugs caused by minor updates.

Above said, I'm very much looking forward to any progress on this issue

@the-spyke
Copy link
Contributor

@migueloller Individual lock-files mean individual Yarn Monorepos. You can't have a lock-file for a Workspace because it breaks deps uniformity in the Monorepo. If you want to do so, you're going away from the original idea of Yarn Monorepo: it's about unifying, hoisting, and reducing deps into a flat list in the root instead of having different versions (and even whole subtrees) in different workspaces.

@dan-cooke
Copy link

dan-cooke commented Jun 19, 2020

@the-spyke but the original issue is about exactly the opposite. A lock file per workspace.

I fail to understand how you cant have a lockfile per workspace.

It breaks deps uniformity in the Monorepo? Sure for development. But the whole purpose of shared dependencies goes out the window when you must deploy lightweight micro services from each of your workspaces

Shared, hoisted deps only makes sense in development.

For a workspace to live in Docker, it requires a lockfile.

@the-spyke
Copy link
Contributor

the-spyke commented Jun 19, 2020

@dan-cooke As you can see, I had this issue in 2018 too, but now I have different opinion.

You're saying Docker and Microservices. But what if I develop a regular npm package? I have no production dependencies subtree to pin, because they will be provided by end-user according to my dependencies specification. So, what I want is to maximize my development experience, and that what Monorepos and Yarn Workspaces perfectly do.

Same time, if you're developing microservices (MSs) there 2 possible situations:

  1. Independent projects. Some MSs are in development, some weren't touched in years. In this case they are completely independent. It is possible to have UserService using LoggingService@1.2.3 and MessagesService using LoggingService@2.3.4. That's not that easy world where you just link folders from Workspaces to the root node_modules. So, no point in having root lock-file. Create separate files (roots) and manage them independently. That called a Multirepo in Yarn docs. But now what you're saying is "I want to run tasks in different folders from the root folder for convenience". And that's a completely different topic.

  2. Projects with unified dependencies like Jest/Babel/etc. This is what Workspaces were made for, but in MSs there additional requirements. During CI stages like linting and testing all works fine because it works the same as you do on a developer machine: deps installed by Yarn into root node_modules and flattened out. Just with addition that you probably cache the yarn install phase to speed up concurrent builds.

    In production it's completely different: starting from that you only need deps for one workspace and ending with how to install that utils package? Should it be linked or downloaded as tarball? So, what you really need is not having lock-files per Workspace, but having a command like yarn install --prod <workspace> that you can run specifying a Workspace and it will install only production deps and same time ignore other not referenced Workspaces. Like if my data WS depends on utils WS, but not on logging WS, then logging itself and its deps should not appear in node_modules. A similar result, but a completely different approach to a "lock-file per workspace".

    If you're publishing build packages into a repository (npm, Arifactory, GutHub), you can get similar behavior by just copying lock-file into a Workspace and doing yarn install --prod here. It should warn about outdated file, but instead of recreating if from scratch with fresh versions it should just remove excess deps from it (just tried and looks legit). Should be even better and robust with using Offline Mirror.

    And in the end you have Focused Workspaces implemented exactly for Multirepos.

So, what I was saying it that maybe the issue doesn't look like what it is.

@dan-cooke
Copy link

dan-cooke commented Jun 19, 2020

@the-spyke
I see what you are saying. I think it definetly does come down to how this result is achieved. Perhaps a lockfile per workspace is not actually the best way to achieve the desired result. You make some good points.

Its definetly not a "one size fits all" solution anyway

@migueloller
Copy link

migueloller commented Jun 26, 2020

@the-spyke, your bring up good points. Perhaps more thought needs to be put into what problems Yarn workspaces was designed to solve and if using it to manage large monorepos is aligned with that design.

I'm curious as to how you would solve a scenario like this:

.
└── packages
    ├── app1
    ├── app2
    ├── lib1
    ├── lib2
    └── lib3

lib3 is a shared library and is depended on by app1 and app2. lib1 is only used by app1 and lib2 is only used by app2. Based on your suggestions, lib1 and app1 should be in their own workspace with their own lockfile and same with lib2 and app2. Now, the question is what to do with lib3 if both app1 and app2 depend on it? Perhaps one could make it so that both workspaces (app1 and app2) add lib3 to their workspace and then run yarn install in each app? Does Yarn allow this? Would there be any conflicts if one wanted to run both app1 and app2 in development locally (perhaps app1 is a React app and app2 a GraphQL API)? This sounds like it could work.

The next question to ask would be "How does one get the benefits of hoisting with this method?" For example, if app1 and app2 share a lot of common dependencies, it would be nice to hoist them. I can see how this might be out of scope, though, and is a problem to be solved by Yarn PnP (it doesn't copy files to node_modules and instead has a shared cache).

I'm going to give this a shot and will report back. If this ends up working, then perhaps we've just been using Yarn workspaces wrong all along...

EDIT: I tried it out, and it does work.

@migueloller
Copy link

migueloller commented Jun 26, 2020

I've changed my stance now and realize that while having an individual lockfile per workspace might be the first thing that comes to mind when managing an entire monorepo with Yarn workspaces, it might not be the right question. A better question might be "Is Yarn workspaces designed to manage a monorepo?". The answer, as usual, is "it depends".

If you're Babel and you have a single team working on the monorepo and everything is meant to change in lockstep, then yes, this is what Yarn workspaces was designed for. But if you're an organization with multiple teams and you're using a monorepo, you likely don't want to manage the entire monorepo with a single Yarn workspace root. You probably just want to use Yarn's default behavior or multiple Yarn workspace roots within the monorepo. This will be determined by what apps you're building, how many teams there are, etc.

For us, it became clear that for each deployable entity (in our case there's a Dockerfile), we want to have a separate yarn install done for each one (whether it's a worksapce root or not). This provides clarity around code ownership, allows for isolated deployments that happen at different cadences, solves caching issues with lockfiles and Docker, etc. There are a few downsides to this, though:

  • What about duplicated node_modules packages? This is a common class of problems with monorepos and while Yarn workspaces help with hoisting, it's not a general monorepo solution. There are other solutions, though. For example, Yarn PnP takes care of this. You could also use Lerna without Yarn and use the --hoist option.
  • What about the utility of running command across workspaces during development? Again, Yarn workspaces lets you do this, but that doesn't mean one should make the entire monorepo a Yarn workspace root. Building the necessary tooling and scripts will be different for each team and depends on their monorepo. Yarn workspaces probably wasn't designed as a monorepo task runner. One might try to bend Yarn workspaces a bit to do this job (i.e., run NPM scripts across the monorepo using yarn workspace ...) but it's important to keep in mind that a single workspace root for the entire monorepo probably won't give you what you need unless you're like Babel, Jest, React, etc.

There's a whole other host of problems that come with running a monorepo. For example, what about tracking dependencies and only rebuilding things that changed to save time in CI? Yarn workspaces could help there by letting you query the dependency graph. Lerna does this, for example, to allow topological sorting of commands being run. Yarn v2 actually lets you query the dependency graph as well. The package manager PNPM also does that. But I would argue that depending on the complexity of the monorepo one might want to try tools built for that (not package managers) like Bazel, Pants, Buck, etc.

@the-spyke
Copy link
Contributor

@migueloller From your requirements I see that you don't need a strictly independent packages or other exotic things, you also do want slimmer developer installs. In such case you should start with regular Yarn Monorepo: single root and all packages as workspaces. You'll have faster installation times, lower disk usage, and app1 will use local linked lib1 and lib3. The only downside will be more often CI cache invalidation because addition of a devDep to lib1 will update shared yarn.lock. But usually you don't update dependencies that often to worry much about this tradeoff.

lib1 may depend on lodash@^4.5.0" and lib2may depend onlodash@^4.10.0". In case of Monorepo you want a single version of lodash being used, so Yarn will install something latest compatible to both specifiers like `lodash@4.17.15" hoisted to root node_modules. And in case of an update you're updating that single unified version, so all workspaces are always stay on the same page. This is the desired behavior.

There're also situations where independent teams develop independent projects. In this case proj1 may want to stay at lodash@4.5.1 with proj2 having lodash@4.17.3 and update that with their own cadences. Of course, you can achieve this by using more strict specifiers like lodash@~4.5.0, but this may still update 2nd level dependency too early. So, in whole those projects may be completely unrelated, just happened to be inside of one git repo. In this case there is no reason to bind them as a Yarn Monorepo and trade-off independence for a shared lock-file. Just treat them as what they are: separate projects with their independent life. And that is called Multirepo. On Unix all your directories are under /, but it doesn't mean that all possible JS projects on you PC should be a Monorepo :-)

Building a minimal possible Docker image for production use is completely unrelated to the Yarn, but you may force Yarn to reuse a development artifact called yarn.lock and help you with this task too.

@migueloller
Copy link

@the-spyke, in this example I only used 3 workspaces but in our actual repo we have over 20 workspaces with a combination of libraries and deployed workloads for both the front-end and back-end. The way I see it now is that we have a monorepo (or what you call multirepo) where it will probably make sense to have multiple Yarn workspace roots with independent lockfiles. We're thinking of using a deployable unit as the unit of separation, it aligns nicely with lockfiles.

I think for me, what makes this work very nicely is the fact that Yarn workspaces supports paths outside the workspace root, even though the initial blog post says otherwise. For example, you can have this:

{
  "workspaces": [
    "../lib1",
    "../lib3"
  ]
}

@borekb
Copy link

borekb commented Jun 30, 2020

We have the same use case as @migueloller and one possible idea is for Yarn to support multiple sets of workspaces, like this:

{
  "workspaces": {
    "frontend-app": ["frontend", "common"],
    "backend-app": ["backend", "common"]
  }
}

Yarn would maintain two additional lock files (I imagine the main yarn.lock would still exist):

.
└── monorepo/
    ├── yarn.frontend-app.lock
    ├── yarn.backend-app.lock
    └── packages/
        ├── frontend
        ├── backend
        └── common

When building a Docker image e.g. for frontend, we'd create a context (e.g., via tar) that includes this:

.
└── <Docker build context>/
    ├── yarn.frontend-app.lock
    └── packages/
        ├── frontend
        └── common

What I didn't think about deeply is whether it's possible to install (link in node_modules) the right versions of dependencies if frontend and backend lock different versions. But purely from the high-level view, two-dimensional Yarn workspaces is probably what we're after.

(Something similar was also posted here.)

@migueloller
Copy link

It looks like that you don’t need a lock file per workspace but instead you require node_modules per workspace for deployment

@gfortaine, if you read the discussion you will realize that that's actually not the case. The reason for having a separate lockfile has nothing to do with installation, but instead having a lockfile that only changes when a specific package changes. A top-level lockfile will change with every workspace dependency change, but a lockfile scoped to a single package will only change when that package's dependencies change.

It might be worth mentioning that that this can be done in user-land. Using the @yarnpkg/lockfile package one can parse the top-level lockfile, and then using yarn workspaces info one can determine workspace dependencies. Using this information, together with the package.json of each workspace, one can generate a lockfile per workspace. Then, one could set up that as a postinstall script in the repo to keep those individual lockfiles in sync with the top-level one.

I might take a stab at building this and report back with my findings.

@gfortaine
Copy link

It looks like that I’ve just found this implementation albeit for pnpm : @pnpm/make-dedicated-lockfile

Hope this helps 👍

@jakebailey
Copy link

My need for this behavior (versioning per workspace, but still have lockfiles in each package) is that I have a nested monorepo, where a subtree is exported to another repo entirely, so must remain independent. Right now I'm stuck with lerna/npm and some custom logic to attempt to even out versions. Would be nice if yarn (I guess v2, given that's where nested support lies?) could manage all of them at once, but leave the correct subset of the global pinning in each.

A postinstall script could attempt to manage the lock files directly, I suppose. Sounds complicated, but would be nice either way.

@varsis
Copy link

varsis commented Dec 7, 2020

For anyone looking to generate a lock file from the workspaces lockfile, I have created a CLI that extracts your dependencies from a passed in package.json and spits out a lockfile using only the contents of the workspace lockfile. You can check it out on npm and GitHub

https://www.npmjs.com/package/generate-lockfile

@heri16
Copy link

heri16 commented Dec 10, 2020

For anyone looking to generate a lock file from the workspaces lockfile, I have created a CLI that extracts your dependencies from a passed in package.json and spits out a lockfile using only the contents of the workspace lockfile. You can check it out on npm and GitHub

https://www.npmjs.com/package/generate-lockfile

Wasn't able to get this working. Shows help

@varsis
Copy link

varsis commented Dec 10, 2020

For anyone looking to generate a lock file from the workspaces lockfile, I have created a CLI that extracts your dependencies from a passed in package.json and spits out a lockfile using only the contents of the workspace lockfile. You can check it out on npm and GitHub
https://www.npmjs.com/package/generate-lockfile

Wasn't able to get this working. Shows help

@heri16 You need to pass in the lockfile and package.json like below:

generate-lockfile --lockfile yarn.lock --package package.json

I have updated the help output to make this more clear

@conradkoh
Copy link

conradkoh commented Jan 3, 2021

For anyone looking to generate a lock file from the workspaces lockfile, I have created a CLI that extracts your dependencies from a passed in package.json and spits out a lockfile using only the contents of the workspace lockfile. You can check it out on npm and GitHub
https://www.npmjs.com/package/generate-lockfile

Wasn't able to get this working. Shows help

@heri16 You need to pass in the lockfile and package.json like below:

generate-lockfile --lockfile yarn.lock --package package.json

I have updated the help output to make this more clear

If you're using yarn workspaces like I am, I used this and managed to generate a lockfile using the following command (run from within the package itself):

generate-lockfile --lockfile ../../yarn.lock --package package.json --write yarn.lock --force

@merceyz
Copy link
Member

merceyz commented Jan 3, 2021

Since the goal here was to create docker deployments i'll close this as fixed in v2 where you can use the plugin https://yarn.build/ or https://gitlab.com/Larry1123/yarn-contrib/-/tree/master/packages/plugin-production-install to handle that

https://yarnpkg.com/getting-started/migration

@merceyz merceyz closed this as completed Jan 3, 2021
@merceyz merceyz added the fixed-in-modern This issue has been fixed / implemented in Yarn 2+. label Jan 3, 2021
@franzmoro
Copy link

@merceyz i think that for many of us following this discussion, migrating to yarn v2 is not a viable option, as it's still incompatible with a lot of dependencies. Migrating to yarn v2 would require far more work than following some of the workarounds posted earlier.

@arcanis
Copy link
Member

arcanis commented Feb 4, 2021

@franzmoro I believe you're mistaken - Yarn's current majors support both PnP and node_modules installs, as our Migration Guide makes abundantly clear. If you hit compatibility issues, you have more than enough options to unblock yourself in a few minutes top. If that isn't the case, feel free to open an issue to let us know in more details what blockers you hit exactly.

In any cases, we don't intend to spend resources developing features on an outdated codebase when we believe a clearly superior option is freely available, so I'll lock this issue for now. Please direct follow ups if any to our new discussion tab.

@yarnpkg yarnpkg locked as resolved and limited conversation to collaborators Feb 4, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
cat-feature fixed-in-modern This issue has been fixed / implemented in Yarn 2+. triaged
Projects
None yet
Development

No branches or pull requests