Skip to content

Commit

Permalink
Allow overriding JAR manifest entries for reproducibility
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
marcphilipp committed Jan 7, 2024
1 parent f53e690 commit 2a4326b
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 24 deletions.
2 changes: 0 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
11 changes: 11 additions & 0 deletions gradle/plugins/build-parameters/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
}
Original file line number Diff line number Diff line change
@@ -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") }
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ plugins {
idea
checkstyle
id("junitbuild.base-conventions")
id("junitbuild.build-parameters")
id("junitbuild.jacoco-java-conventions")
}

Expand All @@ -19,7 +20,6 @@ val modularProjects: List<Project> 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<JavaLibraryExtension>("javaLibrary")

Expand Down Expand Up @@ -211,8 +211,9 @@ tasks.withType<Jar>().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,
Expand Down
12 changes: 9 additions & 3 deletions gradle/scripts/checkBuildReproducibility.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
}


Expand Down

1 comment on commit 2a4326b

@sormuras
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Please sign in to comment.