-
Notifications
You must be signed in to change notification settings - Fork 18k
cmd/go: go mod tidy
ignores go.work
file
#50750
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
Comments
Tidiness is a property of an individual module, not a workspace: if a module is tidy, then a downstream consumer of the module knows which versions to use for every dependency of every package in that module. If you don't particularly care about downstream consumers having a package that is provided by the workspace, you can use Otherwise, you either need to publish the workspace dependencies before running |
@matloob, for Go 1.19 I wonder if we should augment the |
go mod tidy
ignores go.work
` filego mod tidy
ignores go.work
` file
go mod tidy
ignores go.work
` filego mod tidy
ignores go.work
file
Currently with reproducing repository (https://github.com/bozaro/go-work-play/tree/go-mod-tidy). I can run:
And But for This behaviour looks like inconsistent: I expects that |
What valid use cases are there for |
This is also happening for I agree with @liadmord, the only benefit I see get from the |
I agree with @bozaro @liadmord and @AlmogBaku I have a monorepo private project where I have multiple modules, and on each one I need to insert a lot of |
This comment was marked as duplicate.
This comment was marked as duplicate.
This comment was marked as duplicate.
This comment was marked as duplicate.
Encountering the same issue with Furthermore, some other
Expected behaviour: The |
My expected functionality is that a If I don't commit my Once I must make an edit to In other words, if I add this line to
Then |
This is a very big issue for me in the case of creating Docker containers :( I cant use Golang is considered the language for microservices and In the current state of the
I expect 4 things from the
So now, the only thing why I'm using |
Signed-off-by: Valery Piashchynski <[email protected]>
I've faced this issue yesterday but my case is a bit different.. My The solution is simply to move the |
Folks who are commenting here (and please bear in mind https://go.dev/wiki/NoPlusOne!) — have you considered |
@bcmills I'm not sure what the intended workflow is, but I think the feedback here is that the DX is really not intuitive. |
I wish there were |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
However that replace directive seems to be ignored by
|
This comment was marked as spam.
This comment was marked as spam.
I've encountered a similar problem. the current solution is write a shell to |
As someone who is starting with Go, and learning micro-services, I want to share my perspective, on this workspace thread. Here's my user case that I was working on with workspace:
Now I want to build docker images for ms-1 and ms-2. what would be my advantage for using workspace in this scenario?
now here's the problem that is emerging:
Too much trouble, probably I'll just stop using workspace and forget the possible advantages My suggestion for a "go mod tidy --workspace", or "go work tidy".It should recursively go to workspace modules and tidy them too. For example, if inside ms-1 I run the supposed "go work tidy", it should see that it's using the utils module and recursively tidy utils too. If this behavior is debatable put a flag for it e.g. "go work tidy --recursive". |
@EduartePaiva I'm wondering why you didn't place all your packages in the same module? That is the happy path. And then no workspace is required. To others on this issue: I would be like to speak to people about the requirements they have that push them towards multi-module repos with go.work files. I think if we could better understand those requirements we would be better equipped to see how to satisfy them. Please let me know if you're interested in talking about your use case. |
@matloob being honest I was following a microservice course and the teacher started using workspace to separate each microservice. At the time I didn't questioned why, just thought that it was the way to do things. |
@matloob The main reason I was looking for a workspace solution is that a large module doesn't play well with creating docker images from both a caching perspective as well as a building perspective. Workspaces have some challenges here as well without jumping to a solution like Bazel to maintain build dependencies. On top of not being able to independently version any libraries for external consumption. Ultimately we did go with a common module and some complex docker contexts to make sure everything came together. I am no longer at $company with a main repo. This plus things like github CI and the like really loves to make monorepos benefits start to dwindle a lot. |
@matloob In my case I am rewriting several discord bots to go. However they have some shared code which I am putting in their own package & the services themselves. Now I got a dockerfile for each and I copy the mod file for the app, then the shared mod file and download them seperately etc. |
Hi, |
I did this using a Makefile to handle the tidy |
@EduartePaiva Ah, got it. Yeah, in general multi-module repositories should only be reached only if a single repo repository isn't working for your use case. @nemith Do you have modules in your workspace that aren't @jurienhamaker Are the bots in separate modules to ease deployment similar to the problems @nemith mentioned? @KevinFairise2 What were the downsides of importing the whole codebase? Was it to restrict the size of dependencies that were brought in with the imports? Also, are each of the modules independently go-gettable? If you're able to do a call I'd definitely be interested in talking about your use case. |
@matloob The bots itself can function independently as long as they are compiled together with the shared module. The shared module is their only dependency. The dockerfiles in the repository show basically that, I don't add service y & z to the docker container of x. |
@matloob I am going to go off on memory here (maybe I will try to reproduce it if i get time later). But as I remember everything worked fine until I added a new module that wasn't yet committed to the repo. Despite being listed in go.work no other modules could try to use it as it would try to contact the repo (github in my case) to get information on the imported repo instead of just looking locally first. I guess that makes it not If i remember right there were workarounds but all of it added too much cognitive load to the team who's job was to write software and not understand the rube goldberg building machine as much as I could. |
@matloob one consideration is that Go is not necessarily the whole picture for any given project. We have a product with a front-end in Typescript, multiple backend services in Go, and an app in Swift. They all communicate using GRPC, and depend on a single common set of protobufs, from which we generate types, clients, and server stubs. Keeping all the protos, services, apps, and frontends in one repo allows us to change protobufs, regenerate all the client and server stubs, and commit the consequences to the whole system all in one go. in particular, we can understand if anything is going to break before changes are merged to main. New versions of affected services and apps can also be automatically deployed based on changes to protos on which they depend. All the go code being in one repo is a subset of that need, and similar to what others have said: common grpc (and other) code in To make this work, we commit go.work to the repo (even though that's not its intended use). go.work and go.mod look like: go.work:
lib/go/src/go.mod:
services/service-one/go.mod:
Each service can then This all works very nicely. The only slight issue is that
this is fine, because there's nothing to retrieve, but it would be ideal if it paid attention to the |
As for why the services aren't all in one module together - it's because they aren't all one module. They are applications that happen to be written in go, amongst others that are not. They are each deployed separately. They have different environment variables, they have different kubernetes configs. We choose to organize our code by logical service, not by implementation language. This is service one:
This is service three:
The enforced regularity of this layout greatly simplifies devops in a heterogeneous repo, and reduces cognitive load on developers as the system and team grows. |
@majelbstoat I'd like to understand this better. Is the goal of placing the go.mod files in each of the service directories (rather than the top level) to make the versions of the dependencies of the services independent of each other? |
The idea is that each go.mod represents the minimal set of dependencies for each service/library. This helps optimize deployments, where we save the go build cache and results of .circleci.yaml:
Practically speaking, that means if we make a change to the dependencies of service-one, we only have to expire the cache for service-one, not every go service. If they were all in one We actually want common dependencies for each service to be at the same version. For example, it's important that the version of the Aesthetically too, each service imports packages from the common library. If they all were in the same go.mod, imports for a service from the common library would be like |
I think that I have a simpler reason for why this is incorrect behavior right now. Expected behavior: Observed behavior: Workaround: |
This comment has been minimized.
This comment has been minimized.
Thanks for the comments. We're still thinking about this. Let me know if you're interested in doing a call to talk about your use case. It still seems to me that changing the behavior of @fayep, It seems to me that a temporary |
I have a common module that includes some common functions, which are referenced by all projects. For example, project A now references it in the workspace, which is private. |
Since there are multiple situations where you may or may not want the behavior, why not add a flag for go mod tidy to pay attention to go.work? |
@spekary As we currently define it, a tidy module is that it is one that includes the appropriate set of requirements to satisfy its imports. Changing or adding a concept of being tidy with respect to a workspace would complicate things further and modules can be fairly complex as is. We'd like to find a solution that wouldn't add to the complexity of the system. In many of the cases listed on this issue the workspaces do logically function as a single module (for example they are all versioned together, and have the same set of dependency versions) but there are various blockers or concerns that prevent the use of a single module. And there are cases where the modules do function differently but there's friction in the process of making them available to the modules that depend on them via |
I suppose I am suggesting approaching the problem differently. When a developer adds a go.work file, they are saying that they are doing something special. I thought that was the idiom. They are composing a working environment that anticipates eventually removing the go.work file, running go mod tidy, and checking in the go.mod file. But for the time being, they need an environment that can work differently. Before go.work, we would put "replace" statements into the go.mod file, but then we would have to remember not to check that in. go.work was supposed to solve that problem, but I guess it didn't, because go.mod still would need to behave differently because of the dependencies implied in the use of different packages, and so we still have to remember not to check in go.mod during development. I guess we need a go.mod.work file (and of course the ensuing go.mod.sum.work file). So how about this: When go.work is present, go mod tidy creates a go.mod.work file. When a go.mod.work file is present, the various go tools use it instead of go.mod. Yeah, it could create some confusion if the developer doesn't realize that is what is happening. Perhaps the go tools could call that out while processing files, like pointing out that a go.mod.work file was found? In any case, the developer started the process by creating a go.work file, and the developer can fix the problem by removing it and the go.mod.work file. The alternative I was suggesting previously is to use a flag to cause go mod tidy to behave this way. But as I think about it, that flag would have to be across all go tools to tell them to use go.mod.work if it exists. Not an easy problem to solve, but I prefer Go's general use of convention to solve these problems, rather than a proliferation of flags in the tools. |
Yes, a
Are you seeing issues in the case where the dependency module already exists? If you are I'd like to hear more about the specifics. There is definitely more friction in the case where the dependency module doesn't yet exist. We may have to think more about those cases but they should only happen "once" when the dependency module is being introduced.
I'd like to hear more about your use case. In the case where you're using |
This would not be true for my use case and was the sole reason why we wanted to to use a go.work solution inside a monorepo was to version shared libraries for consumption outside of the monorepo separately than services in the monorepo which follow more of a trunk based/v0.0.0/unversioned model. The "monorepo" was really just a shared team repo with other teams doing their own multi or monorepos. We ended up dropped this requirement and following trunk based versioning for the libraries as well but only has a workaround. Combined with also poor monorepo support in docker and github actions it was all not worth it. |
@nemith Yeah the use case you mentioned where you're adding a new module that hasn't been released yet has some friction currently. Just to make sure I understand your use case, you weren't trying to do module-mode operations like In that case, currently we'd recommend adding a |
@matloob The most friction was when adding a new module with inter-mod dependencies. A replace could have fixed it but additional additional load of updating docker files to include sources for dependent modules, using a path-filter with GHA to fix CI on dependent changes, on top of adding Being naive and not knowing better i tried It's sad as a monorepo approach solves a lot of other issues of dependency testing and integration. |
It is not a "friction" - for the bootstrap and air-gapped environments it is entirely counterproductive. Even in open source scenarios it makes the bootstraping phase a pain. Frankly, I can not see where and how workspaces current functionality might give less work. I personally am back to the mod replaces managed by a script. Making a workspace just muds the water, makes project brittle, and doubles the maintenance time. |
@matloob If you care about downstream consumers, then don't forget about just all the other devs who do not see why go workspaces can't address that issue and go mod tidy handle it if one forces it to do so. Bootstrapping and CI/CD are valid use cases where it's currently painful with replace directives management. |
What version of Go are you using (
go version
)?What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
Minimal reproducing repository: https://github.com/bozaro/go-work-play/tree/go-mod-tidy
Full script:
What did you expect to see?
I expect
go.mod
andgo.sum
updated with current working copy state.What did you see instead?
I see that
go mod tidy
try to get modules forshared
go modules from repository ignoringgo.work
content.The text was updated successfully, but these errors were encountered: