-
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
cmd/go: stamp the pseudo-version in builds generated by go build
#50603
Comments
At least speaking personally, for cases like mvdan/sh#519, my intent is to show something like It's true that something like a proper module version might be more useful; a git commit hash doesn't give any hint as to how old a version is, whereas a semver version prefix or a timestamp can give a starting point. So, in principle, I agree with you: 1.18 is a big step forward, but it's still unfortunate that the main module version remains as However, in practice, I still agree with Jay's comment in #29228 (comment); we shouldn't make such a "locally inferred version" look like a normal version, because it's reasonably likely to be wrong or cause confusion with users.
Could you give some examples? I can only think of very unlikely scenarios, such as manually corrupting the module download cache after downloading some dependencies. That cache is read-only by default, and With the main module in a git checkout, I can think of multiple scenarios which seem more likely:
I think that, if we are to implement something like this, the versions must be somehow different from the canonical and unique versions that get computed from fully published commits and tags. This would make it very clear that the versions are inferred from local state, and not guaranteed to be correct. As a simplistic example, imagine that tagging |
To add a more concrete example: if we made the change proposed here, and locally inferred versions looked like fully published versions, I would have a harder time trusting the output of |
I was thinking of the case where since Go itself doesn't expose its own concept of a version to the program, the users themselves are forced to create their own concepts of a version, either through things like Let me rephrase my point. Go can't enforce any useful properties for the user's notion of a version because it doesn't know about it, and as such if we make The user does gain something in the latter case though. They don't have to create build wrappers.
I very much agree with this, with one caveat. If the locally checked-out version is identical to a published release, I would expect the version to match the release. If the locally checked-out version can not be guaranteed to match any release, then yes, it should be published with something like Unfortunately, I can't imagine how this would work without internet access, and quite often a prerequisite of automated systems running |
Hold on, another thought. If we always add the commit hash, and some other metadata to the main module version for local builds, essentially always making them a fully qualified Go pseudo-version, then they will always be different from the published version, so there's no potential for confusion there. Even better, in semver terms these builds will sort before the published version, which is probably what people want. For this, what I said earlier about
can no longer be true, but perhaps that is ok as long as we come up with a documented and stable convention that describes versioning for local builds (as opposed to just dumping a "devel" in the metadata field). |
The main caveat here, I think, is unpublished tags. If I create a local, unpublished tag for, say, That may or may not be a significant issue, though: if we always use a pseudo-version, we'll at least have the commit hash as a common point of reference even if the base versions differ. |
@4ad right, a local build can't always know what is or isn't published, as requiring a network roundtrip takes us back to square one. Your idea of trying to stick to semver, and always using some form of pseudo-version which includes a hash, sounds good. With one caveat, though: the commit hash isn't enough to make the version unambiguous, because I can have infinite kinds of uncommitted changes that do not change the HEAD commit hash. @bcmills good point about tags still messing with pseudo-versions, but at least if we always include a timestamp and some form of unique hash, then I think we're good. With the caveat above about uncommitted changes :) |
We do have another hash available to us, though, which changes whenever any input Go code changes: the build IDs used for the build cache. I seem to recall that one such ID is embedded into binaries, too. Not ideal, as such a hash also includes build parameters like GOOS or -tags, which don't normally affect versions. But at least it fixed the problem with uncommitted files in VCS. |
Yes, uncommitted changes should be explicit in the pseudo-version, but I think we can suffix |
the new buildinfo already records whether the workspace is clean with |
we could use one of So main will always have a version like
|
What is the main motivation of encoding the local version in pseudo-version style rather than keeping those extra info (timestamp?) as extra metadata fields - if it's not guaranteed that they are always available in the origin or proxies? It seems like the BTW, I feel like the main module's version isn't sufficient to describe a tool's behavior in certain cases - |
The main motivation is that
Emphasis mine. It's not rather than, It doesn't replace the existing metadata fields. If you want to read the metadata, you should read it from those fields instead of parsing the pseudo-version. However, that metadata is useful in disambiguating builds produced by
This sounds like an argument to always use the build ID as the version suffix instead of the VCS hash.
I hope so too, but again, I think that discussion is out of scope for this thread, which is more about bringing |
(devel)
in binaries, which is not very usefulgo build
This proposal has been added to the active column of the proposals project |
As a kind of experience report, I'm using custom build-scripts for years, solely to embed version information of the main module into the executable. When building, I attach the following information:
With this kind of information, I'm able to build version-strings however I like. Usually I try to match go's pseudo-version strings, but when I need to stay compatible with the version-scheme from other, non-go projects, I can do so as well.
The version is printed when the application is started with a I don't have anything against adding a build ID, but it wouldn't really solve my problem. My use cases are:
The previously raised issue that git-tags might only be local was never really an issue in my experience. Version-tags aren't made lightheartedly and are always immediately pushed to the server in the projects I work on. I really hope we can this kind of information into a go-executable one day, because I would finally be able to get rid of all my build- and tool-scripts. |
It sounds like @mvdan and @bcmills have some hesitation around the fact that these pseudo-versions would not correspond to any publicly available version, even though they look like those. That does seem like a reason not to do this. We now have Git VCS info separately in the builds (as of Go 1.18; try go version -m). Do we need to add a second way to record that information? |
go build
go build
We can make it unambiguously distinct, for example instead of
No, we certainly only need one way to encode VCS info. The suggestion to put VCS info in the metadata field of the pseudo-version was to match the |
With replace statements in go.mod and the new workspace mode in Go 1.18 it is possible to build Go programs that include local versions of modules besides the main module. For those modules the go tool also lists The new build vcs metadata only helps identify the main module. Adding vcs metadata for all local modules seems valuable and not currently supported. The format suggested above by @4ad would be more informative than |
This may be true, but the concern above seems to be adding vcs metadata that looks like a pseudo-version. It need not, and it probably should not. We can always add that separately; maybe that should be a different proposal. (I think this is the first comment to bring up VCS info for replaced modules that point to other local repos.) |
A thought: if we're only concerned about having a reliable way to always get some useful version for the main module, I think it could be an API of its own, like I personally will be implementing logic like that to replace Another option, if we want this to also work for library modules, would be If the above sounds interesting, I'll happily develop the idea further and create a new proposal. I realise it's not the same as this proposal, but I also think it could be a different solution to the same end-user problem :) |
@mvdan I would very much like to see something like that to replace the boiler place build lines that exist in code at work. |
We use the semantic version of the binaries in order to compute API compatibility between different binaries. I am afraid that if your Now, one might object to using the binary version in this way and perhaps recommend using a separately maintained API version instead that is separate from the binary version. I would tend to agree except this is outside my control. I do not have the operational liberty to change this. |
Since we added a local context to git lookups, we need to be more careful about fetching from remote. We should not fetch when we are stamping a binary because that could slow down builds. For #50603 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest,gotip-windows-amd64-longtest Change-Id: I81a719b7609e8d30b32ffb3c12a05074c5fd0c22 Reviewed-on: https://go-review.googlesource.com/c/go/+/611916 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Michael Matloob <matloob@golang.org>
This CL adds a local only VCS lookup for Mercurial. It fixes a bug in pkg.go by passing in the repo directory to the LookupLocal function instead of the module directory. It could be the case that a binary is built in a subdirectory of the repo. For: #50603 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest,gotip-windows-amd64-longtest Change-Id: Ic36b5a361a8ba3b0ba1a6968cde5f5263c9c8dd0 Reviewed-on: https://go-review.googlesource.com/c/go/+/609155 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Michael Matloob <matloob@golang.org>
This comment was marked as off-topic.
This comment was marked as off-topic.
Change https://go.dev/cl/627295 mentions this issue: |
This CL adds support for tagging binaries in a bzr vcs environment. For: #50603 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest,gotip-windows-amd64-longtest Change-Id: I81eb72d9e0e15dbec8778dd06613ca212820a726 Reviewed-on: https://go-review.googlesource.com/c/go/+/627295 Auto-Submit: Sam Thanawalla <samthanawalla@google.com> Reviewed-by: Michael Matloob <matloob@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Change https://go.dev/cl/630195 mentions this issue: |
This work related to the original issue is completed. Separate proposals will be needed for:
|
@samthanawalla This new capability seems noteworthy enough that it should be mentioned in Go 1.24 release notes, is that right? Reopening as a release blocker to track that. Thanks. |
The RC is planned for next week, and we need a full draft of the release notes before then. Please prioritize writing the release notes for this. Thanks! |
Change https://go.dev/cl/633856 mentions this issue: |
Change https://go.dev/cl/635598 mentions this issue: |
this should start working in go 1.24 see: golang/go#50603
I struggled to map the old ones to the behavior described in golang/go#50603: a pseudo-version will be generated even if a VCS tag is available, but is not the latest commit. Also, a link to BuildInfo.Main feels useful. For golang/go#68545 Change-Id: Id2642c94a18e22ac56439728f20f01bf87d14a70 Reviewed-on: https://go-review.googlesource.com/c/website/+/635598 Reviewed-by: Sam Thanawalla <samthanawalla@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Sam Thanawalla <samthanawalla@google.com> Auto-Submit: Filippo Valsorda <filippo@golang.org> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Reviewed-by: Michael Matloob <matloob@golang.org>
I was testing this with a project and was a bit surprised that using |
@DeedleFake This is intentional. Whether we load version control information is controlled by the You could set |
NOTE: The accepted proposal is #50603 (comment).
cmd/go embeds dependency version information in binaries, which is very useful. From Go 1.18 onwards, cmd/go also embeds VCS information in binaries, which makes it even more useful than it was before.
As #37475 mentions, people place version information in binaries using
-ldflags='-X foo=bar'
, which requires an additional build wrapper. The new VCS stamping feature of cmd/go should alleviate the need for external wrapper, but I am afraid it comes short.The version information, in the sense of Go's pseudo version is not recorded for the main module when doing
go build
:The version is recorded as expected when doing
go install
:I am afraid this limitation of cmd/go will continue to force people to use external build wrappers that set
-ldflags
, which is rather unfortunate.I am not the first to want main module version information in binaries, this has been already asked for in various issues, for example in #29814, which was closed as a duplicate of #37475, but it really wasn't a duplicate, as #37475 is about VCS information, and #29814 is about semantic versioning. Other examples of people asking for this feature are mvdan/sh#519 and #29228 (comment) where various workarounds were proposed.
Speaking of workarounds, the only workaround that I know that currently works would be to create a local module proxy and pass
GOPROXY
togo install
, but that is an extremely high-overhead workaround, andgo install
is not a replacement forgo build
anyway, sincego install
comes with some rather severe limitations regarding how vendoring works and what you can put in go.mod, andgo install
doesn't support controllingGOBIN
when cross-compiling.I realize that Git tags are a local concept, and by doing the "wrong" git operations one could come up with a different pseudo-version for the same source code. I am afraid I don't have any solution or suggestion regarding this git misfeature, except to note that even in this case the hash information is recorded correctly, and in every case by the virtue of having access to the local source code the programmer can always do some local operation that has the potential to cause a version mislabeling. Git is just more prome to do this by accident, but the ability is there, always.
I don't have any stats to back this up, but from my experience most corporate source code is built by
go build
, notgo install
, and it would be great if somehow Go's notion of versioning would be stamped bygo build
.CC @bcmills @mvdan @rsc
The text was updated successfully, but these errors were encountered: