diff --git a/README.md b/README.md index 2ca1bee..142a623 100644 --- a/README.md +++ b/README.md @@ -36,8 +36,10 @@ Tasks * `what-depends-on `: Find out what depends on an artifact. Shows a reverse dependency tree for the selected module. * `dependency-license-info`: show dependencies grouped by declared license - * `ivy-report`: let's ivy generate the resolution report for you project. Use + * `ivy-report`: lets ivy generate the resolution report for you project. Use `show ivy-report` for the filename of the generated report + * `dependency-csv`: Shows a CSV-formatted report of dependencies used in this project suitable for review by your company's legal department. + * `dependency-csv-to-file`: Writes the same CSV-formatted report to the configured file. All tasks can be scoped to a configuration to get the report for a specific configuration. `test:dependency-graph`, for example, prints the dependencies in the `test` configuration. If you don't specify any configuration, `compile` is @@ -55,6 +57,9 @@ Configuration settings * `dependencyDotHeader`: a setting to customize the header of the dot file (e.g. to set your preferred node shapes). * `dependencyDotNodeLabel`: defines the format of a node label (default set to `[organisation]
[name]
[version]`) + * `dependencyCsv3rdOnly`: a setting which when set to true (the default) excludes same-party dependencies from the report. + This is determined by comparing this project's `organization` setting to the same for the given dependency. + * `dependencyCsvFile`: a setting which allows configuring the output path of `dependency-csv-to-file`. E.g. in `build.sbt` you can change configuration settings like this: diff --git a/project.sbt b/project.sbt index 8f3b981..4d9bd8f 100644 --- a/project.sbt +++ b/project.sbt @@ -4,7 +4,7 @@ name := "sbt-dependency-graph" organization := "net.virtual-void" -version := "0.7.5" +version := "0.7.6-SNAPSHOT" homepage := Some(url("http://github.com/jrudolph/sbt-dependency-graph")) diff --git a/src/main/scala/net/virtualvoid/sbt/graph/Csv.scala b/src/main/scala/net/virtualvoid/sbt/graph/Csv.scala new file mode 100644 index 0000000..4edff71 --- /dev/null +++ b/src/main/scala/net/virtualvoid/sbt/graph/Csv.scala @@ -0,0 +1,21 @@ +package net.virtualvoid.sbt.graph + +object Csv { + def toCsv(graph: IvyGraphMLDependencies.ModuleGraph, excludeOrg:Option[String]):String = { + graph.modules.toList.sortBy { case (id, module) => + (id.organisation, id.name) + } + .filterNot { case (id, module) => + Some(id.organisation) == excludeOrg + } + .filter { case (id, module) => + module.evictedByVersion.isEmpty + } + .map { case (id, module) => + val license = '"'+module.license.getOrElse("")+'"' // Surround with quotes because some have commas + List(id.organisation, id.name, id.version, license) + .mkString(",") + } + .mkString("\n") + } +} diff --git a/src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala b/src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala index 3fcafff..abe5d01 100755 --- a/src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala +++ b/src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala @@ -35,7 +35,7 @@ object Plugin extends sbt.Plugin { val dependencyDotHeader = SettingKey[String]("dependency-dot-header", "The header of the dot file. (e.g. to set your preferred node shapes)") val dependencyDot = TaskKey[File]("dependency-dot", - "Creates a dot file containing the dpendency-graph for a project") + "Creates a dot file containing the dependency-graph for a project") val moduleGraph = TaskKey[IvyGraphMLDependencies.ModuleGraph]("module-graph", "The dependency graph for a project") val asciiGraph = TaskKey[String]("dependency-graph-string", @@ -52,8 +52,17 @@ object Plugin extends sbt.Plugin { "A task which returns the location of the ivy report file for a given configuration (default `compile`).") val ignoreMissingUpdate = update in ivyReport val filterScalaLibrary = SettingKey[Boolean]("filter-scala-library", - "Specifies if scala dependency should be filtered in dependency-* output" - ) + "Specifies if scala dependency should be filtered in dependency-* output") + val asciiCsv = TaskKey[String]("dependency-csv-string", + "Returns a string containing the CSV-formatted report of all dependencies including transitives.") + val dependencyCsv = TaskKey[Unit]("dependency-csv", + "Prints the CSV-formatted report of all dependencies to the console.") + val dependencyCsv3rdOnly = SettingKey[Boolean]("dependency-csv-3rd-only", + "Set to true to only include 3rd-party dependencies in the CSV report, based on the configured 'organization' SettingKey. (default: true)") + val dependencyCsvFile = SettingKey[File]("dependency-csv-file", + "The location the CSV file should be generated at.") + val dependencyCsvToFile = TaskKey[File]("dependency-csv-to-file", + "Creates a CSV file containing the dependency report at the location configured in dependency-csv-file SettingKey.") val licenseInfo = TaskKey[Unit]("dependency-license-info", "Aggregates and shows information about the licenses of dependencies") @@ -128,6 +137,11 @@ object Plugin extends sbt.Plugin { dependencyDotNodeLabel := { (organisation: String, name: String, version: String) => """<%s
%s
%s>""".format(organisation, name, version) }, + asciiCsv <<= asciiCsvTask, + dependencyCsv <<= print(asciiCsv), + dependencyCsv3rdOnly := true, + dependencyCsvFile <<= target / "dependencies-%s.csv".format(config.toString), + dependencyCsvToFile <<= dependencyCsvToFileTask, whatDependsOn <<= InputTask(artifactIdParser) { module => (module, streams, moduleGraph) map { (module, streams, graph) => streams.log.info(IvyGraphMLDependencies.asciiTree(IvyGraphMLDependencies.reverseGraphStartingAt(graph, module))) @@ -153,6 +167,17 @@ object Plugin extends sbt.Plugin { streams.log.info("Wrote dependency graph to '%s'" format resultFile) resultFile } + def asciiCsvTask = + (moduleGraph, organization, dependencyCsv3rdOnly).map { (graph, org, thirdOnly) => + val thirdOption = if(thirdOnly) Some(org) else None + Csv.toCsv(graph, thirdOption) + } + def dependencyCsvToFileTask = + (asciiCsv, dependencyCsvFile, streams) map { (csv, resultFile, streams) => + sbt.IO.write(resultFile, csv) + streams.log.info("Wrote dependency csv to '%s'" format resultFile) + resultFile + } def absoluteReportPath = (file: File) => file.getAbsolutePath def print(key: TaskKey[String]) =