Skip to content

[v2.11] Make RPMs and DEBs reproducible#6359

Merged
derekcollison merged 2 commits intonats-io:mainfrom
alexbozhenko:reproducible_rpms
Jan 23, 2025
Merged

[v2.11] Make RPMs and DEBs reproducible#6359
derekcollison merged 2 commits intonats-io:mainfrom
alexbozhenko:reproducible_rpms

Conversation

@alexbozhenko
Copy link
Copy Markdown
Member

@alexbozhenko alexbozhenko commented Jan 10, 2025

Similar to #6299
Make packages set mtime to commitdate.

Upgrade goreleaser to the latest 2.6.1, which includes the feature used in this PR: goreleaser/goreleaser#5392

go install github.com/goreleaser/goreleaser/v2@v2.6.1
goreleaser release --snapshot --clean -f .goreleaser.yml
mv dist/ ~/tmp/dist_before
goreleaser release --snapshot --clean -f .goreleaser.yml 

# now, all the artifacts of the two builds are exactly the same. Only sbom files are different(because those include timestamps)
vimdiff dist/SHA256SUMS ~/tmp/dist_before/SHA256SUMS

image

Signed-off-by: Alex Bozhenko alex@synadia.com

@neilalexander
Copy link
Copy Markdown
Member

Is this still in-flight?

@neilalexander neilalexander changed the title Make RPMs ans DEBs reproducible Make RPMs and DEBs reproducible Jan 22, 2025
Signed-off-by: Alex Bozhenko <alexbozhenko@gmail.com>
@alexbozhenko alexbozhenko marked this pull request as ready for review January 22, 2025 18:32
@alexbozhenko alexbozhenko requested a review from a team as a code owner January 22, 2025 18:32
@alexbozhenko
Copy link
Copy Markdown
Member Author

@neilalexander it's ready for review now.

Copy link
Copy Markdown
Member

@wallyqs wallyqs left a comment

Choose a reason for hiding this comment

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

LGTM

@wallyqs wallyqs changed the title Make RPMs and DEBs reproducible [v2.11] Make RPMs and DEBs reproducible Jan 22, 2025
Copy link
Copy Markdown
Member

@neilalexander neilalexander left a comment

Choose a reason for hiding this comment

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

LGTM

@derekcollison derekcollison merged commit dec670a into nats-io:main Jan 23, 2025
derekcollison added a commit that referenced this pull request Feb 20, 2025
## Details
We have reproducible binaries,
[rpms/debs](#6359) and
[archives](#6299).
And `goreleaser.yml` holds [all the
knobs](https://goreleaser.com/customization/builds/go/) 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:

<https://github.com/nats-io/nats-server/blob/5e6017135b4b1d333b435ff55b720275a39bb7af/.travis.yml#L11-L12>

<https://github.com/nats-io/nats-server/blob/5e6017135b4b1d333b435ff55b720275a39bb7af/.travis.yml#L67>

I spent some time [trying to understand the behavior of go and toolchain
directives](https://alexbozhenko.github.io/posts/2024-12-19-understand-go-toolchain-directive-or-your-money-back/).
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:

* get-nats.io(already uses gorelaser, but toolchain used to build
depends on the build host):

<https://github.com/ConnectEverything/client-tools/blob/eba999ac9a1e107205fbf89ba230df3f80458028/build-nightlies.sh#L184-L188>
* <redacted> number of private repos

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.
    <details>
<summary>Local go(`1.21.0`) < one that is specified in go.mod. Go will
download and use toolchain from go.mod:</summary>

        ```
        # 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
        ```
    </details>

2.
    <details>
<summary>Local go(`1.23.3`) > one that is specified in go.mod. Local
toolchain will be used:</summary>

    ```
    # 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
    ```

    </details>

### After

1.
    <details>
<summary>Local go version (1.23.4) did not affect what was used for the
binary</summary>

It will download (just like it [downloads all the
modules](https://youtu.be/KqTySYYhPUE?feature=shared&t=229))
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
    ```

    </details>

2.
    <details>
<summary>Any go >= go1.21.0 (released 2023-08-08) will build exactly the
same binary:</summary>

    ```
    # 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
    ```

    </details>

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](https://tip.golang.org/doc/modules/managing-dependencies#tools
) in go.mod, and even version goreleaser itself. It will look something
like this:

```
go tool goreleaser build --snapshot --clean --single-target
```
neilalexander added a commit that referenced this pull request Sep 16, 2025
Follow-up to #6359 and
#6701

Since 2.11.2 version of goreleaser, it is allowed to set buildhost for
RPMs
https://github.com/orgs/goreleaser/discussions/5662

I believe with this all the rpms, debs and tars will be reproducible(the
binaries have been reproducible for long time)

## Test plan:
```
# go clean -cache && goreleaser  release --skip=announce,publish,validate --clean -f .goreleaser.yml
```

```
# rpm -qip nats-server-v2.12.0-RC.3-s390x.rpm | grep -i 'Build Host'

Build Host  : synadia.com
```

[skip ci]

Signed-off-by: Alex Bozhenko <alexbozhenko@gmail.com>
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.

4 participants