Replace the Ant build with Gradle, output verified byte-identical#326
Replace the Ant build with Gradle, output verified byte-identical#326pacmano1 wants to merge 4 commits into
Conversation
3e2b399 to
fb98f2d
Compare
|
Looking good! Tests pass, CI passes, released binaries are byte-identical with the main ant build. |
fb98f2d to
3944b31
Compare
jonbartels
left a comment
There was a problem hiding this comment.
Can the file moves be refactored to a separate PR. It is good to move the project to the standard gradle layout. However it would make the first commit smaller. https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceSet.html and main.java.srcDirs
So that would be one PR with a non-standard layout. then a second PR would move to the standard layout. It could also be one PR with multiple commits showing the addition of gradle, removal of JARs, then move of project files.
I think this PR has the same too-large problem one of the earlier Nico PRs had
mgaffigan
left a comment
There was a problem hiding this comment.
Generally looks really good. (Obviously runs fine, since it is byte identical)
Changes requested:
- remove documentation of the change that will quickly grow stale (consider moving to github wiki or another destination)
- remove verifier to separate repo to avoid having to review
- remove/clarify purpose of apparently unused steps
- remove hardcoded "empty" class file generation. Check in the .class file or remove and consider this an intended difference.
Other incidentals:
- Consider having the dependency resolution per-project file (unless it makes this easier to review, we can do that in a separate PR)
- Consider omitting the -D legacy support. I don't think this will work well long term, and it is easier to make a breaking change now.
| - name: Set up Gradle | ||
| uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3 | ||
| with: | ||
| validate-wrappers: true |
There was a problem hiding this comment.
What's the benefit of using this over the built-in wrapper? Wrapper validation?
| server/setup.tar.gz | ||
| server/certchain.pem | ||
| dependency-check-report.html | ||
| dependency-check-report.json | ||
| tools/install4j/oie-installer-config.install4j~ | ||
| yubikey-pkcs11.cfg | ||
| certchain.pem |
There was a problem hiding this comment.
These seem like they are unrelated. I don't think it's a problem, but they don't seem strictly related to the gradle conversion.
There was a problem hiding this comment.
Seems like this should be part of the PR itself - not in-repo. It's going to be stale quickly. Remove.
| def flag = { String name -> | ||
| (System.getProperty(name) ?: providers.gradleProperty(name).getOrNull()) == 'true' | ||
| } |
There was a problem hiding this comment.
Should we maintain this? Or take this as an opportunity for a breaking change. -D to -P is not so big a lift for callers - easier to do so now than later.
| // clear message if one is missing). | ||
| ext.vendoredLayout = file('gradle/vendored-layout.json').exists() | ||
| ? new groovy.json.JsonSlurper().parse(file('gradle/vendored-layout.json')) | ||
| : [:] |
There was a problem hiding this comment.
Can we avoid this being a central json that is going to have merge conflicts? Centralizing makes this unclear. Each project (client/server) should specify the deps it requires. Depending on details, might be better as a follow-up PR.
| // Gradle-9-safe cross-project artifact sharing: each project resolves | ||
| // its OWN runtime configuration and writes an inventory file; consumer | ||
| // projects read the file instead of resolving foreign configurations. | ||
| def inventoryFile = layout.buildDirectory.file('placement/artifacts.json') | ||
| tasks.register('writeArtifactInventory') { | ||
| description = 'Writes the resolved external runtime artifacts as JSON for cross-project staging.' |
There was a problem hiding this comment.
I don't understand what this is doing for the build. Is this effectively a debug log? Presumably should be removed.
| // Writes one SHA line per file of the staged distribution to | ||
| // build/distribution-snapshot.txt. The protocol for changing build | ||
| // logic: snapshot, make the change, build, snapshot again, diff. Only | ||
| // the changes you intended should appear (see CONTRIBUTING.md). | ||
| tasks.register('snapshotDistribution') { | ||
| group = 'verification' | ||
| description = 'Writes a SHA-256 line per file of server/setup for before/after comparison of build changes.' |
There was a problem hiding this comment.
Seems like this is duplicative of the tools/verifier and separate verifiers. The build cannot check itself. Remove/move to tools.
| ### Changing build logic | ||
|
|
||
| The build's correctness is guarded by output comparison, not by unit | ||
| tests of the build scripts. When you change build logic (staging, | ||
| packaging, jar definitions), use the snapshot protocol: | ||
|
|
||
| ```bash | ||
| ./gradlew build snapshotDistribution -DdisableSigning=true | ||
| cp build/distribution-snapshot.txt /tmp/before.txt | ||
| # ... make your build-logic change ... | ||
| ./gradlew build snapshotDistribution -DdisableSigning=true | ||
| diff /tmp/before.txt build/distribution-snapshot.txt | ||
| ``` | ||
|
|
||
| Only the changes you intended should appear. For archive-level analysis | ||
| of a difference, use `tools/build-parity/compare_builds.py` on two | ||
| setup trees. |
There was a problem hiding this comment.
This seems excessive for the getting-started. Remove to wiki.
There was a problem hiding this comment.
I don't think this directory landing in main is critical to this PR landing, I suggest the build-parity directory be removed to a separate PR if it is to be in main.
| @@ -1,6 +1,6 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <install4j version="11.0.3" transformSequenceNumber="11"> | |||
| <application name="Open Integration Engine" applicationId="4145-9206-7630-8076" mediaDir="${compiler:installer:mediaRoot}/server/build" shortName="oie" publisher="Open Integration Engine Project" publisherWeb="https://openintegrationengine.com" version="4.6.0" backupOnSave="true" autoSave="true" macVolumeId="88477e584eb462ba" javaMinVersion="17" javaMaxVersion="25"> | |||
| <application name="Open Integration Engine" applicationId="4145-9206-7630-8076" mediaDir="${compiler:installer:mediaRoot}/server/installer" shortName="oie" publisher="Open Integration Engine Project" publisherWeb="https://openintegrationengine.com" version="4.6.0" backupOnSave="true" autoSave="true" macVolumeId="88477e584eb462ba" javaMinVersion="17" javaMaxVersion="25"> | |||
8b5ee36 to
baf26b5
Compare
|
Thanks, this was a useful pass. All of it is addressed; rundown: Docs that go stale: Verifier to a separate repo: done. Unused/debug steps: Empty Per-project dependency resolution: agree it's the better shape, and I'd like to do it as the follow-up you suggested, one step at a time with the parity check, rather than reshape staging inside the migration. The deps are already declared per-project (the catalog bundles); what's central is the placement map, which is version-independent and has had zero churn since it was created. Happy to take it on next. Drop Wrapper action (build.yaml): you guessed it: install4j / .gitignore incidentals: the install4j change is reverted out of the PR (it was an unrelated local edit). The unrelated One thing worth flagging in good faith: dropping |
|
Fair concern, and the diff is genuinely large. The reason it's one change rather than a sequence is that the evidence is one piece: every archive in the distribution is verified byte-identical against an Ant build of the parent commit. An intermediate PR with a non-standard layout (or the build swap without the dependency swap) would be a state that was never verified and was never meant to ship, so splitting there would mean asking reviewers to sign off on something the proof doesn't cover. The intended way to review this isn't to read all the files; it's to run the comparator and watch it report zero real differences. The recipe is in the PR description, and the tooling is in a separate repo now so it isn't part of what you have to read here. That's the review surface, not the line count. On the The branch is already three commits split along the lines you describe: the 1,806 pure file moves (that same Also, thanks for the Windows SDKMan note, I rebased to address the review and kept your commit on top. |
Pure file moves, no content changes: every rename is 100% similarity. Verify with: git show --stat -M100% (1,806 renames, 0 insertions, 0 deletions). Java sources to src/main/java, resources to src/main/resources, tests to src/test/java and src/test/resources, in every module (donkey already used this layout). Refs OpenIntegrationEngine#52 Signed-off-by: Finnegan's Owner <44065187+pacmano1@users.noreply.github.com>
Native Gradle 8.14.1 build (wrapper committed, distribution checksum pinned): same artifacts, same locations. server/setup is the assembled distribution, server/dist the extension zips. The Ant build files are replaced by tombstones pointing at ./gradlew; Eclipse project files are retired in favor of native IDE Gradle import. CI builds with gradle/actions/setup-gradle (wrapper validation, SHA-pinned action) and the Dockerfile builds with the wrapper. Cross-project staging uses per-project artifact inventories (Gradle-9-safe; no cross-project configuration resolution, no exec(Closure); --warning-mode all carries no Gradle-9-fatal classes). Output parity with the Ant build was verified archive-by-archive; evidence and tooling arrive with the dependency commit. Note: this commit references the version catalog introduced by the next commit and does not build standalone; the branch head does. The .gitattributes line-ending rules follow PR OpenIntegrationEngine#214 by NicoPiel. Refs OpenIntegrationEngine#52, OpenIntegrationEngine#214 Signed-off-by: Finnegan's Owner <44065187+pacmano1@users.noreply.github.com>
374 of 419 vendored jars are replaced by version-catalog coordinates (gradle/libs.versions.toml). Every adopted coordinate was SHA-1-matched byte-identical to the vendored jar it replaces; resolution is non-transitive so the runtime artifact set stays exactly the audited set, and gradle/verification-metadata.xml enforces sha256 on every resolution. gradle/vendored-layout.json maps each artifact to its historical place in the distribution; per-project placement checks (aggregated by verifyVendoredParity, required by every build and by setup assembly) fail if a resolved artifact lacks a placement. The 45 jars without a byte-identical published artifact stay vendored, each with an evidence-based reason in tools/build-parity/jar-provenance.json. Distribution output is verified entry-content identical to an Ant build of this branch's parent commit: 490 of 490 archives, zero differences beyond tool metadata. Tooling and methodology in tools/build-parity/. Refs OpenIntegrationEngine#52, OpenIntegrationEngine#146 Signed-off-by: Finnegan's Owner <44065187+pacmano1@users.noreply.github.com>
Added note about potential file path error on Windows during javadoc step. Signed-off-by: Jon Bartels <jonathan.bartels@gmail.com>
baf26b5 to
64e1a98
Compare
jonbartels
left a comment
There was a problem hiding this comment.
TY for splitting the commits up.
Refs #52.
This replaces the Ant build with a standard Gradle build in one verified step. The product the build produces did not change, and that claim is checkable: the output of both builds was compared archive by archive, file by file. This description is the full write-up (it used to live in
BUILD-MIGRATION.mdin the repo; per review it now lives here, since it describes this change rather than the ongoing build).What changed since the first review
Addressing the review feedback (thanks @mgaffigan):
compare_builds.py, the jar-provenance audit,snapshot_distribution.py). It is migration scaffolding, not part of the build, so it no longer adds to this PR's review surface. If the maintainers would rather it live under the org, say the word and I'll move it toOpenIntegrationEngine/oie-build-parity. It's offered, not imposed.BUILD-MIGRATION.mdis removed; this write-up is now the PR description (it would only go stale sitting in the tree).-P), and-Dsupport is gone. A clean breaking change, as suggested, rather than carrying a compatibility shim.-PdisableSigning=true,-Pcoverage=true,-Pversion=x.y.z, and-Pkey.storepass=<PIN>for signing. The Dockerfile and CI were updated to match.package-info.classgeneration is gone. Ant's javac emitted empty, inert marker classes for the three annotation-freepackage-info.javasources; the Gradle build no longer reproduces them. This is now a documented, zero-runtime-effect difference (the comparison tool classifies it), instead of being faked with a hand-built class file.snapshotDistributiontask moved to the external tooling; thewriteArtifactInventorytask description was clarified; some unrelated personal entries were removed from.gitignore.The substance of the migration below is unchanged.
The short version
git show --stat -M100%confirms zero content change), the build swap, and the dependency swap (plus a Windows-docs note from @jonbartels on top). Only the head commit builds; squash-on-merge is fine.Why one change instead of piecemeal
None of the direction here is new. @NicoPiel proposed the move to a dependency-managed build (#52) and drove three successive Gradle attempts (#54, #100, #214); @jessesaga built a fourth (#243). In those reviews @mgaffigan named the bar a one-step replacement would have to clear, byte-identical output, and wrote the verification toolkit this branch is cross-checked with. The three-commit structure follows the reviewable-commit standard Tony Germano held the project's last large dependency change (#146) to. This branch is built on that groundwork.
The incremental path (wrap the old build first, convert one piece at a time) is the default for a reason: replacing a build system in one step is reckless when you can't prove the output is the same.
This branch takes the other path because the risk became measurable. The bar is met, and here is exactly what "identical" means, including everything that is not identical:
8c1111ba3), apart from build-tool fingerprints rather than product content: (1) 121 archives carry an Ant-version label Gradle doesn't write; (2) the build-written file timestamps inside those same archives differ, because both tools stamp a fixed fake date and chose different ones (third-party jars pass through byte-identical); (3) one small file (version.properties) embeds the build date, so it differs between any two builds; (4) two jars contain one extra folder entry (not a file); and (5) the emptypackage-info.classmarkers Ant's javac emitted for the three annotation-freepackage-info.javasources are not reproduced (inert, no runtime effect; see "What changed since the first review"). Every class file, resource, script, and configuration byte is the same. The comparison tool classifies each of these categories itself; nothing was waved through by hand.jarsigner -verify(strict mode adds the expected self-signed-dev-keystore warning; the old build warns identically).server/setup; a sanity check on top of the archive comparison).A verified cutover is one reviewable event with the proof attached, still split into three commits so each concern can be reviewed on its own.
What changed
src/main/java,src/test/java, …).git show --stat -M100%confirms every one is a pure move.server/setup,server/dist. The Ant entry points become stubs that point to./gradlew; the subproject Ant files are deleted. CI, the Dockerfile, and the contributor docs use the new build (with-Pflags). Eclipse-era conveniences are now build commands (devRun,devLauncher,devClient,createDerbyDb).How mistakes are caught before they ship
The design rule everywhere: a mismatch must stop the build with a clear message, never surface later as a broken product.
For changes to the build logic itself, CONTRIBUTING documents a habit: snapshot the built product before and after, diff the two, confirm only what you intended changed. The tooling for that is
snapshot_distribution.py/compare_builds.pyin oie-build-parity.How this was built
Substantial parts of this migration were produced with AI assistance: the Gradle build scripts, the comparison and provenance tooling, the dependency audit, and the drafts of this write-up. The experiment: can AI-driven work of this size be held to machine-checked claims rather than taken on trust? The controls:
The error ledger
The mistakes made during this work, and what caught each:
Known limitations
…Tests-named classes have never run, before or after (the pattern matches…Test). Preserved unchanged; a separate follow-up.Appendix: the 45 jars that stay in the repository
Generated from the committed audit record (
jar-provenance.jsonin oie-build-parity). 32 distinct jars, 45 files across modules.autocomplete-2.5.4, the tenhapi-*-2.3jars,jtds-1.3.1,rsyntaxtextarea-2.5.6,sqlite-jdbc-3.43.2.1.javaparser-1.0.8, 60 inzip4j_1.3.3, 2 innot-going-to-be-commons-ssl-0.3.18). Local forks; replacing them would change the product.PDFRenderer,backport-util-concurrent-Java60-3.1,istack-commons-runtime-3.0.6,jai_imageio,language_support,looks-2.3.1,openjfx,webdavclient4j-core-0.92,wizard,wsdl4j-1.6.2-fixed.dcm4che-*-2.0.29jars (from dcm4che.org) andmirth-vocab.jar(built in-house by thegeneratormodule).How to re-verify
Needs JDK 17, Apache Ant (acceptance used 1.10.14), Python 3, and network access for the first Gradle run. The tooling lives in oie-build-parity.
Run these from your checkout of this PR's branch (the first line captures it; the rest use absolute paths, so cwd doesn't matter after that):
Expected:
REAL differences (0), with the known tool-fingerprint deltas (Ant-version labels, timestamps,version.properties, the two directory entries, and the emptypackage-info.classmarkers) each classified and counted. The Ant baseline builds in seconds (normal); the Gradle build takes a few minutes and runs the full suite.A successful Gradle build ends with Gradle's stock "incompatible with Gradle 9.0" banner;
--warning-mode allshows it refers to the one disclosed Gradle-10 item under Limitations.The dependency audit is re-checkable with
python3 /tmp/oie-build-parity/sweep_provenance.py --verify-exact(expect0 mismatches). The signed-build check and the rc1 cross-check each have their own recipe in the oie-build-parity README.The Ant baseline hash file (oie-release-verifier format, uncompressed SHA-256
e54cb2ef8c30ea887fb6752fc83a4dbf9fd818de3267177d29d6d2a1eaf3f6e4) is published here. A second maintainer re-running the dual-build recipe before merge is invited.