From 2a4326b572b03e189ed93e4a6b109eb32af4429a Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Sun, 7 Jan 2024 14:01:57 +0100 Subject: [PATCH] Allow overriding JAR manifest entries for reproducibility With these changes, the `Build-Date`, `Build-Time`, and `Created-By` manifest entries can be taken from the released JARs and passed to the build in order to check for reproducibility: ./gradlew assemble \ -Pmanifest.buildTimestamp='2024-01-07 14:00:54.292+0100' \ -Pmanifest.createdBy='21.0.1 (BellSoft 21.0.1+12-LTS)' Closes #3559. --- gradle.properties | 2 -- .../plugins/build-parameters/build.gradle.kts | 11 ++++++ .../junitbuild.build-metadata.gradle.kts | 34 ++++++++++--------- ...tbuild.java-library-conventions.gradle.kts | 7 ++-- gradle/scripts/checkBuildReproducibility.sh | 12 +++++-- 5 files changed, 42 insertions(+), 24 deletions(-) diff --git a/gradle.properties b/gradle.properties index 65cc27143d99..70a1323f9a35 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,8 +9,6 @@ platformVersion = 1.11.0-SNAPSHOT vintageGroup = org.junit.vintage vintageVersion = 5.11.0-SNAPSHOT -defaultBuiltBy = JUnit Team - # We need more metaspace due to apparent memory leak in Asciidoctor/JRuby # The exports are needed due to https://github.com/diffplug/spotless/issues/834 org.gradle.jvmargs=-Xmx1g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError \ diff --git a/gradle/plugins/build-parameters/build.gradle.kts b/gradle/plugins/build-parameters/build.gradle.kts index 25e16d2e5a61..29460dad37fd 100644 --- a/gradle/plugins/build-parameters/build.gradle.kts +++ b/gradle/plugins/build-parameters/build.gradle.kts @@ -68,4 +68,15 @@ buildParameters { description = "Sign artifacts before publishing them to Maven repos" } } + group("manifest") { + string("buildTimestamp") { + description = "Overrides the value of the 'Build-Date' and 'Build-Time' jar manifest entries (e.g. '2023-11-05 17:49:13.996+0100')." + } + string("builtBy") { + description = "Overrides the value of the 'Built-By' jar manifest entry" + } + string("createdBy") { + description = "Overrides the value of the 'Created-By' jar manifest entry" + } + } } diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild.build-metadata.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.build-metadata.gradle.kts index ad58b4898de5..63c414f4be4a 100644 --- a/gradle/plugins/common/src/main/kotlin/junitbuild.build-metadata.gradle.kts +++ b/gradle/plugins/common/src/main/kotlin/junitbuild.build-metadata.gradle.kts @@ -1,28 +1,30 @@ -import java.time.Instant import java.time.OffsetDateTime -import java.time.ZoneOffset import java.time.format.DateTimeFormatter +import java.time.format.DateTimeFormatterBuilder -val buildTimeAndDate = - if (System.getenv().containsKey("SOURCE_DATE_EPOCH")) { - - // SOURCE_DATE_EPOCH is a UNIX timestamp for pinning build metadata against - // in order to achieve reproducible builds - // - // More details - https://reproducible-builds.org/docs/source-date-epoch/ - val sourceDateEpoch = System.getenv("SOURCE_DATE_EPOCH").toLong() +plugins { + id("junitbuild.build-parameters") +} - Instant.ofEpochSecond(sourceDateEpoch).atOffset(ZoneOffset.UTC) +val dateFormatter = DateTimeFormatter.ISO_LOCAL_DATE +val timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss.SSSZ") - } else { - OffsetDateTime.now() +val buildTimeAndDate = buildParameters.manifest.buildTimestamp + .map { + DateTimeFormatterBuilder() + .append(dateFormatter) + .appendLiteral(' ') + .append(timeFormatter) + .toFormatter() + .parse(it) } + .orNull + ?: OffsetDateTime.now() -val buildDate: String by extra { DateTimeFormatter.ISO_LOCAL_DATE.format(buildTimeAndDate) } -val buildTime: String by extra { DateTimeFormatter.ofPattern("HH:mm:ss.SSSZ").format(buildTimeAndDate) } +val buildDate: String by extra { dateFormatter.format(buildTimeAndDate) } +val buildTime: String by extra { timeFormatter.format(buildTimeAndDate) } val buildRevision: String by extra { providers.exec { commandLine("git", "rev-parse", "--verify", "HEAD") }.standardOutput.asText.get() } -val builtByValue by extra { project.findProperty("builtBy") ?: project.property("defaultBuiltBy") } diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild.java-library-conventions.gradle.kts b/gradle/plugins/common/src/main/kotlin/junitbuild.java-library-conventions.gradle.kts index 5a18f0cc4b3d..b4b0245255d7 100644 --- a/gradle/plugins/common/src/main/kotlin/junitbuild.java-library-conventions.gradle.kts +++ b/gradle/plugins/common/src/main/kotlin/junitbuild.java-library-conventions.gradle.kts @@ -11,6 +11,7 @@ plugins { idea checkstyle id("junitbuild.base-conventions") + id("junitbuild.build-parameters") id("junitbuild.jacoco-java-conventions") } @@ -19,7 +20,6 @@ val modularProjects: List by rootProject.extra val buildDate: String by rootProject.extra val buildTime: String by rootProject.extra val buildRevision: Any by rootProject.extra -val builtByValue: String by rootProject.extra val extension = extensions.create("javaLibrary") @@ -211,8 +211,9 @@ tasks.withType().configureEach { tasks.jar { manifest { attributes( - "Created-By" to "${System.getProperty("java.version")} (${System.getProperty("java.vendor")} ${System.getProperty("java.vm.version")})", - "Built-By" to builtByValue, + "Created-By" to (buildParameters.manifest.createdBy.orNull + ?: "${System.getProperty("java.version")} (${System.getProperty("java.vendor")} ${System.getProperty("java.vm.version")})"), + "Built-By" to buildParameters.manifest.builtBy.orElse("JUnit Team"), "Build-Date" to buildDate, "Build-Time" to buildTime, "Build-Revision" to buildRevision, diff --git a/gradle/scripts/checkBuildReproducibility.sh b/gradle/scripts/checkBuildReproducibility.sh index b07275624da1..36830355d0eb 100755 --- a/gradle/scripts/checkBuildReproducibility.sh +++ b/gradle/scripts/checkBuildReproducibility.sh @@ -2,18 +2,24 @@ rm -rf checksums* -export SOURCE_DATE_EPOCH=$(date +%s) +BUILD_TIMESTAMP=$(date -Iseconds) function calculate_checksums() { OUTPUT=$1 - ./gradlew --no-build-cache clean assemble --parallel -Porg.gradle.java.installations.auto-download=false -Dscan.tag.Reproducibility + ./gradlew \ + --no-build-cache \ + -Porg.gradle.java.installations.auto-download=false \ + -Dscan.tag.Reproducibility \ + -Pmanifest.buildTimestamp="${BUILD_TIMESTAMP}" \ + clean \ + assemble find . -name '*.jar' \ | grep '/build/libs/' \ | grep --invert-match 'javadoc' \ | sort \ - | xargs sha256sum > ${OUTPUT} + | xargs sha256sum > "${OUTPUT}" }