You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@grails.apache.org by "jamesfredley (via GitHub)" <gi...@apache.org> on 2026/04/30 22:24:06 UTC
[PR] fix(sbom): mix projectPath into deterministic UUID seed [grails-core]
jamesfredley opened a new pull request, #15614:
URL: https://github.com/apache/grails-core/pull/15614
## Summary
Fixes duplicate `urn:uuid` `serialNumber` values across SBOMs after the upgrade to Gradle 9 + CycloneDX gradle plugin 3.0.0.
## Problem
SbomPlugin post-processes each module's BOM JSON and rewrites the `serialNumber` deterministically via `UUID.nameUUIDFromBytes(<json bytes>)` so rebuilds yield identical SBOMs (and therefore identical jar checksums). After moving to Gradle 9.4.1 and CycloneDX gradle plugin 3.0.0, several modules now produce post-processed JSON whose body collides between modules (most visibly the empty BOM platforms `grails-bom`, `grails-base-bom`, `grails-hibernate5-bom`, `grails-micronaut-bom`). Hashing identical content gives identical UUIDs, so the resulting SBOMs share the same `serialNumber` - which violates the [CycloneDX 1.6 spec for `serialNumber`](https://cyclonedx.org/docs/1.6/json/#serialNumber) (must be unique per BOM).
## Fix
Mix the captured `projectPath` into the hash input:
` groovy
def uuidSeed = "\n"
def uuid = UUID.nameUUIDFromBytes(uuidSeed.getBytes(StandardCharsets.UTF_8.name()))
`
- Different modules always seed the hash with a different prefix → unique `serialNumber` per BOM.
- The same module + same content still yields the same UUID across rebuilds → reproducible builds preserved.
- `projectPath` is already captured at configuration time (line 217), so no new `Task.project` access at execution time and no configuration-cache regression.
## Verification
Ran `cyclonedxDirectBom` on six modules on Gradle 9.4.1:
| Module | `serialNumber` |
|---|---|
| `:grails-base-bom` | `urn:uuid:1f5bfc59-8a1d-380b-9a65-69aac07e18b0` |
| `:grails-bom` | `urn:uuid:70b08f03-ec8b-3b7b-9cb4-d76a6a21ad90` |
| `:grails-hibernate5-bom` | `urn:uuid:f0a86eec-77f6-3236-aed3-d99f00933967` |
| `:grails-micronaut-bom` | `urn:uuid:23919eca-e83e-3e5a-a27a-1a7b1ce5cc31` |
| `:grails-bootstrap` | `urn:uuid:3fe6e19d-48e4-3097-810e-1b504da46be6` |
| `:grails-encoder` | `urn:uuid:7c15d8fa-2a54-39a5-8b14-c87cfa0b012b` |
- **Uniqueness**: all six `serialNumber` values are distinct.
- **Determinism**: re-running `--rerun-tasks` produces the exact same six values.
Reproducible-build verification via `etc/bin/test-reproducible-builds.sh` inside the `etc/bin/Dockerfile` container is being run separately to confirm jar checksums match across two clean builds.
## Notes
- `build-logic/plugins` does not have a Spock test sourceset wired up today, matching the pattern of recent SBOM fixes (`54b718b526`, `8c5d1d182d`). Adding one would be a separate, larger change.
- No public API changes; only the build-tooling plugin `org.apache.grails.buildsrc.sbom`.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@grails.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] fix(sbom): mix projectPath into deterministic UUID seed [grails-core]
Posted by "jamesfredley (via GitHub)" <gi...@apache.org>.
jamesfredley merged PR #15614:
URL: https://github.com/apache/grails-core/pull/15614
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@grails.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] fix(sbom): mix projectPath into deterministic UUID seed [grails-core]
Posted by "jamesfredley (via GitHub)" <gi...@apache.org>.
jamesfredley commented on PR #15614:
URL: https://github.com/apache/grails-core/pull/15614#issuecomment-4360020417
@jdaugherty I will not be able to look until this evening, please proceed if you have time on this and the other PR to fix the build.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@grails.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] fix(sbom): mix projectPath into deterministic UUID seed [grails-core]
Posted by "Copilot (via GitHub)" <gi...@apache.org>.
Copilot commented on code in PR #15614:
URL: https://github.com/apache/grails-core/pull/15614#discussion_r3171167352
##########
build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/SbomPlugin.groovy:
##########
@@ -261,10 +261,20 @@ class SbomPlugin implements Plugin<Project> {
}
}
- // force the serialNumber to be reproducible by removing it & recalculating
+ // force the serialNumber to be reproducible by removing it & recalculating.
+ // Mix the projectPath into the UUID seed so two modules whose post-processed
+ // BOM JSON happens to be identical (for example, empty BOM platforms with no
+ // runtime dependencies, or modules whose metadata.component is filled in
+ // identically by the CycloneDX plugin) still receive distinct serialNumbers
+ // as required by the CycloneDX specification. Including projectPath preserves
+ // reproducibility because the same project path + same content always yields
+ // the same UUID across rebuilds. This guards against collisions introduced by
+ // CycloneDX 3.0.0 / Gradle 9 metadata changes.
+ // See: https://cyclonedx.org/docs/1.6/json/#serialNumber
bom['serialNumber'] = ''
def withOutSerial = JsonOutput.prettyPrint(JsonOutput.toJson(bom))
Review Comment:
The comment says the serialNumber is "remov[ed]" before recalculating, but the code sets it to an empty string (`bom['serialNumber'] = ''`). Either update the comment to match the behavior (blanking) or actually remove the key (e.g., `bom.remove('serialNumber')`) so the documentation and implementation stay consistent.
##########
build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/SbomPlugin.groovy:
##########
@@ -261,10 +261,20 @@ class SbomPlugin implements Plugin<Project> {
}
}
- // force the serialNumber to be reproducible by removing it & recalculating
+ // force the serialNumber to be reproducible by removing it & recalculating.
+ // Mix the projectPath into the UUID seed so two modules whose post-processed
+ // BOM JSON happens to be identical (for example, empty BOM platforms with no
+ // runtime dependencies, or modules whose metadata.component is filled in
+ // identically by the CycloneDX plugin) still receive distinct serialNumbers
+ // as required by the CycloneDX specification. Including projectPath preserves
+ // reproducibility because the same project path + same content always yields
+ // the same UUID across rebuilds. This guards against collisions introduced by
+ // CycloneDX 3.0.0 / Gradle 9 metadata changes.
+ // See: https://cyclonedx.org/docs/1.6/json/#serialNumber
bom['serialNumber'] = ''
def withOutSerial = JsonOutput.prettyPrint(JsonOutput.toJson(bom))
- def uuid = UUID.nameUUIDFromBytes(withOutSerial.getBytes(StandardCharsets.UTF_8.name()))
+ def uuidSeed = "${projectPath}\n${withOutSerial}"
Review Comment:
Local variable name `withOutSerial` is hard to read and inconsistent with typical camelCase (would usually be `withoutSerial`). Consider renaming for clarity since it's now referenced in the new `uuidSeed` logic.
##########
build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/SbomPlugin.groovy:
##########
@@ -261,10 +261,20 @@ class SbomPlugin implements Plugin<Project> {
}
}
- // force the serialNumber to be reproducible by removing it & recalculating
+ // force the serialNumber to be reproducible by removing it & recalculating.
+ // Mix the projectPath into the UUID seed so two modules whose post-processed
+ // BOM JSON happens to be identical (for example, empty BOM platforms with no
+ // runtime dependencies, or modules whose metadata.component is filled in
+ // identically by the CycloneDX plugin) still receive distinct serialNumbers
+ // as required by the CycloneDX specification. Including projectPath preserves
+ // reproducibility because the same project path + same content always yields
+ // the same UUID across rebuilds. This guards against collisions introduced by
+ // CycloneDX 3.0.0 / Gradle 9 metadata changes.
+ // See: https://cyclonedx.org/docs/1.6/json/#serialNumber
bom['serialNumber'] = ''
def withOutSerial = JsonOutput.prettyPrint(JsonOutput.toJson(bom))
- def uuid = UUID.nameUUIDFromBytes(withOutSerial.getBytes(StandardCharsets.UTF_8.name()))
+ def uuidSeed = "${projectPath}\n${withOutSerial}"
+ def uuid = UUID.nameUUIDFromBytes(uuidSeed.getBytes(StandardCharsets.UTF_8.name()))
Review Comment:
`uuidSeed.getBytes(StandardCharsets.UTF_8.name())` uses the String-based charset overload; using the `Charset` overload (`getBytes(StandardCharsets.UTF_8)`) avoids the extra lookup/string conversion and makes the intent clearer.
```suggestion
def uuid = UUID.nameUUIDFromBytes(uuidSeed.getBytes(StandardCharsets.UTF_8))
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@grails.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] fix(sbom): mix projectPath into deterministic UUID seed [grails-core]
Posted by "jdaugherty (via GitHub)" <gi...@apache.org>.
jdaugherty commented on code in PR #15614:
URL: https://github.com/apache/grails-core/pull/15614#discussion_r3171696646
##########
build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/SbomPlugin.groovy:
##########
@@ -384,4 +396,53 @@ class SbomPlugin implements Plugin<Project> {
}
}
}
+
+ /**
+ * Wires this project's own SBOM into the shadow jar produced by the
+ * com.gradleup.shadow plugin.
+ *
+ * Without this, shadow's first-wins merge picks up a META-INF/sbom.json
+ * from one of the bundled transitive jars (typically grails-shell-cli's),
+ * giving the fat jar the wrong serialNumber and metadata.component
+ * (it ends up describing grails-shell-cli rather than the fat jar's own
+ * project). Two fat jars that both bundle grails-shell-cli (e.g.
+ * :grails-cli and :grails-cli-shadow) then end up with byte-identical
+ * META-INF/sbom.json entries and identical urn:uuid serialNumbers,
+ * which violates the CycloneDX 1.6 specification.
+ *
+ * The fix is symmetrical with publishSbomForJarProjects: we exclude any
+ * META-INF/sbom.json that arrives from transitive dependencies during
+ * the shadow merge, then re-introduce this project's own SBOM (whose
+ * serialNumber is project-path-seeded and unique per fix(sbom): mix
+ * projectPath into deterministic UUID seed).
+ *
+ * Uses the broad Task type to avoid a compile-time dependency on
Review Comment:
We had a compile dependency before that was removed as part of the gradle 9 update, this should remain. We're complicated a generic plugin specifically to work around one project instead of configuring the individual gradle project
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@grails.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] fix(sbom): mix projectPath into deterministic UUID seed [grails-core]
Posted by "jamesfredley (via GitHub)" <gi...@apache.org>.
jamesfredley commented on PR #15614:
URL: https://github.com/apache/grails-core/pull/15614#issuecomment-4357433622
Pushed [`23f586d4f6`](https://github.com/apache/grails-core/pull/15614/commits/23f586d4f6) addressing @jdaugherty's review.
## What changed
Moved the shadow jar SBOM wiring **out of** the generic `SbomPlugin` and **into** `grails-forge/grails-cli/build.gradle`, the only project that actually combines `com.gradleup.shadow` with `org.apache.grails.buildsrc.sbom`.
* `SbomPlugin` lost `publishSbomForShadowJarProjects` and the raw `Task -> Jar` cast it required, plus the now-unused `import org.gradle.api.Task`. The plugin's only responsibility is back to wiring `cyclonedxDirectBom` into the regular `jar` via `publishSbomForJarProjects`.
* `grails-cli/build.gradle` now uses a typed `TaskProvider<CyclonedxDirectTask>` and `cyclonedxDirectBomTask.flatMap { it.jsonOutput }` inside the existing `shadowJarTask.configure { ShadowJar it -> ... }` block. `ShadowJar`, `CyclonedxDirectTask`, `CopySpec`, and `Manifest` are all on the buildscript classpath at that point, so the wiring is fully type-safe with no compile dep added to `build-logic`.
* `skipJavaComponent` guard preserved to match the convention used by `publishSbomForJarProjects`.
* `:grails-cli-shadow/build.gradle` is unchanged - it already excludes `META-INF/sbom.json` directly because it doesn't apply the SBOM plugin.
## Verification (Gradle 9.4.1, `--rerun-tasks`)
| Jar | `serialNumber` | `metadata.component.name` |
|---|---|---|
| `:grails-cli` (regular) | `urn:uuid:beabd2c0-...` | `grails-cli` |
| `:grails-cli` (`-all` FAT) | `urn:uuid:beabd2c0-...` | `grails-cli` |
| `:grails-cli-shadow` (`-all` FAT) | _(no SBOM - excluded)_ | _(no SBOM)_ |
| `:grails-forge-cli` (regular) | `urn:uuid:401e573c-...` | `grails-forge-cli` |
* `:grails-cli`'s regular and `-all` jar share a `serialNumber` by design (same project).
* The fat jar's `metadata.component.name` is `grails-cli` (not the leaked `grails-shell-cli` from before the fix).
* `:grails-cli-shadow` correctly has no `META-INF/sbom.json` in its fat jar.
* `:grails-forge-cli` has a distinct `serialNumber` describing the right module.
Replied to both review threads inline.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@grails.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] fix(sbom): mix projectPath into deterministic UUID seed [grails-core]
Posted by "testlens-app[bot] (via GitHub)" <gi...@apache.org>.
testlens-app[bot] commented on PR #15614:
URL: https://github.com/apache/grails-core/pull/15614#issuecomment-4357606395
## ✅ All tests passed ✅
🏷️ Commit: 23f586d4f6b363ebe889ea4225d1f45df2eed861
▶️ Tests: 19783 executed
⚪️ Checks: 35/35 completed
---
_Learn more about TestLens at [testlens.app](https://testlens.app)._
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@grails.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] fix(sbom): mix projectPath into deterministic UUID seed [grails-core]
Posted by "jamesfredley (via GitHub)" <gi...@apache.org>.
jamesfredley commented on PR #15614:
URL: https://github.com/apache/grails-core/pull/15614#issuecomment-4357183912
Pushed [`4d1a78f1e2`](https://github.com/apache/grails-core/pull/15614/commits/4d1a78f1e2) - second-order fix for `com.gradleup.shadow` fat jars.
## Why a follow-up commit
The `projectPath`-seed fix in `d6a80e0c9e` made each project's *own* `cyclonedxDirectBom` output unique and reproducible, but it did not change how the shadow plugin merges sibling jars. When I extracted `META-INF/sbom.json` from the fat jars produced by `:grails-cli` and `:grails-cli-shadow` after the first fix, both still had:
| Jar | `serialNumber` | `metadata.component.name` |
|---|---|---|
| `grails-cli-8.0.0-SNAPSHOT-all.jar` | `urn:uuid:631bbcd5-...` | `grails-shell-cli` |
| `grails-cli-shadow-8.0.0-SNAPSHOT-all.jar` | `urn:uuid:631bbcd5-...` | `grails-shell-cli` |
Two distinct fat jars sharing one UUID, both attributed to the wrong module - because shadow's first-wins merge picked up `grails-shell-cli`'s `META-INF/sbom.json` (a transitive dep) before either fat-jar project's own SBOM. Same CycloneDX 1.6 violation, different cause.
## What changed
1. `SbomPlugin.publishSbomForShadowJarProjects` (new) - symmetrical to `publishSbomForJarProjects`. When a project applies `com.gradleup.shadow`, its `shadowJar` task now `exclude`s incoming `META-INF/sbom.json` from the merge and `from`s the project's own `cyclonedxDirectBom` output, plus sets the same `Sbom-Location` / `Sbom-Format` manifest attributes the regular jar already gets. Uses the broad `Task` type with a cast to `Jar` so build-logic does not need a compile-time dep on the shadow plugin types.
2. `grails-forge/grails-cli-shadow/build.gradle` - that module does not apply `org.apache.grails.buildsrc.sbom` (it is an intermediate build artifact, not published), so the SbomPlugin hook does not fire for it. Its `shadowJar` exclude list now drops `META-INF/sbom.json` directly so the intermediate fat jar - which feeds `:grails-cli`'s `shadowCombined` configuration - cannot smuggle a wrong SBOM back in.
## Verification (Gradle 9.4.1, `SOURCE_DATE_EPOCH=git log -1 --pretty=%ct`, `--no-build-cache --rerun-tasks`)
After the second fix, `META-INF/sbom.json` in each CLI jar:
| Jar | `serialNumber` | `metadata.component.name` |
|---|---|---|
| `:grails-shell-cli` (regular) | `631bbcd5-...` | `grails-shell-cli` |
| `:grails-wrapper` (regular) | `9c5f6980-...` | `grails-wrapper` |
| `:grails-cli` (regular) | `3e4ea827-...` | `grails-cli` |
| `:grails-cli` (`-all` FAT) | `3e4ea827-...` | `grails-cli` |
| `:grails-cli-shadow` (regular, empty) | _(no SBOM)_ | _(no SBOM)_ |
| `:grails-cli-shadow` (`-all` FAT) | _(no SBOM - excluded)_ | _(no SBOM)_ |
| `:grails-forge-cli` (regular) | `890372ec-...` | `grails-forge-cli` |
Four distinct `serialNumber` values across four user-facing jars; zero cross-module collisions. `:grails-cli`'s regular and `-all` jar share a `serialNumber` *by design* - they ship the same project's SBOM, the `-all` jar just adds bundled classes; CycloneDX uniqueness applies across BOMs describing different artifacts, not across two jars whose embedded BOM describes the same project.
Smoke tests against the same flow `verify-cli-distribution.sh` runs:
- `apache-grails-8.0.0-SNAPSHOT-bin.zip` extracts cleanly, `LICENSE` and `NOTICE` present, `bin/` populated.
- `grails-shell-cli --version` => `Grails Version: 8.0.0-SNAPSHOT`, exit 0.
- `grails-forge-cli --version` => `Grails Version: 8.0.0-SNAPSHOT`, exit 0.
- `grails-shell-cli create-app ShellApp` generates a complete Grails app, exit 0.
- `grails-forge-cli create-app -x -g mongodb -f gradle-settings-file ForgeApp` generates a complete Grails app, exit 0.
- `java -jar grails-cli-8.0.0-SNAPSHOT-all.jar --version` still launches the CLI with the new SBOM in place, exit 0.
`apache-grails-wrapper-8.0.0-SNAPSHOT-bin.zip` also extracts cleanly with `LICENSE`, `NOTICE`, `grailsw`, `grailsw.bat`, `grails-wrapper.jar` present.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@grails.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] fix(sbom): mix projectPath into deterministic UUID seed [grails-core]
Posted by "jdaugherty (via GitHub)" <gi...@apache.org>.
jdaugherty commented on code in PR #15614:
URL: https://github.com/apache/grails-core/pull/15614#discussion_r3171694280
##########
build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/SbomPlugin.groovy:
##########
@@ -384,4 +396,53 @@ class SbomPlugin implements Plugin<Project> {
}
}
}
+
+ /**
+ * Wires this project's own SBOM into the shadow jar produced by the
+ * com.gradleup.shadow plugin.
+ *
+ * Without this, shadow's first-wins merge picks up a META-INF/sbom.json
+ * from one of the bundled transitive jars (typically grails-shell-cli's),
+ * giving the fat jar the wrong serialNumber and metadata.component
+ * (it ends up describing grails-shell-cli rather than the fat jar's own
+ * project). Two fat jars that both bundle grails-shell-cli (e.g.
+ * :grails-cli and :grails-cli-shadow) then end up with byte-identical
+ * META-INF/sbom.json entries and identical urn:uuid serialNumbers,
+ * which violates the CycloneDX 1.6 specification.
+ *
+ * The fix is symmetrical with publishSbomForJarProjects: we exclude any
+ * META-INF/sbom.json that arrives from transitive dependencies during
+ * the shadow merge, then re-introduce this project's own SBOM (whose
+ * serialNumber is project-path-seeded and unique per fix(sbom): mix
+ * projectPath into deterministic UUID seed).
+ *
+ * Uses the broad Task type to avoid a compile-time dependency on
+ * com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar from
+ * build-logic; the cast is safe because ShadowJar extends Jar.
+ */
+ private static void publishSbomForShadowJarProjects(Project project, Provider<RegularFile> sbomOutputLocation) {
Review Comment:
I don't think ti's correct configure these is the sbom plugin. Why aren't we using a transform on the project in question?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@grails.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] fix(sbom): mix projectPath into deterministic UUID seed [grails-core]
Posted by "testlens-app[bot] (via GitHub)" <gi...@apache.org>.
testlens-app[bot] commented on PR #15614:
URL: https://github.com/apache/grails-core/pull/15614#issuecomment-4363771378
## ✅ All tests passed ✅
🏷️ Commit: 0453de0f5fd8d87c95feb6c3eb3b0357ca32a6fc
▶️ Tests: 9879 executed
⚪️ Checks: 35/35 completed
---
_Learn more about TestLens at [testlens.app](https://testlens.app)._
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@grails.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] fix(sbom): mix projectPath into deterministic UUID seed [grails-core]
Posted by "jdaugherty (via GitHub)" <gi...@apache.org>.
jdaugherty commented on code in PR #15614:
URL: https://github.com/apache/grails-core/pull/15614#discussion_r3171696646
##########
build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/SbomPlugin.groovy:
##########
@@ -384,4 +396,53 @@ class SbomPlugin implements Plugin<Project> {
}
}
}
+
+ /**
+ * Wires this project's own SBOM into the shadow jar produced by the
+ * com.gradleup.shadow plugin.
+ *
+ * Without this, shadow's first-wins merge picks up a META-INF/sbom.json
+ * from one of the bundled transitive jars (typically grails-shell-cli's),
+ * giving the fat jar the wrong serialNumber and metadata.component
+ * (it ends up describing grails-shell-cli rather than the fat jar's own
+ * project). Two fat jars that both bundle grails-shell-cli (e.g.
+ * :grails-cli and :grails-cli-shadow) then end up with byte-identical
+ * META-INF/sbom.json entries and identical urn:uuid serialNumbers,
+ * which violates the CycloneDX 1.6 specification.
+ *
+ * The fix is symmetrical with publishSbomForJarProjects: we exclude any
+ * META-INF/sbom.json that arrives from transitive dependencies during
+ * the shadow merge, then re-introduce this project's own SBOM (whose
+ * serialNumber is project-path-seeded and unique per fix(sbom): mix
+ * projectPath into deterministic UUID seed).
+ *
+ * Uses the broad Task type to avoid a compile-time dependency on
Review Comment:
We had a compile depdnency before, this should remain. We're complicated a generic plugin specifically to work around one project instead of configuring the individual gradle project
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@grails.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] fix(sbom): mix projectPath into deterministic UUID seed [grails-core]
Posted by "jamesfredley (via GitHub)" <gi...@apache.org>.
jamesfredley commented on code in PR #15614:
URL: https://github.com/apache/grails-core/pull/15614#discussion_r3171193826
##########
build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/SbomPlugin.groovy:
##########
@@ -261,10 +261,20 @@ class SbomPlugin implements Plugin<Project> {
}
}
- // force the serialNumber to be reproducible by removing it & recalculating
+ // force the serialNumber to be reproducible by removing it & recalculating.
+ // Mix the projectPath into the UUID seed so two modules whose post-processed
+ // BOM JSON happens to be identical (for example, empty BOM platforms with no
+ // runtime dependencies, or modules whose metadata.component is filled in
+ // identically by the CycloneDX plugin) still receive distinct serialNumbers
+ // as required by the CycloneDX specification. Including projectPath preserves
+ // reproducibility because the same project path + same content always yields
+ // the same UUID across rebuilds. This guards against collisions introduced by
+ // CycloneDX 3.0.0 / Gradle 9 metadata changes.
+ // See: https://cyclonedx.org/docs/1.6/json/#serialNumber
bom['serialNumber'] = ''
def withOutSerial = JsonOutput.prettyPrint(JsonOutput.toJson(bom))
Review Comment:
Good catch - updated the comment to say `clearing it` in 462b2f8d45 to match the actual `bom['serialNumber'] = ''` behavior. Kept the blanking approach (rather than `bom.remove`) so the field position is preserved in the JSON output and existing serialNumbers stay byte-identical.
##########
build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/SbomPlugin.groovy:
##########
@@ -261,10 +261,20 @@ class SbomPlugin implements Plugin<Project> {
}
}
- // force the serialNumber to be reproducible by removing it & recalculating
+ // force the serialNumber to be reproducible by removing it & recalculating.
+ // Mix the projectPath into the UUID seed so two modules whose post-processed
+ // BOM JSON happens to be identical (for example, empty BOM platforms with no
+ // runtime dependencies, or modules whose metadata.component is filled in
+ // identically by the CycloneDX plugin) still receive distinct serialNumbers
+ // as required by the CycloneDX specification. Including projectPath preserves
+ // reproducibility because the same project path + same content always yields
+ // the same UUID across rebuilds. This guards against collisions introduced by
+ // CycloneDX 3.0.0 / Gradle 9 metadata changes.
+ // See: https://cyclonedx.org/docs/1.6/json/#serialNumber
bom['serialNumber'] = ''
def withOutSerial = JsonOutput.prettyPrint(JsonOutput.toJson(bom))
- def uuid = UUID.nameUUIDFromBytes(withOutSerial.getBytes(StandardCharsets.UTF_8.name()))
+ def uuidSeed = "${projectPath}\n${withOutSerial}"
Review Comment:
Renamed to `withoutSerial` (proper camelCase) in 462b2f8d45.
##########
build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/SbomPlugin.groovy:
##########
@@ -261,10 +261,20 @@ class SbomPlugin implements Plugin<Project> {
}
}
- // force the serialNumber to be reproducible by removing it & recalculating
+ // force the serialNumber to be reproducible by removing it & recalculating.
+ // Mix the projectPath into the UUID seed so two modules whose post-processed
+ // BOM JSON happens to be identical (for example, empty BOM platforms with no
+ // runtime dependencies, or modules whose metadata.component is filled in
+ // identically by the CycloneDX plugin) still receive distinct serialNumbers
+ // as required by the CycloneDX specification. Including projectPath preserves
+ // reproducibility because the same project path + same content always yields
+ // the same UUID across rebuilds. This guards against collisions introduced by
+ // CycloneDX 3.0.0 / Gradle 9 metadata changes.
+ // See: https://cyclonedx.org/docs/1.6/json/#serialNumber
bom['serialNumber'] = ''
def withOutSerial = JsonOutput.prettyPrint(JsonOutput.toJson(bom))
- def uuid = UUID.nameUUIDFromBytes(withOutSerial.getBytes(StandardCharsets.UTF_8.name()))
+ def uuidSeed = "${projectPath}\n${withOutSerial}"
+ def uuid = UUID.nameUUIDFromBytes(uuidSeed.getBytes(StandardCharsets.UTF_8.name()))
Review Comment:
Switched to the `Charset` overload (`StandardCharsets.UTF_8`) in 462b2f8d45. Re-ran `cyclonedxDirectBom` on the same six modules and confirmed all `serialNumber` values are byte-identical to the prior commit, so this is purely a cleanup with no behavior change.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@grails.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] fix(sbom): mix projectPath into deterministic UUID seed [grails-core]
Posted by "jamesfredley (via GitHub)" <gi...@apache.org>.
jamesfredley commented on code in PR #15614:
URL: https://github.com/apache/grails-core/pull/15614#discussion_r3171778301
##########
build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/SbomPlugin.groovy:
##########
@@ -384,4 +396,53 @@ class SbomPlugin implements Plugin<Project> {
}
}
}
+
+ /**
+ * Wires this project's own SBOM into the shadow jar produced by the
+ * com.gradleup.shadow plugin.
+ *
+ * Without this, shadow's first-wins merge picks up a META-INF/sbom.json
+ * from one of the bundled transitive jars (typically grails-shell-cli's),
+ * giving the fat jar the wrong serialNumber and metadata.component
+ * (it ends up describing grails-shell-cli rather than the fat jar's own
+ * project). Two fat jars that both bundle grails-shell-cli (e.g.
+ * :grails-cli and :grails-cli-shadow) then end up with byte-identical
+ * META-INF/sbom.json entries and identical urn:uuid serialNumbers,
+ * which violates the CycloneDX 1.6 specification.
+ *
+ * The fix is symmetrical with publishSbomForJarProjects: we exclude any
+ * META-INF/sbom.json that arrives from transitive dependencies during
+ * the shadow merge, then re-introduce this project's own SBOM (whose
+ * serialNumber is project-path-seeded and unique per fix(sbom): mix
+ * projectPath into deterministic UUID seed).
+ *
+ * Uses the broad Task type to avoid a compile-time dependency on
+ * com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar from
+ * build-logic; the cast is safe because ShadowJar extends Jar.
+ */
+ private static void publishSbomForShadowJarProjects(Project project, Provider<RegularFile> sbomOutputLocation) {
Review Comment:
Good call - moved the entire shadow jar SBOM wiring out of SbomPlugin and into `grails-forge/grails-cli/build.gradle` in 23f586d4f6. The generic plugin no longer knows about `com.gradleup.shadow` at all; its only responsibility is wiring `cyclonedxDirectBom` into the regular `jar` via `publishSbomForJarProjects`.
Where the logic lives now (inside the existing `shadowJarTask.configure { ShadowJar it -> ... }` block):
```groovy
TaskProvider<CyclonedxDirectTask> cyclonedxDirectBomTask = tasks.named('cyclonedxDirectBom', CyclonedxDirectTask)
shadowJarTask.configure { ShadowJar it ->
// ...existing transforms / mergeServiceFiles / excludes...
it.exclude(
// ...
'META-INF/sbom.json',
// ...
)
if (!project.findProperty('skipJavaComponent')) {
it.from(cyclonedxDirectBomTask.flatMap { CyclonedxDirectTask t -> t.jsonOutput }) { CopySpec spec ->
spec.into('META-INF')
spec.rename { 'sbom.json' }
}
it.manifest { Manifest manifest ->
manifest.attributes('Sbom-Location': 'META-INF/sbom.json')
manifest.attributes('Sbom-Format': 'CycloneDX')
}
}
}
```
Why this fits better than a hook in `SbomPlugin`:
* No more raw `Task -> Jar` cast - `grails-cli/build.gradle` already imports `com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar` and now also imports `org.cyclonedx.gradle.CyclonedxDirectTask`, so the wiring is fully type-safe at the consumer.
* `cyclonedxDirectBomTask.flatMap { it.jsonOutput }` ties the shadow jar to exactly the post-processed JSON output (rather than `from(taskProvider)`, which would copy any future task outputs verbatim).
* The `skipJavaComponent` guard is preserved to match the convention used by `publishSbomForJarProjects`.
* `:grails-cli-shadow/build.gradle` is left as-is - it already excludes `META-INF/sbom.json` directly because it does not apply `org.apache.grails.buildsrc.sbom`.
Verified on Gradle 9.4.1 with `--rerun-tasks`:
| Jar | `serialNumber` | `metadata.component.name` |
|---|---|---|
| `:grails-cli` (regular) | `urn:uuid:beabd2c0-...` | `grails-cli` |
| `:grails-cli` (`-all` FAT) | `urn:uuid:beabd2c0-...` | `grails-cli` |
| `:grails-cli-shadow` (`-all` FAT) | _(no SBOM - excluded)_ | _(no SBOM)_ |
| `:grails-forge-cli` (regular) | `urn:uuid:401e573c-...` | `grails-forge-cli` |
Distinct `serialNumber` values across distinct projects, the fat jar's `metadata.component.name` is `grails-cli` (not the leaked `grails-shell-cli` from before), and the `:grails-cli` regular and `-all` jar deliberately share a `serialNumber` because they describe the same project.
##########
build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/SbomPlugin.groovy:
##########
@@ -384,4 +396,53 @@ class SbomPlugin implements Plugin<Project> {
}
}
}
+
+ /**
+ * Wires this project's own SBOM into the shadow jar produced by the
+ * com.gradleup.shadow plugin.
+ *
+ * Without this, shadow's first-wins merge picks up a META-INF/sbom.json
+ * from one of the bundled transitive jars (typically grails-shell-cli's),
+ * giving the fat jar the wrong serialNumber and metadata.component
+ * (it ends up describing grails-shell-cli rather than the fat jar's own
+ * project). Two fat jars that both bundle grails-shell-cli (e.g.
+ * :grails-cli and :grails-cli-shadow) then end up with byte-identical
+ * META-INF/sbom.json entries and identical urn:uuid serialNumbers,
+ * which violates the CycloneDX 1.6 specification.
+ *
+ * The fix is symmetrical with publishSbomForJarProjects: we exclude any
+ * META-INF/sbom.json that arrives from transitive dependencies during
+ * the shadow merge, then re-introduce this project's own SBOM (whose
+ * serialNumber is project-path-seeded and unique per fix(sbom): mix
+ * projectPath into deterministic UUID seed).
+ *
+ * Uses the broad Task type to avoid a compile-time dependency on
Review Comment:
On the missing-compile-dependency angle: I went back through `build-logic/plugins/build.gradle` and the git history (`git log -S 'shadow' -- build-logic/`) and could not find a prior commit where build-logic had a compile dependency on `com.gradleup.shadow`. The only build-logic implementation deps that have ever been there are `grails-publish-plugin`, `org.gradle.crypto.checksum`, and `org.cyclonedx.bom`.
Either way, the new approach side-steps that question entirely: `grails-cli/build.gradle` already pulls in `com.gradleup.shadow` via its own plugins block, so the shadow types are on the buildscript classpath at the point where they are actually used, with no need to add anything to build-logic.
If a second module ever ends up needing both `com.gradleup.shadow` and `org.apache.grails.buildsrc.sbom`, the natural next step would be a small dedicated convention plugin (e.g. `org.apache.grails.buildsrc.shadow-sbom`) that explicitly depends on shadow, rather than re-adding shadow knowledge to the generic SBOM plugin. Today there is exactly one such project, so a per-project configuration is the simplest fit.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@grails.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] fix(sbom): mix projectPath into deterministic UUID seed [grails-core]
Posted by "jdaugherty (via GitHub)" <gi...@apache.org>.
jdaugherty commented on PR #15614:
URL: https://github.com/apache/grails-core/pull/15614#issuecomment-4359663564
@jamesfredley there are test failures & did you see my feedback? If we can resolve this we can move forward with the 8.0.0-M1 release
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@grails.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] fix(sbom): mix projectPath into deterministic UUID seed [grails-core]
Posted by "jamesfredley (via GitHub)" <gi...@apache.org>.
jamesfredley commented on code in PR #15614:
URL: https://github.com/apache/grails-core/pull/15614#discussion_r3176545865
##########
grails-forge/grails-cli-shadow/build.gradle:
##########
@@ -77,6 +77,12 @@ shadowJarTask.configure { ShadowJar it ->
it.exclude(
'META-INF/DEPENDENCIES', // until we publish our own SBOM, this won't be correct so exclude
+ // This module does not apply org.apache.grails.buildsrc.sbom (it's an intermediate build
+ // artifact, not published). Without this exclude, shadow's first-wins merge picks one of
Review Comment:
Done in 1144b74e8a - shortened to a single inline note on the exclude line: `'META-INF/sbom.json', // intermediate build, exclude conflicting files`
##########
grails-forge/grails-cli/build.gradle:
##########
@@ -122,8 +133,27 @@ shadowJarTask.configure { ShadowJar it ->
'META-INF/DEPENDENCIES', // until we publish our own SBOM, this won't be correct so exclude
'META-INF/grails-plugin.xml', // we do not start or compile a grails application so these files are not needed (grails-core, url mappings, etc plugins)
'META-INF/grails-plugin.xml.asc', // avoid signing artifacts
+ // Drop any incoming sbom.json that arrives via transitive jars during the shadow merge;
+ // re-introduced below from this project's own cyclonedxDirectBom output.
+ 'META-INF/sbom.json',
'about.html' // restatement of the Eclipse Distribution License - Version 1.0 for jakarta
)
+
+ // Re-introduce this project's own SBOM after the merge (mirrors the regular jar wiring done by
+ // the org.apache.grails.buildsrc.sbom plugin). Mirrored only when skipJavaComponent is unset to
+ // match the convention used elsewhere in the build for projects that opt out of jar publication.
+ if (!project.findProperty('skipJavaComponent')) {
+ it.from(cyclonedxDirectBomTask.flatMap { CyclonedxDirectTask t -> t.jsonOutput }) { CopySpec spec ->
Review Comment:
Done in 1144b74e8a - swapped to passing the task provider directly:
`it.from(cyclonedxDirectBomTask) { CopySpec spec -> spec.into('META-INF'); spec.rename { 'sbom.json' } }`
Verified `BaseCyclonedxTask` declares two `@OutputFile @Optional RegularFileProperty` outputs (`getJsonOutput` / `getXmlOutput`), but `SbomPlugin.configureSbomTask` calls `xmlOutput.unsetConvention()` and only ever sets `jsonOutput`, so at execution time `jsonOutput` is the only effective output.
(Minor nit on the original phrasing: `flatMap` on a `Provider` is lazy in the Provider API sense - it returns a `Provider<T>` that is only evaluated when needed - but you are right that since there is only one effective output here, the direct task reference is simpler and reads better. Same byte-identical SBOM as before: `urn:uuid:beabd2c0-0175-3997-bae5-bb8dd245f97b` for `:grails-cli` regular and -all jar.)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@grails.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] fix(sbom): mix projectPath into deterministic UUID seed [grails-core]
Posted by "jdaugherty (via GitHub)" <gi...@apache.org>.
jdaugherty commented on code in PR #15614:
URL: https://github.com/apache/grails-core/pull/15614#discussion_r3171812163
##########
build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/SbomPlugin.groovy:
##########
@@ -140,7 +140,10 @@ class SbomPlugin implements Plugin<Project> {
configureNormalization(project)
ensureLicensesValidated(project)
- // sboms are only published to Grails jar files at this time
+ // sboms are only published to Grails jar files at this time. Projects that produce a fat
Review Comment:
We should restore the original comment and remove this verbosity
##########
grails-forge/grails-cli-shadow/build.gradle:
##########
@@ -77,6 +77,12 @@ shadowJarTask.configure { ShadowJar it ->
it.exclude(
'META-INF/DEPENDENCIES', // until we publish our own SBOM, this won't be correct so exclude
+ // This module does not apply org.apache.grails.buildsrc.sbom (it's an intermediate build
+ // artifact, not published). Without this exclude, shadow's first-wins merge picks one of
Review Comment:
we dont need such a long comment:
this is an intermediate build, exclude conflicting filed
##########
grails-forge/grails-cli/build.gradle:
##########
@@ -122,8 +133,27 @@ shadowJarTask.configure { ShadowJar it ->
'META-INF/DEPENDENCIES', // until we publish our own SBOM, this won't be correct so exclude
'META-INF/grails-plugin.xml', // we do not start or compile a grails application so these files are not needed (grails-core, url mappings, etc plugins)
'META-INF/grails-plugin.xml.asc', // avoid signing artifacts
+ // Drop any incoming sbom.json that arrives via transitive jars during the shadow merge;
+ // re-introduced below from this project's own cyclonedxDirectBom output.
+ 'META-INF/sbom.json',
'about.html' // restatement of the Eclipse Distribution License - Version 1.0 for jakarta
)
+
+ // Re-introduce this project's own SBOM after the merge (mirrors the regular jar wiring done by
+ // the org.apache.grails.buildsrc.sbom plugin). Mirrored only when skipJavaComponent is unset to
+ // match the convention used elsewhere in the build for projects that opt out of jar publication.
+ if (!project.findProperty('skipJavaComponent')) {
+ it.from(cyclonedxDirectBomTask.flatMap { CyclonedxDirectTask t -> t.jsonOutput }) { CopySpec spec ->
Review Comment:
flatMap is non lazy; isnt the json output the only output? If so just pass the task here
##########
grails-forge/grails-cli/build.gradle:
##########
@@ -92,6 +93,16 @@ jarTask.configure { Jar it ->
}
}
+// The shadowJar merges multiple jars (including transitive ones from grails-shell-cli, grails-forge-cli,
+// etc.) into a single fat jar. Each of those source jars carries its own META-INF/sbom.json published by
+// the org.apache.grails.buildsrc.sbom convention plugin, and shadow's first-wins merge would otherwise
+// pick a transitive sbom.json (typically grails-shell-cli's) and produce a fat jar whose SBOM describes
+// the wrong module. We exclude any incoming META-INF/sbom.json during the merge and then re-introduce
+// this project's own SBOM (whose serialNumber is project-path-seeded and unique). This keeps fat-jar
+// packaging concerns local to this project rather than leaking shadow knowledge into the generic
+// org.apache.grails.buildsrc.sbom plugin. See: https://cyclonedx.org/docs/1.6/json/#serialNumber
+TaskProvider<CyclonedxDirectTask> cyclonedxDirectBomTask = tasks.named('cyclonedxDirectBom', CyclonedxDirectTask)
Review Comment:
Move this into the configure since it doesn't appear to be used outside of it
##########
grails-forge/grails-cli/build.gradle:
##########
@@ -122,8 +133,27 @@ shadowJarTask.configure { ShadowJar it ->
'META-INF/DEPENDENCIES', // until we publish our own SBOM, this won't be correct so exclude
'META-INF/grails-plugin.xml', // we do not start or compile a grails application so these files are not needed (grails-core, url mappings, etc plugins)
'META-INF/grails-plugin.xml.asc', // avoid signing artifacts
+ // Drop any incoming sbom.json that arrives via transitive jars during the shadow merge;
+ // re-introduced below from this project's own cyclonedxDirectBom output.
+ 'META-INF/sbom.json',
'about.html' // restatement of the Eclipse Distribution License - Version 1.0 for jakarta
)
+
+ // Re-introduce this project's own SBOM after the merge (mirrors the regular jar wiring done by
+ // the org.apache.grails.buildsrc.sbom plugin). Mirrored only when skipJavaComponent is unset to
+ // match the convention used elsewhere in the build for projects that opt out of jar publication.
+ if (!project.findProperty('skipJavaComponent')) {
+ it.from(cyclonedxDirectBomTask.flatMap { CyclonedxDirectTask t -> t.jsonOutput }) { CopySpec spec ->
+ spec.into('META-INF')
+ spec.rename {
+ 'sbom.json'
+ }
+ }
+ it.manifest { Manifest manifest ->
+ manifest.attributes('Sbom-Location': 'META-INF/sbom.json')
Review Comment:
Is this added by the cyclonedx plugin already? Did you check the manifest files? Do we do this anywhere else?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@grails.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] fix(sbom): mix projectPath into deterministic UUID seed [grails-core]
Posted by "jamesfredley (via GitHub)" <gi...@apache.org>.
jamesfredley commented on PR #15614:
URL: https://github.com/apache/grails-core/pull/15614#issuecomment-4363669051
@jdaugherty pushed [1144b74e8a](https://github.com/apache/grails-core/pull/15614/commits/1144b74e8acf8b0086e5241ff0ab57b8a84e055a) addressing all five threads from your review.
## Diffstat
``
build-logic/.../SbomPlugin.groovy | 5 +----
grails-forge/grails-cli-shadow/build.gradle | 7 +------
grails-forge/grails-cli/build.gradle | 21 ++++-----------------
3 files changed, 6 insertions(+), 27 deletions(-)
``
## Per-thread summary
| # | Thread | Change |
|---|---|---|
| 1 | `SbomPlugin.groovy` "restore the original comment" | Reverted to one-liner `// sboms are only published to Grails jar files at this time`. |
| 2 | `grails-cli-shadow/build.gradle` "we don't need such a long comment" | Replaced with `'META-INF/sbom.json', // intermediate build, exclude conflicting files`. |
| 3 | `grails-cli/build.gradle` "Move this into the configure" | `cyclonedxDirectBomTask` is now declared inside the `shadowJar` configure block, behind the `skipJavaComponent` guard (its only caller). |
| 4 | `grails-cli/build.gradle` "flatMap is non lazy; isnt the json output the only output?" | Switched to `it.from(cyclonedxDirectBomTask)`. `BaseCyclonedxTask` does declare two `@OutputFile` properties (`jsonOutput` / `xmlOutput`) but `SbomPlugin` calls `xmlOutput.unsetConvention()` and only ever sets `jsonOutput`, so it is the only effective output. |
| 5 | `grails-cli/build.gradle` "Is this added by the cyclonedx plugin already? Did you check the manifest files? Do we do this anywhere else?" | (a) No, cyclonedx-gradle-plugin does not touch `MANIFEST.MF`. (b) Verified `Sbom-Location` / `Sbom-Format` appear in both `grails-cli-...jar` and `grails-cli-...-all.jar` after the change. (c) Yes, `SbomPlugin.publishSbomForJarProjects` does the same on the regular jar, so the shadow path mirrors that for downstream SBOM discovery. Manifest block kept. |
## Verification (Gradle 9.4.1, `--rerun-tasks` x2)
| Jar | `serialNumber` | `component.name` |
|---|---|---|
| `:grails-cli` regular jar | `urn:uuid:beabd2c0-0175-3997-bae5-bb8dd245f97b` | `grails-cli` |
| `:grails-cli` `-all` fat jar | `urn:uuid:beabd2c0-0175-3997-bae5-bb8dd245f97b` | `grails-cli` |
| `:grails-cli-shadow` `-all` fat jar | _(no SBOM, excluded as designed)_ | _(no SBOM)_ |
| `:grails-bootstrap` regular jar | `urn:uuid:3fe6e19d-48e4-3097-810e-1b504da46be6` | `grails-bootstrap` |
* `:grails-bootstrap` matches byte-for-byte the value in the original PR description, confirming end-to-end reproducibility across the full build is preserved.
* Same `serialNumber` between the regular and `-all` jars of `:grails-cli` is by design (same project's SBOM); no collisions across distinct projects.
* Shadow fat jar manifest contains `Sbom-Location: META-INF/sbom.json` and `Sbom-Format: CycloneDX`.
## On the test failures you flagged
The three failing checks on the previous head (`23f586d4f6`) are infrastructure-only, not caused by this PR:
* `Build Grails-Core (macos-latest, 21)` failed with `java.net.UnknownHostException: repository.apache.org: nodename nor servname provided, or not known` while resolving `grails-spring-security-rest-8.0.0-SNAPSHOT.jar` - GitHub Actions DNS hiccup against `repository.apache.org`.
* `Build Grails-Core Rerunning all Tasks (ubuntu-latest, 21)` and `build_grails` show no test assertion failures in their logs - they timed out after `39m` / `55m` waiting on the same snapshot artifacts.
The Linux 21/25 build, Windows 25, both Forge variants, all three Functional Tests matrices, both Hibernate5 matrices, all five MongoDB matrices, plus TestLens (`19783 executed` / `35/35 checks`) all passed on the previous commit, so the regression surface is empty. CI on `1144b74e8a` should run cleanly once the snapshot DNS recovers.
Ready for re-review.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@grails.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] fix(sbom): mix projectPath into deterministic UUID seed [grails-core]
Posted by "jamesfredley (via GitHub)" <gi...@apache.org>.
jamesfredley commented on code in PR #15614:
URL: https://github.com/apache/grails-core/pull/15614#discussion_r3176546101
##########
grails-forge/grails-cli/build.gradle:
##########
@@ -122,8 +133,27 @@ shadowJarTask.configure { ShadowJar it ->
'META-INF/DEPENDENCIES', // until we publish our own SBOM, this won't be correct so exclude
'META-INF/grails-plugin.xml', // we do not start or compile a grails application so these files are not needed (grails-core, url mappings, etc plugins)
'META-INF/grails-plugin.xml.asc', // avoid signing artifacts
+ // Drop any incoming sbom.json that arrives via transitive jars during the shadow merge;
+ // re-introduced below from this project's own cyclonedxDirectBom output.
+ 'META-INF/sbom.json',
'about.html' // restatement of the Eclipse Distribution License - Version 1.0 for jakarta
)
+
+ // Re-introduce this project's own SBOM after the merge (mirrors the regular jar wiring done by
+ // the org.apache.grails.buildsrc.sbom plugin). Mirrored only when skipJavaComponent is unset to
+ // match the convention used elsewhere in the build for projects that opt out of jar publication.
+ if (!project.findProperty('skipJavaComponent')) {
+ it.from(cyclonedxDirectBomTask.flatMap { CyclonedxDirectTask t -> t.jsonOutput }) { CopySpec spec ->
+ spec.into('META-INF')
+ spec.rename {
+ 'sbom.json'
+ }
+ }
+ it.manifest { Manifest manifest ->
+ manifest.attributes('Sbom-Location': 'META-INF/sbom.json')
Review Comment:
I checked all three:
1. **Is this added by the cyclonedx plugin already?** No. Searched the cyclonedx-gradle-plugin source (3.0.x and master) for `Sbom-Location` / `Sbom-Format` and there are zero hits - the plugin does not touch the jar manifest at all.
2. **Did you check the manifest files?** Yes. Extracted `META-INF/MANIFEST.MF` from `grails-cli-8.0.0-SNAPSHOT.jar` (regular) and `-all.jar` (fat) after `--rerun-tasks`:
- `Sbom-Location: META-INF/sbom.json`
- `Sbom-Format: CycloneDX`
Without the explicit `manifest { ... }` block on the shadowJar configure, those entries vanish from the fat jar (shadow merges manifests but does not regenerate the SBOM-pointing entries).
3. **Do we do this anywhere else?** Yes - `SbomPlugin.publishSbomForJarProjects` adds the same two attributes to the regular jar in the same module (lines 389-392). The shadow-jar wiring intentionally mirrors that to keep both jars discoverable by downstream SBOM consumers via the standard manifest header.
Left the manifest block in place; the entries are validated to appear in the output.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@grails.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] fix(sbom): mix projectPath into deterministic UUID seed [grails-core]
Posted by "jamesfredley (via GitHub)" <gi...@apache.org>.
jamesfredley commented on code in PR #15614:
URL: https://github.com/apache/grails-core/pull/15614#discussion_r3176545492
##########
build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/SbomPlugin.groovy:
##########
@@ -140,7 +140,10 @@ class SbomPlugin implements Plugin<Project> {
configureNormalization(project)
ensureLicensesValidated(project)
- // sboms are only published to Grails jar files at this time
+ // sboms are only published to Grails jar files at this time. Projects that produce a fat
Review Comment:
Done in 1144b74e8a - restored the original one-line // sboms are only published to Grails jar files at this time comment. Now that the shadow-jar wiring lives entirely in grails-cli/build.gradle, the verbose explanation in the generic plugin is unnecessary.
##########
grails-forge/grails-cli/build.gradle:
##########
@@ -92,6 +93,16 @@ jarTask.configure { Jar it ->
}
}
+// The shadowJar merges multiple jars (including transitive ones from grails-shell-cli, grails-forge-cli,
+// etc.) into a single fat jar. Each of those source jars carries its own META-INF/sbom.json published by
+// the org.apache.grails.buildsrc.sbom convention plugin, and shadow's first-wins merge would otherwise
+// pick a transitive sbom.json (typically grails-shell-cli's) and produce a fat jar whose SBOM describes
+// the wrong module. We exclude any incoming META-INF/sbom.json during the merge and then re-introduce
+// this project's own SBOM (whose serialNumber is project-path-seeded and unique). This keeps fat-jar
+// packaging concerns local to this project rather than leaking shadow knowledge into the generic
+// org.apache.grails.buildsrc.sbom plugin. See: https://cyclonedx.org/docs/1.6/json/#serialNumber
+TaskProvider<CyclonedxDirectTask> cyclonedxDirectBomTask = tasks.named('cyclonedxDirectBom', CyclonedxDirectTask)
Review Comment:
Done in 1144b74e8a - moved the cyclonedxDirectBomTask declaration inside the shadowJarTask.configure { ... } block (and inside the skipJavaComponent guard, since that is its only caller).
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@grails.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] fix(sbom): mix projectPath into deterministic UUID seed [grails-core]
Posted by "testlens-app[bot] (via GitHub)" <gi...@apache.org>.
testlens-app[bot] commented on PR #15614:
URL: https://github.com/apache/grails-core/pull/15614#issuecomment-4356870744
## ✅ All tests passed ✅
🏷️ Commit: 462b2f8d45de729f345086de8ccc4115e0831665
▶️ Tests: 34448 executed
⚪️ Checks: 35/35 completed
---
_Learn more about TestLens at [testlens.app](https://testlens.app)._
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@grails.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] fix(sbom): mix projectPath into deterministic UUID seed [grails-core]
Posted by "testlens-app[bot] (via GitHub)" <gi...@apache.org>.
testlens-app[bot] commented on PR #15614:
URL: https://github.com/apache/grails-core/pull/15614#issuecomment-4357353308
## ✅ All tests passed ✅
🏷️ Commit: 4d1a78f1e2b77329dc2b0b354bb5dd46d8354d99
▶️ Tests: 24910 executed
⚪️ Checks: 35/35 completed
---
_Learn more about TestLens at [testlens.app](https://testlens.app)._
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@grails.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] fix(sbom): mix projectPath into deterministic UUID seed [grails-core]
Posted by "jdaugherty (via GitHub)" <gi...@apache.org>.
jdaugherty commented on PR #15614:
URL: https://github.com/apache/grails-core/pull/15614#issuecomment-4360056164
@jamesfredley it will be late before I can proceed. If I get time, will make a pass at this
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@grails.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org