Skip to content

Provide a stable task DSL independent of CycloneDX Core in 4.0 #883

Description

@skhokhlov

User problem

Several task properties currently expose cyclonedx-core-java model and enum types directly. This makes ordinary
plugin configuration depend on the implementation library's API and turns core upgrades into migration work for
plugin users.

Users need a configuration surface that is natural in both Groovy and Kotlin, remains stable when CycloneDX Core
evolves, and offers a gradual migration from existing 3.x builds.

Desired outcome

CycloneDX Gradle Plugin 4.0 provides a plugin-owned, Gradle-native DSL directly on the Direct and Aggregate SBOM tasks.
Users can configure the same effective metadata available through the existing core-backed properties without
constructing CycloneDX Core objects.

The 4.0 experience should have these properties:

  • The canonical DSL uses plugin-owned terms and types, with validated string aliases where they improve Groovy and
    Kotlin usability.
  • It has capability parity with every effective field reachable through the five existing core-backed properties,
    rather than attempting to mirror the complete CycloneDX object model.
  • Existing 3.x configurations using schemaVersion, projectType, organizationalEntity, licenseChoice, and
    externalReferences remain functional throughout 4.x as a deprecated compatibility facade. Removal is no earlier
    than 5.0.
  • Canonical and legacy configuration describe one result: equivalent duplicate values are accepted and conflicts fail
    with actionable diagnostics rather than silent precedence.
  • Migration guidance is mechanical and example-driven for both Groovy and Kotlin. Detectable legacy use produces at
    most one migration warning per build.
  • The canonical DSL does not expose a raw CycloneDX Core escape hatch.
  • CycloneDX 1.7 becomes the default SBOM schema in 4.0 after support is delivered by Add support for CycloneDX v1.7 schema #832. Explicitly selecting 1.6
    remains supported for users who need the previous output contract.

Apart from explicitly documented 4.0 output-contract changes, equivalent legacy and canonical configuration must
produce equivalent parsed CycloneDX meaning. Serializer formatting and irrelevant collection ordering are not
compatibility guarantees.

Proposed DSL shape

The following examples preserve the direction discussed during planning. They are a reference for the task-local
hierarchy and migration experience, not a commitment to every identifier before Groovy and Kotlin compilation
fixtures validate the final API.

Kotlin, using plugin-owned typed values:

tasks.cyclonedxDirectBom {
    bom {
        schemaVersion(SchemaVersion.VERSION_17)
        projectType(ProjectType.APPLICATION)

        organization {
            name = "Example"
            url("https://example.com")
            contact {
                name = "Security"
                email = "security@example.com"
            }
        }

        licenses {
            license {
                id = "Apache-2.0"
            }
        }

        externalReference {
            type = ExternalReferenceType.WEBSITE
            url = "https://example.com/project"
        }
    }
}

Groovy, demonstrating the permanent normalized string aliases:

tasks.named("cyclonedxDirectBom") {
    bom {
        schemaVersion "1.7"
        projectType "application"

        organization {
            name = "Example"
            url "https://example.com"
            contact {
                name = "Security"
                email = "security@example.com"
            }
        }

        licenses {
            license {
                id = "Apache-2.0"
            }
        }

        externalReference {
            type = "website"
            url = "https://example.com/project"
        }
    }
}

The same bom block is available on cyclonedxBom. It owns schema selection, the main component, and Metadata
Enrichment only. Dependency selection, project discovery, aggregation membership, outputs, and root-to-member
relationships remain outside this block.

Architecture outcome

  • Configuration remains task-local, as recorded in
    ADR 0003;
    no shared root-owned plugin extension is introduced.
  • Gradle-managed DSL values normalize into a plugin-owned internal configuration model. CycloneDX Core types exist
    only at the compatibility boundary and in a one-way mapping layer used to build the SBOM.
  • The canonical DSL and resolved configuration stay independent of project discovery, aggregation membership,
    outgoing variants, and root-to-member dependency-edge policy.
  • The design preserves the per-project, variant-based seams required by explicit aggregation in Migrate multi-project aggregation to an explicit aggregator setup #848 and Project
    Isolation in Support Gradle Project Isolation #847. It neither implements nor competes with those issues.
  • A shared service may deduplicate a migration diagnostic only. It must not become a mutable registry of projects,
    task state, artifacts, aggregation members, or dependency edges.
  • SBOM compatibility follows
    ADR 0004.
  • Java 8 and 11 and the Gradle 8.4 minimum remain supported throughout 4.x.

Acceptance outcomes

  • Documented Groovy and Kotlin examples demonstrate the canonical task-local DSL and the minimal migration from each
    legacy property.
  • Canonical configuration covers the effective legacy capability surface, including capabilities available with
    CycloneDX Core 13 and schema 1.7.
  • Existing legacy Groovy and Kotlin configuration fixtures continue to compile and generate valid SBOMs in 4.x.
  • Equivalent legacy and canonical configurations are verified through parsed JSON and XML across supported schema
    selectors.
  • The default-schema change to 1.7 and the one-line opt-back to 1.6 are called out in the 4.0 migration guide.
  • Public canonical signatures contain no CycloneDX Core types or Gradle project-topology types.
  • Configuration-cache and task-cache behavior account for every effective nested value.

Out of scope

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions