The GitHub Actions job "Code Style" on grails-core.git/fix/sbom-groovydoc-reproducibility has failed. Run started by GitHub user jamesfredley (triggered by jamesfredley).
Head commit for run: 99a365c3eb362df90cd672f0de174831fbde44d1 / James Fredley <[email protected]> fix: make SBOM and groovydoc output reproducible across CI and local builds The container-based release-vote verification described in RELEASE.md fails on v8.0.0-M1 because nine jars (grails-async*, grails-bootstrap, grails-bootstrap-javadoc, grails-cache) are not byte-identical between the CI-published copies and a fresh local rebuild from the source distribution. Three independent root causes are addressed. 1. SbomPlugin direct task: strip the build-system externalReference The upstream cyclonedx-gradle-plugin auto-injects an externalReference of type build-system pointing at the GitHub Actions run URL when CI environment variables are present. That URL is unknowable from source, so its presence breaks the deterministic serialNumber hash that SbomPlugin already computes from the BOM content. Filtering it out before the hash recomputation makes META-INF/sbom.json match between CI and local rebuilds. 2. SbomPlugin aggregate task: apply the same reproducibility transforms Spring Boot 4 CycloneDxPluginAction wires the cyclonedx-gradle-plugin aggregate task (CyclonedxAggregateTask, default name cyclonedxBom) into Grails-plugin jars, packaging its output at META-INF/sbom/application.cdx.json. SbomPlugin previously only configured the direct task (CyclonedxDirectTask), so the aggregate SBOM had a random serialNumber, a non-reproducible Instant.now() timestamp, and CI auto-detected externalReferences. The rewrite logic is extracted into a shared rewriteSbomFile helper applied to both task types via configureSbomTask and a new configureAggregateSbomReproducibility method. The aggregate strip set covers both build-system and vcs because the aggregate task has no explicit externalReferences configuration to fall back on. 3. FieldDefinition / PropertyDefinition: qualify Builder return types Both classes declare a static inner Builder class with the same simple name, and both extend AbstractMemberDefinition. The static builder() method previously used the unqualified return type "static Builder builder()". When groovydoc walks the class graph, it resolves that simple name based on file system iteration order; one filesystem ordering causes it to render every Builder reference as PropertyDefinition.Builder, another causes FieldDefinition.Builder. Qualifying the return types as "static FieldDefinition.Builder builder()" and "static PropertyDefinition.Builder builder()" forces groovydoc to render the correct (and consistent) types regardless of iteration order. Compiled bytecode is unchanged because the nested Builder classes already resolved correctly via lexical scope. Verified by building grails-async-core, grails-bootstrap, and grails-cache twice with the same SOURCE_DATE_EPOCH and confirming both META-INF/sbom.json and META-INF/sbom/application.cdx.json are byte-identical across runs, with build-system and vcs externalReferences stripped and a deterministic urn:uuid serialNumber. Existing FieldDefinitionSpec and PropertyDefinitionSpec test suites continue to pass because the bytecode-level builder() return type is unchanged. Assisted-by: claude-code:claude-opus-4 Report URL: https://github.com/apache/grails-core/actions/runs/25258260139 With regards, GitHub Actions via GitBox
