From be46795975b845a6f23d1be026423623cba86df0 Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 15 Aug 2024 20:53:47 +0200 Subject: [PATCH] [CHORE] Support reproducible builds This is a step towards making Forgejo's binaries (the one listed in the release tab) reproducible. In order to make the actual binary reproducible, we have to ensure that the release workflow has the correct configuration to produce such reproducible binaries. The release workflow currently uses the Dockerfile to produce binaries, as this is one of the easiest ways to do cross-compiling for Go binaries with CGO enabled (due to SQLite). In the Dockerfile, two new arguments are being given to the build command. `-trimpath` ensures that the workpath directory doesn't get included in the binary; this means that file names (such as for panics) are relative (to the workpath) and not absolute, which shouldn't impact debugging. `-buildid=` is added to the linker flag; it sets the BuildID of the Go linker to be empty; the `-buildid` hashes the input actions and output content; these vary from build to build for unknown reasons, but likely because of the involvement of temporary file names, this doesn't have any effect on the behavior of the resulting binary. The Makefile receives a new command, `reproduce-build#$VERSION` which can be used by people to produce a reproducible Forgejo binary of a particular release; it roughly does what the release workflow also does. Build the Dockerfile and extract the Forgejo binary from it. This doesn't allow to produce a reproducible version for every release, only for those that include this patch, as it needs to call the makefile of that version in order to make a reproducible binary. There's one thing left to do: the Dockerfile pins the Go version to a minor level and not to a patch level. This means that if a new Go patch version is released, that will be used instead and will result in a different binary that isn't bit to bit the same as the one that Forgejo has released. --- Dockerfile | 2 +- Makefile | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index eba2fb97d3..ad8ec80dd5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,7 +36,7 @@ WORKDIR ${GOPATH}/src/code.gitea.io/gitea RUN make clean RUN make frontend RUN go build contrib/environment-to-ini/environment-to-ini.go && xx-verify environment-to-ini -RUN make RELEASE_VERSION=$RELEASE_VERSION go-check generate-backend static-executable && xx-verify gitea +RUN make RELEASE_VERSION=$RELEASE_VERSION GOFLAGS="-trimpath" LDFLAGS="-buildid=" go-check generate-backend static-executable && xx-verify gitea # Copy local files COPY docker/root /tmp/local diff --git a/Makefile b/Makefile index cf4828948a..9489db2b7a 100644 --- a/Makefile +++ b/Makefile @@ -268,6 +268,7 @@ help: @echo " - tidy run go mod tidy" @echo " - test[\#TestSpecificName] run unit test" @echo " - test-sqlite[\#TestSpecificName] run integration test for sqlite" + @echo " - reproduce-build\#version build a reproducible binary for the specified release version" ### # Check system and environment requirements @@ -890,6 +891,26 @@ release-sources: | $(DIST_DIRS) release-docs: | $(DIST_DIRS) docs tar -czf $(DIST)/release/gitea-docs-$(VERSION).tar.gz -C ./docs . +.PHONY: reproduce-build +reproduce-build: +# Start building the Dockerfile with the RELEASE_VERSION tag set. GOPROXY is set +# for convience, because the default of the Dockerfile is `direct` which can be +# quite slow. + @docker build --build-arg="RELEASE_VERSION=$(RELEASE_VERSION)" --build-arg="GOPROXY=$(shell $(GO) env GOPROXY)" --tag "forgejo-reproducibility" . + @id=$$(docker create forgejo-reproducibility); \ + docker cp $$id:/app/gitea/gitea ./forgejo; \ + docker rm -v $$id; \ + docker image rm forgejo-reproducibility:latest + +.PHONY: reproduce-build\#% +reproduce-build\#%: + @git switch -d "$*" +# All the current variables are based on information before the git checkout happened. +# Call the makefile again, so these variables are correct and can be used for building +# a reproducible binary. Always execute git switch -, to go back to the previous branch. + @make reproduce-build; \ + (code=$$?; git switch -; exit $${code}) + ### # Dependency management ###