Skip to content

Commit

Permalink
Add support for additional columns
Browse files Browse the repository at this point in the history
  • Loading branch information
mdedetrich committed May 11, 2023
1 parent 571f446 commit 4d76f23
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 48 deletions.
12 changes: 10 additions & 2 deletions src/main/scala/sbtlicensereport/SbtLicenseReport.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ object SbtLicenseReport extends AutoPlugin {
type TargetLanguage = sbtlicensereport.license.TargetLanguage
type LicenseReportConfiguration = sbtlicensereport.license.LicenseReportConfiguration
type DepModuleInfo = sbtlicensereport.license.DepModuleInfo
type Column = sbtlicensereport.license.Column
val DepModuleInfo = sbtlicensereport.license.DepModuleInfo
val Column = sbtlicensereport.license.Column
def LicenseReportConfiguration = sbtlicensereport.license.LicenseReportConfiguration
def Html = sbtlicensereport.license.Html
def MarkDown = sbtlicensereport.license.MarkDown
Expand All @@ -34,6 +36,8 @@ object SbtLicenseReport extends AutoPlugin {
val dumpLicenseReportAnyProject = taskKey[File](
"Dumps a report file against all projects of the license report (using the target language) and combines it into a single file."
)
val licenseReportColumns =
settingKey[Seq[Column]]("Additional columns to be added to the final report")
val licenseReportDir = settingKey[File]("The location where we'll write the license reports.")
val licenseReportStyleRules = settingKey[Option[String]]("The style rules for license report styling.")
val licenseReportTitle = settingKey[String]("The name of the license report.")
Expand Down Expand Up @@ -75,12 +79,14 @@ object SbtLicenseReport extends AutoPlugin {
val ignore = update.value
val overrides = licenseOverrides.value.lift
val depExclusions = licenseDepExclusions.value.lift
val originatingModule = DepModuleInfo(organization.value, name.value, version.value)
license.LicenseReport.makeReport(
ivyModule.value,
licenseConfigurations.value,
licenseSelection.value,
overrides,
depExclusions,
originatingModule,
streams.value.log
)
},
Expand All @@ -101,7 +107,8 @@ object SbtLicenseReport extends AutoPlugin {
notesLookup,
licenseFilter.value,
dir,
styleRules
styleRules,
licenseReportColumns.value
)
Seq(config)
},
Expand Down Expand Up @@ -137,6 +144,7 @@ object SbtLicenseReport extends AutoPlugin {
licenseDepExclusions := PartialFunction.empty,
licenseFilter := TypeFunctions.const(true),
licenseReportStyleRules := None,
licenseReportTypes := Seq(MarkDown, Html, Csv)
licenseReportTypes := Seq(MarkDown, Html, Csv),
licenseReportColumns := Seq(Column.Category, Column.License, Column.Dependency)
)
}
48 changes: 48 additions & 0 deletions src/main/scala/sbtlicensereport/license/Column.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package sbtlicensereport.license

trait Column {
def columnName: String

def render(depLicense: DepLicense, language: TargetLanguage): String
}

object Column {
case object Category extends Column {
override val columnName: String = "Category"

override def render(depLicense: DepLicense, language: TargetLanguage): String =
depLicense.license.category.name
}

case object License extends Column {
override val columnName: String = "License"

override def render(depLicense: DepLicense, language: TargetLanguage): String =
language.createHyperLink(depLicense.license.url, depLicense.license.name)
}

case object Dependency extends Column {
override val columnName: String = "Dependency"

override def render(depLicense: DepLicense, language: TargetLanguage): String =
depLicense.homepage match {
case None => depLicense.module.toString
case Some(url) => language.createHyperLink(url.toExternalForm, depLicense.module.toString)
}
}

case object Configuration extends Column {
override val columnName: String = "Maven/Ivy Configurations"

override def render(depLicense: DepLicense, language: TargetLanguage): String = {
depLicense.configs.mkString(",")
}
}

case object OriginatingArtifactName extends Column {
override val columnName: String = "Originating Artifact"

override def render(depLicense: DepLicense, language: TargetLanguage): String =
depLicense.originatingModule.name
}
}
47 changes: 31 additions & 16 deletions src/main/scala/sbtlicensereport/license/LicenseReport.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ import sbtlicensereport.SbtCompat._
case class DepModuleInfo(organization: String, name: String, version: String) {
override def toString = s"${organization} # ${name} # ${version}"
}
case class DepLicense(module: DepModuleInfo, license: LicenseInfo, homepage: Option[URL], configs: Set[String]) {
case class DepLicense(
module: DepModuleInfo,
license: LicenseInfo,
homepage: Option[URL],
configs: Set[String],
originatingModule: DepModuleInfo
) {
override def toString =
s"$module ${homepage.map(url => s" from $url")} on $license in ${configs.mkString("(", ",", ")")}"
}
Expand Down Expand Up @@ -54,18 +60,17 @@ object LicenseReport {
withPrintableFile(reportFile) { print =>
print(language.documentStart(title, reportStyleRules))
print(makeHeader(language))
print(language.tableHeader("Category", "License", "Dependency", "Notes"))
print(language.tableHeader("Notes", config.licenseReportColumns.map(_.columnName): _*))
val rendered = (ordered map { dep =>
val licenseLink = language.createHyperLink(dep.license.url, dep.license.name)
val moduleLink = dep.homepage match {
case None => dep.module.toString
case Some(url) => language.createHyperLink(url.toExternalForm, dep.module.toString)
}
(dep.license.category.name, licenseLink, moduleLink, notes(dep.module) getOrElse "")
val notesRendered = notes(dep.module) getOrElse ""
(
notesRendered,
config.licenseReportColumns map (_.render(dep, language))
)
}).distinct

for ((name, licenseLink, moduleLink, notes) <- rendered) {
print(language.tableRow(name, licenseLink, moduleLink, notes))
for ((notes, rest) <- rendered) {
print(language.tableRow(notes, rest: _*))
}
print(language.tableEnd)
print(language.documentEnd)
Expand All @@ -84,11 +89,12 @@ object LicenseReport {
licenseSelection: Seq[LicenseCategory],
overrides: DepModuleInfo => Option[LicenseInfo],
exclusions: DepModuleInfo => Option[Boolean],
originatingModule: DepModuleInfo,
log: Logger
): LicenseReport = {
val (report, err) = resolve(module, log)
err foreach (x => throw x) // Bail on error
makeReportImpl(report, configs, licenseSelection, overrides, exclusions, log)
makeReportImpl(report, configs, licenseSelection, overrides, exclusions, originatingModule, log)
}

/**
Expand Down Expand Up @@ -118,7 +124,8 @@ object LicenseReport {
private def pickLicenseForDep(
dep: IvyNode,
configs: Set[String],
categories: Seq[LicenseCategory]
categories: Seq[LicenseCategory],
originatingModule: DepModuleInfo
): Option[DepLicense] =
for {
d <- Option(dep)
Expand All @@ -138,17 +145,24 @@ object LicenseReport {
.apply(Some(url(loc)))
)
// TODO - grab configurations.
} yield DepLicense(getModuleInfo(dep), pickLicense(categories)(licenses), homepage, filteredConfigs)
} yield DepLicense(
getModuleInfo(dep),
pickLicense(categories)(licenses),
homepage,
filteredConfigs,
originatingModule
)

private def getLicenses(
report: ResolveReport,
configs: Set[String] = Set.empty,
categories: Seq[LicenseCategory] = LicenseCategory.all
categories: Seq[LicenseCategory] = LicenseCategory.all,
originatingModule: DepModuleInfo
): Seq[DepLicense] = {
import collection.JavaConverters._
for {
dep <- report.getDependencies.asInstanceOf[java.util.List[IvyNode]].asScala
report <- pickLicenseForDep(dep, configs, categories)
report <- pickLicenseForDep(dep, configs, categories, originatingModule)
} yield report
}

Expand All @@ -158,9 +172,10 @@ object LicenseReport {
categories: Seq[LicenseCategory],
overrides: DepModuleInfo => Option[LicenseInfo],
exclusions: DepModuleInfo => Option[Boolean],
originatingModule: DepModuleInfo,
log: Logger
): LicenseReport = {
val licenses = getLicenses(report, configs, categories) filterNot { dep =>
val licenses = getLicenses(report, configs, categories, originatingModule) filterNot { dep =>
exclusions(dep.module).getOrElse(false)
} map { l =>
overrides(l.module) match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ case class LicenseReportConfiguration(
notes: DepModuleInfo => Option[String],
licenseFilter: LicenseCategory => Boolean,
reportDir: File,
reportStyleRules: Option[String] = None
reportStyleRules: Option[String] = None,
licenseReportColumns: Seq[Column]
)
70 changes: 42 additions & 28 deletions src/main/scala/sbtlicensereport/license/TargetLanguage.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ sealed trait TargetLanguage {
def header1(msg: String): String

/** The syntax for the header of a table. */
def tableHeader(firstColumn: String, secondColumn: String, thirdColumn: String, fourthColumn: String): String
def tableHeader(notes: String, columns: String*): String

/** The syntax for a row of a table. */
def tableRow(firstColumn: String, secondColumn: String, thirdColumn: String, fourthColumn: String): String
def tableRow(notes: String, columns: String*): String

/** And a "table" */
def tableEnd: String
Expand All @@ -38,13 +38,17 @@ case object MarkDown extends TargetLanguage {
s"[$content]($link)"
def blankLine(): String = "\n"
def header1(msg: String): String = s"# $msg\n"
def tableHeader(firstColumn: String, secondColumn: String, thirdColumn: String, fourthColumn: String): String =
s"""
$firstColumn | $secondColumn | $thirdColumn | $fourthColumn
--- | --- | --- | ---
"""
def tableRow(firstColumn: String, secondColumn: String, thirdColumn: String, fourthColumn: String): String =
s"$firstColumn | $secondColumn | $thirdColumn | <notextile>${escapeHtml(fourthColumn)}</notextile>\n"
def tableHeader(notes: String, columns: String*): String = {
val all = columns :+ notes
val firstRow = "\n" + all.mkString(" | ")
val secondRow = List.fill(all.size - 1)("").mkString("--- |", " --- |", " ---")
firstRow ++ "\n" ++ secondRow + "\n"
}
def tableRow(notes: String, columns: String*): String = {
val main = columns.mkString("", " | ", " | ")
val notesEscaped = s"<notextile>${escapeHtml(notes)}</notextile>\n"
main ++ notesEscaped
}
def tableEnd: String = "\n"

def markdownEncode(s: String): String = s.flatMap {
Expand All @@ -71,14 +75,21 @@ case object Html extends TargetLanguage {
s"""<a href="$link">$content</a>"""
def blankLine(): String = "<p>&nbsp;</p>"
def header1(msg: String): String = s"<h1>$msg</h1>"
def tableHeader(firstColumn: String, secondColumn: String, thirdColumn: String, fourthColumn: String): String =
def tableHeader(notes: String, columns: String*): String = {
val all = columns :+ notes
val th = all.mkString("<th>", "</th><th>", "</th>")
s"""<table border="0" cellspacing="0" cellpading="1">
<thead><tr><th>$firstColumn</th><th>$secondColumn</th><th>$thirdColumn</th><th>$fourthColumn</th></tr></thead>
<thead><tr>$th</tr></thead>
<tbody>"""
def tableRow(firstColumn: String, secondColumn: String, thirdColumn: String, fourthColumn: String): String =
s"""<tr><td>${firstColumn}&nbsp;</td><td>${secondColumn}&nbsp;</td><td>${thirdColumn}&nbsp;</td><td>${htmlEncode(
fourthColumn
)}</td></tr>"""
}
def tableRow(notes: String, columns: String*): String = {
val main = columns.mkString("""<tr><td>""", """&nbsp;</td><td>""", """&nbsp;</td><td>""")
val notesEscaped = s"${htmlEncode(
notes
)}</td></tr>"

main + notesEscaped
}
def tableEnd: String = "</tbody></table>"

def htmlEncode(s: String) = org.apache.commons.lang3.StringEscapeUtils.escapeHtml4(s)
Expand All @@ -93,10 +104,15 @@ case object Csv extends TargetLanguage {
}
def blankLine(): String = ""
def header1(msg: String): String = ""
def tableHeader(firstColumn: String, secondColumn: String, thirdColumn: String, fourthColumn: String): String =
tableRow(firstColumn, secondColumn, thirdColumn, fourthColumn)
def tableRow(firstColumn: String, secondColumn: String, thirdColumn: String, fourthColumn: String): String =
s"""${csvEncode(firstColumn)},${csvEncode(secondColumn)},${csvEncode(thirdColumn)},${csvEncode(fourthColumn)}\n"""
def tableHeader(notes: String, columns: String*): String = {
tableRow(notes, columns: _*)
}
def tableRow(notes: String, columns: String*): String = {
val all = columns :+ notes
val escaped = all map csvEncode
escaped.mkString("", ",", "\n")
}

def tableEnd: String = ""
def csvEncode(s: String): String = org.apache.commons.lang3.StringEscapeUtils.escapeCsv(s)
}
Expand All @@ -108,16 +124,14 @@ case object ConfluenceWikiMarkup extends TargetLanguage {
def createHyperLink(link: String, content: String): String = s"[${trim(content)}|${trim(link)}]"
def blankLine(): String = "\n"
def header1(msg: String): String = s"h1.$msg\n"
def tableHeader(firstColumn: String, secondColumn: String, thirdColumn: String, fourthColumn: String): String = {
s"|| $firstColumn || $secondColumn || $thirdColumn || $fourthColumn ||\n"
def tableHeader(notes: String, columns: String*): String = {
val all = columns :+ notes
all.mkString("|| ", " || ", " ||\n")
}
def tableRow(notes: String, columns: String*): String = {
val all = columns :+ notes
all.mkString("| ", " | ", " |\n")
}

def tableRow(
firstColumn: String,
secondColumn: String,
thirdColumn: String,
fourthColumn: String
): String = s"| $firstColumn | $secondColumn | $thirdColumn | $fourthColumn |\n"
def tableEnd: String = "\n"

def markdownEncode(s: String): String = s.flatMap {
Expand Down
4 changes: 3 additions & 1 deletion src/sbt-test/dumpLicenseReport/custom-report/example.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ licenseReportConfigurations +=
language => language.header1("Testing the configuration"),
dep => Option("Default notes"),
category => category == LicenseCategory.BSD,
licenseReportDir.value
licenseReportDir.value,
None,
Seq(Column.Category, Column.License, Column.Dependency)
)

val check = taskKey[Unit]("check the license report.")
Expand Down

0 comments on commit 4d76f23

Please sign in to comment.