This is an automated email from the ASF dual-hosted git repository.

epugh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr-mcp.git


The following commit(s) were added to refs/heads/main by this push:
     new c800f9d3 refactor(build): read the SBOM with the CycloneDX model 
instead of JsonSlurper (#247)
c800f9d3 is described below

commit c800f9d3f2e9b6ca8a7eb060eb0f231bc898fefe
Author: Aditya Parikh <[email protected]>
AuthorDate: Tue Oct 6 14:28:20 2026 -0400

    refactor(build): read the SBOM with the CycloneDX model instead of 
JsonSlurper (#247)
    
    GenerateBinaryLicense parses the CycloneDX SBOM with cyclonedx-core-java's
    JsonParser and typed Bom/Component/License model rather than walking
    untyped maps from Groovy's JsonSlurper, which removes the unchecked casts.
    
    cyclonedx-core-java 10.2.1 is the version the org.cyclonedx.bom 2.4.1
    plugin (which writes the SBOM) already uses. It is a buildSrc-only
    dependency and does not ship. Output is unchanged: the generated LICENSE
    is byte-identical.
    
    Signed-off-by: Aditya Parikh <[email protected]>
    Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
---
 buildSrc/build.gradle.kts                          |  4 +++
 .../apache/solr/mcp/build/GenerateBinaryLicense.kt | 39 ++++++++--------------
 2 files changed, 17 insertions(+), 26 deletions(-)

diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
index d1296d79..5bccab6a 100644
--- a/buildSrc/build.gradle.kts
+++ b/buildSrc/build.gradle.kts
@@ -36,6 +36,10 @@ dependencies {
     // the plugin's upstream releases.
     
implementation("org.nosphere.apache.rat:org.nosphere.apache.rat.gradle.plugin:0.8.1")
 
+    // Typed reader for the CycloneDX SBOM. Pinned to the version the 
org.cyclonedx.bom plugin
+    // (which writes the SBOM) already uses, so reader and writer share one 
model.
+    implementation("org.cyclonedx:cyclonedx-core-java:10.2.1")
+
     // Only used by the task unit tests under src/test (the main code needs no 
extra deps;
     // the Gradle API is provided by the kotlin-dsl plugin).
     testImplementation("org.junit.jupiter:junit-jupiter:5.12.2")
diff --git 
a/buildSrc/src/main/kotlin/org/apache/solr/mcp/build/GenerateBinaryLicense.kt 
b/buildSrc/src/main/kotlin/org/apache/solr/mcp/build/GenerateBinaryLicense.kt
index 077bbb37..3da0c157 100644
--- 
a/buildSrc/src/main/kotlin/org/apache/solr/mcp/build/GenerateBinaryLicense.kt
+++ 
b/buildSrc/src/main/kotlin/org/apache/solr/mcp/build/GenerateBinaryLicense.kt
@@ -16,7 +16,8 @@
  */
 package org.apache.solr.mcp.build
 
-import groovy.json.JsonSlurper
+import org.cyclonedx.model.Component
+import org.cyclonedx.parsers.JsonParser
 import org.gradle.api.DefaultTask
 import org.gradle.api.GradleException
 import org.gradle.api.file.RegularFileProperty
@@ -90,24 +91,18 @@ abstract class GenerateBinaryLicense : DefaultTask() {
     /** Gradle runs this method when the task executes (`@TaskAction`). */
     @TaskAction
     fun generate() {
-        val slurper = JsonSlurper()
-
         // 1. Index every SBOM component's licenses by "group:name" and 
"group:name:version".
         //    The version-keyed map is preferred so the exact shipped version 
wins; the
         //    coarser key is the fallback when versions differ between SBOM 
and classpath.
-        @Suppress("UNCHECKED_CAST")
-        val sbomJson = slurper.parse(sbom.get().asFile) as Map<String, Any?>
-
-        @Suppress("UNCHECKED_CAST")
-        val components = (sbomJson["components"] as? List<Map<String, 
Any?>>).orEmpty()
+        val components = 
JsonParser().parse(sbom.get().asFile).components.orEmpty()
         val byGroupArtifact = HashMap<String, List<License>>()
         val byGroupArtifactVersion = HashMap<String, List<License>>()
         for (component in components) {
-            val group = component["group"] as? String ?: continue
-            val name = component["name"] as? String ?: continue
+            val group = component.group ?: continue
+            val name = component.name ?: continue
             val licenses = licensesOf(component)
             byGroupArtifact["$group:$name"] = licenses
-            (component["version"] as? String)?.let { 
byGroupArtifactVersion["$group:$name:$it"] = licenses }
+            component.version?.let { 
byGroupArtifactVersion["$group:$name:$it"] = licenses }
         }
 
         // 2. For each dependency that actually ships, look up its license(s) 
in the SBOM and
@@ -166,23 +161,15 @@ abstract class GenerateBinaryLicense : DefaultTask() {
     }
 
     /** Distinct (label, url?) licenses of an SBOM component; prefers SPDX id, 
else name/expression. */
-    private fun licensesOf(component: Map<String, Any?>): List<License> {
+    private fun licensesOf(component: Component): List<License> {
+        val choice = component.licenses ?: return emptyList()
         val out = LinkedHashMap<String, String?>()
-
-        @Suppress("UNCHECKED_CAST")
-        val nodes = component["licenses"] as? List<Map<String, Any?>> ?: 
return emptyList()
-        for (node in nodes) {
-            @Suppress("UNCHECKED_CAST")
-            val license = node["license"] as? Map<String, Any?>
-            if (license != null) {
-                val id = license["id"] as? String
-                val label = id ?: (license["name"] as? String) ?: "Unspecified"
-                val url = (license["url"] as? String) ?: id?.let { spdxUrl(it) 
}
-                out.putIfAbsent(label, url)
-            } else {
-                (node["expression"] as? String)?.let { out.putIfAbsent(it, 
null) }
-            }
+        for (license in choice.licenses.orEmpty()) {
+            val id = license.id
+            val label = id ?: license.name ?: "Unspecified"
+            out.putIfAbsent(label, license.url ?: id?.let { spdxUrl(it) })
         }
+        choice.expression?.value?.let { out.putIfAbsent(it, null) }
         return out.map { License(it.key, it.value) }
     }
 

Reply via email to