Browse Source

Raise the minimum supported version of the CycloneDX plugin to 3.0.0

Closes gh-47250
pull/47740/head
Andy Wilkinson 2 months ago
parent
commit
b7589a1471
  1. 6
      build-plugin/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/pages/reacting.adoc
  2. 62
      build-plugin/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/CyclonedxPluginAction.java
  3. 2
      build-plugin/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java
  4. 9
      build-plugin/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/CyclonedxPluginActionIntegrationTests.java
  5. 22
      build-plugin/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/PluginClasspathGradleBuild.java
  6. 2
      documentation/spring-boot-docs/src/docs/antora/modules/how-to/pages/build.adoc
  7. 2
      platform/spring-boot-internal-dependencies/build.gradle

6
build-plugin/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/pages/reacting.adoc

@ -97,6 +97,10 @@ When the {url-native-build-tools-docs-gradle-plugin}[GraalVM Native Image plugin @@ -97,6 +97,10 @@ When the {url-native-build-tools-docs-gradle-plugin}[GraalVM Native Image plugin
When the {url-cyclonedx-docs-gradle-plugin}[CycloneDX plugin] is applied to a project, the Spring Boot plugin:
. Configures the `cyclonedxBom` task to use the `application` project type and output the SBOM to the `application.cdx` file in JSON format without full license texts.
. Configures the `cyclonedxBom` task to:
.. Use the `application` project type.
.. Output a JSON-format SBOM to the `application.cdx.json` file.
.. Disable the XML-format SBOM.
.. Disable full license texts.
. Adds the SBOM under `META-INF/sbom` in the generated jar or war file.
. Adds the `Sbom-Format` and `Sbom-Location` to the manifest of the jar or war file.

62
build-plugin/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/CycloneDxPluginAction.java → build-plugin/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/CyclonedxPluginAction.java

@ -16,8 +16,9 @@ @@ -16,8 +16,9 @@
package org.springframework.boot.gradle.plugin;
import org.cyclonedx.gradle.CycloneDxPlugin;
import org.cyclonedx.gradle.CycloneDxTask;
import org.cyclonedx.gradle.CyclonedxAggregateTask;
import org.cyclonedx.gradle.CyclonedxPlugin;
import org.cyclonedx.model.Component;
import org.gradle.api.Action;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
@ -34,78 +35,83 @@ import org.springframework.boot.gradle.tasks.bundling.BootJar; @@ -34,78 +35,83 @@ import org.springframework.boot.gradle.tasks.bundling.BootJar;
import org.springframework.boot.gradle.tasks.bundling.BootWar;
/**
* {@link Action} that is executed in response to the {@link CycloneDxPlugin} being
* {@link Action} that is executed in response to the {@link CyclonedxPlugin} being
* applied.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
*/
final class CycloneDxPluginAction implements PluginApplicationAction {
final class CyclonedxPluginAction implements PluginApplicationAction {
@Override
public Class<? extends Plugin<? extends Project>> getPluginClass() {
return CycloneDxPlugin.class;
return CyclonedxPlugin.class;
}
@Override
public void execute(Project project) {
TaskProvider<CycloneDxTask> cycloneDxTaskProvider = project.getTasks()
.named("cyclonedxBom", CycloneDxTask.class);
configureCycloneDxTask(cycloneDxTaskProvider);
TaskProvider<CyclonedxAggregateTask> cycloneDxTaskProvider = project.getTasks()
.named("cyclonedxBom", CyclonedxAggregateTask.class);
configureCycloneDxTask(cycloneDxTaskProvider, project);
configureJavaPlugin(project, cycloneDxTaskProvider);
configureSpringBootPlugin(project, cycloneDxTaskProvider);
}
private void configureCycloneDxTask(TaskProvider<CycloneDxTask> taskProvider) {
private void configureCycloneDxTask(TaskProvider<CyclonedxAggregateTask> taskProvider, Project project) {
taskProvider.configure((task) -> {
task.getProjectType().convention("application");
task.getOutputFormat().convention("json");
task.getOutputName().convention("application.cdx");
task.getProjectType().convention(Component.Type.APPLICATION);
task.getXmlOutput().unsetConvention();
task.getJsonOutput()
.convention(project.getLayout().getBuildDirectory().file("reports/cyclonedx/application.cdx.json"));
task.getIncludeLicenseText().convention(false);
});
}
private void configureJavaPlugin(Project project, TaskProvider<CycloneDxTask> cycloneDxTaskProvider) {
private void configureJavaPlugin(Project project, TaskProvider<CyclonedxAggregateTask> cycloneDxTaskProvider) {
configurePlugin(project, JavaPlugin.class, (javaPlugin) -> {
JavaPluginExtension javaPluginExtension = project.getExtensions().getByType(JavaPluginExtension.class);
SourceSet main = javaPluginExtension.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME);
configureTask(project, main.getProcessResourcesTaskName(), Copy.class, (copy) -> {
copy.dependsOn(cycloneDxTaskProvider);
Provider<String> sbomFileName = cycloneDxTaskProvider
.map((cycloneDxTask) -> cycloneDxTask.getOutputName().get() + getSbomExtension(cycloneDxTask));
copy.from(cycloneDxTaskProvider, (spec) -> spec.include(sbomFileName.get()).into("META-INF/sbom"));
Provider<String> sbomFileName = cycloneDxTaskProvider.flatMap(
(cycloneDxTask) -> cycloneDxTask.getJsonOutput().map((file) -> file.getAsFile().getName()));
copy.from(cycloneDxTaskProvider,
(spec) -> spec.include((element) -> element.getName().equals(sbomFileName.get()))
.into("META-INF/sbom"));
});
});
}
private void configureSpringBootPlugin(Project project, TaskProvider<CycloneDxTask> cycloneDxTaskProvider) {
private void configureSpringBootPlugin(Project project,
TaskProvider<CyclonedxAggregateTask> cycloneDxTaskProvider) {
configurePlugin(project, SpringBootPlugin.class, (springBootPlugin) -> {
configureBootJarTask(project, cycloneDxTaskProvider);
configureBootWarTask(project, cycloneDxTaskProvider);
});
}
private void configureBootJarTask(Project project, TaskProvider<CycloneDxTask> cycloneDxTaskProvider) {
private void configureBootJarTask(Project project, TaskProvider<CyclonedxAggregateTask> cycloneDxTaskProvider) {
configureTask(project, SpringBootPlugin.BOOT_JAR_TASK_NAME, BootJar.class,
(bootJar) -> configureBootJarTask(bootJar, cycloneDxTaskProvider));
}
private void configureBootWarTask(Project project, TaskProvider<CycloneDxTask> cycloneDxTaskProvider) {
private void configureBootWarTask(Project project, TaskProvider<CyclonedxAggregateTask> cycloneDxTaskProvider) {
configureTask(project, SpringBootPlugin.BOOT_WAR_TASK_NAME, BootWar.class,
(bootWar) -> configureBootWarTask(bootWar, cycloneDxTaskProvider));
}
private void configureBootJarTask(BootJar task, TaskProvider<CycloneDxTask> cycloneDxTaskProvider) {
private void configureBootJarTask(BootJar task, TaskProvider<CyclonedxAggregateTask> cycloneDxTaskProvider) {
configureJarTask(task, cycloneDxTaskProvider, "");
}
private void configureBootWarTask(BootWar task, TaskProvider<CycloneDxTask> cycloneDxTaskProvider) {
private void configureBootWarTask(BootWar task, TaskProvider<CyclonedxAggregateTask> cycloneDxTaskProvider) {
configureJarTask(task, cycloneDxTaskProvider, "WEB-INF/classes/");
}
private void configureJarTask(Jar task, TaskProvider<CycloneDxTask> cycloneDxTaskProvider,
private void configureJarTask(Jar task, TaskProvider<CyclonedxAggregateTask> cycloneDxTaskProvider,
String sbomLocationPrefix) {
Provider<String> sbomFileName = cycloneDxTaskProvider.map((cycloneDxTask) -> "META-INF/sbom/"
+ cycloneDxTask.getOutputName().get() + getSbomExtension(cycloneDxTask));
Provider<String> sbomFileName = cycloneDxTaskProvider
.map((cycloneDxTask) -> "META-INF/sbom/" + cycloneDxTask.getJsonOutput().get().getAsFile().getName());
task.manifest((manifest) -> {
manifest.getAttributes().put("Sbom-Format", "CycloneDX");
manifest.getAttributes()
@ -113,14 +119,6 @@ final class CycloneDxPluginAction implements PluginApplicationAction { @@ -113,14 +119,6 @@ final class CycloneDxPluginAction implements PluginApplicationAction {
});
}
private String getSbomExtension(CycloneDxTask task) {
String format = task.getOutputFormat().get();
if ("all".equals(format)) {
return ".json";
}
return "." + format;
}
private <T extends Task> void configureTask(Project project, String name, Class<T> type, Action<T> action) {
project.getTasks().withType(type).configureEach((task) -> {
if (task.getName().equals(name)) {

2
build-plugin/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/SpringBootPlugin.java

@ -148,7 +148,7 @@ public class SpringBootPlugin implements Plugin<Project> { @@ -148,7 +148,7 @@ public class SpringBootPlugin implements Plugin<Project> {
List<PluginApplicationAction> actions = Arrays.asList(new JavaPluginAction(singlePublishedArtifact),
new WarPluginAction(singlePublishedArtifact), new DependencyManagementPluginAction(),
new ApplicationPluginAction(), new KotlinPluginAction(), new NativeImagePluginAction(),
new CycloneDxPluginAction());
new CyclonedxPluginAction());
for (PluginApplicationAction action : actions) {
withPluginClassOfAction(action,
(pluginClass) -> project.getPlugins().withType(pluginClass, (plugin) -> action.execute(project)));

9
build-plugin/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/CycloneDxPluginActionIntegrationTests.java → build-plugin/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/CyclonedxPluginActionIntegrationTests.java

@ -18,6 +18,8 @@ package org.springframework.boot.gradle.plugin; @@ -18,6 +18,8 @@ package org.springframework.boot.gradle.plugin;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.gradle.testkit.runner.BuildResult;
@ -31,12 +33,12 @@ import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; @@ -31,12 +33,12 @@ import org.springframework.boot.testsupport.gradle.testkit.GradleBuild;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link CycloneDxPluginAction}.
* Integration tests for {@link CyclonedxPluginAction}.
*
* @author Andy Wilkinson
*/
@GradleCompatibility
class CycloneDxPluginActionIntegrationTests {
class CyclonedxPluginActionIntegrationTests {
@SuppressWarnings("NullAway.Init")
GradleBuild gradleBuild;
@ -61,7 +63,8 @@ class CycloneDxPluginActionIntegrationTests { @@ -61,7 +63,8 @@ class CycloneDxPluginActionIntegrationTests {
assertThat(jar.getManifest().getMainAttributes().getValue("Sbom-Format")).isEqualTo("CycloneDX");
String sbomLocation = jar.getManifest().getMainAttributes().getValue("Sbom-Location");
assertThat(sbomLocation).isEqualTo(sbomLocationPrefix + "META-INF/sbom/application.cdx.json");
assertThat(jar.getEntry(sbomLocation)).isNotNull();
List<String> entryNames = jar.stream().map(JarEntry::getName).toList();
assertThat(entryNames).contains(sbomLocation);
}
}

22
build-plugin/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/testkit/PluginClasspathGradleBuild.java

@ -29,7 +29,7 @@ import org.apache.commons.compress.archivers.ArchiveEntry; @@ -29,7 +29,7 @@ import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.hc.client5.http.io.HttpClientConnectionManager;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.cyclonedx.gradle.CycloneDxPlugin;
import org.cyclonedx.gradle.CyclonedxPlugin;
import org.gradle.testkit.runner.GradleRunner;
import org.jetbrains.kotlin.gradle.fus.BuildUidService;
import org.jetbrains.kotlin.gradle.model.KotlinProject;
@ -113,20 +113,20 @@ public class PluginClasspathGradleBuild extends GradleBuild { @@ -113,20 +113,20 @@ public class PluginClasspathGradleBuild extends GradleBuild {
classpath.add(new File(pathOfJarContaining("org.graalvm.buildtools.gradle.NativeImagePlugin")));
classpath.add(new File(pathOfJarContaining("org.graalvm.reachability.GraalVMReachabilityMetadataRepository")));
classpath.add(new File(pathOfJarContaining("org.graalvm.buildtools.utils.SharedConstants")));
// CycloneDx dependencies
classpath.add(new File(pathOfJarContaining(CycloneDxPlugin.class)));
classpath.add(new File(pathOfJarContaining("com.github.packageurl.MalformedPackageURLException")));
classpath.add(new File(pathOfJarContaining("org.cyclonedx.parsers.Parser")));
classpath.add(new File(pathOfJarContaining("org.apache.maven.project.MavenProject")));
classpath.add(new File(pathOfJarContaining("org.apache.maven.model.building.ModelBuildingException")));
classpath.add(new File(pathOfJarContaining("org.codehaus.plexus.util.xml.pull.XmlPullParserException")));
classpath.add(new File(pathOfJarContaining("org.apache.commons.lang3.StringUtils")));
classpath.add(new File(pathOfJarContaining("com.networknt.schema.resource.SchemaMapper")));
// Cyclonedx dependencies
classpath.add(new File(pathOfJarContaining(CyclonedxPlugin.class)));
classpath.add(new File(pathOfJarContaining("com.ctc.wstx.api.WriterConfig")));
classpath.add(new File(pathOfJarContaining("com.fasterxml.jackson.core.Versioned")));
classpath.add(new File(pathOfJarContaining("com.fasterxml.jackson.databind.JsonSerializer")));
classpath.add(new File(pathOfJarContaining("com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator")));
classpath.add(new File(pathOfJarContaining("com.github.packageurl.MalformedPackageURLException")));
classpath.add(new File(pathOfJarContaining("com.google.common.collect.ImmutableMap")));
classpath.add(new File(pathOfJarContaining("com.networknt.schema.resource.SchemaMappers")));
classpath.add(new File(pathOfJarContaining("org.apache.commons.collections4.CollectionUtils")));
classpath.add(new File(pathOfJarContaining("org.apache.commons.io.FileUtils")));
classpath.add(new File(pathOfJarContaining("org.apache.maven.model.building.ModelBuildingException")));
classpath.add(new File(pathOfJarContaining("org.codehaus.plexus.util.xml.pull.XmlPullParserException")));
classpath.add(new File(pathOfJarContaining("org.codehaus.stax2.ri.Stax2WriterAdapter")));
classpath.add(new File(pathOfJarContaining("org.cyclonedx.model.ExternalReference")));
return classpath;
}

2
documentation/spring-boot-docs/src/docs/antora/modules/how-to/pages/build.adoc

@ -112,7 +112,7 @@ Gradle users can achieve the same result by using the {url-cyclonedx-docs-gradle @@ -112,7 +112,7 @@ Gradle users can achieve the same result by using the {url-cyclonedx-docs-gradle
[source,gradle]
----
plugins {
id 'org.cyclonedx.bom' version '2.3.0'
id 'org.cyclonedx.bom' version '3.0.0'
}
----

2
platform/spring-boot-internal-dependencies/build.gradle

@ -77,7 +77,7 @@ bom { @@ -77,7 +77,7 @@ bom {
]
}
}
library("CycloneDX Gradle Plugin", "2.3.0") {
library("CycloneDX Gradle Plugin", "3.0.1") {
group("org.cyclonedx") {
modules = [
"cyclonedx-gradle-plugin"

Loading…
Cancel
Save