The term "reproducible build" refers to a build process that produces identical results on every run. With a reproducible build process, the build outputs of each run behave identically and they can be verified (via checksum) to be bit-by-bit identical.
Reproducible builds are important for a number of reasons:
As you might expect, creating reproducible builds is harder than it sounds, because of the large number of variables involved in the usual build process. These variables include:
Implementing a reproducible build strategy thus implies extreme and strict control over all the variables involved in the build...not an easy task!
Dagger and Reproducible Builds
While Dagger doesn't claim to solve reproducible builds, we are working towards making them easier to implement.
Here's an example of this in action:
c, _ := dagger.Connect(ctx, dagger.WithLogOutput(os.Stdout))
gitTag := "v0.3.10"
repo := c.Git("https://github.com/dagger/dagger.git").Tag(gitTag).Tree()
daggerBinary := c.Container().From("golang:1.19-alpine3.17").
WithMountedDirectory("/src", repo).
WithEnvVariable("CGO_ENABLED", "0").
WithWorkdir("/src").
WithExec([]string{"go", "build", "-o", "dagger", "./cmd/dagger"}).
File("dagger")
defaultTimestamp, _ := c.Container().From("alpine:3.17").
WithMountedFile("/dagger", daggerBinary).
WithExec([]string{"stat", "-c", "%y %s", "/dagger"}).
Stdout(ctx)
fixedTimestamp, _ := c.Container().From("alpine:3.17").
WithMountedFile("/dagger", daggerBinary.WithTimestamps(0)).
WithExec([]string{"stat", "-c", "%y %s", "/dagger"}).
Stdout(ctx)
fmt.Println("defaultTimestamp:", defaultTimestamp)
fmt.Println("fixedTimestamp:", fixedTimestamp)
The Future
Containerization, resource pinning and timestamp fixation are only a few aspects of a reproducible build strategy. Other areas where Dagger could potentially be helpful are:
Do you need support for one of the issues mentioned above? Leave us a comment in the issue to let us know. We look forward to continuing to improve the reproducible builds experience with Dagger.