skip to content

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?

level: seniorimportance: should knowfreq 22%

answer

  1. isPreserveFileTimestamps = false
  2. isReproducibleFileOrder = true
  3. AbstractArchiveTask.configureEach
  4. filePermissions { unix("0755") } (8.3+)
  5. fileMode/dirMode deprecated

basics

~10 s

Set 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 s

Reproducible 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 lines
kotlin
tasks.withType<AbstractArchiveTask>().configureEach {
    isPreserveFileTimestamps = false
    isReproducibleFileOrder = true
}

distributions {
    create("tools") {
        contents {
            from("scripts") {
                into("bin")
                filePermissions { unix("0755") }
            }
            dirPermissions { unix("0755") }
        }
    }
}

go deeper

for a junior

Likely unaware of reproducibility flags; may know files have permissions but not how to set them.

for a middle

Knows the two reproducibility flags exist and can set filePermissions for a bin folder.

for a senior

Configures reproducibility across all archive tasks, sets correct permissions per group, and knows zip-vs-tar permission caveats.

for a principal

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.

context