-
Notifications
You must be signed in to change notification settings - Fork 17.8k
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
proposal: cmd/go: automatic and partial vendoring in module mode #30240
Comments
Why are partial vendor folders something we want to encourage? Most use cases listed here and in the linked issues would require all dependencies vendored at all times. Also, can you clarify if |
Some dependencies are more robust than others. For example, you might trust |
We could perhaps make |
More generally, though, the main goal of automatic vendor updates is to prevent version skew. Copying in newly-added modules does not further that goal, since there are no out-of-date contents in the first place. |
I would suspect the most common use case might be vendoring 100% of dependencies? If so, and if vendoring in 1.13 is going to be able to track updates via |
It definitely makes sense to support partial ones. I just suspect (and might be wrong!) that 90% of users opting into vendoring really mean to vendor everything, and a reasonable chunk of that 90% would be surprised by it behaving otherwise. |
In the presence of a reliable proxy, I can't think of any reasonable cases for a partial vendor directory, and lots of possible confusion. |
@ianthehat, one use-cases for vendoring, given proxies, is to vendor in private code for which the proxies do not have access. For example, a contract-based startup might want to vendor in their proprietary utility modules before delivering the code to their customers. |
@bcmills Could you comment on the interplay with -mod=readonly, and/or options to disable automatic downloads for people who would prefer to fail if vendor is missing something? |
@bcmills you can achieve the same effect by copying to an internal package and rewriting the import paths, which would be more honest, and also allow for local modifications (something else that those kinds of contractors often need to do as well). If you don't want to rewrite the import paths, you could check it in as a sub-module and use a replace directive (you probably have full control of the main go.mods for that kind of work) |
I like removing complexity, flags, and per user (or per project) configuration when using vendored mode. I think that automatic detection of vendor folder (and assume you are in vendored mode) when a vendor folder is present it's a great idea. I sometimes mix vendored and non-vendored projects, and switching between would be great to be as transparent as possible. I agree though with the opinion of some of the folks above, IMHO supporting partial vendoring would be confusing and it will add complexity. For example, in our usecase, we are using non-vendored mode for our main projects, adding a GOPROXY for public libraries, but don't want to cache our private libraries there (for security, and because cache server and source server are on the same local network, it just doesn't add any benefit for us). #26334 would be enough for this. Vendored mode, in the other hand, it's great to distribute self-contained/small apps/tools. |
Part of the point of this proposal is to avoid the need for a distinct “vendor mode”. Modules are integrated into the normal |
We've considered that, but it really doesn't work well with version control systems: the diffs are incomprehensible and the blobs can end up consuming a lot more space than they ought to (depending on the encoding). |
Re partial vendoring: given module proxies, the major use-case for vendoring is for modules that are not available via the public proxies. (Recall that the word “vendor” literally means “one who sells”.) Module mode substantially reduces the need to duplicate code: you no longer have to copy all of your dependencies into your own repository, and you especially don't need to do that for stable, publicly-available, open-source dependencies. It is important to me that we make it easy to duplicate the minimum amount of code necessary for each use-case: minimal duplication shouldn't be an “extreme edge [case]”, it should be the default mode of operation. It's not realistic to expect folks to manually apply The point of vendoring in module mode is not to provide an alternative to using modules. It is to provide a complementary feature set for the cases that modules cannot address well: namely, the distribution of proprietary code. |
That said, let's think about that sticky-pattern problem. I don't buy the “full vendoring as a default” argument, but there is a more general case that really ought to work. Suppose that I run If we support that, then we can view |
So how about this alternative. For a given module pattern,
And then |
Under this proposal,
|
I think this works very well for me, thanks. I couldn't reason through all the cases you listed in your original post (I'll try to go through them over the weekend), but surely this command line API looks good and the sticky mode is really good. Is there really a need to introduce a third metadata file ( |
@bcmills In addition to the proposed new behavior described above, is the thinking that this would also land in 1.13:
If so, under the latest proposal, is this an example of what a module author could do if they want to fail if vendor is incomplete:
|
I hadn't really considered it: I think @rsc added I suppose that we could record the patterns in |
Updated the proposal to incorporate sticky patterns (#30240 (comment)). |
If your use case is because the code cannot live in a public proxy, why do you care about the diff, you would not see the diff if it was in the public proxy. It's also trivial to fix, use a non compressed text archive. This also fixes the space issue. |
I think we ought to start by enumerating the actual problems we are hoping to solve with vendoring, and checking it is the right solution to those problems. Vendoring comes with a lot of serious problems, it needs to be worth the cost. |
For a start, if you're vendoring the code because it is proprietary, you want to be sure that you are shipping only what was actually promised to the customer. (In contrast, if the module is already publicly available, you probably don't care which parts you're re-publishing in your
That is essentially what the |
@bcmills: What do you think about an alternative solution, where you'd flip a switch in
I feel like this would be the most sane solution for me, and pretty much comparable to dep or glide workflow which worked a treat for us for a long time. Edit: |
@bcmills I want to add a recent story of how the proper vendoring saved us a lots of time: https://success.docker.com/article/docker-hub-user-notification. As a part of security review after receiving this notification -- we performed an audit of all the dependencies in Java, NPM, etc. Auditing our Go code took exactly 0 seconds, because we have all the dependencies committed and we don't go online during build process at all. |
I agree. I think we should consider actually removing |
Following a good discussion at GopherCon with @ChrisHines, we concluded that a key reason for needing
Put another way: vendor is used because there isn't a better alternative to reviewing dependency changes alongside (and as part of the same process as) changes to one's own code. We further concluded that all other aspects of his requirements (including reproducible builds, self-contained CI runs etc) could be satisfied by alternative means, not least, for example, an approach similar to #27618. None of these alternatives are currently as polished/easy as the Back to the point on reviewing dependency changes. This point is obviously critical. Not only for those people who prefer the That said, to avoid the problems of only having parts of modules "vendored", I think this points towards a solution where entire modules are "vendored" with a directory structure similar (identical?) to that found under Apologies if I'm late to the party on all of this: I just wanted to stress/highlight that this point on code review has a life well beyond this issue that is of much wider interest. |
On that back of my last comment I've just raised #33466 |
Just to slightly row back on this point: whether we need the entire module to be "vendored" is actually something I defer to Bryan (and Ian) on. If I previously wrote "entire module" because I read (perhaps incorrectly) that this had become necessary. But Bryan/Ian are the authorities on that point, so restating that point for clarity. |
I am withdrawing this proposal in favor of #33848. My reasoning is as follows: Should vendored dependencies be updated automatically?Here I have proposed that However, in the time since then, we have observed that users are often confused by the implicitness of updates to the I now believe that we should not make such updates automatically. Should we allow vendoring of only a subset of packages?Here I have proposed that I still think that's a good idea in concept, particularly for repositories that contain multiple interdependent modules and |
It would great to vendor a single module. We have a library that has a directory with .yaml files(openAPI types common for multiple services). These files are used by other projects, they include these files in their own API specifications. Now we vendor all dependencies, and can invoke a command from makefile that generates go code from service api spec and types from lib (can reference them ./vendor/someRepo/file.yaml ) |
This proposal overlaps with (and hopefully unifies) several existing issues, linked in the text below.
I'd like to implement it soon, in the
1.131.14 cycle, so if you have feedback please do respond quickly. 🙂Problem summary
Users want a durable, local view of their source code that works with existing diff tools and does not require per-user configuration in cloned repositories.
-mod=vendor
requires configuration per user (GOFLAGS
) or per invocation, and makes it too easy to ship code that produces a different build in vendored mode than in the normal module mode.Proposal
Under this proposal, the source code for the packages listed in
vendor/modules.txt
— and thego.mod
files for the modules listed invendor/modules.txt
, if any — will be drawn from the vendor directory automatically (#27227).If a
replace
directive in the main module specifies a module path, the module source code will be vendored under the path that provides the replacement, not the path being replaced. That preserves the 1:1 correspondence between import paths and filesystem directories, while allowing replacement targets to alias other modules (#26904). If areplace
directive specifies a file path, then either that path must be outside thevendor
directory or thevendor/modules.txt
file must not exist (#29169).Package patterns such as
all
andexample.com/...
will match only the packages that are present in thevendor
directory, not unvendored packages from the same module. During the build, if additional packages from the vendored modules are needed in order to satisfy an import, the source for those packages will be fetched (from the module cache, if available) and added to thevendor
directory. (Packages from outside the already-vendored modules will not be vendored automatically.)Any time the
go.mod
file is written, if a module path found invendor/modules.txt
has a different version than that found in the build list, the already-vendored packages andgo.mod
file from the previous version will be deleted, and updated versions of those packages will be written in their place (#29058). Transitive imports of those packages will be resolved, and may populate additional packages in other already-vendored modules.If
go get
removes a module from the build list entirely, its package source andgo.mod
file will be removed, but an entry for the module (with versionnone
) will remain invendor/modules.txt
. That way, if a future operation (such as ago get
orgo build
) adds the module to the build list again, it will remain vendored as before.When
go mod tidy
is run, it will add or remove packages from thevendor
directory so that it continues to contain only the subset of packages found in the transitive import graph. It will also removego.mod
files and entries invendor/modules.txt
for modules that are no longer present in the build list.To encourage the minimal use of vendor directories, the
go mod vendor
subcommand will accept an optional list of packages or modules.go mod vendor <module>
will update thevendor
directory to contain thego.mod
file for<module>
and source code for its packages that appear in the transitive import graph of the main module. (Note that, since the criterion for inclusion of a package is its existence in the import graph, vendoring in an additional module should not affect the contents of any previously-vendored modules.)go mod vendor <pattern>
for an arbitrary module pattern will add# <pattern>
tovendor/modules.txt
, and vendor in thego.mod
files (and any packages found in the import graph) for modules matching<pattern>
, adding individual comments tovendor/modules.txt
for those modules.Note in particular that
go mod vendor all
will copy ingo.mod
files for all of the module dependencies in the module graph (and add entries invendor/modules.txt
for those modules). That ensures that aftergo mod vendor all
,go list
can produce accurate results without making any further network requests (see also #19234 and #29772).The
go mod vendor
subcommand will accept a new flag,-d
.go mod vendor -d <pattern>
will remove all previously-vendored modules matching<pattern>
from the vendor directory (and fromvendor/modules.txt
), as well as any previously-stored patterns matching those modules (including<pattern>
itself, if present).go mod vendor
, without further arguments, is equivalent togo mod vendor all
.go mod vendor -d
is equivalent togo mod vendor -d all
. Ifgo mod vendor -d
causesvendor/modules.txt
to become empty, it will also remove the entirevendor
directory.Edits
The text was updated successfully, but these errors were encountered: