mirror of https://github.com/sbt/sbt.git
Snapshot versioning
Fixes https://github.com/alexarchambault/coursier/issues/61
This commit is contained in:
parent
f2331b388e
commit
0418c790b6
|
|
@ -49,7 +49,15 @@ case class MavenRepository(
|
|||
val url = new URL(urlStr)
|
||||
|
||||
def log = Task(logger.foreach(_.downloading(urlStr)))
|
||||
def get = MavenRepository.readFully(url.openStream())
|
||||
def get = {
|
||||
val conn = url.openConnection()
|
||||
// Dummy user-agent instead of the default "Java/...",
|
||||
// so that we are not returned incomplete/erroneous metadata
|
||||
// (Maven 2 compatibility? - happens for snapshot versioning metadata,
|
||||
// this is SO FUCKING CRAZY)
|
||||
conn.setRequestProperty("User-Agent", "")
|
||||
MavenRepository.readFully(conn.getInputStream())
|
||||
}
|
||||
|
||||
log.flatMap(_ => get)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,8 @@ case class Project(
|
|||
dependencyManagement: Seq[Dependency],
|
||||
properties: Map[String, String],
|
||||
profiles: Seq[Profile],
|
||||
versions: Option[Versions]
|
||||
versions: Option[Versions],
|
||||
snapshotVersioning: Option[SnapshotVersioning]
|
||||
) {
|
||||
def moduleVersion = (module, version)
|
||||
}
|
||||
|
|
@ -99,6 +100,25 @@ object Versions {
|
|||
)
|
||||
}
|
||||
|
||||
case class SnapshotVersion(
|
||||
classifier: String,
|
||||
extension: String,
|
||||
value: String,
|
||||
updated: Option[Versions.DateTime]
|
||||
)
|
||||
|
||||
case class SnapshotVersioning(
|
||||
module: Module,
|
||||
version: String,
|
||||
latest: String,
|
||||
release: String,
|
||||
timestamp: String,
|
||||
buildNumber: Option[Int],
|
||||
localCopy: Option[Boolean],
|
||||
lastUpdated: Option[Versions.DateTime],
|
||||
snapshotVersions: Seq[SnapshotVersion]
|
||||
)
|
||||
|
||||
case class Artifact(
|
||||
url: String,
|
||||
checksumUrls: Map[String, String],
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ object Repository {
|
|||
|
||||
case class MavenSource(root: String, ivyLike: Boolean) extends Artifact.Source {
|
||||
import Repository._
|
||||
import BaseMavenRepository._
|
||||
|
||||
def artifacts(
|
||||
dependency: Dependency,
|
||||
|
|
@ -108,7 +109,7 @@ case class MavenSource(root: String, ivyLike: Boolean) extends Artifact.Source {
|
|||
): Seq[Artifact] = {
|
||||
|
||||
def ivyLikePath0(subDir: String, baseSuffix: String, ext: String) =
|
||||
BaseMavenRepository.ivyLikePath(
|
||||
ivyLikePath(
|
||||
dependency.module.organization,
|
||||
dependency.module.name,
|
||||
project.version,
|
||||
|
|
@ -120,12 +121,20 @@ case class MavenSource(root: String, ivyLike: Boolean) extends Artifact.Source {
|
|||
val path =
|
||||
if (ivyLike)
|
||||
ivyLikePath0(dependency.attributes.`type` + "s", "", dependency.attributes.`type`)
|
||||
else
|
||||
else {
|
||||
val versioning =
|
||||
project
|
||||
.snapshotVersioning
|
||||
.flatMap(versioning =>
|
||||
mavenVersioning(versioning, dependency.attributes.classifier, dependency.attributes.`type`)
|
||||
)
|
||||
|
||||
dependency.module.organization.split('.').toSeq ++ Seq(
|
||||
dependency.module.name,
|
||||
project.version,
|
||||
s"${dependency.module.name}-${project.version}${Some(dependency.attributes.classifier).filter(_.nonEmpty).map("-"+_).mkString}.${dependency.attributes.`type`}"
|
||||
s"${dependency.module.name}-${versioning getOrElse project.version}${Some(dependency.attributes.classifier).filter(_.nonEmpty).map("-"+_).mkString}.${dependency.attributes.`type`}"
|
||||
)
|
||||
}
|
||||
|
||||
var artifact =
|
||||
Artifact(
|
||||
|
|
@ -139,6 +148,9 @@ case class MavenSource(root: String, ivyLike: Boolean) extends Artifact.Source {
|
|||
if (dependency.attributes.`type` == "jar") {
|
||||
artifact = artifact.withDefaultSignature
|
||||
|
||||
// FIXME Snapshot versioning of sources and javadoc is not taken into account here.
|
||||
// Will be ok if it's the same as the main JAR though.
|
||||
|
||||
artifact =
|
||||
if (ivyLike) {
|
||||
val srcPath = root + ivyLikePath0("srcs", "-sources", "jar").mkString("/")
|
||||
|
|
@ -181,6 +193,17 @@ object BaseMavenRepository {
|
|||
s"$name$baseSuffix.$ext"
|
||||
)
|
||||
|
||||
def mavenVersioning(
|
||||
snapshotVersioning: SnapshotVersioning,
|
||||
classifier: String,
|
||||
extension: String
|
||||
): Option[String] =
|
||||
snapshotVersioning
|
||||
.snapshotVersions
|
||||
.find(v => v.classifier == classifier && v.extension == extension)
|
||||
.map(_.value)
|
||||
.filter(_.nonEmpty)
|
||||
|
||||
}
|
||||
|
||||
abstract class BaseMavenRepository(
|
||||
|
|
@ -198,16 +221,27 @@ abstract class BaseMavenRepository(
|
|||
|
||||
val source = MavenSource(root, ivyLike)
|
||||
|
||||
def projectArtifact(module: Module, version: String): Artifact = {
|
||||
def projectArtifact(
|
||||
module: Module,
|
||||
version: String,
|
||||
versioningValue: Option[String]
|
||||
): Artifact = {
|
||||
|
||||
val path = (
|
||||
if (ivyLike)
|
||||
ivyLikePath(module.organization, module.name, version, "poms", "", "pom")
|
||||
ivyLikePath(
|
||||
module.organization,
|
||||
module.name,
|
||||
versioningValue getOrElse version,
|
||||
"poms",
|
||||
"",
|
||||
"pom"
|
||||
)
|
||||
else
|
||||
module.organization.split('.').toSeq ++ Seq(
|
||||
module.name,
|
||||
version,
|
||||
s"${module.name}-$version.pom"
|
||||
s"${module.name}-${versioningValue getOrElse version}.pom"
|
||||
)
|
||||
) .map(encodeURIComponent)
|
||||
|
||||
|
|
@ -242,6 +276,32 @@ abstract class BaseMavenRepository(
|
|||
Some(artifact)
|
||||
}
|
||||
|
||||
def snapshotVersioningArtifact(
|
||||
module: Module,
|
||||
version: String
|
||||
): Option[Artifact] =
|
||||
if (ivyLike) None
|
||||
else {
|
||||
val path = (
|
||||
module.organization.split('.').toSeq ++ Seq(
|
||||
module.name,
|
||||
version,
|
||||
"maven-metadata.xml"
|
||||
)
|
||||
) .map(encodeURIComponent)
|
||||
|
||||
val artifact =
|
||||
Artifact(
|
||||
path.mkString("/"),
|
||||
Map.empty,
|
||||
Map.empty,
|
||||
Attributes("pom", "")
|
||||
)
|
||||
.withDefaultChecksums
|
||||
|
||||
Some(artifact)
|
||||
}
|
||||
|
||||
def versions(
|
||||
module: Module,
|
||||
cachePolicy: CachePolicy = CachePolicy.Default
|
||||
|
|
@ -265,14 +325,80 @@ abstract class BaseMavenRepository(
|
|||
)
|
||||
}
|
||||
|
||||
def snapshotVersioning(
|
||||
module: Module,
|
||||
version: String,
|
||||
cachePolicy: CachePolicy = CachePolicy.Default
|
||||
): EitherT[Task, String, SnapshotVersioning] = {
|
||||
|
||||
EitherT(
|
||||
snapshotVersioningArtifact(module, version) match {
|
||||
case None => Task.now(-\/("Not supported"))
|
||||
case Some(artifact) =>
|
||||
fetch(artifact, cachePolicy)
|
||||
.run
|
||||
.map(eitherStr =>
|
||||
for {
|
||||
str <- eitherStr
|
||||
xml <- \/.fromEither(compatibility.xmlParse(str))
|
||||
_ <- if (xml.label == "metadata") \/-(()) else -\/("Metadata not found")
|
||||
snapshotVersioning <- Xml.snapshotVersioning(xml)
|
||||
} yield snapshotVersioning
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
def findNoInterval(
|
||||
module: Module,
|
||||
version: String,
|
||||
cachePolicy: CachePolicy
|
||||
): EitherT[Task, String, Project] =
|
||||
EitherT{
|
||||
def withSnapshotVersioning =
|
||||
snapshotVersioning(module, version, cachePolicy)
|
||||
.flatMap { snapshotVersioning =>
|
||||
val versioningOption =
|
||||
mavenVersioning(snapshotVersioning, "", "jar")
|
||||
.orElse(mavenVersioning(snapshotVersioning, "", ""))
|
||||
|
||||
versioningOption match {
|
||||
case None =>
|
||||
EitherT[Task, String, Project](
|
||||
Task.now(-\/("No snapshot versioning value found"))
|
||||
)
|
||||
case versioning @ Some(_) =>
|
||||
findVersioning(module, version, versioning, cachePolicy)
|
||||
.map(_.copy(snapshotVersioning = Some(snapshotVersioning)))
|
||||
}
|
||||
}
|
||||
|
||||
findVersioning(module, version, None, cachePolicy)
|
||||
.run
|
||||
.flatMap{ eitherProj =>
|
||||
if (eitherProj.isLeft)
|
||||
withSnapshotVersioning
|
||||
.run
|
||||
.map(eitherProj0 =>
|
||||
if (eitherProj0.isLeft)
|
||||
eitherProj
|
||||
else
|
||||
eitherProj0
|
||||
)
|
||||
else
|
||||
Task.now(eitherProj)
|
||||
}
|
||||
}
|
||||
|
||||
def findVersioning(
|
||||
module: Module,
|
||||
version: String,
|
||||
versioningValue: Option[String],
|
||||
cachePolicy: CachePolicy
|
||||
): EitherT[Task, String, Project] = {
|
||||
|
||||
EitherT {
|
||||
fetch(projectArtifact(module, version), cachePolicy)
|
||||
fetch(projectArtifact(module, version, versioningValue), cachePolicy)
|
||||
.run
|
||||
.map(eitherStr =>
|
||||
for {
|
||||
|
|
|
|||
|
|
@ -210,10 +210,24 @@ object Xml {
|
|||
depMgmts,
|
||||
properties.toMap,
|
||||
profiles,
|
||||
None,
|
||||
None
|
||||
)
|
||||
}
|
||||
|
||||
def parseDateTime(s: String): Option[Versions.DateTime] =
|
||||
if (s.length == 14 && s.forall(_.isDigit))
|
||||
Some(Versions.DateTime(
|
||||
s.substring(0, 4).toInt,
|
||||
s.substring(4, 6).toInt,
|
||||
s.substring(6, 8).toInt,
|
||||
s.substring(8, 10).toInt,
|
||||
s.substring(10, 12).toInt,
|
||||
s.substring(12, 14).toInt
|
||||
))
|
||||
else
|
||||
None
|
||||
|
||||
def versions(node: Node): String \/ Versions = {
|
||||
import Scalaz._
|
||||
|
||||
|
|
@ -230,24 +244,111 @@ object Xml {
|
|||
release = text(xmlVersioning, "release", "Release version")
|
||||
.getOrElse("")
|
||||
|
||||
versions <- xmlVersioning.child
|
||||
versionsOpt = xmlVersioning.child
|
||||
.find(_.label == "versions")
|
||||
.map(_.child.filter(_.label == "version").flatMap(_.child.collectFirst{case Text(t) => t}))
|
||||
.toRightDisjunction("Version list not found in metadata")
|
||||
|
||||
lastUpdatedOpt = text(xmlVersioning, "lastUpdated", "Last update date and time")
|
||||
.toOption
|
||||
.filter(s => s.length == 14 && s.forall(_.isDigit))
|
||||
.map(s => Versions.DateTime(
|
||||
s.substring(0, 4).toInt,
|
||||
s.substring(4, 6).toInt,
|
||||
s.substring(6, 8).toInt,
|
||||
s.substring(8, 10).toInt,
|
||||
s.substring(10, 12).toInt,
|
||||
s.substring(12, 14).toInt
|
||||
))
|
||||
.flatMap(parseDateTime)
|
||||
|
||||
} yield Versions(latest, release, versions.toList, lastUpdatedOpt)
|
||||
} yield Versions(latest, release, versionsOpt.map(_.toList).getOrElse(Nil), lastUpdatedOpt)
|
||||
}
|
||||
|
||||
def snapshotVersion(node: Node): String \/ SnapshotVersion = {
|
||||
def textOrEmpty(name: String, desc: String) =
|
||||
text(node, name, desc)
|
||||
.toOption
|
||||
.getOrElse("")
|
||||
|
||||
val classifier = textOrEmpty("classifier", "Classifier")
|
||||
val ext = textOrEmpty("extension", "Extensions")
|
||||
val value = textOrEmpty("value", "Value")
|
||||
|
||||
val updatedOpt = text(node, "updated", "Updated")
|
||||
.toOption
|
||||
.flatMap(parseDateTime)
|
||||
|
||||
\/-(SnapshotVersion(
|
||||
classifier,
|
||||
ext,
|
||||
value,
|
||||
updatedOpt
|
||||
))
|
||||
}
|
||||
|
||||
def snapshotVersioning(node: Node): String \/ SnapshotVersioning = {
|
||||
import Scalaz._
|
||||
|
||||
// FIXME Quite similar to Versions above
|
||||
for {
|
||||
organization <- text(node, "groupId", "Organization")
|
||||
name <- text(node, "artifactId", "Name")
|
||||
version = readVersion(node)
|
||||
|
||||
xmlVersioning <- node.child
|
||||
.find(_.label == "versioning")
|
||||
.toRightDisjunction("Versioning info not found in metadata")
|
||||
|
||||
latest = text(xmlVersioning, "latest", "Latest version")
|
||||
.getOrElse("")
|
||||
release = text(xmlVersioning, "release", "Release version")
|
||||
.getOrElse("")
|
||||
|
||||
versionsOpt = xmlVersioning.child
|
||||
.find(_.label == "versions")
|
||||
.map(_.child.filter(_.label == "version").flatMap(_.child.collectFirst{case Text(t) => t}))
|
||||
|
||||
lastUpdatedOpt = text(xmlVersioning, "lastUpdated", "Last update date and time")
|
||||
.toOption
|
||||
.flatMap(parseDateTime)
|
||||
|
||||
xmlSnapshotOpt = xmlVersioning.child
|
||||
.find(_.label == "snapshot")
|
||||
|
||||
timestamp = xmlSnapshotOpt
|
||||
.flatMap(
|
||||
text(_, "timestamp", "Snapshot timestamp")
|
||||
.toOption
|
||||
)
|
||||
.getOrElse("")
|
||||
|
||||
buildNumber = xmlSnapshotOpt
|
||||
.flatMap(
|
||||
text(_, "buildNumber", "Snapshot build number")
|
||||
.toOption
|
||||
)
|
||||
.filter(s => s.nonEmpty && s.forall(_.isDigit))
|
||||
.map(_.toInt)
|
||||
|
||||
localCopy = xmlSnapshotOpt
|
||||
.flatMap(
|
||||
text(_, "localCopy", "Snapshot local copy")
|
||||
.toOption
|
||||
)
|
||||
.collect{
|
||||
case "true" => true
|
||||
case "false" => false
|
||||
}
|
||||
|
||||
xmlSnapshotVersions = xmlVersioning.child
|
||||
.find(_.label == "snapshotVersions")
|
||||
.map(_.child.filter(_.label == "snapshotVersion"))
|
||||
.getOrElse(Seq.empty)
|
||||
snapshotVersions <- xmlSnapshotVersions
|
||||
.toList
|
||||
.traverseU(snapshotVersion)
|
||||
} yield SnapshotVersioning(
|
||||
Module(organization, name),
|
||||
version,
|
||||
latest,
|
||||
release,
|
||||
timestamp,
|
||||
buildNumber,
|
||||
localCopy,
|
||||
lastUpdatedOpt,
|
||||
snapshotVersions
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ package object test {
|
|||
dependencyManagement: Seq[Dependency] = Seq.empty,
|
||||
properties: Map[String, String] = Map.empty,
|
||||
profiles: Seq[Profile] = Seq.empty,
|
||||
versions: Option[core.Versions] = None): Project =
|
||||
core.Project(module, version, dependencies, parent, dependencyManagement, properties, profiles, versions)
|
||||
versions: Option[core.Versions] = None,
|
||||
snapshotVersioning: Option[core.SnapshotVersioning] = None): Project =
|
||||
core.Project(module, version, dependencies, parent, dependencyManagement, properties, profiles, versions, snapshotVersioning)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue