You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@grails.apache.org by GitBox <gi...@apache.org> on 2026/05/02 18:07:08 UTC
[GH] (grails-core/fix/sbom-groovydoc-reproducibility): Workflow run "Code Style" failed!
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 <ja...@users.noreply.github.com>
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