Skip to content

Instantly share code, notes, and snippets.

@kthompson
Last active July 30, 2018 16:14
Show Gist options
  • Save kthompson/ea63730428d3a2f586f23592f13d1cbb to your computer and use it in GitHub Desktop.
Save kthompson/ea63730428d3a2f586f23592f13d1cbb to your computer and use it in GitHub Desktop.
SBT Plugin to save warnings to a file as well as cap max warnings for a project
import java.io.{BufferedWriter, FileWriter}
import java.util.Calendar
import sbt.{Def, _}
import sbt.Keys._
import sbt.internal.inc.Analysis
import sbt.plugins.JvmPlugin
import xsbti.Problem
import scala.util.{Random, Try}
object SaveWarningsPlugin extends AutoPlugin {
object autoImport {
lazy val maxWarnings = settingKey[Option[Int]]("Maximum number of warnings allowed for build")
lazy val maxTestWarnings = settingKey[Option[Int]]("Maximum number of warnings allowed for tests")
}
import autoImport._
override def trigger: PluginTrigger = noTrigger
override def globalSettings: Seq[Def.Setting[_]] = Seq(maxWarnings := None, maxTestWarnings := None)
override lazy val projectSettings: Seq[Def.Setting[_]] =
Seq(
compile in Compile := {
val analysis = (compile in Compile).value
val projectName = name.value
analysis match {
case a: Analysis =>
val warningCount = dumpWarningsToFile(a, projectName, "compile", maxWarnings.value, streams.value.log)
//only do this under TC
if (sys.env.get("TEAMCITY_VERSION").isDefined) {
println(s"##teamcity[buildStatisticValue key='$projectName.warnings' value='$warningCount']")
println(s"##teamcity[buildStatisticValue key='InspectionStatsW' value='$warningCount']")
}
case _ => ()
}
analysis
},
compile in Test := {
val analysis = (compile in Test).value
val projectName = name.value
analysis match {
case a: Analysis =>
dumpWarningsToFile(a, projectName, "test", maxTestWarnings.value, streams.value.log)
case _ => ()
}
analysis
}
)
private def clean(s: String): String =
s.replace(new java.io.File(".").getCanonicalPath, "")
.replace("\\", "/")
.replace("\"", "\\\"")
.replace("\r", "")
.replace("\n", " ")
private def dumpWarningsToFile(analysis: Analysis,
projectName: String,
configName: String,
maxWarnings: Option[Int],
log: Logger): Int = {
val warningProblems = getAllWarnings(analysis)
val problemsJson = problemsToJson(warningProblems)
import java.io._
val fileName = if (configName == "compile") {
s"$projectName-warnings.json"
} else {
s"$projectName-$configName-warnings.json"
}
val pw = new PrintWriter(new File(fileName))
pw.write(problemsJson)
pw.close()
val warningCount = warningProblems.size
maxWarnings.foreach { maxWarnings =>
if (warningCount > maxWarnings) {
log.error(
s"$projectName:$configName - Warning count exceeded allowed maximum [count=$warningCount, max=$maxWarnings]"
)
throw new RuntimeException(
s"Aborting build: $projectName:$configName - Warning count exceeded allowed maximum " +
s"[count=$warningCount, max=$maxWarnings]"
)
} else {
val message = s"$projectName:$configName - Warning count: [count=$warningCount, max=$maxWarnings]"
val reduction = if (warningCount < maxWarnings) {
s" - can be reduced by ${maxWarnings - warningCount}"
} else {
""
}
log.info(message + reduction)
}
}
warningCount
}
private def problemsToJson(warningProblems: Iterable[Problem]) =
warningProblems.toList
.sortBy { problem =>
val src = Try(problem.position().sourcePath().get()).map(clean).getOrElse("")
val line = Try(problem.position().line().get().toInt).toOption.getOrElse(0)
(src, line)
}
.map { x =>
val problemFields = List(
("line", Try(x.position().line().get()).toOption.map(_.toString).map(clean)),
("sourceFile", Try(x.position().sourcePath().get()).toOption.map(clean)),
("content", Try(x.position().lineContent()).toOption.map(clean))
).flatMap {
case (fieldName, value) =>
value.toList.map { v =>
s""""$fieldName": "$v""""
}
}
.mkString(",\n ")
s"""
|{
| "category": "${x.category()}",
| "message": "${clean(x.message())}",
| "severity": "${x.severity()}",
| $problemFields
|}""".stripMargin
}
.mkString("[", ",", "]")
private def getAllWarnings(analysis: Analysis) =
for {
sourceInfo <- analysis.infos.allInfos.values
problem <- sourceInfo.getReportedProblems
if problem.severity() == xsbti.Severity.Warn
if !Try(problem.position().sourcePath().get()).toOption.exists(_.endsWith("TestApp.scala"))
} yield problem
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment