mirror of https://github.com/sbt/sbt.git
json-report: one file per dependency instead of multiple; use m2 coords (#782)
When classifiers are used as part of dependency specifications, it's important to be able to select just the classified artifact. Unfortunately, in the current json, dependencies don't specify classifiers, so it isn't possible to just get one of the artifacts for a dependency when a classifier is required. This patch introduces maven style artifact prefixes in order to include classifier and packaging information in the coordinates. By doing that, we can use them as keys in dependency lists more easily and it allows consumers of the json to treat those dependency keys as mostly opaque ids rather than having to parse them. Addresses #743
This commit is contained in:
parent
2d4ab34408
commit
d0b46864c8
|
|
@ -584,17 +584,33 @@ class Helper(
|
|||
}
|
||||
|
||||
private def getDepArtifactsForClassifier(sources: Boolean, javadoc: Boolean, res0: Resolution): Seq[(Dependency, Artifact)] = {
|
||||
if (classifier0.nonEmpty || sources || javadoc) {
|
||||
var classifiers = classifier0
|
||||
if (sources)
|
||||
classifiers = classifiers + "sources"
|
||||
if (javadoc)
|
||||
classifiers = classifiers + "javadoc"
|
||||
val raw: Seq[(Dependency, Artifact)] = if (hasOverrideClassifiers(sources, javadoc)) {
|
||||
//TODO: this function somehow gives duplicated things
|
||||
res0.dependencyClassifiersArtifacts(classifiers.toVector.sorted)
|
||||
res0.dependencyClassifiersArtifacts(overrideClassifiers(sources, javadoc).toVector.sorted)
|
||||
} else {
|
||||
res0.dependencyArtifacts(withOptional = true)
|
||||
}
|
||||
|
||||
raw.map({ case (dep, artifact) =>
|
||||
(
|
||||
dep.copy(
|
||||
attributes = dep.attributes.copy(classifier = artifact.classifier)),
|
||||
artifact
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
private def overrideClassifiers(sources: Boolean, javadoc:Boolean): Set[String] = {
|
||||
var classifiers = classifier0
|
||||
if (sources)
|
||||
classifiers = classifiers + "sources"
|
||||
if (javadoc)
|
||||
classifiers = classifiers + "javadoc"
|
||||
classifiers
|
||||
}
|
||||
|
||||
private def hasOverrideClassifiers(sources: Boolean, javadoc: Boolean): Boolean = {
|
||||
classifier0.nonEmpty || sources || javadoc
|
||||
}
|
||||
|
||||
def fetchMap(
|
||||
|
|
@ -708,9 +724,17 @@ class Helper(
|
|||
val artifacts: Seq[(Dependency, Artifact)] = res.dependencyArtifacts
|
||||
|
||||
val jsonReq = JsonPrintRequirement(artifactToFile, depToArtifacts)
|
||||
val roots = deps.toVector.map(JsonElem(_, artifacts, Option(jsonReq), res, printExclusions = verbosityLevel >= 1, excluded = false, colors = false))
|
||||
val jsonStr = JsonReport(roots, conflictResolutionForRoots)(_.children, _.reconciledVersionStr, _.requestedVersionStr, _.downloadedFiles)
|
||||
|
||||
val roots = deps.toVector.map(JsonElem(_, artifacts, Option(jsonReq), res, printExclusions = verbosityLevel >= 1, excluded = false, colors = false, overrideClassifiers = overrideClassifiers(sources, javadoc)))
|
||||
val jsonStr = JsonReport(
|
||||
roots,
|
||||
conflictResolutionForRoots,
|
||||
overrideClassifiers(sources, javadoc)
|
||||
)(
|
||||
_.children,
|
||||
_.reconciledVersionStr,
|
||||
_.requestedVersionStr,
|
||||
_.downloadedFile
|
||||
)
|
||||
val pw = new PrintWriter(new File(jsonOutputFile))
|
||||
pw.write(jsonStr)
|
||||
pw.close()
|
||||
|
|
|
|||
|
|
@ -12,9 +12,18 @@ import scala.collection.parallel.ParSeq
|
|||
import argonaut._
|
||||
import Argonaut._
|
||||
|
||||
/**
|
||||
* Lookup table for files and artifacts to print in the JsonReport.
|
||||
*/
|
||||
final case class JsonPrintRequirement(fileByArtifact: Map[String, File], depToArtifacts: Map[Dependency, Vector[Artifact]])
|
||||
|
||||
final case class DepNode(coord: String, files: Vector[(String, String)], dependencies: Set[String])
|
||||
/**
|
||||
* Represents a resolved dependency's artifact in the JsonReport.
|
||||
* @param coord String representation of the artifact's maven coordinate.
|
||||
* @param file The path to the file for the artifact.
|
||||
* @param dependencies The dependencies of the artifact.
|
||||
*/
|
||||
final case class DepNode(coord: String, file: Option[String], dependencies: Set[String])
|
||||
|
||||
final case class ReportNode(conflict_resolution: Map[String, String], dependencies: Vector[DepNode], version: String)
|
||||
|
||||
|
|
@ -34,7 +43,7 @@ object ReportNode {
|
|||
import argonaut.ArgonautShapeless._
|
||||
implicit val encodeJson = EncodeJson.of[ReportNode]
|
||||
implicit val decodeJson = DecodeJson.of[ReportNode]
|
||||
val version = "0.0.1"
|
||||
val version = "0.1.0"
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -42,8 +51,8 @@ object JsonReport {
|
|||
|
||||
private val printer = PrettyParams.nospace.copy(preserveOrder = true)
|
||||
|
||||
def apply[T](roots: IndexedSeq[T], conflictResolutionForRoots: Map[String, String])
|
||||
(children: T => Seq[T], reconciledVersionStr: T => String, requestedVersionStr: T => String, getFiles: T => Seq[(String, String)]): String = {
|
||||
def apply[T](roots: IndexedSeq[T], conflictResolutionForRoots: Map[String, String], overrideClassifiers: Set[String])
|
||||
(children: T => Seq[T], reconciledVersionStr: T => String, requestedVersionStr: T => String, getFile: T => Option[String]): String = {
|
||||
|
||||
val rootDeps: ParSeq[DepNode] = roots.par.map(r => {
|
||||
|
||||
|
|
@ -64,10 +73,10 @@ object JsonReport {
|
|||
|
||||
val acc = scala.collection.mutable.Set[String]()
|
||||
flattenDeps(Seq(r), Set(), acc)
|
||||
DepNode(reconciledVersionStr(r), getFiles(r).toVector, acc.toSet)
|
||||
DepNode(reconciledVersionStr(r), getFile(r), acc.toSet)
|
||||
|
||||
})
|
||||
val report = ReportNode(conflictResolutionForRoots, rootDeps.toVector, ReportNode.version)
|
||||
val report = ReportNode(conflictResolutionForRoots, rootDeps.toVector.sortBy(_.coord), ReportNode.version)
|
||||
printer.pretty(report.asJson)
|
||||
}
|
||||
|
||||
|
|
@ -80,7 +89,9 @@ final case class JsonElem(dep: Dependency,
|
|||
resolution: Resolution,
|
||||
colors: Boolean,
|
||||
printExclusions: Boolean,
|
||||
excluded: Boolean) {
|
||||
excluded: Boolean,
|
||||
overrideClassifiers: Set[String]
|
||||
) {
|
||||
|
||||
val (red, yellow, reset) =
|
||||
if (colors)
|
||||
|
|
@ -89,23 +100,24 @@ final case class JsonElem(dep: Dependency,
|
|||
("", "", "")
|
||||
|
||||
// This is used to printing json output
|
||||
// Seq of (classifier, file path) tuple
|
||||
lazy val downloadedFiles: Seq[(String, String)] = {
|
||||
jsonPrintRequirement match {
|
||||
case Some(req) =>
|
||||
// Option of the file path
|
||||
lazy val downloadedFile: Option[String] = {
|
||||
jsonPrintRequirement.flatMap(req =>
|
||||
req.depToArtifacts.getOrElse(dep, Seq())
|
||||
.map(x => (x.classifier, req.fileByArtifact.get(x.url)))
|
||||
.filter(_._2.isDefined)
|
||||
.map(x => (x._1, x._2.get.getPath))
|
||||
case None => Seq()
|
||||
}
|
||||
.filter(_.classifier == dep.attributes.classifier)
|
||||
.map(x => req.fileByArtifact.get(x.url))
|
||||
.filter(_.isDefined)
|
||||
.filter(_.nonEmpty)
|
||||
.map(_.get.getPath)
|
||||
.headOption
|
||||
)
|
||||
}
|
||||
|
||||
lazy val reconciledVersion: String = resolution.reconciledVersions
|
||||
.getOrElse(dep.module, dep.version)
|
||||
|
||||
// These are used to printing json output
|
||||
val reconciledVersionStr = s"${dep.module}:$reconciledVersion"
|
||||
val reconciledVersionStr = s"${dep.mavenPrefix}:$reconciledVersion"
|
||||
val requestedVersionStr = s"${dep.module}:${dep.version}"
|
||||
|
||||
lazy val repr =
|
||||
|
|
@ -150,6 +162,12 @@ final case class JsonElem(dep: Dependency,
|
|||
withReconciledVersions = false
|
||||
).sortBy { trDep =>
|
||||
(trDep.module.organization, trDep.module.name, trDep.version)
|
||||
}.map { d =>
|
||||
if (overrideClassifiers.contains(dep0.attributes.classifier)) {
|
||||
d.copy(attributes = d.attributes.copy(classifier = dep0.attributes.classifier))
|
||||
} else {
|
||||
d
|
||||
}
|
||||
}
|
||||
|
||||
def excluded = resolution
|
||||
|
|
@ -164,17 +182,18 @@ final case class JsonElem(dep: Dependency,
|
|||
.filterNot(dependencies.map(_.moduleVersion).toSet).map {
|
||||
case (mod, ver) =>
|
||||
JsonElem(
|
||||
Dependency(mod, ver, "", Set.empty, Attributes("", ""), false, false),
|
||||
Dependency(mod, ver, "", Set.empty, Attributes("", ""), optional = false, transitive = false),
|
||||
artifacts,
|
||||
jsonPrintRequirement,
|
||||
resolution,
|
||||
colors,
|
||||
printExclusions,
|
||||
excluded = true
|
||||
excluded = true,
|
||||
overrideClassifiers = overrideClassifiers
|
||||
)
|
||||
}
|
||||
|
||||
dependencies.map(JsonElem(_, artifacts, jsonPrintRequirement, resolution, colors, printExclusions, excluded = false)) ++
|
||||
dependencies.map(JsonElem(_, artifacts, jsonPrintRequirement, resolution, colors, printExclusions, excluded = false, overrideClassifiers = overrideClassifiers)) ++
|
||||
(if (printExclusions) excluded else Nil)
|
||||
}
|
||||
|
||||
|
|
@ -183,5 +202,5 @@ final case class JsonElem(dep: Dependency,
|
|||
* children's children, causing performance issue. Hash collision should be rare, but when that happens, the
|
||||
* default equality check should take of the recursive aspect of `children`.
|
||||
*/
|
||||
override def hashCode(): Int = Objects.hash(dep, requestedVersionStr, reconciledVersion, downloadedFiles)
|
||||
override def hashCode(): Int = Objects.hash(dep, requestedVersionStr, reconciledVersion, downloadedFile)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ junit_tests(
|
|||
"3rdparty/jvm:scalatest",
|
||||
"cli/src/main/scala-2.12:cli",
|
||||
],
|
||||
sources = ["CliFetchIntegrationTest.scala", "CliUnitTest.scala"],
|
||||
sources = rglobs("*.scala", exclude = ["CliTestLib.scala", "CliBootstrapIntegrationTest.scala"]),
|
||||
)
|
||||
|
||||
scala_library(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package coursier.cli
|
||||
|
||||
import java.io._
|
||||
import java.util.zip.ZipInputStream
|
||||
|
||||
import java.net.URLEncoder.encode
|
||||
import argonaut.Argonaut._
|
||||
import coursier.cli.util.{DepNode, ReportNode}
|
||||
|
|
@ -27,6 +27,8 @@ class CliFetchIntegrationTest extends FlatSpec with CliTestLib {
|
|||
}
|
||||
}
|
||||
|
||||
private val fileNameLength: DepNode => Int = _.file.getOrElse("").length
|
||||
|
||||
"Normal fetch" should "get all files" in {
|
||||
|
||||
val fetchOpt = FetchOptions(common = CommonOptions())
|
||||
|
|
@ -92,7 +94,7 @@ class CliFetchIntegrationTest extends FlatSpec with CliTestLib {
|
|||
|
||||
// org.apache.commons:commons-compress:1.4.1 should not contain deps underneath it.
|
||||
val compressNode = node.dependencies.find(_.coord == "org.apache.commons:commons-compress:1.4.1")
|
||||
assert(compressNode.isDefined)
|
||||
assert(node.dependencies.exists(_.coord == "org.apache.commons:commons-compress:1.4.1"))
|
||||
assert(compressNode.get.dependencies.isEmpty)
|
||||
}
|
||||
}
|
||||
|
|
@ -200,10 +202,10 @@ class CliFetchIntegrationTest extends FlatSpec with CliTestLib {
|
|||
|
||||
val node: ReportNode = getReportFromJson(jsonFile)
|
||||
|
||||
val compressNode = node.dependencies.find(_.coord == "org.apache.commons:commons-compress:1.5")
|
||||
val compressNode = node.dependencies.find(_.coord == "org.apache.commons:commons-compress:jar:tests:1.5")
|
||||
|
||||
assert(compressNode.isDefined)
|
||||
assert(compressNode.get.files.head._1 == "tests")
|
||||
assert(compressNode.get.files.head._2.contains("commons-compress-1.5-tests.jar"))
|
||||
compressNode.get.file.map(f => assert(f.contains("commons-compress-1.5-tests.jar"))).orElse(fail("Not Defined"))
|
||||
assert(compressNode.get.dependencies.contains("org.tukaani:xz:1.2"))
|
||||
}
|
||||
}
|
||||
|
|
@ -237,14 +239,15 @@ class CliFetchIntegrationTest extends FlatSpec with CliTestLib {
|
|||
val node: ReportNode = getReportFromJson(jsonFile)
|
||||
|
||||
val compressNodes: Seq[DepNode] = node.dependencies
|
||||
.filter(_.coord == "org.apache.commons:commons-compress:1.5")
|
||||
.sortBy(_.files.head._1.length) // sort by first classifier length
|
||||
assert(compressNodes.length == 2)
|
||||
assert(compressNodes.head.files.head._1 == "")
|
||||
assert(compressNodes.head.files.head._2.contains("commons-compress-1.5.jar"))
|
||||
.filter(_.coord.startsWith("org.apache.commons:commons-compress"))
|
||||
.sortBy(_.coord.length) // sort by coord length
|
||||
|
||||
assert(compressNodes.last.files.head._1 == "tests")
|
||||
assert(compressNodes.last.files.head._2.contains("commons-compress-1.5-tests.jar"))
|
||||
assert(compressNodes.length == 2)
|
||||
assert(compressNodes.head.coord == "org.apache.commons:commons-compress:1.5")
|
||||
compressNodes.head.file.map( f => assert(f.contains("commons-compress-1.5.jar"))).orElse(fail("Not Defined"))
|
||||
|
||||
assert(compressNodes.last.coord == "org.apache.commons:commons-compress:jar:tests:1.5")
|
||||
compressNodes.last.file.map( f => assert(f.contains("commons-compress-1.5-tests.jar"))).orElse(fail("Not Defined"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -266,8 +269,7 @@ class CliFetchIntegrationTest extends FlatSpec with CliTestLib {
|
|||
val node: ReportNode = getReportFromJson(jsonFile)
|
||||
val compressNode = node.dependencies.find(_.coord == "org.apache.commons:commons-compress:1.5")
|
||||
assert(compressNode.isDefined)
|
||||
assert(compressNode.get.files.head._1 == "")
|
||||
assert(compressNode.get.files.head._2.contains("commons-compress-1.5.jar"))
|
||||
compressNode.get.file.map( f => assert(f.contains("commons-compress-1.5.jar"))).orElse(fail("Not Defined"))
|
||||
|
||||
assert(compressNode.get.dependencies.isEmpty)
|
||||
}
|
||||
|
|
@ -290,10 +292,9 @@ class CliFetchIntegrationTest extends FlatSpec with CliTestLib {
|
|||
|
||||
val node: ReportNode = getReportFromJson(jsonFile)
|
||||
|
||||
val compressNode = node.dependencies.find(_.coord == "org.apache.commons:commons-compress:1.5")
|
||||
val compressNode = node.dependencies.find(_.coord == "org.apache.commons:commons-compress:jar:tests:1.5")
|
||||
assert(compressNode.isDefined)
|
||||
assert(compressNode.get.files.head._1 == "tests")
|
||||
assert(compressNode.get.files.head._2.contains("commons-compress-1.5-tests.jar"))
|
||||
compressNode.get.file.map( f => assert(f.contains("commons-compress-1.5-tests.jar"))).orElse(fail("Not Defined"))
|
||||
|
||||
assert(compressNode.get.dependencies.isEmpty)
|
||||
}
|
||||
|
|
@ -321,10 +322,10 @@ class CliFetchIntegrationTest extends FlatSpec with CliTestLib {
|
|||
|
||||
assert(!node.dependencies.exists(_.coord == "org.apache.commons:commons-compress:1.5"))
|
||||
|
||||
val compressNode = node.dependencies.find(_.coord == "org.apache.commons:commons-compress:1.4.1")
|
||||
val compressNode = node.dependencies.find(_.coord == "org.apache.commons:commons-compress:jar:tests:1.4.1")
|
||||
|
||||
assert(compressNode.isDefined)
|
||||
assert(compressNode.get.files.head._1 == "tests")
|
||||
assert(compressNode.get.files.head._2.contains("commons-compress-1.4.1-tests.jar"))
|
||||
compressNode.get.file.map( f => assert(f.contains("commons-compress-1.4.1-tests.jar"))).orElse(fail("Not Defined"))
|
||||
|
||||
assert(compressNode.get.dependencies.size == 1)
|
||||
assert(compressNode.get.dependencies.head == "org.tukaani:xz:1.0")
|
||||
|
|
@ -352,10 +353,10 @@ class CliFetchIntegrationTest extends FlatSpec with CliTestLib {
|
|||
|
||||
assert(!node.dependencies.exists(_.coord == "org.apache.commons:commons-compress:1.5"))
|
||||
|
||||
val compressNode = node.dependencies.find(_.coord == "org.apache.commons:commons-compress:1.4.1")
|
||||
val compressNode = node.dependencies.find(_.coord == "org.apache.commons:commons-compress:jar:tests:1.4.1")
|
||||
|
||||
assert(compressNode.isDefined)
|
||||
assert(compressNode.get.files.head._1 == "tests")
|
||||
assert(compressNode.get.files.head._2.contains("commons-compress-1.4.1-tests.jar"))
|
||||
compressNode.get.file.map( f => assert(f.contains("commons-compress-1.4.1-tests.jar"))).orElse(fail("Not Defined"))
|
||||
|
||||
assert(compressNode.get.dependencies.isEmpty)
|
||||
}
|
||||
|
|
@ -381,6 +382,29 @@ class CliFetchIntegrationTest extends FlatSpec with CliTestLib {
|
|||
assert(!node.dependencies.exists(_.coord.startsWith("org.scala-lang:scala-library:2.11.")))
|
||||
}
|
||||
|
||||
"com.spotify:helios-testing:0.9.193" should "have dependencies with classifiers" in withFile() {
|
||||
(excludeFile, _) =>
|
||||
withFile() {
|
||||
(jsonFile, _) => {
|
||||
val commonOpt = CommonOptions(jsonOutputFile = jsonFile.getPath)
|
||||
val fetchOpt = FetchOptions(common = commonOpt)
|
||||
|
||||
val heliosCoord = "com.spotify:helios-testing:0.9.193"
|
||||
|
||||
Fetch(
|
||||
fetchOpt,
|
||||
RemainingArgs(Seq(heliosCoord), Seq())
|
||||
)
|
||||
val node: ReportNode = getReportFromJson(jsonFile)
|
||||
val testEntry: DepNode = node.dependencies.find(_.coord == heliosCoord).get
|
||||
assert(
|
||||
testEntry.dependencies.exists(_.startsWith("com.spotify:docker-client:jar:shaded:")))
|
||||
assert(
|
||||
node.dependencies.exists(_.coord.startsWith("com.spotify:docker-client:jar:shaded:")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Result:
|
||||
* |└─ org.apache.commons:commons-compress:1.5
|
||||
|
|
@ -411,11 +435,10 @@ class CliFetchIntegrationTest extends FlatSpec with CliTestLib {
|
|||
|
||||
val depNodes: Seq[DepNode] = node.dependencies
|
||||
.filter(_.coord == "org.apache.commons:commons-compress:1.5")
|
||||
.sortBy(_.files.head._1.length)
|
||||
.sortBy(fileNameLength)
|
||||
assert(depNodes.length == 1)
|
||||
|
||||
val urlInJsonFile = depNodes.head.files.head._2
|
||||
assert(depNodes.head.files.head._1 == "")
|
||||
val urlInJsonFile = depNodes.head.file.get
|
||||
assert(urlInJsonFile.contains(path))
|
||||
|
||||
// open jar and inspect contents
|
||||
|
|
@ -453,10 +476,9 @@ class CliFetchIntegrationTest extends FlatSpec with CliTestLib {
|
|||
|
||||
val depNodes: Seq[DepNode] = node.dependencies
|
||||
.filter(_.coord == "org.apache.commons:commons-compress:1.5")
|
||||
.sortBy(_.files.head._1.length)
|
||||
.sortBy(fileNameLength)
|
||||
assert(depNodes.length == 1)
|
||||
assert(depNodes.head.files.head._1 == "")
|
||||
assert(depNodes.head.files.head._2.contains("junit/junit/4.12/junit-4.12.jar"))
|
||||
depNodes.head.file.map( f => assert(f.contains("junit/junit/4.12/junit-4.12.jar"))).orElse(fail("Not Defined"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -487,10 +509,9 @@ class CliFetchIntegrationTest extends FlatSpec with CliTestLib {
|
|||
|
||||
val depNodes: Seq[DepNode] = node.dependencies
|
||||
.filter(_.coord == "a:b:c")
|
||||
.sortBy(_.files.head._1.length)
|
||||
.sortBy(fileNameLength)
|
||||
assert(depNodes.length == 1)
|
||||
assert(depNodes.head.files.head._1 == "")
|
||||
assert(depNodes.head.files.head._2.contains("junit/junit/4.12/junit-4.12.jar"))
|
||||
depNodes.head.file.map( f => assert(f.contains("junit/junit/4.12/junit-4.12.jar"))).orElse(fail("Not Defined"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -519,12 +540,96 @@ class CliFetchIntegrationTest extends FlatSpec with CliTestLib {
|
|||
val node: ReportNode = getReportFromJson(jsonFile)
|
||||
|
||||
val depNodes: Seq[DepNode] = node.dependencies
|
||||
.filter(_.coord == "org.apache.commons:commons-compress:1.5")
|
||||
.sortBy(_.files.head._1.length)
|
||||
.filter(_.coord.startsWith("org.apache.commons:commons-compress:"))
|
||||
.sortBy(fileNameLength)
|
||||
|
||||
|
||||
val coords: Seq[String] = node.dependencies
|
||||
.map(_.coord)
|
||||
.sorted
|
||||
|
||||
assert(depNodes.length == 1)
|
||||
// classifier doesn't matter when we have a url so it is not listed
|
||||
assert(depNodes.head.files.head._1 == "")
|
||||
assert(depNodes.head.files.head._2.contains("junit/junit/4.12/junit-4.12.jar"))
|
||||
depNodes.head.file.map( f => assert(f.contains("junit/junit/4.12/junit-4.12.jar"))).orElse(fail("Not Defined"))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Result:
|
||||
* |└─ org.apache.commons:commons-compress:1.5
|
||||
* | └─ org.tukaani:xz:1.2
|
||||
* |└─ org.tukaani:xz:1.2 // with the file from the URL
|
||||
*/
|
||||
"external dep url with classifier that is a transitive dep" should "fetch junit-4.12.jar and classifier gets thrown away" in withFile() {
|
||||
(jsonFile, _) => {
|
||||
val commonOpt = CommonOptions(jsonOutputFile = jsonFile.getPath)
|
||||
val fetchOpt = FetchOptions(common = commonOpt)
|
||||
|
||||
// encode path to different jar than requested
|
||||
val externalUrl = encode("http://central.maven.org/maven2/junit/junit/4.12/junit-4.12.jar", "UTF-8")
|
||||
|
||||
Fetch.run(
|
||||
fetchOpt,
|
||||
RemainingArgs(
|
||||
Seq(
|
||||
"org.apache.commons:commons-compress:1.5",
|
||||
"org.tukaani:xz:1.2,classifier=tests,url="+externalUrl
|
||||
),
|
||||
Seq()
|
||||
)
|
||||
)
|
||||
|
||||
val node: ReportNode = getReportFromJson(jsonFile)
|
||||
val depNodes: Seq[DepNode] = node.dependencies
|
||||
.filter(_.coord.startsWith("org.tukaani:xz:"))
|
||||
.sortBy(fileNameLength)
|
||||
val coords: Seq[String] = node.dependencies.map(_.coord).sorted
|
||||
|
||||
assert(coords == Seq("org.apache.commons:commons-compress:1.5", "org.tukaani:xz:1.2"))
|
||||
assert(depNodes.length == 1)
|
||||
assert(depNodes.last.file.isDefined)
|
||||
depNodes.last.file.map( f => assert(f.contains("junit/junit/4.12/junit-4.12.jar"))).orElse(fail("Not Defined"))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Result:
|
||||
* |└─ org.apache.commons:commons-compress:1.5,classifier=sources
|
||||
* └─ org.tukaani:xz:1.2,classifier=sources
|
||||
*/
|
||||
"classifier sources" should "fetch sources jar" in withFile() {
|
||||
(jsonFile, _) => {
|
||||
val commonOpt = CommonOptions(jsonOutputFile = jsonFile.getPath)
|
||||
val fetchOpt = FetchOptions(common = commonOpt, sources=true)
|
||||
|
||||
// encode path to different jar than requested
|
||||
|
||||
Fetch.run(
|
||||
fetchOpt,
|
||||
RemainingArgs(
|
||||
Seq(
|
||||
"org.apache.commons:commons-compress:1.5,classifier=sources"
|
||||
),
|
||||
Seq()
|
||||
)
|
||||
)
|
||||
val node: ReportNode = getReportFromJson(jsonFile)
|
||||
val coords: Seq[String] = node.dependencies.map(_.coord).sorted
|
||||
val depNodes: Seq[DepNode] = node.dependencies
|
||||
.filter(_.coord.startsWith("org.apache.commons"))
|
||||
.sortBy(fileNameLength)
|
||||
|
||||
assert(depNodes.length == 1)
|
||||
assert(depNodes.head.file.isDefined)
|
||||
depNodes.head.file.map(f => assert(f.contains("1.5-sources.jar"))).orElse(fail("Not Defined"))
|
||||
depNodes.head.dependencies.foreach(d => {
|
||||
assert(d.contains(":sources:"))
|
||||
})
|
||||
|
||||
assert(coords == Seq(
|
||||
"org.apache.commons:commons-compress:jar:sources:1.5",
|
||||
"org.tukaani:xz:jar:sources:1.2")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -559,24 +664,23 @@ class CliFetchIntegrationTest extends FlatSpec with CliTestLib {
|
|||
|
||||
val compressNodes = depNodes
|
||||
.filter(_.coord == "org.apache.commons:commons-compress:1.5")
|
||||
.sortBy(_.files.head._1.length)
|
||||
.sortBy(fileNameLength)
|
||||
assert(compressNodes.length == 1)
|
||||
assert(compressNodes.head.files.head._1 == "")
|
||||
assert(compressNodes.head.files.head._2.contains("junit/junit/4.12/junit-4.12.jar"))
|
||||
compressNodes.head.file.map( f => assert(f.contains("junit/junit/4.12/junit-4.12.jar"))).orElse(fail("Not Defined"))
|
||||
|
||||
val jacksonMapperNodes = depNodes
|
||||
.filter(_.coord == "org.codehaus.jackson:jackson-mapper-asl:1.8.8")
|
||||
.sortBy(_.files.head._1.length)
|
||||
.sortBy(fileNameLength)
|
||||
assert(jacksonMapperNodes.length == 1)
|
||||
assert(jacksonMapperNodes.head.files.head._2.contains("org/codehaus/jackson/jackson-mapper-asl/1.8.8/jackson-mapper-asl-1.8.8.jar"))
|
||||
jacksonMapperNodes.head.file.map( f => assert(f.contains("org/codehaus/jackson/jackson-mapper-asl/1.8.8/jackson-mapper-asl-1.8.8.jar"))).orElse(fail("Not Defined"))
|
||||
assert(jacksonMapperNodes.head.dependencies.size == 1)
|
||||
assert(jacksonMapperNodes.head.dependencies.head == "org.codehaus.jackson:jackson-core-asl:1.8.8")
|
||||
|
||||
val jacksonCoreNodes = depNodes
|
||||
.filter(_.coord == "org.codehaus.jackson:jackson-core-asl:1.8.8")
|
||||
.sortBy(_.files.head._1.length)
|
||||
.sortBy(fileNameLength)
|
||||
assert(jacksonCoreNodes.length == 1)
|
||||
assert(jacksonCoreNodes.head.files.head._2.contains("org/codehaus/jackson/jackson-core-asl/1.8.8/jackson-core-asl-1.8.8.jar"))
|
||||
jacksonCoreNodes.head.file.map( f => assert(f.contains("org/codehaus/jackson/jackson-core-asl/1.8.8/jackson-core-asl-1.8.8.jar"))).orElse(fail("Not Defined"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -634,8 +738,7 @@ class CliFetchIntegrationTest extends FlatSpec with CliTestLib {
|
|||
|
||||
val depNodes: Seq[DepNode] = node.dependencies
|
||||
assert(depNodes.length == 1)
|
||||
assert(depNodes.head.files.head._1 == "")
|
||||
assert(depNodes.head.files.head._2.contains("junit/junit/4.12/junit-4.12.jar"))
|
||||
depNodes.head.file.map( f => assert(f.contains("junit/junit/4.12/junit-4.12.jar"))).orElse(fail("Not Defined"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -666,10 +769,9 @@ class CliFetchIntegrationTest extends FlatSpec with CliTestLib {
|
|||
|
||||
val depNodes: Seq[DepNode] = node.dependencies
|
||||
.filter(_.coord == "org.apache.commons:commons-compress:1.5")
|
||||
.sortBy(_.files.head._1.length)
|
||||
.sortBy(fileNameLength)
|
||||
assert(depNodes.length == 1)
|
||||
assert(depNodes.head.files.head._1 == "")
|
||||
assert(depNodes.head.files.head._2.contains("junit/junit/4.12/junit-4.12.jar"))
|
||||
depNodes.head.file.map( f => assert(f.contains("junit/junit/4.12/junit-4.12.jar"))).orElse(fail("Not Defined"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -701,7 +803,7 @@ class CliFetchIntegrationTest extends FlatSpec with CliTestLib {
|
|||
|
||||
val depNode = node.dependencies.find(_.coord == "org.apache.commons:commons-compress:1.5")
|
||||
assert(depNode.isDefined)
|
||||
assert(depNode.get.files.head._2.contains("commons-compress-1.5.jar"))
|
||||
depNode.get.file.map( f => assert(f.contains("commons-compress-1.5.jar"))).orElse(fail("Not Defined"))
|
||||
|
||||
assert(depNode.get.dependencies.size == 1)
|
||||
assert(depNode.get.dependencies.head.contains("org.tukaani:xz:1.2"))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
package coursier.cli.util
|
||||
|
||||
import coursier.cli.CliTestLib
|
||||
import org.junit.runner.RunWith
|
||||
import org.scalatest.FlatSpec
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class JsonReportTest extends FlatSpec with CliTestLib {
|
||||
"empty JsonReport" should "be empty" in {
|
||||
val report: String = JsonReport[String](IndexedSeq(), Map(), Set())(
|
||||
children = _ => Seq(),
|
||||
reconciledVersionStr = _ => "",
|
||||
requestedVersionStr = _ => "",
|
||||
getFile = _ => Option("")
|
||||
)
|
||||
|
||||
assert(
|
||||
report == "{\"conflict_resolution\":{},\"dependencies\":[],\"version\":\"0.1.0\"}")
|
||||
}
|
||||
|
||||
"JsonReport containing two deps" should "not be empty" in {
|
||||
val children = Map("a" -> Seq("b"), "b" -> Seq())
|
||||
val report: String = JsonReport[String](
|
||||
roots = IndexedSeq("a", "b"),
|
||||
conflictResolutionForRoots = Map(),
|
||||
overrideClassifiers = Set()
|
||||
)(
|
||||
children = children(_),
|
||||
reconciledVersionStr = s => s"$s:reconciled",
|
||||
requestedVersionStr = s => s"$s:requested",
|
||||
getFile = _ => Option("")
|
||||
)
|
||||
|
||||
assert(
|
||||
report == "{\"conflict_resolution\":{},\"dependencies\":[" +
|
||||
"{\"coord\":\"a:reconciled\",\"file\":\"\",\"dependencies\":[\"b:reconciled\"]}," +
|
||||
"{\"coord\":\"b:reconciled\",\"file\":\"\",\"dependencies\":[]}]," +
|
||||
"\"version\":\"0.1.0\"}")
|
||||
}
|
||||
"JsonReport containing two deps" should "be sorted alphabetically regardless of input order" in {
|
||||
val children = Map("a" -> Seq("b"), "b" -> Seq())
|
||||
val report: String = JsonReport[String](
|
||||
roots = IndexedSeq( "b", "a"),
|
||||
conflictResolutionForRoots = Map(),
|
||||
overrideClassifiers = Set()
|
||||
)(
|
||||
children = children(_),
|
||||
reconciledVersionStr = s => s"$s:reconciled",
|
||||
requestedVersionStr = s => s"$s:requested",
|
||||
getFile = _ => Option("")
|
||||
)
|
||||
|
||||
assert(
|
||||
report == "{\"conflict_resolution\":{},\"dependencies\":[" +
|
||||
"{\"coord\":\"a:reconciled\",\"file\":\"\",\"dependencies\":[\"b:reconciled\"]}," +
|
||||
"{\"coord\":\"b:reconciled\",\"file\":\"\",\"dependencies\":[]}]," +
|
||||
"\"version\":\"0.1.0\"}")
|
||||
}
|
||||
}
|
||||
|
|
@ -58,6 +58,14 @@ final case class Dependency(
|
|||
lazy val moduleVersion = (module, version)
|
||||
|
||||
override lazy val hashCode = Dependency.unapply(this).get.hashCode()
|
||||
|
||||
def mavenPrefix: String = {
|
||||
if (attributes.isEmpty)
|
||||
module.orgName
|
||||
else {
|
||||
s"${module.orgName}:${attributes.packagingAndClassifier}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Maven-specific
|
||||
|
|
@ -65,6 +73,19 @@ final case class Attributes(
|
|||
`type`: String,
|
||||
classifier: String
|
||||
) {
|
||||
def packaging: String = if (`type`.isEmpty)
|
||||
"jar"
|
||||
else
|
||||
`type`
|
||||
|
||||
def packagingAndClassifier: String = if (isEmpty) {
|
||||
""
|
||||
} else if (classifier.isEmpty) {
|
||||
packaging
|
||||
} else {
|
||||
s"$packaging:$classifier"
|
||||
}
|
||||
|
||||
def publication(name: String, ext: String): Publication =
|
||||
Publication(name, `type`, ext, classifier)
|
||||
|
||||
|
|
@ -238,4 +259,4 @@ final case class Authentication(
|
|||
) {
|
||||
override def toString: String =
|
||||
s"Authentication($user, *******)"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue