SnykSbtPlugin-0.1x.scala 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. package snyk
  2. import java.io.StringWriter
  3. import net.virtualvoid.sbt.graph.DependencyGraphKeys.moduleGraph
  4. import net.virtualvoid.sbt.graph.{DependencyGraphPlugin, ModuleId}
  5. import sbt._
  6. import Keys._
  7. import org.json4s._
  8. object SnykSbtPlugin extends AutoPlugin {
  9. val ConfigBlacklist: Set[String] =
  10. Set("windows", "universal", "universal-docs", "debian", "rpm", "universal-src", "docker", "linux", "web-assets", "web-plugin", "web-assets-test", "scalafix")
  11. case class SnykModuleInfo(version: String, configurations: Set[String])
  12. case class SnykProjectData(projectId: String,
  13. modules: Map[String, SnykModuleInfo],
  14. dependencies: Map[String, Set[String]]) {
  15. def merge(otherModules: Map[String, SnykModuleInfo], otherDeps: Map[String, Set[String]]): SnykProjectData = {
  16. val mergedModules = otherModules.foldLeft(modules) {
  17. case (acc, (moduleName, moduleInfo)) =>
  18. acc.get(moduleName) match {
  19. case Some(existing) =>
  20. acc + (moduleName -> SnykModuleInfo(
  21. existing.version,
  22. existing.configurations ++ moduleInfo.configurations
  23. ))
  24. case None =>
  25. acc + (moduleName -> moduleInfo)
  26. }
  27. }
  28. val mergedDeps = dependencies ++ otherDeps
  29. SnykProjectData(projectId, mergedModules, mergedDeps)
  30. }
  31. }
  32. object autoImport {
  33. lazy val snykExtractProjectData = taskKey[SnykProjectData]("Extracts the dependency information for each project")
  34. lazy val snykRenderTree = taskKey[Unit]("Renders the dependency information for all projects")
  35. }
  36. import autoImport._
  37. override lazy val globalSettings = Seq(snykRenderTree := Def.taskDyn {
  38. val allProjs = buildStructure.value.allProjectRefs
  39. val filter = ScopeFilter(inProjects(allProjs: _*))
  40. Def.task {
  41. val allProjectDatas = snykExtractProjectData.all(filter).value
  42. val writer = JsonWriter.streaming(new StringWriter())
  43. writer.addJValue(
  44. JObject(
  45. allProjectDatas.toList.map {
  46. case SnykProjectData(projectId, modules, deps) =>
  47. projectId -> JObject(
  48. "modules" -> JObject(
  49. modules
  50. .mapValues { moduleInfo =>
  51. JObject(
  52. List(
  53. "version" -> JString(moduleInfo.version),
  54. "configurations" -> JArray(moduleInfo.configurations.toList.map(JString))
  55. )
  56. )
  57. }
  58. .toList
  59. ),
  60. "dependencies" -> JObject(
  61. deps.mapValues(vs => JArray(vs.toList.map(JString))).toList
  62. )
  63. )
  64. }
  65. )
  66. )
  67. println("Snyk Output Start")
  68. println(writer.result.toString)
  69. println("Snyk Output End")
  70. }
  71. }.value)
  72. override lazy val projectSettings = Seq(
  73. snykExtractProjectData := Def.taskDyn {
  74. def formatModuleId(m: ModuleId) = s"${m.organisation}:${m.name}"
  75. val thisProjectId = formatModuleId((moduleGraph in Compile).value.roots.head.id)
  76. val thisProjectConfigs = thisProject.value.configurations.filterNot { c =>
  77. ConfigBlacklist.contains(c.name)
  78. }
  79. val filter = ScopeFilter(configurations = inConfigurations(thisProjectConfigs: _*))
  80. val configAndModuleGraph = Def.task {
  81. val graph = moduleGraph.value
  82. val configName = configuration.value.name
  83. configName -> graph
  84. }
  85. Def.task {
  86. val graphs = configAndModuleGraph.all(filter).value
  87. graphs.foldLeft(SnykProjectData(thisProjectId, Map.empty, Map.empty)) {
  88. case (projectData, (configName, graph)) =>
  89. val modules = graph.modules.flatMap {
  90. case (moduleId, module) =>
  91. if (module.isUsed) Some(formatModuleId(moduleId) -> SnykModuleInfo(moduleId.version, Set(configName)))
  92. else None
  93. }
  94. val depMap = graph.dependencyMap
  95. val dependencies = graph.modules.flatMap {
  96. case (moduleId, module) =>
  97. if (module.isUsed)
  98. depMap.get(moduleId).map { deps =>
  99. formatModuleId(moduleId) -> deps.collect {
  100. case dep if dep.isUsed => formatModuleId(dep.id)
  101. }.toSet
  102. } else None
  103. }
  104. projectData.merge(modules, dependencies)
  105. }
  106. }
  107. }.value
  108. )
  109. override def requires = sbt.plugins.JvmPlugin && DependencyGraphPlugin
  110. override def trigger = allRequirements
  111. }