Skip to content

Commit 00ca832

Browse files
authored
Built: improve reproducible archive files (#2432)
As part of the effort for #2204, this change fixes a few aspects around reproducible builds: Some Gradle projects produce archive files, but don't get the necessary Gradle archive-tasks settings applied: one not-published project but also the tarball&zip of the distribution. This change moves the logic to the new build-plugin `polaris-reproducible`. Another change is to have some Quarkus generated jar files adhere to the same conventions, which are constant timestamps for the zip entries and a deterministic order of the entries. That's sadly not a full fix, as the classes that are generated or instumented by Quarkus differ in each build. Contributes to #2204
1 parent b6e247d commit 00ca832

File tree

5 files changed

+87
-5
lines changed

5 files changed

+87
-5
lines changed

build-logic/src/main/kotlin/Utilities.kt

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414
* limitations under the License.
1515
*/
1616

17+
import java.io.File
18+
import java.io.FileOutputStream
19+
import java.nio.file.attribute.FileTime
20+
import java.util.zip.ZipFile
21+
import java.util.zip.ZipOutputStream
1722
import org.gradle.api.Project
1823
import org.gradle.process.JavaForkOptions
1924

@@ -61,3 +66,41 @@ fun JavaForkOptions.addSparkJvmOptions() {
6166
"-Djdk.reflect.useDirectMethodHandle=false",
6267
)
6368
}
69+
70+
/**
71+
* Rewrites the given ZIP file.
72+
*
73+
* The timestamps of all entries are set to `1980-02-01 00:00`, zip entries appear in a
74+
* deterministic order.
75+
*/
76+
fun makeZipReproducible(source: File) {
77+
val t = FileTime.fromMillis(318211200_000) // 1980-02-01 00:00 GMT
78+
79+
val outFile = File(source.absolutePath + ".tmp.out")
80+
81+
val names = mutableListOf<String>()
82+
ZipFile(source).use { zip -> zip.stream().forEach { e -> names.add(e.name) } }
83+
names.sort()
84+
85+
ZipOutputStream(FileOutputStream(outFile)).use { dst ->
86+
ZipFile(source).use { zip ->
87+
names.forEach { n ->
88+
val e = zip.getEntry(n)
89+
zip.getInputStream(e).use { src ->
90+
e.setCreationTime(t)
91+
e.setLastAccessTime(t)
92+
e.setLastModifiedTime(t)
93+
dst.putNextEntry(e)
94+
src.copyTo(dst)
95+
dst.closeEntry()
96+
src.close()
97+
}
98+
}
99+
}
100+
}
101+
102+
val origFile = File(source.absolutePath + ".tmp.orig")
103+
source.renameTo(origFile)
104+
outFile.renameTo(source)
105+
origFile.delete()
106+
}

build-logic/src/main/kotlin/polaris-java.gradle.kts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ plugins {
3434
`jvm-test-suite`
3535
checkstyle
3636
id("polaris-spotless")
37+
id("polaris-reproducible")
3738
id("jacoco-report-aggregation")
3839
id("net.ltgt.errorprone")
3940
}
@@ -253,9 +254,20 @@ configurations.all {
253254
}
254255
}
255256

256-
// ensure jars conform to reproducible builds
257-
// (https://docs.gradle.org/current/userguide/working_with_files.html#sec:reproducible_archives)
258-
tasks.withType<AbstractArchiveTask>().configureEach {
259-
isPreserveFileTimestamps = false
260-
isReproducibleFileOrder = true
257+
if (plugins.hasPlugin("io.quarkus")) {
258+
tasks.named("quarkusBuild") {
259+
actions.addLast {
260+
listOf(
261+
"quarkus-app/quarkus-run.jar",
262+
"quarkus-app/quarkus/generated-bytecode.jar",
263+
"quarkus-app/quarkus/transformed-bytecode.jar",
264+
)
265+
.forEach { name ->
266+
val file = project.layout.buildDirectory.get().file(name).asFile
267+
if (file.exists()) {
268+
makeZipReproducible(file)
269+
}
270+
}
271+
}
272+
}
261273
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
// ensure jars conform to reproducible builds
21+
// (https://docs.gradle.org/current/userguide/working_with_files.html#sec:reproducible_archives)
22+
tasks.withType<AbstractArchiveTask>().configureEach {
23+
isPreserveFileTimestamps = false
24+
isReproducibleFileOrder = true
25+
}

runtime/distribution/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import publishing.PublishingHelperPlugin
2323
plugins {
2424
id("distribution")
2525
id("signing")
26+
id("polaris-reproducible")
2627
}
2728

2829
description = "Apache Polaris Binary Distribution"

tools/config-docs/site/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
plugins {
2121
`java-library`
22+
id("polaris-reproducible")
2223
}
2324

2425
description = "Polaris site - reference docs"

0 commit comments

Comments
 (0)