-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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 Request: mechanism to "vendor" a workspace module's node_modules #4521
Comments
There's one issue here: Ideally you want the So really what you want is something like The result of that operation should be that the output directory doesn't reference any files outside of the current project directory. Now, you can copy that to a Docker container and run |
@idris, I believe that's different than my original request. In my original request I wanted symlinks to the parent workspace node_modules folder, in yours you probably want a copy. Unless I misunderstand you. Perhaps it's better to open a new issue and link back to this one. |
Currently looking to do this same thing. I have a handful of tiny modules that I want to deploy separately via AWS lambda. By using yarn workspaces, I can create shared local modules that other modules can depend on. This works out really well, until it comes time to deploy anything. Lambda expects you to upload your entire project with all dependencies as a zip file. There's really no way to accomplish this with workspaces unless I opt to upload the entire node_modules folder from workspace root. It would be great to have some kind of |
I ran into the same challenge: deploy multiple packages from a yarn workspace into multiple docker images with the packages having dependencies on one-another. I would very much like to not use a npm registry because I'm running this in a Jenkins multibrach build: Packages are being build for each git branch and thus there are many builds of the same package in the same version. This doesn't play well with pushing them to a singleton registry. |
@bryanlarsen one of the main reasons to use monorepo is that you can make changes across your packages without requiring them to be published, provided you have the whole workspaces checkout. If you primarily work off a particular package, like in your example use case, maybe consider not use monorepo and just set them up as separate standalone packages. You can then use If you want to collect all your dependencies into a self-sufficient pack for deployment, one would think that is what many existing bundlers are for, such as webpack/browserify/metro. Bundling is not trivial, locating modules is hardly the only challenge you will face. Scanning yarn.lock or traversing the node_modules yourself might not be the shortest route... If your dependent library made a monorepo-incompatible assumption of where the module/artifact are, you should probably file an issue to have them fix it. If they could not fix it right away, you can use the new feature nohoist to exclude yarn from hoisting the particular modules to the root. I didn't quite get @meysholdt docker use case, maybe an example repo would make it more clear... |
let's say I have a monorepo:
With both Dockerfiles doing something like this:
to copy all contents from folder The nohoist-feature you (@connectdotz ) mention seems to go to the right direction, but what I was looking for to disable hoisting for all dependencies and thus not to have the I'm aware this doesn't follow the advice from @idris:
But for that to work the missing piece is a way to make dependencies from the same workspace available to the docker engine: In this example, let's assume |
ok, so looks like there is a common thread emerging from these discussions (including #5428): to be able to publish or deploy individual packages with their dependencies resolved. As I mentioned earlier, this is the area of package bundling, which is not trivial and already have many existing solutions. But what is the real experience? How does it really work in workspaces and how painful is it? I decided to give it a try, you can see the sample repo here: workspaces-webpack-docker there are 2 docker containers there:
The webpack config is pretty trivial, the Dockerfiles are pretty simple, both with huge community support for more. This is just a quick throw-together sample, I am sure there is much room for improvement/optimization, but overall I am happy with the ease of use. Also realized that even with local node_modules all populated or package-level yarn.lock file, I still can't run my app without more tooling like transpiling es6, which is already handled by webpack+babel with many useful examples to work with. To conclude: for most use cases, I think it is better off to just use the package bundlers, like webpack, to bundle your package if you need to deploy it as a standalone unit for testing or production use. IMHO, it probably doesn't make sense for yarn to venture into this area. @evocateur, lerna owner, stated similar opinion. As this simple experiment showed that module resolution is hardly the only thing needed to be done in deploying individual, self-sufficient packages... It is best to leave the bundling to the bundler... |
So what we're basically doing right now is (basically) shipping the whole monorepo and it's deps for every component. That docker image is now 6GB. :) It's sort of like deploying your primary container. It works, but eventually it won't scale. We're now almost completely switched over to node 8.5+ with --experimental-modules, which means no more transpiling. Yay! I'd need a damn good reason to bring it back. The other big problem is code sharing with clients. Each client directory is a git subrepo inside of our monorepo. So far we've only had one customer who actually tried building the code, but it should be theoretically possible for all of them. |
We're also following @bryanlarsen 's approach - several of our dependencies don't work with webpack (due to dynamic imports which can't be resolved at build time and/or legacy structuring) and we'd like to avoid transpilation on the server. What would be ideal for us would be the ability to find out which dependencies are needed for a particular workspace package . Currently |
@bryanlarsen, if your monorepo is too big and you only need a single package anyway, why not just bundle that package then deploy it to docker, similar to publish only a single package? you can certainly run webpack without transpiler. Did I miss something? @inversion, your issue seems to be with webpack, you might be better off to resolve that, or explore deploying full repo, private registry, among others... even if you can get a flat package node_modules like you have suggested, keep in mind that it will contain all modules: used, unused, devDependencies etc. If you already worry about the repo being too big (I assume you do otherwise why not just take the whole repo to docker), wouldn't you want to only include the ones that actually used at runtime? How about minification, that could save your footprint by half sometimes... Optimization like these, among others, is what a good bundler aims to offer, where I don't see yarn will ever venture into. Hopefully, we all realized by now that a flat module tree is probably not the only challenge for these use cases. Not that I think webpack is the perfect solution, but it does offer useful features you would need, with a fraction of the time, thus you should maybe consider it as a preferred option. Having said that, I understand we all sometimes have to work around problems, even with less than ideal solutions... While nohoist is not designed to solve publish/deploy, you can indeed use it to stop hoisting the whole package if you so desire:
This will put every dependency of w2 in |
@connectdotz So here is my concern with bundling as a solution. Let's say in your example project that I know this example may be contrived a bit, but it feels like a smell to me. I want Am I making sense? 🤔 |
I've managed to hack together a tool that fulfills my needs. It uses https://github.com/ripeworks/workspace-pack You specify the name of the folder of your local package, it will resolve all the local and remote deps, then jam it all into a Maybe this can become a thing? Oh, also, you'll probably need a recent node.js version to run it, since i'm not transpiling it (yet) |
@netanelgilad yes you are absolutely right. Having non-code assets in a js library is a well-known portability killer, with or without workspaces. That's why it is an anti-pattern that one should try to avoid. If this is really your use case, maybe consider fixing you asset access first; or maybe you just want to point out sometimes there are issues with bundling, which I completely agree, but that doesn't necessarily mean that yarn should just step in and fix them... Let's get back to your actual use case, from your earlier comment:
For production use, all your packages should have already been published to the private registry, right? Couldn't you just deploy the microservice package (without any dependency) to docker then do a clean |
not when building feature branches and deploying them to testing environments. In this scenario I don't want to deploy the packages to a central private npm repository because that would mix the packages from all feature branches. And launching a dedicated npm registry for every feature branch seems cumbersome. |
I agree with you that yarn shouldn't have to solve problems related to non-code assets. My example was mainly directed at the issue of one package having to bundle another package (and the problems that come with it) instead of properly installing the package (which is why we use package managers for production installations, and not just bundle everything into an
So here is the crux of the problem. I want to run |
@netanelgilad you can run a Then you need to publish this, and that means committing and bumping npm versions on your ci-cd pipeline. |
Thanks for the advice @wenisman, but I gave it a try and the generated lock entry doesn't contain all the exact versions of the dependencies of the workspace. It just mentioned the workspace and the ranges of the dependencies already found in the But I did try the option of just using the |
@netanelgilad I think there is a big raised that the generate lock doesn't produce the same result as the yarn install. However it's good to see that the workspace yarn.lock is working for you. We are struggling with the same problem, we have a mono repo and simply wish to create some docker images of some of our components. The individual libraries are fine to publish to npm but our microservices we want to deploy. So we have copied the code, remove the node_modules of the service and then have yarn install inside a docker image. It's a bit of work and so this vendoring of packages would help in this regard |
We've got the same problem. The --focus option that was introduced lately does not currently solve the problem adequately for us because some of our local dependencies are private npm modules that we don't want to publish on npm. And even if we did that, we would still not want to publish them for feature branches or pull requests for instance. The problem would be solved for us by allowing the --focus option to copy local packages from the local filesystem instead of attempting to retrieve them from npm. |
We also have the same problem. Particularly for packaging AWS Lambda functions. The workspace feature has simplified our process dramatically, but this remains our last issue. Our current solution involves using a local verdaccio container running w/ Docker. We target it to stage local packages during the build process. This works well, but is a lot of overhead. I would also love to see an option for As @idris pointed out there is a pitfall to this approach. Particularly if you are installing libraries that have native extensions and your local environment differs from your deployment environment. We have solved this problem by running the installation inside of a Docker container that mimics the deployment environment. |
That's a good, although hefty workaround @vjleblanc. I might consider that for us too in the meantime. Thanks! The pitfall that @idris mentioned does also not apply to us because we build and test the in multi stage docker builds running in the exact same configuration as the final production containers that inherit the "build artifacts" from the previous build stages within the CI and are then deployed on dockerhub with all the native extensions functioning properly. |
I don't suppose anyone is actively working on this? I am also desperately trying to figure out how to do something like |
@sgronblo, FWIW, I've found webpack to be the current best way to package code for lambda deployment zips. The tree shaking and minification that you get becomes essential when trying to stay under the 50MB zip size limit, once you have a couple dependencies involved. |
@acmcelwee Thanks. I ended up setting up webpack and I think it seems to solve the problem as expected, even when using yarn workspaces. It's just a shame the output bundle is kind of a mess to look at. |
I tried the webpack approach this past week. I am pretty pleased with it. It decreased the size of lambda packages significantly (10x in my test cases). For those concerned about the readability of the packed code, I've found that setting the mode to |
Definitely need this!!!! |
I solution that works for me is:
now, to create a clean install of one of your packages:
Enjoy your installation without devDependencies and without storing packages in npm-registries. This works great for git-multibranch-builds. |
How do you solve package conflicts if using webpack? E.g. I have a package that requires |
While bundling before copying to Docker might seem like it would solve issues there's the problem of native dependencies that have a post install build step that relies on the current environment. In addition to that, features like babel-preset-env's selective transpiling will only transpile what's necessary for the current runtime. For this reason, running the entire build process in the Docker image itself is desireable. The other issue is that of symlinks. Commands like The suggestion to have a EDIT: One more thing. While the lockfile from the root of the repository could be copied, that would invalidate Docker cached layers when dependencies of other packages in the repository change. |
Hey everyone, Did someone manage to find a workaround or solution for this already? We a medium sized mono repo with 14 packages, where 5 of them are entry points as a node app. I would be thankful for every hint as well as suggestions for alternatives, as we also cannot use webpack (due to complicated imports in our TS projects). |
Workspaces are awesome, but they're inconvenient for deployment.
I presume that the intention of workspaces is that for deployment, you're supposed to push all your packages to an NPM registry, and then
yarn install
inside a copy of your module outside the workspace will work.However, there are a bunch of reasons you might not want to push. If it's an open source module, then bumping the version releases the module to the wild. But often you want to test in a production staging environment before releasing the module. If it's a private module then you have to worry about authentication tokens and/or private registries.
The other issue is that there's no yarn.lock for the modules, only a single global yarn.lock.
To elaborate further, take the example from the blog post,
jest
. And let's assume thatjest-diff
is an app that can be deployed to a server.Suppose I download
jest-diff
vianpm pack
. I can runyarn install
inside of that package, but I'll be installing without the benefit of a yarn.lock, and I need to have access tojest-get-type
. Easy enough since it's been published, but publishing has the drawbacks listed above.What I'd like to be able to do, is in a fresh checkout of jest,
cd packages/jest-diff
, and thenyarn install --vendored
. This would then fully populate packages/jest-diff/node_modules. It would copy packages/jest-get-type into packages/jest-diff/node_modules/jest-get-type. It would not create a root node_modules nor a packages/jest-get-type/node_modules, although of course it would create packages/jest-diff/node_modules/jest-get-type/node_modules if it was necessary. It would use the yarn.lock from the repo root for resolutions.I could then take a tarball of the packages/jest-diff directory and put it on a server to run it there, or run a docker build from the packages/jest-diff directory without having to send my entire workspace as a docker context. I could also check in packages/jest-diff as a deployment branch into git to make sure that subsequent deployments are completely deterministic and don't require a network.
The text was updated successfully, but these errors were encountered: