Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,10 +329,39 @@ packages:

**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).
For Docker packages, leeway automatically enables BuildKit (`DOCKER_BUILDKIT=1`) and exports `SOURCE_DATE_EPOCH`. Additionally, leeway passes `SOURCE_DATE_EPOCH` as a build arg to enable deterministic image timestamps.

BuildKit is the default builder since Docker Engine v23.0 and is always used in Docker Desktop.

**Dockerfile requirements for deterministic images:**

Dockerfiles MUST declare the build arg for BuildKit to use the timestamp for image metadata:

```dockerfile
FROM alpine:3.18
ARG SOURCE_DATE_EPOCH
COPY app /usr/local/bin/app
```

With the `ARG SOURCE_DATE_EPOCH` declaration, BuildKit (>= v0.13) automatically uses the timestamp for:
- Layer creation timestamps
- Image config `created` timestamp
- History timestamps
- OCI annotations

Without the ARG declaration, images will have non-deterministic timestamps even though leeway sets the environment variable.

For multi-stage builds, declare the ARG in each stage:

```dockerfile
FROM golang:1.21 AS builder
ARG SOURCE_DATE_EPOCH
RUN go build -o app

FROM alpine:3.18
ARG SOURCE_DATE_EPOCH
COPY --from=builder /app /app
```
## 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:
Expand Down
12 changes: 9 additions & 3 deletions pkg/leeway/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -1992,6 +1992,8 @@ func (p *Package) buildDocker(buildctx *buildContext, wd, result string) (res *p
buildcmd = append(buildcmd, "--build-arg", fmt.Sprintf("DEP_%s=%s", arg, val))
}
buildcmd = append(buildcmd, "--build-arg", fmt.Sprintf("__GIT_COMMIT=%s", p.C.Git().Commit))
// Pass SOURCE_DATE_EPOCH for deterministic Docker image timestamps
buildcmd = append(buildcmd, "--build-arg", fmt.Sprintf("SOURCE_DATE_EPOCH=%d", mtime))
if cfg.Squash {
buildcmd = append(buildcmd, "--squash")
}
Expand Down Expand Up @@ -2219,7 +2221,11 @@ func (p *Package) buildDocker(buildctx *buildContext, wd, result string) (res *p

// Add PostProcess to create structured metadata file
res.PostProcess = func(buildCtx *buildContext, pkg *Package, buildDir string) error {
return createDockerExportMetadata(buildDir, version, cfg)
mtime, err := pkg.getDeterministicMtime()
if err != nil {
return fmt.Errorf("failed to get deterministic mtime: %w", err)
}
return createDockerExportMetadata(buildDir, version, cfg, mtime)
}

// Add subjects function for provenance generation
Expand Down Expand Up @@ -2365,11 +2371,11 @@ type DockerImageMetadata struct {
}

// createDockerExportMetadata creates metadata file for exported Docker images
func createDockerExportMetadata(wd, version string, cfg DockerPkgConfig) error {
func createDockerExportMetadata(wd, version string, cfg DockerPkgConfig, mtime int64) error {
metadata := DockerImageMetadata{
ImageNames: cfg.Image,
BuiltVersion: version,
BuildTime: time.Now(),
BuildTime: time.Unix(mtime, 0),
CustomMeta: cfg.Metadata,
}

Expand Down
Loading