Skip to content

Commit

Permalink
Merge pull request #30 from siculo/projects/v0.2.0
Browse files Browse the repository at this point in the history
Projects/v0.2.0 (to keep master in sync with all v0.2.0 changes)
  • Loading branch information
siculo authored Mar 23, 2022
2 parents a7c1878 + eeb08ad commit 5ac2998
Show file tree
Hide file tree
Showing 18 changed files with 766 additions and 70 deletions.
28 changes: 24 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,33 @@
The aim of this project is to:

- extract a valid [CycloneDx](https://cyclonedx.org/) bom file from sbt projects
- ensure that the bom file is processable with Software Composition Analysis tools (like Dependency Track)
- ensure that the bom file is processable with Software Composition Analysis tools (like Dependency Track)

## how to test the plugin
## usage

see: ["exists" test project README.md](src/sbt-test/sbt-bom/exists/README.md)
## testing

There are two types of test: unit test done with scalatest and scripted test

### unit test

Unit tests are written using scalatest syntax. Only pure logic classes are tested using these tests.

To run unit tests use the `test` command to run all tests, or `testOnly ...` command specifying the list of test to be
executed.

### scripted tests

[Scripted](https://www.scala-sbt.org/1.x/docs/Testing-sbt-plugins.html) is a tool that allow you to test sbt plugins.
For each test it is necessary to create a specially crafted project. These projects are inside src/sbt-test directory.

Scripted tests are run using `scripted` comand.

## changelog

### v0.0.1
### v0.2.0
- The cyclonedx-core-java library has been integrated and is used to generate the BOM
- Removed all old model classes used so far

### v0.1.0
- First release
14 changes: 9 additions & 5 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
ThisBuild / organization := "sbtBom"
ThisBuild / organizationName := "SBT BOM"
ThisBuild / version := "0.2.0-SNAPSHOT"
ThisBuild / scalaVersion := "2.12.8"
ThisBuild / homepage := Some(url("https://github.com/siculo/sbt-bom"))

lazy val root = (project in file("."))
.enablePlugins(ScriptedPlugin)
.settings(
name := "sbt-bom",
organization := "sbtBom",
organizationName := "SBT BOM",
version := "0.2.0-SNAPSHOT",
sbtPlugin := true,
scalaVersion := "2.12.8",
libraryDependencies ++= Dependencies.library,
scriptedLaunchOpts += ("-Dplugin.version=" + version.value),
scriptedLaunchOpts := {
scriptedLaunchOpts.value ++ Seq("-Xmx1024M", "-Dplugin.version=" + version.value)
},
scriptedBufferLog := false,
dependencyOverrides += "org.typelevel" %% "jawn-parser" % "0.14.1"
)
15 changes: 11 additions & 4 deletions src/main/scala/sbtBom/BomExtractor.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package sbtBom

import com.github.packageurl.PackageURL
import org.cyclonedx.CycloneDxSchema
import org.cyclonedx.model.{Bom, Component, License, LicenseChoice}
import sbt.librarymanagement.ModuleReport
import sbt.{Logger, UpdateReport, _}
Expand All @@ -11,9 +10,8 @@ import java.util.UUID
import scala.collection.JavaConverters._


class BomExtractor(schemaVersion: CycloneDxSchema.Version, report: UpdateReport, log: Logger) {
class BomExtractor(settings: BomExtractorParams, report: UpdateReport, log: Logger) {
private val serialNumber: String = "urn:uuid:" + UUID.randomUUID.toString
private val configuration: Configuration = Compile

def bom: Bom = {
val bom = new Bom
Expand All @@ -23,8 +21,17 @@ class BomExtractor(schemaVersion: CycloneDxSchema.Version, report: UpdateReport,
}

private def components: Seq[Component] = {
val configurations = Seq(Compile, Provided, Runtime, Test, IntegrationTest)
configurations.foldLeft(Seq[Component]()) {
case (collected, configuration) =>
collected ++ componentsForConfiguration(configuration)
}
}

private def componentsForConfiguration(configuration: Configuration): Seq[Component] = {
(report.configuration(configuration) map {
configurationReport =>
log.info(s"Configuration name = ${configurationReport.configuration.name}, modules: ${configurationReport.modules.size}")
configurationReport.modules.map {
module =>
new ComponentExtractor(module).component
Expand All @@ -50,7 +57,7 @@ class BomExtractor(schemaVersion: CycloneDxSchema.Version, report: UpdateReport,
component.setScope(Component.Scope.REQUIRED)
licenseChoice.foreach(component.setLicenseChoice)

logComponent(component)
// logComponent(component)

component
}
Expand Down
7 changes: 7 additions & 0 deletions src/main/scala/sbtBom/BomExtractorParams.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package sbtBom

import org.cyclonedx.CycloneDxSchema
import sbt.Configuration

case class BomExtractorParams(schemaVersion: CycloneDxSchema.Version,
configuration: Configuration)
14 changes: 0 additions & 14 deletions src/main/scala/sbtBom/BomSbtKeys.scala

This file was deleted.

18 changes: 16 additions & 2 deletions src/main/scala/sbtBom/BomSbtPlugin.scala
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
package sbtBom

import sbt.Keys.target
import sbt._

/**
* plugin object
*/
object BomSbtPlugin extends AutoPlugin {

override def requires: Plugins = empty

override def trigger: PluginTrigger = allRequirements

object autoImport extends BomSbtKeys
object autoImport {
lazy val targetBomFile: SettingKey[sbt.File] = settingKey[File]("target file to store the generated bom")
lazy val makeBom: TaskKey[sbt.File] = taskKey[sbt.File]("Generates bom file which includes all project dependencies")
lazy val listBom: TaskKey[String] = taskKey[String]("Returns a bom which includes all project dependencies")
lazy val supportedConfigurations: Seq[Configuration] = Seq(Compile, Runtime, Test)
}

override lazy val projectSettings: Seq[Setting[_]] = BomSbtSettings.projectSettings
import autoImport._

override lazy val projectSettings: Seq[Setting[_]] =
Seq(
targetBomFile := target.value / "bom.xml",
makeBom := Def.taskDyn(BomSbtSettings.makeBomTask(Classpaths.updateTask.value)).value,
listBom := Def.taskDyn(BomSbtSettings.listBomTask(Classpaths.updateTask.value)).value,
)
}
31 changes: 14 additions & 17 deletions src/main/scala/sbtBom/BomSbtSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,24 @@ import org.apache.commons.io.FileUtils
import org.cyclonedx.model.Bom
import org.cyclonedx.{BomGeneratorFactory, CycloneDxSchema}
import sbt.Keys.{sLog, target}
import sbt.{Def, File, Setting, _}
import sbt.{Compile, Def, File, Setting, _}
import sbtBom.BomSbtPlugin.autoImport._

import java.nio.charset.Charset

object BomSbtSettings {
val schemaVersion: CycloneDxSchema.Version = CycloneDxSchema.Version.VERSION_10

def projectSettings: Seq[Setting[_]] = {
// val configs = Seq(Compile, Test, IntegrationTest, Runtime, Provided, Optional)
Seq(
targetBomFile := target.value / "bom.xml",
makeBom := Def.taskDyn(makeBomTask(Classpaths.updateTask.value)).value,
listBom := Def.taskDyn(listBomTask(Classpaths.updateTask.value)).value,
)
}

private def makeBomTask(report: UpdateReport): Def.Initialize[Task[sbt.File]] = Def.task[File] {
def makeBomTask(report: UpdateReport): Def.Initialize[Task[sbt.File]] = Def.task[File] {
val log: Logger = sLog.value
val bomFile = targetBomFile.value

log.info(s"Creating bom file ${bomFile.getAbsolutePath}")

val bom: Bom = new BomExtractor(schemaVersion, report, log).bom
val params = extractorParams
val bom: Bom = new BomExtractor(params, report, log).bom
val bomText: String = getXmlText(bom, schemaVersion)
logBomInfo(log, bom)
logBomInfo(log, params, bom)

FileUtils.write(bomFile, bomText, Charset.forName("UTF-8"), false)

Expand All @@ -38,24 +30,29 @@ object BomSbtSettings {
bomFile
}

private def listBomTask(report: UpdateReport): Def.Initialize[Task[String]] =
def listBomTask(report: UpdateReport): Def.Initialize[Task[String]] =
Def.task[String] {
val log: Logger = sLog.value

log.info("Creating bom")

val bom: Bom = new BomExtractor(schemaVersion, report, log).bom
val params = extractorParams
val bom: Bom = new BomExtractor(params, report, log).bom
val bomText: String = getXmlText(bom, schemaVersion)
logBomInfo(log, bom)
logBomInfo(log, params, bom)

log.info("Bom created")

bomText
}

private def logBomInfo(log: Logger, bom: Bom): Unit = {
private def extractorParams: BomExtractorParams =
BomExtractorParams(schemaVersion, Compile)

private def logBomInfo(log: Logger, params: BomExtractorParams, bom: Bom): Unit = {
log.info(s"Schema version: ${schemaVersion.getVersionString}")
log.info(s"Serial number : ${bom.getSerialNumber}")
log.info(s"Scope : ${params.configuration.id}")
}

private def getXmlText(bom: Bom, schemaVersion: CycloneDxSchema.Version) = {
Expand Down
1 change: 1 addition & 0 deletions src/sbt-test/sbt-bom/dependencies/.bsp/sbt.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"name":"sbt","version":"1.5.2","bspVersion":"2.0.0-M5","languages":["scala"],"argv":["/home/fabrizio/Applications/java/jdk1.8.0_211/jre/bin/java","-Xms100m","-Xmx100m","-classpath","/home/fabrizio/Applications/sbt/sbt-1.5.2/bin/sbt-launch.jar","xsbt.boot.Boot","-bsp","--sbt-launch-jar=/home/fabrizio/Applications/sbt/sbt-1.5.2/bin/sbt-launch.jar"]}
19 changes: 19 additions & 0 deletions src/sbt-test/sbt-bom/dependencies/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import scala.xml.XML


lazy val root = (project in file("."))
.settings(
name := "dependencies",
version := "0.1",
libraryDependencies ++= Dependencies.library,
scalaVersion := "2.12.8",
check := checkTask.value
)

lazy val check = taskKey[Unit]("check")
lazy val checkTask = Def.task {
val context = thisProject.value
val expected = XML.loadFile(file(s"${context.base}/etc/bom.xml"))
val actual = XML.loadFile(file(s"${context.base}/target/bom.xml"))
require(expected \ "components" == actual \ "components", s"${context.id} is failed.")
}
Loading

0 comments on commit 5ac2998

Please sign in to comment.