Compare release artifacts with jardiff before publishing to Maven Central#11856
Compare release artifacts with jardiff before publishing to Maven Central#11856bric3 wants to merge 1 commit into
Conversation
|
🎯 Code Coverage (details) 🔗 Commit SHA: 23288b0 | Docs | Datadog PR Page | Give us feedback! |
🟢 Java Benchmark SLOs — All performance SLOs passed
PR vs. master results
Commit: Load and DaCapo benchmarks can be triggered manually in the GitLab pipeline. Results will appear in the Benchmarking Platform UI after completion. |
c8a141d to
2b41fdf
Compare
`deploy_to_maven_central` rebuilds the artifacts (reusing the build cache) before publishing, so a fault in the build script could make the rebuild to produce a faulty jar that differs from the one the build job. Which was validated across the whole test chain. This add a `compareToReferenceJar` task (via a new `dd-trace-java.jardiff` plugin) that runs jardiff `--stat --exit-code` against a reference jar (the one from `build` job) and fails on any difference. This allows to gate maven publication (and github releases).
2b41fdf to
23288b0
Compare
Is the motivation here that if the build was faulty, then it would result in a cache miss during the deploy to maven step, and that's why we're comparing the two artifacts? .. Or would the maven deployment step use the faulty build cache anyways? Because that would result in the same build, no? |
Not necessarily a cache miss, but anything that alters the output in an unintended way, that could include how gradle uses it's build cache and if the build infrastructure we have has "bugs" that surface in these conditions.
It's supposed to result in the same, however, if Gradle detects out-of-date items, it can re-execute parts of the build to account for that, this is the whole idea behind that feature, only rebuild what changed. It's unlikely to happen, but if our customization to the build script have an error this can lead to unintended bad interaction. This is what happened during 1.60.0 release where parts of the classes disappeared. |
What Does This Do
Adds a jar comparison between the artifacts produced by the
buildjob and those produced by thedeploy_to_maven_centraljob before the jar gets published to maven central, i.e. beforepublishMavenPublicationToSonatypeRepositoryexecutes.If a difference is found the
deploy_to_maven_centralfails. It will also prevent to publish the jar to github releases.This PR introduces a
dd-trace-java.jardiffplugin that uses under the hood the jardiff. The tool is run with--stat --exit-codeto compare a freshly built jar against a reference jar frombuild.Failing the build if they differ.
Trimmed task graph (
--task-graph) when invokingpublishToSonatype:Motivation
The
buildjob produces deliverable jar artifacts, those are used during the whole pipeline for all tests, system-tests, benchmark included. It is also used for OCI images. The build job also pushes to thelibgitlab cache, the Gradle build cache.However, the
deploy_to_maven_centraldepends onbuildjob and reuse the build cache rehydrated from thelibgitlab cache.That allows to reuse what was computed in the
buildjob, including the produced artifact, and usually that works fine.However, if for any reason the build script has a bug, it might result in a different artifact being built, possibly a faulty artifact.
That artifact is the one that gets published to customer of maven central or github release.
Note
The above description iswhat happened in release 1.60.0: a faulty jar reached Maven Central and GitHub.
In order to prevent such a faulty artifact to be published, this PR introduces an intermediate tasks whose role is to compare the artifacts produced from the
buildjob with the one from the currentdeploy_to_maven_centralGradle invocation.Additional Notes
feature-flagging-apidd-openfeaturein particular disable the task at this point because build do not build these jarsdeploy_snapshot_with_ddprof_snapshotexcludes it (-x compareToReferenceJar) since its ddprof-snapshot jar differs by designpublishToMavenLocalstays ungated (it is aPublishToMavenLocal, not aPublishToMavenRepository), so local installs are unaffected.Simple tests to compare to another jar
Run it directly, no credentials or network:
Build a "reference" jar (stand-in for the build job's validated artifact)
Compare the freshly built jar against it, if identical this succeeds
The current wiring do not handle
publishToMavenLocal. E.g../gradlew :dd-trace-api:publishToMavenLocalinstalls to~/.m2without triggering it.Simulating a divergent rebuild
Reproducing a second build that produces a different jar, with a real subproject change flowing into a published shadow jar.
dd-trace-otbundles the:dd-trace-ot:correlation-id-injectionsubproject (relocated underddtrot/):Reference: the shadow jar as it stands today.
Patch a class in the bundled subproject, e.g. add a method to
dd-trace-ot/correlation-id-injection/src/main/java/datadog/trace/correlation/Slf4jCorrelationIdInjector.javaAny change to a bundled class works: add/remove/rename a method or class)
Rebuild and compare, the changed class is recompiled and re-bundled, as compareToReferenceJar has the jar task as dependency.
The comparison should fail and should pinpoint what's changed.
Other alternatives
During the previous attempt in #11733, an idea emerged to use a non Gradle tool to make the publication, like jreleaser. However, this require a deeper change (build has to make all the necessary artifacts, they need to be in a proper shape to be handed over to jreleaser, configuring jreleaser, make team aware of jreleaser failure modes).