From 015b47f3e98e6d1b29734e41195851793e24ca24 Mon Sep 17 00:00:00 2001 From: Leo Di Donato <120051+leodido@users.noreply.github.com> Date: Wed, 19 Nov 2025 16:20:20 +0000 Subject: [PATCH] feat: export SOURCE_DATE_EPOCH for build commands Export SOURCE_DATE_EPOCH environment variable for all build commands to enable reproducible builds without requiring .git directory. Build commands can use SOURCE_DATE_EPOCH directly for deterministic timestamps. Benefits: - Works in CI environments without full .git history - Standard approach (reproducible-builds.org) - Same timestamp used for tar archives and Docker images - No dependency on git being available Co-authored-by: Ona --- README.md | 36 ++++++++++++++++++++++++++++++++++++ pkg/leeway/build.go | 12 ++++++++++++ 2 files changed, 48 insertions(+) diff --git a/README.md b/README.md index 20c776d2..05503253 100644 --- a/README.md +++ b/README.md @@ -297,6 +297,42 @@ Leeway supports built-in build arguments: - `__git_commit` contains the current Git commit if the build is executed from within a Git working copy. If this variable is used and the build is not executed from within a Git working copy the variable resolution will fail. If the package sources contain uncommitted files/directories, then `__pkg_version` will be appended to `__git_commit` - `__git_commit_short` shortened version of `__git_commit` to the first 7 characters. +## Environment Variables + +Build commands have access to the following environment variables: + +### `SOURCE_DATE_EPOCH` + +Unix timestamp for reproducible builds. Contains the git commit timestamp (or value from `SOURCE_DATE_EPOCH` environment variable if set before running leeway). + +This enables deterministic timestamps without requiring .git directory, which is useful in CI environments with shallow clones. + +**Example usage:** + +```yaml +packages: + - name: app + type: go + config: + buildCommand: + - sh + - -c + - | + # SOURCE_DATE_EPOCH is automatically set by leeway + go build -ldflags "-X main.BuildTime=$SOURCE_DATE_EPOCH" -o app +``` + +**Benefits:** +- Works without .git directory (CI-friendly) +- Standard approach ([reproducible-builds.org](https://reproducible-builds.org/docs/source-date-epoch/)) +- Same timestamp used for tar archives and Docker images + +**Docker builds:** + +For Docker packages, leeway automatically enables BuildKit (`DOCKER_BUILDKIT=1`) and exports `SOURCE_DATE_EPOCH`. For deterministic Docker images, see PR #285 which passes the value as a build arg (requires `ARG SOURCE_DATE_EPOCH` in Dockerfiles). + +BuildKit is the default builder since Docker Engine v23.0 and is always used in Docker Desktop. + ## Package Variants Leeway supports build-time variance through "package variants". Those variants are defined on the workspace level and can modify the list of sources, environment variables and config of packages. For example consider a `WORKSPACE.YAML` with this variants section: diff --git a/pkg/leeway/build.go b/pkg/leeway/build.go index 964d0ba3..1af3e351 100644 --- a/pkg/leeway/build.go +++ b/pkg/leeway/build.go @@ -2590,6 +2590,18 @@ func executeCommandsForPackage(buildctx *buildContext, p *Package, wd string, co env := append(os.Environ(), p.Environment...) env = append(env, fmt.Sprintf("%s=%s", EnvvarWorkspaceRoot, p.C.W.Origin)) + + // Export SOURCE_DATE_EPOCH for reproducible builds + // BuildKit (Docker >= v23.0) automatically uses this for deterministic image timestamps + mtime, err := p.getDeterministicMtime() + if err == nil { + env = append(env, fmt.Sprintf("SOURCE_DATE_EPOCH=%d", mtime)) + } + + // Enable BuildKit to ensure SOURCE_DATE_EPOCH is used for Docker builds + // BuildKit is default since Docker v23.0, but we set it explicitly for older versions + env = append(env, "DOCKER_BUILDKIT=1") + for _, cmd := range commands { if len(cmd) == 0 { continue // Skip empty commands