How to ensure that the Go version specified in go.mod is picked?
matteoolivi opened this issue · 9 comments
What happened?
-
What were you attempting to do?
Trying to build a Go project that specifies go 1.17 as the version in go.mod.
Command:bin/pack build <my image name> --builder=paketobuildpacks/builder:tiny
-
What did you expect to happen?
The pack runs the build with Go 1.17 as that's the language version specified in thego.mod
file. Note that it's not just that I expected this to happen, it's what I actually want, I don't want a different build version to be used. i.e., I don't want to hardcode a version, I want the version ingo.mod
to be used. -
What was the actual behavior? Please provide log output, if possible.
Go 1.18 was picked instead. An excerpt from the logs:
===> DETECTING
5 of 9 buildpacks participating
paketo-buildpacks/ca-certificates 3.2.1
paketo-buildpacks/go-dist 1.1.3
paketo-buildpacks/git 0.4.2
paketo-buildpacks/go-mod-vendor 0.6.0
paketo-buildpacks/go-build 1.1.2
===> ANALYZING
Previous image with name "<my image name>" not found
===> RESTORING
===> BUILDING
Paketo CA Certificates Buildpack 3.2.1
https://github.com/paketo-buildpacks/ca-certificates
Launch Helper: Contributing to layer
Creating /layers/paketo-buildpacks_ca-certificates/helper/exec.d/ca-certificates-helper
Paketo Go Distribution Buildpack 1.1.3
Resolving Go version
Candidate version sources (in priority order):
go.mod -> ">= 1.17"
<unknown> -> ""
Selected Go version (using go.mod): 1.18.1
Executing build process
Installing Go 1.18.1
Completed in 1m55.095s
Build Configuration
- What platform (
pack
,kpack
,tekton
buildpacks plugin, etc.) are you
using? Please include a version.
$ pack version
pack 0.21.1+git-e09e397.build-2823
- What buildpacks are you using? Please include versions.
paketo-buildpacks/ca-certificates 3.2.1
paketo-buildpacks/go-dist 1.1.3
paketo-buildpacks/git 0.4.2
paketo-buildpacks/go-mod-vendor 0.6.0
paketo-buildpacks/go-build 1.1.2
- What builder are you using? If custom, can you provide the output from
pack inspect-builder <builder>
?
paketobuildpacks/builder:tiny
$ pack inspect-builder paketobuildpacks/builder:tiny
Inspecting image: paketobuildpacks/builder:tiny
REMOTE:
Stack: io.paketo.stacks.tiny
Base Image:
Top Layer:
Run Images:
(none)
Buildpacks:
(buildpack metadata not present)
LOCAL:
Stack: io.paketo.stacks.tiny
Base Image:
Top Layer:
Run Images:
(none)
Buildpacks:
(buildpack metadata not present)
- Can you provide a sample app or relevant configuration (
buildpack.yml
,
nginx.conf
, etc.)? Not at this time.
Checklist
- [ x ] I have included log output.
- The log output includes an error message.
- I have included steps for reproduction.
Hi @matteoolivi – I'm curious about how this version selection is impacting your build. Is something going wrong with your build or your running app? Some undesired/unexpected behaviour?
For a bit of background context on why the buildpack behaves this way today, the buildpack interprets the version of go in the go.mod
file as the minimum go version requirement. This is aligned with Golang's documentation of the meaning of the go
directive in a go.mod
file.
But this decision could be revisited if we get a sense that this causes undesired behaviour for users. How is this impacting you?
Hi @fg-j , thanks for the prompt answer, and sorry for my belated reply.
Thanks for the pointer, I wasn't aware that the semantics of the version in go.mod
is "at least" - I thought it was stricter than that.
I would still prefer to use the version in go.mod
rather than a more recent one, so I'll lobby for support for that feature here. I think it allows to make the build more deterministic and more consistent between different mechanisms (e.g. manual build triggered via a makefile and an automated build with something such as buildpacks). With just one look at one standard file one knows which version is being used for the build, rather than having to look at logs of the build process or the configuration for it. I also think it's safer. I know in theory if a program has been written with go 1.17 in mind it should be totally safe to build it with Go 1.x
as long as x >= 17
, but in practice there could be oversights and mistakes from the language developers that break that (and not necessarily in obvious ways).
I know I can configure the buildpack to a specific version: https://paketo.io/docs/howto/go/#override-the-detected-go-version . But I'd rather have a unique, universally known place where the version for the build is used, so that any developer who looks at go.mod knows with which version the project is being built with. So it'd be nice if there was a way to configure the buildpack to always use the version in go.mod
.
I don't think we should enforce a semantic that is different than what the Go development team itself defines. Its actually relatively common to see applications with relatively old go
directives in their go.mod
file. In these cases, the buildpack could fail to build their application if it specifies a version that is now unsupported even though it should be reasonably buildable with any recent version of Go. I think many Go developers would find that behavior surprising since Go just "does the right thing" for them on their workstation even when they have a relatively old version specified.
As you've seen, we have other mechanisms that allow you to define what version you would like to be used.
@ryanmoran what about making the semantics I described a configuration option? i.e. keep the existing behavior, but allow providing a special value for the go version to use that loyally picks the version specified in go.mod
?
I think that could work. Maybe something like BP_LOCK_GOMOD_VERSION=true
? If this gets set, we'd treat a go.mod
file with the directive go 1.17
as 1.17.x
instead of >= 1.17.0
.
Thanks a lot for being open to consider this.
Yes, what you proposed would work.
An alternative would be to use a special value for the BP_GO_VERSION
variable, such as follow-go-mod
, the advantage is that there aren't two different variables that affect the same behavior.
@matteoolivi I like the idea of using the existing BP_GO_VERSION
env var. Other @paketo-buildpacks/go-maintainers what do y'all think about that?
Personally, I'm a little skeptical of the value of this feature, because it means the buildpack is operating in a way that is inconsistent with golang semantics outside of the buildpack context (as @ryanmoran pointed out above), and there are ways to achieve the desired deterministic behavior that I think are more idiomatic for both golang applications and the paketo buildpack ecosystem.
To address the point about discoverability of the locked-down golang version: If a user sets BP_GO_VERSION=follow-go-mod
(or whatever the proposed configuration bcomes), then people debugging the build would have to know that a) this environment variable is set, and b) that it results in behavior that the golang ecosystem does not naturally support. If they know all that information, I think it is clearer for the environment variable BP_GO_VERSION
to be set explicitly to the desired golang version.
All of the arguments for locking down the version would be equally applicable outside of the buildpack context, and they would be expected failure modes for a golang application. More concretely, if an application has go 1.17
and this application somehow fails with go 1.18
, then this is a bug in golang, and not something I think the buildpack should sidestep. If this were a local environment, the application developer would have to uninstall golang 1.18
and re-install golang 1.17
. If this were a buildpack environment, the user would set BP_GO_VERSION=1.17
and re-run their build.
@matteoolivi You describe that this behavior would result in more consistent behavior between a manual build and a Makefile
- can you provide an example of what this looks like? I can't visualize this, and it would really help me understand the use-case you're aiming to acheive.
This issue has been open for a year without further comment. I'm going to close it as I don't think we're going to implement go.mod
go version semantics that contradict the behavior of go.mod
outside of a buildpack context.
If there are examples of how this proposed behavior would improve the user experience of a go application developer I'm happy to hear them and we can re-open this issue.