The GitHub Actions job "CI" on 
grails-core.git/fix/sbom-groovydoc-reproducibility has failed.
Run started by GitHub user jamesfredley (triggered by jamesfredley).

Head commit for run:
7f753f8be59701bd4b4b51d6b300ef36958e22cb / James Fredley 
<[email protected]>
fix(sbom): make per-module SBOM and groovydoc output byte-reproducible

Container-based release-vote verification (per RELEASE.md) on v8.0.0-M1
found nine jars not byte-identical between CI-published copies and a
fresh local rebuild from source: grails-async{,-core,-gpars,-rxjava,
-rxjava2,-rxjava3}, grails-bootstrap, grails-cache, and the
grails-bootstrap-javadoc jar. Two distinct root causes; both fixed at
source rather than via JSON post-processing or jar excludes.

1. SbomPlugin: register CyclonedxDirectTask without applying
   CyclonedxPlugin, and set includeBuildSystem = false

   The cyclonedx-gradle-plugin's apply() method unconditionally
   registers BOTH the per-module direct task (cyclonedxDirectBom) AND
   the aggregate task (cyclonedxBom, a CyclonedxAggregateTask). The
   aggregate is wired by Spring Boot 4's CycloneDxPluginAction to
   package its output at META-INF/sbom/application.cdx.json - misleading
   for library jars (Grails plugins are libraries, not applications)
   and an extra non-reproducible artifact to manage. Grails ships only
   per-module SBOMs at META-INF/sbom.json.

   Registering CyclonedxDirectTask ourselves means the aggregate task
   is never registered, so there is nothing to disable, exclude, or
   filter; Spring Boot's reaction never fires either since it triggers
   on plugin-application of CyclonedxPlugin.

   Separately, cyclonedx-gradle-plugin 3.0.0 began auto-injecting a
   build-system externalReference populated from CI env vars (e.g. the
   GitHub Actions run URL), which is unknowable from a local rebuild
   and broke byte-identical reproducibility. The plugin exposes a
   first-class includeBuildSystem property; setting it to false
   suppresses the reference at the plugin level instead of stripping
   it from the JSON afterwards. We intentionally leave build info out
   of the SBOM until that toolchain is ready.

   PR #15614's projectPath-into-UUID-seed fix is preserved unchanged.

2. FieldDefinition / PropertyDefinition: qualify Builder return types

   Both classes declare a static inner Builder of the same simple
   name. Groovydoc's SimpleGroovyClassDoc resolves unqualified Builder
   references via a shared short-name cache populated in filesystem
   iteration order, producing different (and incorrect) HTML between
   filesystems. Qualifying the return types as
   FieldDefinition.Builder / PropertyDefinition.Builder makes
   groovydoc output reproducible. Bytecode is unchanged (groovyc
   resolves nested Builder via lexical scope at compile time).

   This is the root cause filed upstream as GROOVY-11954 and fixed in
   Groovy 4.0.32, 5.0.6, and 6.0.0-alpha-1
   (https://github.com/apache/groovy/pull/2484). Grails currently
   pins groovy.version=4.0.31 and 4.0.32 has not yet been published
   to Maven Central, so the qualification stays in place for v8.0.0-M1.
   Each builder() factory carries a TODO(GROOVY-11954) note so a future
   Groovy bump can drop the qualification (the bytecode is unaffected).

Verified
--------

- :grails-bootstrap:tasks --all reports cyclonedxDirectBom and no
  cyclonedxBom on grails-plugin modules.
- META-INF/sbom.json contains no build-system externalReference.
- Two consecutive :grails-bootstrap:jar runs with the same
  SOURCE_DATE_EPOCH produce byte-identical jars (same SHA-256).
- Per-module META-INF/sbom.json is the only SBOM in produced jars
  (no META-INF/sbom/application.cdx.json).
- :grails-bootstrap:test for FieldDefinitionSpec and
  PropertyDefinitionSpec passes.

Assisted-by: claude-code:claude-4.6-opus

Report URL: https://github.com/apache/grails-core/actions/runs/25278557421

With regards,
GitHub Actions via GitBox

Reply via email to