How do you make Gradle archive tasks produce byte-for-byte reproducible artifacts, and which settings are involved?
answer
- isPreserveFileTimestamps = false
- isReproducibleFileOrder = true
- withType(AbstractArchiveTask).configureEach
- constant timestamp + sorted entries
- also pin deps / stable manifest
basics
~10 sOn each archive task set isPreserveFileTimestamps = false and isReproducibleFileOrder = true. These zero out entry timestamps and sort entries deterministically so the same inputs always produce identical archive bytes.
solid answer
~50 sArchive tasks normally embed each entry's last-modified timestamp and order entries by filesystem traversal, so two builds of identical sources produce different bytes. Setting `isPreserveFileTimestamps = false` makes Gradle write a constant timestamp for every entry, and `isReproducibleFileOrder = true` sorts entries by a stable, platform-independent order. With both enabled on a `Jar`/`Zip`/`Tar`, identical inputs yield byte-identical archives. Apply them on every archive task — typically in a global `tasks.withType<AbstractArchiveTask>().configureEach { ... }` block so jar, sources jar, javadoc jar, and distribution zips are all covered. You must also keep other inputs deterministic: avoid volatile manifest attributes (timestamps, hostnames), pin dependency versions, and ensure generated resources are stable. Reproducible artifacts improve build-cache and remote-cache hit rates, let consumers verify published artifacts, and underpin supply-chain integrity. The two settings are the necessary archive-level switches; reproducibility overall is an input-determinism discipline.
code
kotlin · 4 linestasks.withType<AbstractArchiveTask>().configureEach {
isPreserveFileTimestamps = false
isReproducibleFileOrder = true
}go deeper
Name the two flags and that they make archives identical for identical inputs.
Explain what each flag does (constant timestamp vs sorted order) and apply them via withType().configureEach across all archives.
Discuss the broader input-determinism requirements (manifest, pinned deps, codegen) and the build-cache / verification payoffs.
Mandate reproducibility org-wide via a convention plugin and tie it to supply-chain verification and remote-cache efficiency policy.
## The problem A ZIP/JAR is a container of entries, each stamped with a last-modified time, written in whatever order the directory walk produced. Both vary between machines and runs, so two builds of the **same** source produce **different** bytes — defeating verification and cache reuse. ## The two archive switches `AbstractArchiveTask` exposes two booleans: - `isPreserveFileTimestamps` (default `true`). Set to `false` to write a **fixed constant timestamp** for every entry instead of the real mtime. - `isReproducibleFileOrder` (default `false`). Set to `true` to write entries in a **deterministic, sorted order** independent of the filesystem. ```kotlin tasks.withType<AbstractArchiveTask>().configureEach { isPreserveFileTimestamps = false isReproducibleFileOrder = true } ``` Applying it via `withType(...).configureEach` ensures **every** archive — main jar, sources jar, javadoc jar, distribution zip/tar — is covered without repeating yourself, and `configureEach` keeps it lazy (no eager task realization). ## Other inputs that must be deterministic The two flags fix archive packaging, but byte-equality also requires the **contents** to be stable: - **Manifest**: drop volatile attributes (build time, hostname, random build IDs). - **Dependencies**: pin versions / use a lockfile so the same classes are bundled. - **Generated code/resources**: ensure annotation processors, codegen, and property-file ordering are deterministic. - **Locale/encoding**: keep file encoding and sort locale stable. ## Why it matters - **Build cache**: identical outputs maximize local and remote build-cache hits; non-reproducible archives constantly miss. - **Verification & supply chain**: consumers (and tools like reproducible-builds verification, Sigstore, dependency verification) can confirm a published artifact matches its declared source. - **Diff-ability**: identical inputs yielding identical artifacts means a changed checksum genuinely signals a changed input. ## Relationship to publishing For a published library you want every artifact under your coordinates to be reproducible so downstream verifiers and your own cache behave predictably. These settings are therefore a standard part of a publishing-ready convention plugin.
- Why apply the flags via withType(...).configureEach rather than on the jar task only?So every archive — sources jar, javadoc jar, distribution zips/tars — is reproducible too, and configureEach keeps configuration lazy without realizing tasks eagerly.
- Do these two flags alone guarantee a reproducible build?No. They fix archive packaging, but you also need deterministic contents: stable manifest values, pinned dependencies, and reproducible code/resource generation.
- How does reproducibility interact with Gradle's build cache?Deterministic outputs maximize cache hits locally and remotely; non-reproducible archives change their bytes each run and miss the cache.
saying these in an interview costs you the question
- Setting isPreserveFileTimestamps = true thinking it enables reproducibility (it's the opposite).
- Claiming the two flags alone fully guarantee reproducibility regardless of contents.
- Applying them only to the jar task and forgetting sources/javadoc/distribution archives.