Skip to content

feat(build): generate the IP-clearance license row from the SBOM - #245

Closed
adityamparikh wants to merge 1 commit into
apache:mainfrom
adityamparikh:feat/ip-clearance-license-row
Closed

adityamparikh wants to merge 1 commit into
apache:mainfrom
adityamparikh:feat/ip-clearance-license-row

Conversation

@adityamparikh

@adityamparikh adityamparikh commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Generates the "all items depended upon by the project are covered by approved licenses" row of the Incubator IP-clearance status document from the CycloneDX SBOM, and documents how and where it shows up. The row has to be refreshed for every release, so it is built, not hand-written.

  • generateIpClearanceLicenseReport (new) writes build/generated/license/ip-clearance-licenses.xml: a single <tr> in the format used by Incubator IP clearance documents (completion date, the standard checklist wording, a <ul> of group:artifact — license for every bundled dependency). It uses the same inputs as generateBinaryLicense: the shipped productionRuntimeClasspath and the SBOM. A dependency missing from the SBOM fails the task.
  • generateLicenseDocs (new aggregate) runs generateBinaryLicense, generateBinaryNotice and the IP-clearance task, so all license documents land in build/generated/license/. It is part of check, so a plain ./gradlew build produces all three.
  • CI (build-and-publish.yml): after ./gradlew build, uploads build/generated/license/ as the solr-mcp-license-docs artifact (30 days), next to the SBOM artifact.
  • Docs: a table of the generated files and their Gradle tasks and a new "Incubator IP-Clearance Row" section on the Licensing & Notices page (docs/site/.../licensing.md) covering how to generate it, where it shows up locally and in CI, what it contains and how to use it; plus AGENTS.md and buildSrc/README.md.
  • The SBOM license lookup is extracted into a shared SbomLicenses helper used by both license tasks.

What the build generates

File Format Ships in the release? Audience and purpose How it is generated Gradle task
LICENSE Plain text Yes — META-INF/LICENSE in the executable JAR and the Docker images Users and ASF release policy: the legally required binary-form license The base Apache-2.0 LICENSE plus an appendix listing every bundled dependency (version, license, link), read from the CycloneDX SBOM and filtered to the shipped productionRuntimeClasspath generateBinaryLicense
NOTICE Plain text Yes — META-INF/NOTICE in the executable JAR and the Docker images Same legal audience: the attribution notices the bundled dependencies require The base NOTICE plus the META-INF/NOTICE files lifted verbatim (de-duplicated) from the bundled jars generateBinaryNotice
ip-clearance-licenses.xml XML <tr> row No — build output only The Incubator: pasted into the "all items depended upon are covered by approved licenses" row of the IP-clearance status document Rendered from the same SBOM and shipped classpath: completion date, the checklist wording and a <ul> of group:artifact — license (no versions) generateIpClearanceLicenseReport
application.cdx.json (the SBOM) CycloneDX JSON Yes — META-INF/sbom/application.cdx.json, served at /actuator/sbom/application in HTTP mode The machine-readable source the other three are derived from Generated by the CycloneDX Gradle plugin from the resolved dependency graph cyclonedxBom

LICENSE, NOTICE and ip-clearance-licenses.xml are written to build/generated/license/; the SBOM is written to build/reports/. generateLicenseDocs runs the first three tasks, and check (and so build) depends on it. The LICENSE and IP-clearance tasks depend on cyclonedxBom because they read the SBOM; the NOTICE task reads the bundled jars instead.

Design notes

  • Licenses are reported exactly as the SBOM reports them, with no allow-list and no Category A/B judgement, consistent with the existing "disclose, don't judge" LICENSE task. Confirming that every license is acceptable (and adding the closing sign-off sentence) is a human step, as documented.
  • The IP-clearance row shares the SBOM completeness gate with generateBinaryLicense, so wiring it into check adds no new failure condition.

Testing

  • ./gradlew -p buildSrc test — 14 tests pass, including a well-formed-XML check on the generated row.
  • ./gradlew build (JDK 25) — 421 tests, 0 failed, 0 skipped; leaves LICENSE, NOTICE and a well-formed ip-clearance-licenses.xml listing 157 dependencies in build/generated/license/.
  • ./gradlew spotlessCheck passes.

For review

Two SBOM labels look imprecise and should be checked upstream before any "Category A/B" sign-off: org.antlr:antlr-runtime / ST4 are labelled "BSD licence" / BSD-4-Clause (believed to be BSD-3-Clause), and org.springaicommunity:mcp-server-security is labelled "Apache-1.0" (believed to be Apache-2.0).

@epugh, please review.

🤖 Generated with Claude Code

@adityamparikh
adityamparikh force-pushed the feat/ip-clearance-license-row branch 2 times, most recently from b435f1e to 703f634 Compare October 4, 2026 22:09
Add generateIpClearanceLicenseReport, which writes
build/generated/license/ip-clearance-licenses.xml: the "all items depended
upon by the project are covered by approved licenses" row of the Incubator
IP-clearance status document, listing group:artifact and license for every
bundled dependency as reported by the CycloneDX SBOM. A dependency missing
from the SBOM fails the task, the same completeness gate the binary LICENSE
uses.

Add the generateLicenseDocs aggregate (LICENSE, NOTICE and the IP-clearance
row) and make check depend on it, so a plain ./gradlew build leaves all three
in build/generated/license/. CI uploads that directory as the
solr-mcp-license-docs artifact. Extract the SBOM license lookup into a shared
SbomLicenses helper used by both license tasks.

Document the row on the Licensing & Notices page, in AGENTS.md and in
buildSrc/README.md, including a table of the generated files with the Gradle
task that produces each.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Aditya Parikh <aditya.m.parikh@gmail.com>
@adityamparikh

Copy link
Copy Markdown
Contributor Author

Closing: IP clearance is a one-time job, so the row doesn't need a build task, CI artifact or docs. I generated the file once for the IP-clearance form (#216). The CycloneDX-model SBOM reader from this stack lives on in #247, which is now standalone against main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant