Skip to content

Commit b657987

Browse files
committed
WIP: Add CycloneDX SBOM
* Java: Uses cyclonedx-gradle-plugin * Python: Uses cyclonedx-bom * Not as rich as the SBOM for Java :(
1 parent 98d34ee commit b657987

File tree

10 files changed

+518
-7
lines changed

10 files changed

+518
-7
lines changed

Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,14 @@ client-license-check: client-setup-env ## Run license compliance check
168168
@$(ACTIVATE_AND_CD) && pip-licenses
169169
@echo "--- License compliance check complete ---"
170170

171+
.PHONY: client-license-check
172+
client-sbom: client-setup-env ## Generate SBOM
173+
@echo "--- Starting SBOM gernation ---"
174+
@$(ACTIVATE_AND_CD) && mkdir -p dist; \
175+
cyclonedx-py poetry --only main --output-reproducible --validate --output-format JSON --output-file dist/bom.json --verbose; \
176+
cyclonedx-py poetry --only main --output-reproducible --validate --output-format XML --output-file dist/bom.xml --verbose
177+
@echo "--- SBOM gernation complete ---"
178+
171179
.PHONY: client-build
172180
client-build: client-setup-env ## Build client distribution. Pass FORMAT=sdist or FORMAT=wheel to build a specific format.
173181
@echo "--- Building client distribution ---"

build-logic/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ plugins { `kotlin-dsl` }
2121

2222
dependencies {
2323
implementation(gradleKotlinDsl())
24+
implementation(baselibs.cyclonedx)
2425
implementation(baselibs.errorprone)
2526
implementation(baselibs.idea.ext)
2627
implementation(baselibs.jandex)
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
import java.util.Base64
21+
import org.cyclonedx.gradle.CyclonedxDirectTask
22+
import org.cyclonedx.model.AttachmentText
23+
import org.cyclonedx.model.Component.Type.APPLICATION
24+
import org.cyclonedx.model.License
25+
import org.cyclonedx.model.LicenseChoice
26+
import org.cyclonedx.model.Property
27+
import sbom.CyclonedxBundleTask
28+
import sbom.createCyclonedxConfigurations
29+
30+
val cyclonedxApplicationBomTask =
31+
tasks.register<CyclonedxBundleTask>("cyclonedxApplicationBom") {
32+
group = "publishing"
33+
description = "Generate CycloneDX SBOMs for this as an application"
34+
35+
val cyclonedxDirectBom = tasks.named<CyclonedxDirectTask>("cyclonedxDirectBom")
36+
inputBoms.from(cyclonedxDirectBom.map { it.jsonOutput })
37+
38+
// want to include the original license text in the generated SBOM as it may include variations
39+
// from the "standard" license text
40+
includeLicenseText = true
41+
42+
projectType = APPLICATION
43+
44+
// Needed for projects that use subdirectories in their build/ directory, like the Spark plugin
45+
jsonOutput.set(project.layout.buildDirectory.file("reports/cyclonedx-app/bom.json"))
46+
xmlOutput.set(project.layout.buildDirectory.file("reports/cyclonedx-app/bom.xml"))
47+
48+
val relativeProjectDir = project.projectDir.relativeTo(project.rootProject.projectDir)
49+
val gitInfo = GitInfo.memoized(project)
50+
51+
licenseChoice.set(
52+
LicenseChoice().apply {
53+
addLicense(
54+
License().apply {
55+
val gitCommit = GitInfo.memoized(project).gitHead
56+
id = "Apache-2.0"
57+
// TODO URL or text ??
58+
url = gitInfo.rawGithubLink("$relativeProjectDir/distribution/LICENSE")
59+
setLicenseText(
60+
AttachmentText().apply() {
61+
contentType = "plain/text"
62+
encoding = "base64"
63+
text =
64+
Base64.getEncoder()
65+
.encodeToString(project.file("distribution/LICENSE").readBytes())
66+
}
67+
)
68+
69+
// TODO Is there a better way to include NOTICE + DISCLAIMER in a CycloneDX SBOM?
70+
val props = mutableListOf<Property>()
71+
props.add(
72+
Property().apply {
73+
name = "NOTICE"
74+
value =
75+
project.file("distribution/NOTICE").readText(Charsets.UTF_8).replace("\n", "\\n")
76+
}
77+
)
78+
val disclaimerFile = project.file("distribution/DISCLAIMER")
79+
if (disclaimerFile.isFile) {
80+
props.add(
81+
Property().apply {
82+
name = "DISCLAIMER"
83+
value = disclaimerFile.readText(Charsets.UTF_8).replace("\n", "\\n")
84+
}
85+
)
86+
}
87+
properties = props
88+
}
89+
)
90+
}
91+
)
92+
}
93+
94+
createCyclonedxConfigurations(project, cyclonedxApplicationBomTask)

build-logic/src/main/kotlin/publishing/PublishingHelperPlugin.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import org.gradle.kotlin.dsl.registering
3838
import org.gradle.kotlin.dsl.withType
3939
import org.gradle.plugins.signing.SigningExtension
4040
import org.gradle.plugins.signing.SigningPlugin
41+
import sbom.configureCycloneDx
4142

4243
/**
4344
* Release-publishing helper plugin to generate publications that pass Sonatype validations,
@@ -95,6 +96,7 @@ constructor(private val softwareComponentFactory: SoftwareComponentFactory) : Pl
9596

9697
apply(plugin = "maven-publish")
9798
apply(plugin = "signing")
99+
apply(plugin = "org.cyclonedx.bom")
98100

99101
// Generate a source tarball for a release to be uploaded to
100102
// https://dist.apache.org/repos/dist/dev/<name>/apache-<name>-<version-with-rc>/
@@ -176,5 +178,7 @@ constructor(private val softwareComponentFactory: SoftwareComponentFactory) : Pl
176178
}
177179
}
178180
}
181+
182+
configureCycloneDx()
179183
}
180184
}

0 commit comments

Comments
 (0)