goreleaser / nfpm

nFPM is Not FPM - a simple deb, rpm, apk, ipk, and arch linux packager written in Go
https://nfpm.goreleaser.com/
MIT License
2.16k stars 158 forks source link

Add support for SOURCE_DATE_EPOCH #734

Closed cognifloyd closed 10 months ago

cognifloyd commented 11 months ago

Is your feature request related to a problem? Please describe.

If a package is re-built with the same contents, but at two different times, the two packages will have binary differences because of the embedded timestamps.

I need to be able to build packages in a way that is completely reproducible, so that the same inputs gives me byte-for-byte the same package output.

For the files I'm packaging, I can set the mtime via the config file, or by changing the mtime on the file tree before running nfpm. However, there is no current mechanism for me to manage the timestamps generated within nFPM.

Describe the solution you'd like

Make the package build process reproducible by using the standard SOURCE_DATE_EPOCH env var (if available) instead of using time.Now().

More specifically, I think we can replace all of the time.Now() occurrences with one value calculated early on. Then, adjust that calculation to use SOURCE_DATE_EPOCH instead, if available.

This is the go example of calculating that: https://reproducible-builds.org/docs/source-date-epoch/#go

import (
        "fmt"
        "os"
        "strconv"
        "time"
)

[...]

source_date_epoch := os.Getenv("SOURCE_DATE_EPOCH")
var build_date string
if source_date_epoch == "" {
        build_date = time.Now().UTC().Format(http.TimeFormat)
} else {
        sde, err := strconv.ParseInt(source_date_epoch, 10, 64)
        if err != nil {
                panic(fmt.Sprintf("Invalid SOURCE_DATE_EPOCH: %s", err))
        }
        build_date = time.Unix(sde, 0).UTC().Format(http.TimeFormat)
}

We can probably also use this build_date for a default mtime of package contents, but that is NOT what this issue is about. This issue is about controlling the current usages of time.Now(). These are the current instances that github search shows: https://github.com/search?q=repo%3Agoreleaser%2Fnfpm+time.now&type=code

Describe alternatives you've considered

A new cli flag, or any other non-standard option means any automation has to learn a new trick instead of reusing the standard env var. More detailed description on why the env var is better: https://wiki.debian.org/ReproducibleBuilds/StandardEnvironmentVariables#A.22We.27ll_add_a_command-line_flag_instead.22

Search

Code of Conduct

Additional context

More background on why reproducible builds are important: https://reproducible-builds.org/

The spec for SOURCE_DATE_EPOCH: https://reproducible-builds.org/specs/source-date-epoch/

Apparently, dpkg automatically sets SOURCE_DATE_EPOCH when building packages based on the latest entry in the debian/changelog file. Many other tools have adopted that standard as well: https://reproducible-builds.org/docs/source-date-epoch/#reading-the-variable

caarlos0 commented 10 months ago

check #748