Skip to content
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

Explicitly set go toolchain in goreleaser config #6420

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

alexbozhenko
Copy link
Contributor

@alexbozhenko alexbozhenko commented Jan 28, 2025

Details

We have reproducible binaries, rpms/debs and archives.
And goreleaser.yml holds all the knobs that can be tuned to build the binary.

The only thing that affects the build and is not defined in the goreleaser config is the go toolchain version.
Currently, we set the version of the go toolchain that is used for releases in Travis:

nats-server/.travis.yml

Lines 11 to 12 in 5e60171

- "1.23.5"
- "1.22.11"

condition: ($TRAVIS_GO_VERSION =~ 1.23) && ($TEST_SUITE = "compile")

I spent some time trying to understand the behavior of go and toolchain directives. I think setting the toolchain used for releases in .goreleser.yml would make it more explicit and future-proof.

With this change, any human or script who has go>1.21.0 and goreleaser installed can checkout the repo at any commit, run one command, and get binaries that will be exactly as if we were cutting a release on that commit.

goreleaser build --snapshot --clean --single-target

Several places would benefit from not having to worry about keeping toolchain and all the go build flags in sync:

If we land this, we can update the above places to use goreleaser build, thus making sure we use exactly the same binary everywhere, and forever forget about managing/updating go versions in other places that need to build the binary

Reproducible test plan

Before

Behavior is not hermetic. We depend on toolchain that happens to be installed on the build host.

  1. Local go(`1.21.0`) < one that is specified in go.mod. Go will download and use toolchain from go.mod:
     ```
     # go version
     go version go1.21.0 linux/amd64
    
     # TARGET='linux_amd64' goreleaser build --snapshot --clean --single-target
     # go version -m  dist/nats-server_linux_amd64_v1/nats-server | grep go1
     dist/nats-server_linux_amd64_v1/nats-server: go1.22.8
     ```
    
  2. Local go(`1.23.3`) > one that is specified in go.mod. Local toolchain will be used:
    # wget https://go.dev/dl/go1.23.3.linux-amd64.tar.gz
    # sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.23.3.linux-amd64.tar.gz
    # go version
    go version go1.23.3 linux/amd64
    
    # TARGET='linux_amd64' goreleaser build --snapshot --clean --single-target
    # go version -m  dist/nats-server_linux_amd64_v1/nats-server | grep go1
    dist/nats-server_linux_amd64_v1/nats-server: go1.23.3
    

After

  1. Local go version (1.23.4) did not affevt what was used for the binary

    It will download (just like it downloads all the modules)
    the version of the toolchain specified in goreleaer config, and use it for building the binary.

    
    # go version
    go version go1.23.4 linux/amd64
    
    # TARGET='linux_amd64' goreleaser build --snapshot --clean --single-target
    # go version -m  dist/nats-server_linux_amd64_v1/nats-server | grep go1
    dist/nats-server_linux_amd64_v1/nats-server: go1.23.5
    
    # sha256sum  dist/nats-server_linux_amd64_v1/nats-server
    95d52ed8656f74abd0c0576d8d9be50fcc00562c8e18215d1f1f04b8c0b6fc3d  dist/nats-server_linux_amd64_v1/nats-server
    
  2. Any go >= go1.21.0 (released 2023-08-08) will build exactly the same binary:
    # wget <https://go.dev/dl/go1.21.0.linux-amd64.tar.gz>
    # sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.21.0.linux-amd64.tar.gz
    # go version
    go version go1.21.0 linux/amd64
    
    # TARGET='linux_amd64' goreleaser build --snapshot --clean --single-target
    # go version -m  dist/nats-server_linux_amd64_v1/nats-server | grep go1
    dist/nats-server_linux_amd64_v1/nats-server: go1.23.5
    
    # sha256sum  dist/nats-server_linux_amd64_v1/nats-server
    95d52ed8656f74abd0c0576d8d9be50fcc00562c8e18215d1f1f04b8c0b6fc3d  dist/nats-server_linux_amd64_v1/nats-server
    
  3. To build using specific toolchain one would set the GORELEASER_TOOLCHAIN env variable:

    # GORELEASER_TOOLCHAIN="go1.22.8" TARGET='linux_amd64' goreleaser build --snapshot --clean --single-target
    # go version -m  dist/nats-server_linux_amd64_v1/nats-server | grep go1
    dist/nats-server_linux_amd64_v1/nats-server: go1.22.8
    

Note that starting from go1.24.0 we can use the tool directive in go.mod, and even version goreleaser itself. It will look something like this:

go tool goreleaser build --snapshot --clean --single-target

Signed-off-by: Alex Bozhenko <[email protected]>
@alexbozhenko alexbozhenko requested a review from a team as a code owner January 28, 2025 16:38
@alexbozhenko
Copy link
Contributor Author

alexbozhenko commented Jan 28, 2025

To land this, we would need to change .travis.yml and runTestsOnTravis.sh a little, to make sure the way it sets up Go is compatible with setting the toolchain in goreleaser.yml

Copy link
Member

@philpennock philpennock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use goreleaser for official releases, and it's a fantastic tool, but people who are developing don't have to run goreleaser to do a regular build. Folks need to be able to run go build and get a working server. So if we remove the toolchain directive from go.mod then we create a regression: folks may end up trying to build with an older version of Go because they don't get the bump up to the minimum specified there.

It should be sufficient to just set GOTOOLCHAIN in the environment, which will override the go.mod directive.

@alexbozhenko
Copy link
Contributor Author

alexbozhenko commented Jan 29, 2025

@philpennock I am not sure if the toolchain directive was added on purpose or by accident
This
179c352
added 1.22.8. but Travis currently specifies 1.22.11.
I feel like the presence of the toolchain directive adds more confusion...
But I defer to @neilalexander to comment on this...

Ok, let's leave go.mod alone in this PR, since that version does not affect goreleaser process, and I am more concerned with having reproducible builds that can be used by other tools, e.g. binaries.nats.dev.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants