Summary
Move from the current "apply at root, implicitly aggregate all subprojects" model to an explicit, opt-in aggregator model modeled on Gradle's test-report-aggregation / jacoco-report-aggregation plugins. This is the concrete refactor that unblocks Project Isolation (#847) and is a breaking change warranting a major version bump.
Current model
Today the plugin is applied once at the root. The root discovers all subprojects, configures a Direct BOM task on each, and the aggregate task implicitly includes every subproject. Membership is automatic and the wiring is centralized on the root.
Target model
- Per-project application. Each project that should contribute to an aggregate applies the plugin itself and publishes its own Direct BOM as a consumable variant.
- Explicit aggregation membership. The aggregating project declares which projects to include, e.g.
dependencies { cyclonedxAggregation project(":app-a") }, mirroring jacocoAggregation / testReportAggregation. The aggregator resolves these members and merges their BOMs. No auto-discovery, no silent inclusion.
- Loud failure. If a declared member is not wired correctly, or its BOM is missing or unparseable, the aggregate task fails — it does not silently drop the member. Silent under-reporting of an SBOM (a security-consumed, public-contract artifact) is worse than a hard failure.
What changes for users
- Applying the plugin only at the root no longer aggregates subprojects. Each contributing project must apply the plugin, and the aggregator must declare its members. This is the deliberate, documented break.
- The "aggregate the whole tree with zero per-project wiring" experience moves to a user-side concern: a CI init script can apply the plugin across all projects and declare every subproject as a member generically. That script uses cross-project iteration and therefore runs only with Project Isolation disabled, which is acceptable for that use case.
Why not keep it implicit
Implicit, root-driven membership requires the root to read its subprojects at configuration time — the exact cross-project access Project Isolation forbids. Explicit membership is the only model that is both Isolation-safe and configuration-cache-safe, and it makes the contents of an aggregate BOM auditable rather than magical.
Work items
Related
Summary
Move from the current "apply at root, implicitly aggregate all subprojects" model to an explicit, opt-in aggregator model modeled on Gradle's
test-report-aggregation/jacoco-report-aggregationplugins. This is the concrete refactor that unblocks Project Isolation (#847) and is a breaking change warranting a major version bump.Current model
Today the plugin is applied once at the root. The root discovers all subprojects, configures a Direct BOM task on each, and the aggregate task implicitly includes every subproject. Membership is automatic and the wiring is centralized on the root.
Target model
dependencies { cyclonedxAggregation project(":app-a") }, mirroringjacocoAggregation/testReportAggregation. The aggregator resolves these members and merges their BOMs. No auto-discovery, no silent inclusion.What changes for users
Why not keep it implicit
Implicit, root-driven membership requires the root to read its subprojects at configuration time — the exact cross-project access Project Isolation forbids. Explicit membership is the only model that is both Isolation-safe and configuration-cache-safe, and it makes the contents of an aggregate BOM auditable rather than magical.
Work items
cyclonedxAggregationconfiguration and member-declaration API.Related