A distribution archive must be reproducible bit-for-bit and preserve correct file permissions (e.g. executable scripts) across builds. How do you configure that on the distribution/archive tasks?
answer
- isPreserveFileTimestamps = false
- isReproducibleFileOrder = true
- AbstractArchiveTask.configureEach
- filePermissions { unix("0755") } (8.3+)
- fileMode/dirMode deprecated
basics
~10 sSet isReproducibleFileOrder = true and isPreserveFileTimestamps = false on the *DistZip/*DistTar tasks, and declare permissions with filePermissions { unix("0755") } in the CopySpec for executables.
solid answer
~40 sReproducible archives require eliminating two sources of nondeterminism: file ordering and timestamps. On any `AbstractArchiveTask` (`Zip`, `Tar`, and the generated `*DistZip`/`*DistTar`), set `isPreserveFileTimestamps = false` (zeroes entry timestamps) and `isReproducibleFileOrder = true` (sorts entries deterministically). For permissions, modern Gradle (8.3+) uses `filePermissions { unix("0755") }` and `dirPermissions { unix("0755") }` on the CopySpec, replacing the deprecated integer `fileMode`/`dirMode`. Apply executable bits to your `bin/` scripts and read bits to data files. You typically configure the reproducibility flags across all archive tasks via `tasks.withType<AbstractArchiveTask>().configureEach { ... }`. Combined, you get identical archives for identical inputs — important for caching, supply-chain verification, and signature stability — while still shipping runnable scripts with the right mode.
code
kotlin · 16 linestasks.withType<AbstractArchiveTask>().configureEach {
isPreserveFileTimestamps = false
isReproducibleFileOrder = true
}
distributions {
create("tools") {
contents {
from("scripts") {
into("bin")
filePermissions { unix("0755") }
}
dirPermissions { unix("0755") }
}
}
}go deeper
Likely unaware of reproducibility flags; may know files have permissions but not how to set them.
Knows the two reproducibility flags exist and can set filePermissions for a bin folder.
Configures reproducibility across all archive tasks, sets correct permissions per group, and knows zip-vs-tar permission caveats.
Mandates reproducible archives org-wide for supply-chain verification and cache stability, with a convention plugin enforcing the flags and permissions.
## Making distribution archives deterministic Two defaults make zip/tar archives non-reproducible: 1. **Embedded timestamps** — each entry stores its last-modified time. 2. **Filesystem-dependent ordering** — entry order can follow directory iteration order. Change both inputs identical → archive bytes identical. ### The two flags `AbstractArchiveTask` (the parent of `Zip`, `Tar`, `Jar`, and the distribution's `*DistZip`/`*DistTar`) exposes: ```kotlin tasks.withType<AbstractArchiveTask>().configureEach { isPreserveFileTimestamps = false // zero out entry timestamps isReproducibleFileOrder = true // sort entries deterministically } ``` With both set, the archive is byte-for-byte stable across machines and runs (given identical content). This is the canonical Gradle recipe for reproducible builds. ### Permissions (Gradle 8.3+) The old integer API (`fileMode = 0x755` / `dirMode`) is deprecated. Use the `ConfigurableFilePermissions` DSL on the CopySpec: ```kotlin distributions { create("tools") { contents { from("scripts") { into("bin") filePermissions { unix("0755") } // rwxr-xr-x for executables } from("data") { into("data") filePermissions { unix("0644") } // rw-r--r-- for data } dirPermissions { unix("0755") } } } } ``` You can also use the builder form: `filePermissions { user { read = true; execute = true }; group { read = true }; other { read = true } }`. Permissions are stored in zip/tar entries and restored on extraction (on Unix-like systems). ### Why it matters - **Caching / signatures**: a deterministic archive hashes consistently, so build caches, checksums, and detached signatures stay valid. - **Supply chain**: reproducibility lets independent rebuilds verify the published artifact. - **Runnable scripts**: without explicit `filePermissions`, a `bin/` launcher may unpack without the execute bit, breaking the distribution. ### Gotchas - Permissions in *zip* are an extension field; some tools ignore them. *tar* preserves Unix modes more universally — for executables, also ship `distTar`. - On Windows the producing machine has no Unix modes, so always set `filePermissions` explicitly rather than relying on the source file's bits.
- Which two task properties remove the main sources of archive nondeterminism, and what do they do?`isPreserveFileTimestamps = false` zeroes per-entry timestamps so the same content always hashes the same regardless of when it was built; `isReproducibleFileOrder = true` sorts archive entries deterministically instead of following filesystem iteration order. Both live on `AbstractArchiveTask`.
- Why might you ship `distTar` rather than only `distZip` for distributions containing executable scripts?Tar preserves Unix file modes (the execute bit) reliably across extraction tools, whereas zip stores permissions in an extension field that some extractors ignore — so a script could unpack without its execute bit. Tar is safer for runnable content on Unix-like systems.
- What replaced the deprecated `fileMode`/`dirMode` integer properties?The `filePermissions { ... }` and `dirPermissions { ... }` DSL on the CopySpec (Gradle 8.3+), configured via `unix("0755")` or the user/group/other builder, exposing a `ConfigurableFilePermissions`.
saying these in an interview costs you the question
- Relying on the source file's execute bit (absent on Windows) instead of declaring `filePermissions`.
- Forgetting `isReproducibleFileOrder`/`isPreserveFileTimestamps`, then wondering why checksums differ between machines.
- Using the deprecated `fileMode` integer in modern Gradle.