Skip to content

First steps towards build reproducibility #48869

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

gsmet
Copy link
Member

@gsmet gsmet commented Jul 10, 2025

About this work:

  • I have been revisiting the work @ppalaga started a long time ago toward reproducibility. @ppalaga's focus was towards reproducibility of the build of a Quarkus application. I'm working on both this and the reproducibility of our own builds.
  • This PR contains some work on both:

There's a companion PR here: quarkusio/quarkus-platform-bom-generator#375

Our builds is a lot better after these PRs: I was able to get a reproducible build for most of our own artifacts except for quarkus-cli-runner which is a Quarkus app and not yet reproducible and all the modules after it (mostly the ITs, which are also Quarkus apps).

Note that this is just a start, there are a lot of tricky issues to solve to get the build of a Quarkus app reproducible. I'll continue to iterate on this task, I already started extracting and rebasing some of @ppalaga 's old PRs - they definitely go in the right direction.

@gsmet gsmet requested a review from aloubyansky July 10, 2025 13:42
@quarkus-bot quarkus-bot bot added area/core area/devtools Issues/PR related to maven, gradle, platform and cli tooling/plugins area/gradle Gradle area/maven area/platform Issues related to definition and interaction with Quarkus Platform labels Jul 10, 2025
@gsmet
Copy link
Member Author

gsmet commented Jul 10, 2025

Better reviewed commit per commit as they are semantic and isolated.

This comment has been minimized.

Copy link

github-actions bot commented Jul 10, 2025

🎊 PR Preview 8fab080 has been successfully built and deployed to https://quarkus-pr-main-48869-preview.surge.sh/version/main/guides/

  • Images of blog posts older than 3 months are not available.
  • Newsletters older than 3 months are not available.

This comment has been minimized.

@gsmet gsmet force-pushed the reproducible-zips branch from 5b9076a to ab142a8 Compare July 11, 2025 09:24
Copy link

quarkus-bot bot commented Jul 11, 2025

Status for workflow Quarkus Documentation CI

This is the status report for running Quarkus Documentation CI on commit ab142a8.

✅ The latest workflow run for the pull request has completed successfully.

It should be safe to merge provided you have a look at the other checks in the summary.

Warning

There are other workflow runs running, you probably need to wait for their status before merging.

* <p>
* The approach is similar to what is done by the maven-jar-plugin with `project.build.outputTimestamp`.
*/
Optional<Instant> outputTimestamp();
Copy link
Contributor

Choose a reason for hiding this comment

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

What happens if the user does not set the value? Is project.build.outputTimestamp propagated? I wonder if the JavaDoc should contain this kind of info.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, yes, I can add a comment. You actually don't need to set it in the Maven case, I propagate project.build.outputTimestamp.

As for Gradle, I'll see when I get a Maven build fully reproducible.

Copy link

quarkus-bot bot commented Jul 11, 2025

Status for workflow Quarkus CI

This is the status report for running Quarkus CI on commit ab142a8.

✅ The latest workflow run for the pull request has completed successfully.

It should be safe to merge provided you have a look at the other checks in the summary.

You can consult the Develocity build scans.


Flaky tests - Develocity

⚙️ JVM Tests - JDK 17

📦 extensions/micrometer-opentelemetry/deployment

io.quarkus.micrometer.opentelemetry.deployment.compatibility.MicrometerTimedInterceptorTest.testTimeMethod_AsyncFailed - History

  • Stream has no elements - java.lang.IllegalArgumentException
java.lang.IllegalArgumentException: Stream has no elements
	at io.quarkus.micrometer.opentelemetry.deployment.common.MetricDataFilter.lambda$lastReading$2(MetricDataFilter.java:213)
	at java.base/java.util.Optional.orElseThrow(Optional.java:403)
	at io.quarkus.micrometer.opentelemetry.deployment.common.MetricDataFilter.lastReading(MetricDataFilter.java:213)
	at io.quarkus.micrometer.opentelemetry.deployment.common.MetricDataFilter.lastReadingDataPoint(MetricDataFilter.java:231)
	at io.quarkus.micrometer.opentelemetry.deployment.compatibility.MicrometerTimedInterceptorTest.testTimeMethod_AsyncFailed(MicrometerTimedInterceptorTest.java:150)
	at java.base/java.lang.reflect.Method.invoke(Method.java:569)
	at io.quarkus.test.QuarkusUnitTest.runExtensionMethod(QuarkusUnitTest.java:534)

📦 extensions/smallrye-reactive-messaging/deployment

io.quarkus.smallrye.reactivemessaging.hotreload.ConnectorChangeTest.testUpdatingConnector - History

  • Expecting actual: ["-6","-7","-8","-10","-11","-12","-13","-14"] to start with: ["-6", "-7", "-8", "-9"] - java.lang.AssertionError
java.lang.AssertionError: 

Expecting actual:
  ["-6","-7","-8","-10","-11","-12","-13","-14"]
to start with:
  ["-6", "-7", "-8", "-9"]

	at io.quarkus.smallrye.reactivemessaging.hotreload.ConnectorChangeTest.testUpdatingConnector(ConnectorChangeTest.java:41)

⚙️ JVM Tests - JDK 17 Windows

📦 extensions/micrometer-opentelemetry/deployment

io.quarkus.micrometer.opentelemetry.deployment.compatibility.MicrometerTimedInterceptorTest.testTimeMethod - History

  • Stream has no elements - java.lang.IllegalArgumentException
java.lang.IllegalArgumentException: Stream has no elements
	at io.quarkus.micrometer.opentelemetry.deployment.common.MetricDataFilter.lambda$lastReading$2(MetricDataFilter.java:213)
	at java.base/java.util.Optional.orElseThrow(Optional.java:403)
	at io.quarkus.micrometer.opentelemetry.deployment.common.MetricDataFilter.lastReading(MetricDataFilter.java:213)
	at io.quarkus.micrometer.opentelemetry.deployment.common.MetricDataFilter.lastReadingDataPoint(MetricDataFilter.java:231)
	at io.quarkus.micrometer.opentelemetry.deployment.compatibility.MicrometerTimedInterceptorTest.testTimeMethod(MicrometerTimedInterceptorTest.java:77)
	at java.base/java.lang.reflect.Method.invoke(Method.java:569)
	at io.quarkus.test.QuarkusUnitTest.runExtensionMethod(QuarkusUnitTest.java:534)

⚙️ JVM Integration Tests - JDK 17

📦 integration-tests/kafka-devservices

io.quarkus.it.kafka.continuoustesting.DevServicesDevModeTest.testDevModeServiceDoesNotRestartContainersOnCodeChange - History

  • New containers: [Container(command=sh -c 'while [ ! -f /work/run.sh ]; do sleep 0.1; done; sleep 0.1; /work/run.sh', created=1752227437, id=3632b6c290f3b4393131fdd447e16b7e2b52cdf3b38659ded3569768a448805f, image=quay.io/ogunalp/kafka-native:latest, imageId=sha256:99fa71388449ac7ff46bda7f6d645fb4435d9a21dce3d4dbcaedfc8a738afd6b, names=[/vigorous_wiles], ports=[ContainerPort(ip=0.0.0.0, privatePort=9092, publicPort=32857, type=tcp), ContainerPort(ip=::, privatePort=9092, publicPort=32857, type=tcp)], labels={architecture=x86_64, build-date=2025-03-25T21:45:00Z, com.redhat.component=ubi9-micro-container, com.redhat.license_terms=https://www.redhat.com/en/about/red-hat-end-user-license-agreements\#UBI, description=Very small image which doesn't install the package manager., distribution-scope=public, io.buildah.version=1.39.0-dev, io.k8s.description=Very small image which doesn't install the package manager., io.k8s.display-name=Red Hat Universal Base Image 9 Micro, io.openshift.expose-s... - org.opentest4j.AssertionFailedError
org.opentest4j.AssertionFailedError: 
New containers: [Container(command=sh -c 'while [ ! -f /work/run.sh ]; do sleep 0.1; done; sleep 0.1; /work/run.sh', created=1752227437, id=3632b6c290f3b4393131fdd447e16b7e2b52cdf3b38659ded3569768a448805f, image=quay.io/ogunalp/kafka-native:latest, imageId=sha256:99fa71388449ac7ff46bda7f6d645fb4435d9a21dce3d4dbcaedfc8a738afd6b, names=[/vigorous_wiles], ports=[ContainerPort(ip=0.0.0.0, privatePort=9092, publicPort=32857, type=tcp), ContainerPort(ip=::, privatePort=9092, publicPort=32857, type=tcp)], labels={architecture=x86_64, build-date=2025-03-25T21:45:00Z, com.redhat.component=ubi9-micro-container, com.redhat.license_terms=https://www.redhat.com/en/about/red-hat-end-user-license-agreements#UBI, description=Very small image which doesn't install the package manager., distribution-scope=public, io.buildah.version=1.39.0-dev, io.k8s.description=Very small image which doesn't install the package manager., io.k8s.display-name=Red Hat Universal Bas...

⚙️ JVM Integration Tests - JDK 21

📦 integration-tests/kafka-devservices

io.quarkus.it.kafka.continuoustesting.DevServicesDevModeTest.testDevModeServiceDoesNotRestartContainersOnCodeChange - History

  • New containers: [Container(command=sh -c 'while [ ! -f /work/run.sh ]; do sleep 0.1; done; sleep 0.1; /work/run.sh', created=1752227466, id=40c0ea08c04f6d8a58aba279fe43275728f10efc1c1e422442e50412e742228c, image=quay.io/ogunalp/kafka-native:latest, imageId=sha256:99fa71388449ac7ff46bda7f6d645fb4435d9a21dce3d4dbcaedfc8a738afd6b, names=[/sleepy_shamir], ports=[ContainerPort(ip=0.0.0.0, privatePort=9092, publicPort=32857, type=tcp), ContainerPort(ip=::, privatePort=9092, publicPort=32857, type=tcp)], labels={architecture=x86_64, build-date=2025-03-25T21:45:00Z, com.redhat.component=ubi9-micro-container, com.redhat.license_terms=https://www.redhat.com/en/about/red-hat-end-user-license-agreements\#UBI, description=Very small image which doesn't install the package manager., distribution-scope=public, io.buildah.version=1.39.0-dev, io.k8s.description=Very small image which doesn't install the package manager., io.k8s.display-name=Red Hat Universal Base Image 9 Micro, io.openshift.expose-se... - org.opentest4j.AssertionFailedError
org.opentest4j.AssertionFailedError: 
New containers: [Container(command=sh -c 'while [ ! -f /work/run.sh ]; do sleep 0.1; done; sleep 0.1; /work/run.sh', created=1752227466, id=40c0ea08c04f6d8a58aba279fe43275728f10efc1c1e422442e50412e742228c, image=quay.io/ogunalp/kafka-native:latest, imageId=sha256:99fa71388449ac7ff46bda7f6d645fb4435d9a21dce3d4dbcaedfc8a738afd6b, names=[/sleepy_shamir], ports=[ContainerPort(ip=0.0.0.0, privatePort=9092, publicPort=32857, type=tcp), ContainerPort(ip=::, privatePort=9092, publicPort=32857, type=tcp)], labels={architecture=x86_64, build-date=2025-03-25T21:45:00Z, com.redhat.component=ubi9-micro-container, com.redhat.license_terms=https://www.redhat.com/en/about/red-hat-end-user-license-agreements#UBI, description=Very small image which doesn't install the package manager., distribution-scope=public, io.buildah.version=1.39.0-dev, io.k8s.description=Very small image which doesn't install the package manager., io.k8s.display-name=Red Hat Universal Base...

📦 integration-tests/opentelemetry-grpc-only

io.quarkus.it.opentelemetry.grpc.HelloGrpcClientTest.testHello - History

  • java.lang.RuntimeException: Failed to start quarkus - java.lang.RuntimeException
java.lang.RuntimeException: java.lang.RuntimeException: Failed to start quarkus
	at io.quarkus.test.junit.QuarkusTestExtension.throwBootFailureException(QuarkusTestExtension.java:668)
	at io.quarkus.test.junit.QuarkusTestExtension.interceptTestClassConstructor(QuarkusTestExtension.java:763)
	at java.base/java.util.Optional.orElseGet(Optional.java:364)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
Caused by: java.lang.RuntimeException: Failed to start quarkus
	at io.quarkus.runner.ApplicationImpl.doStart(Unknown Source)

final Iterator<Path> i = files.iterator();
final Iterator<Path> i = files
// we sort the elements to be deterministic, we compare the toString() to make sure the order is the same on Linux and Windows
.sorted((p1, p2) -> p1.toString().compareTo(p2.toString()))
Copy link
Member

Choose a reason for hiding this comment

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

Does this appear to be generally necessary or only in specific cases?

Copy link
Member Author

Choose a reason for hiding this comment

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

So it's probably only necessary in specific cases... but figuring out which cases is hard and when you had new use cases, people will have to think about it (and will forget).

I think in general bringing a more deterministic behavior is nice.

As for the specific comparator, it's the only way to get the same ordering on Linux and Windows.

Now, maybe we shouldn't merge this PR for now and wait for the whole thing and then try to evaluate the cost of it?

@gsmet gsmet marked this pull request as draft July 16, 2025 14:28
* <p>
* The approach is similar to what is done by the maven-jar-plugin with `project.build.outputTimestamp`.
*/
Optional<Instant> outputTimestamp();
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this similar to what I suggested before in this enhancement request (#41070)? quarkus.build.timestamp

Copy link
Member Author

Choose a reason for hiding this comment

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

If we decide to go this way, it could be used to do what you were asking for.

For now, the main reason for it is to make sure the Quarkus jars are generated reproducibly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/core area/devtools Issues/PR related to maven, gradle, platform and cli tooling/plugins area/gradle Gradle area/maven area/platform Issues related to definition and interaction with Quarkus Platform triage/flaky-test
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants