mirror of https://github.com/sbt/sbt.git
Merge pull request #108 from alexarchambault/topic/ivy-cache
Add support for ~/.ivy2/cache as a repository
This commit is contained in:
commit
b7a0c62c25
|
|
@ -16,6 +16,9 @@ import java.io.{ Serializable => _, _ }
|
||||||
|
|
||||||
object Cache {
|
object Cache {
|
||||||
|
|
||||||
|
// Check SHA-1 if available, else be fine with no checksum
|
||||||
|
val defaultChecksums = Seq(Some("SHA-1"), None)
|
||||||
|
|
||||||
private def withLocal(artifact: Artifact, cache: Seq[(String, File)]): Artifact = {
|
private def withLocal(artifact: Artifact, cache: Seq[(String, File)]): Artifact = {
|
||||||
def local(url: String) =
|
def local(url: String) =
|
||||||
if (url.startsWith("file:///"))
|
if (url.startsWith("file:///"))
|
||||||
|
|
@ -398,7 +401,7 @@ object Cache {
|
||||||
artifact: Artifact,
|
artifact: Artifact,
|
||||||
cache: Seq[(String, File)] = default,
|
cache: Seq[(String, File)] = default,
|
||||||
cachePolicy: CachePolicy = CachePolicy.FetchMissing,
|
cachePolicy: CachePolicy = CachePolicy.FetchMissing,
|
||||||
checksums: Seq[Option[String]] = Seq(Some("SHA-1")),
|
checksums: Seq[Option[String]] = defaultChecksums,
|
||||||
logger: Option[Logger] = None,
|
logger: Option[Logger] = None,
|
||||||
pool: ExecutorService = defaultPool
|
pool: ExecutorService = defaultPool
|
||||||
): EitherT[Task, FileError, File] = {
|
): EitherT[Task, FileError, File] = {
|
||||||
|
|
@ -449,7 +452,7 @@ object Cache {
|
||||||
def fetch(
|
def fetch(
|
||||||
cache: Seq[(String, File)] = default,
|
cache: Seq[(String, File)] = default,
|
||||||
cachePolicy: CachePolicy = CachePolicy.FetchMissing,
|
cachePolicy: CachePolicy = CachePolicy.FetchMissing,
|
||||||
checksums: Seq[Option[String]] = Seq(Some("SHA-1")),
|
checksums: Seq[Option[String]] = defaultChecksums,
|
||||||
logger: Option[Logger] = None,
|
logger: Option[Logger] = None,
|
||||||
pool: ExecutorService = defaultPool
|
pool: ExecutorService = defaultPool
|
||||||
): Fetch.Content[Task] = {
|
): Fetch.Content[Task] = {
|
||||||
|
|
@ -467,13 +470,33 @@ object Cache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private lazy val ivy2HomeUri = {
|
||||||
|
// a bit touchy on Windows... - don't try to manually write down the URI with s"file://..."
|
||||||
|
val str = new File(sys.props("user.home") + "/.ivy2/").toURI.toString
|
||||||
|
if (str.endsWith("/"))
|
||||||
|
str
|
||||||
|
else
|
||||||
|
str + "/"
|
||||||
|
}
|
||||||
|
|
||||||
lazy val ivy2Local = IvyRepository(
|
lazy val ivy2Local = IvyRepository(
|
||||||
// a bit touchy on Windows... - don't try to get the URI manually like s"file://..."
|
ivy2HomeUri + "local/" +
|
||||||
new File(sys.props("user.home") + "/.ivy2/local/").toURI.toString +
|
|
||||||
"[organisation]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/" +
|
"[organisation]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/" +
|
||||||
"[artifact](-[classifier]).[ext]"
|
"[artifact](-[classifier]).[ext]"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
lazy val ivy2Cache = IvyRepository(
|
||||||
|
ivy2HomeUri + "cache/" +
|
||||||
|
"(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[organisation]/[module]/[type]s/[artifact]-[revision](-[classifier]).[ext]",
|
||||||
|
metadataPatternOpt = Some(
|
||||||
|
ivy2HomeUri + "cache/" +
|
||||||
|
"(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[organisation]/[module]/[type]-[revision](-[classifier]).[ext]"
|
||||||
|
),
|
||||||
|
withChecksums = false,
|
||||||
|
withSignatures = false,
|
||||||
|
dropInfoAttributes = true
|
||||||
|
)
|
||||||
|
|
||||||
lazy val defaultBase = new File(
|
lazy val defaultBase = new File(
|
||||||
sys.env.getOrElse(
|
sys.env.getOrElse(
|
||||||
"COURSIER_CACHE",
|
"COURSIER_CACHE",
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ object CacheParse {
|
||||||
def repository(s: String): Validation[String, Repository] =
|
def repository(s: String): Validation[String, Repository] =
|
||||||
if (s == "ivy2local" || s == "ivy2Local")
|
if (s == "ivy2local" || s == "ivy2Local")
|
||||||
Cache.ivy2Local.success
|
Cache.ivy2Local.success
|
||||||
|
else if (s == "ivy2cache" || s == "ivy2Cache")
|
||||||
|
Cache.ivy2Cache.success
|
||||||
else {
|
else {
|
||||||
val repo = Parse.repository(s)
|
val repo = Parse.repository(s)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,146 +2,27 @@ package coursier.ivy
|
||||||
|
|
||||||
import coursier.Fetch
|
import coursier.Fetch
|
||||||
import coursier.core._
|
import coursier.core._
|
||||||
import scala.annotation.tailrec
|
|
||||||
import scala.util.matching.Regex
|
|
||||||
import scalaz._
|
import scalaz._
|
||||||
import java.util.regex.Pattern.quote
|
|
||||||
|
|
||||||
object IvyRepository {
|
|
||||||
|
|
||||||
val optionalPartRegex = (quote("(") + "[^" + quote("{()}") + "]*" + quote(")")).r
|
|
||||||
val variableRegex = (quote("[") + "[^" + quote("{[()]}") + "]*" + quote("]")).r
|
|
||||||
val propertyRegex = (quote("${") + "[^" + quote("{[()]}") + "]*" + quote("}")).r
|
|
||||||
|
|
||||||
sealed abstract class PatternPart(val effectiveStart: Int, val effectiveEnd: Int) extends Product with Serializable {
|
|
||||||
require(effectiveStart <= effectiveEnd)
|
|
||||||
def start = effectiveStart
|
|
||||||
def end = effectiveEnd
|
|
||||||
|
|
||||||
// FIXME Some kind of validation should be used here, to report all the missing variables,
|
|
||||||
// not only the first one missing.
|
|
||||||
def apply(content: String): Map[String, String] => String \/ String
|
|
||||||
}
|
|
||||||
object PatternPart {
|
|
||||||
case class Literal(override val effectiveStart: Int, override val effectiveEnd: Int) extends PatternPart(effectiveStart, effectiveEnd) {
|
|
||||||
def apply(content: String): Map[String, String] => String \/ String = {
|
|
||||||
assert(content.length == effectiveEnd - effectiveStart)
|
|
||||||
val matches = variableRegex.findAllMatchIn(content).toList
|
|
||||||
|
|
||||||
variables =>
|
|
||||||
@tailrec
|
|
||||||
def helper(idx: Int, matches: List[Regex.Match], b: StringBuilder): String \/ String =
|
|
||||||
if (idx >= content.length)
|
|
||||||
\/-(b.result())
|
|
||||||
else {
|
|
||||||
assert(matches.headOption.forall(_.start >= idx))
|
|
||||||
matches.headOption.filter(_.start == idx) match {
|
|
||||||
case Some(m) =>
|
|
||||||
val variableName = content.substring(m.start + 1, m.end - 1)
|
|
||||||
variables.get(variableName) match {
|
|
||||||
case None => -\/(s"Variable not found: $variableName")
|
|
||||||
case Some(value) =>
|
|
||||||
b ++= value
|
|
||||||
helper(m.end, matches.tail, b)
|
|
||||||
}
|
|
||||||
case None =>
|
|
||||||
val nextIdx = matches.headOption.fold(content.length)(_.start)
|
|
||||||
b ++= content.substring(idx, nextIdx)
|
|
||||||
helper(nextIdx, matches, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
helper(0, matches, new StringBuilder)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case class Optional(start0: Int, end0: Int) extends PatternPart(start0 + 1, end0 - 1) {
|
|
||||||
override def start = start0
|
|
||||||
override def end = end0
|
|
||||||
|
|
||||||
def apply(content: String): Map[String, String] => String \/ String = {
|
|
||||||
assert(content.length == effectiveEnd - effectiveStart)
|
|
||||||
val inner = Literal(effectiveStart, effectiveEnd).apply(content)
|
|
||||||
|
|
||||||
variables =>
|
|
||||||
\/-(inner(variables).fold(_ => "", x => x))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def substituteProperties(s: String, properties: Map[String, String]): String =
|
|
||||||
propertyRegex.findAllMatchIn(s).toVector.foldRight(s) { case (m, s0) =>
|
|
||||||
val key = s0.substring(m.start + "${".length, m.end - "}".length)
|
|
||||||
val value = properties.getOrElse(key, "")
|
|
||||||
s0.take(m.start) + value + s0.drop(m.end)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
case class IvyRepository(
|
case class IvyRepository(
|
||||||
pattern: String,
|
pattern: String,
|
||||||
|
metadataPatternOpt: Option[String] = None,
|
||||||
changing: Option[Boolean] = None,
|
changing: Option[Boolean] = None,
|
||||||
properties: Map[String, String] = Map.empty,
|
properties: Map[String, String] = Map.empty,
|
||||||
withChecksums: Boolean = true,
|
withChecksums: Boolean = true,
|
||||||
withSignatures: Boolean = true,
|
withSignatures: Boolean = true,
|
||||||
withArtifacts: Boolean = true
|
withArtifacts: Boolean = true,
|
||||||
|
// hack for SBT putting infos in properties
|
||||||
|
dropInfoAttributes: Boolean = false
|
||||||
) extends Repository {
|
) extends Repository {
|
||||||
|
|
||||||
|
def metadataPattern: String = metadataPatternOpt.getOrElse(pattern)
|
||||||
|
|
||||||
import Repository._
|
import Repository._
|
||||||
import IvyRepository._
|
|
||||||
|
|
||||||
private val pattern0 = substituteProperties(pattern, properties)
|
private val pattern0 = Pattern(pattern, properties)
|
||||||
|
private val metadataPattern0 = Pattern(metadataPattern, properties)
|
||||||
val parts = {
|
|
||||||
val optionalParts = optionalPartRegex.findAllMatchIn(pattern0).toList.map { m =>
|
|
||||||
PatternPart.Optional(m.start, m.end)
|
|
||||||
}
|
|
||||||
|
|
||||||
val len = pattern0.length
|
|
||||||
|
|
||||||
@tailrec
|
|
||||||
def helper(
|
|
||||||
idx: Int,
|
|
||||||
opt: List[PatternPart.Optional],
|
|
||||||
acc: List[PatternPart]
|
|
||||||
): Vector[PatternPart] =
|
|
||||||
if (idx >= len)
|
|
||||||
acc.toVector.reverse
|
|
||||||
else
|
|
||||||
opt match {
|
|
||||||
case Nil =>
|
|
||||||
helper(len, Nil, PatternPart.Literal(idx, len) :: acc)
|
|
||||||
case (opt0 @ PatternPart.Optional(start0, end0)) :: rem =>
|
|
||||||
if (idx < start0)
|
|
||||||
helper(start0, opt, PatternPart.Literal(idx, start0) :: acc)
|
|
||||||
else {
|
|
||||||
assert(idx == start0, s"idx: $idx, start0: $start0")
|
|
||||||
helper(end0, rem, opt0 :: acc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
helper(0, optionalParts, Nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(pattern0.isEmpty == parts.isEmpty)
|
|
||||||
if (pattern0.nonEmpty) {
|
|
||||||
for ((a, b) <- parts.zip(parts.tail))
|
|
||||||
assert(a.end == b.start)
|
|
||||||
assert(parts.head.start == 0)
|
|
||||||
assert(parts.last.end == pattern0.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val substituteHelpers = parts.map { part =>
|
|
||||||
part(pattern0.substring(part.effectiveStart, part.effectiveEnd))
|
|
||||||
}
|
|
||||||
|
|
||||||
def substitute(variables: Map[String, String]): String \/ String =
|
|
||||||
substituteHelpers.foldLeft[String \/ String](\/-("")) {
|
|
||||||
case (acc0, helper) =>
|
|
||||||
for {
|
|
||||||
acc <- acc0
|
|
||||||
s <- helper(variables)
|
|
||||||
} yield acc + s
|
|
||||||
}
|
|
||||||
|
|
||||||
// See http://ant.apache.org/ivy/history/latest-milestone/concept.html for a
|
// See http://ant.apache.org/ivy/history/latest-milestone/concept.html for a
|
||||||
// list of variables that should be supported.
|
// list of variables that should be supported.
|
||||||
|
|
@ -194,7 +75,7 @@ case class IvyRepository(
|
||||||
}
|
}
|
||||||
|
|
||||||
val retainedWithUrl = retained.flatMap { p =>
|
val retainedWithUrl = retained.flatMap { p =>
|
||||||
substitute(variables(
|
pattern0.substitute(variables(
|
||||||
dependency.module,
|
dependency.module,
|
||||||
dependency.version,
|
dependency.version,
|
||||||
p.`type`,
|
p.`type`,
|
||||||
|
|
@ -236,7 +117,7 @@ case class IvyRepository(
|
||||||
|
|
||||||
val eitherArtifact: String \/ Artifact =
|
val eitherArtifact: String \/ Artifact =
|
||||||
for {
|
for {
|
||||||
url <- substitute(
|
url <- metadataPattern0.substitute(
|
||||||
variables(module, version, "ivy", "ivy", "xml", None)
|
variables(module, version, "ivy", "ivy", "xml", None)
|
||||||
)
|
)
|
||||||
} yield {
|
} yield {
|
||||||
|
|
@ -259,14 +140,28 @@ case class IvyRepository(
|
||||||
for {
|
for {
|
||||||
artifact <- EitherT(F.point(eitherArtifact))
|
artifact <- EitherT(F.point(eitherArtifact))
|
||||||
ivy <- fetch(artifact)
|
ivy <- fetch(artifact)
|
||||||
proj <- EitherT(F.point {
|
proj0 <- EitherT(F.point {
|
||||||
for {
|
for {
|
||||||
xml <- \/.fromEither(compatibility.xmlParse(ivy))
|
xml <- \/.fromEither(compatibility.xmlParse(ivy))
|
||||||
_ <- if (xml.label == "ivy-module") \/-(()) else -\/("Module definition not found")
|
_ <- if (xml.label == "ivy-module") \/-(()) else -\/("Module definition not found")
|
||||||
proj <- IvyXml.project(xml)
|
proj <- IvyXml.project(xml)
|
||||||
} yield proj
|
} yield proj
|
||||||
})
|
})
|
||||||
} yield (source, proj)
|
} yield {
|
||||||
|
val proj =
|
||||||
|
if (dropInfoAttributes)
|
||||||
|
proj0.copy(
|
||||||
|
module = proj0.module.copy(
|
||||||
|
attributes = proj0.module.attributes.filter {
|
||||||
|
case (k, _) => !k.startsWith("info.")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else
|
||||||
|
proj0
|
||||||
|
|
||||||
|
(source, proj)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,142 @@
|
||||||
|
package coursier.ivy
|
||||||
|
|
||||||
|
import scala.annotation.tailrec
|
||||||
|
|
||||||
|
import scalaz._
|
||||||
|
|
||||||
|
import scala.util.matching.Regex
|
||||||
|
import java.util.regex.Pattern.quote
|
||||||
|
|
||||||
|
object Pattern {
|
||||||
|
|
||||||
|
val propertyRegex = (quote("${") + "[^" + quote("{[()]}") + "]*" + quote("}")).r
|
||||||
|
val optionalPartRegex = (quote("(") + "[^" + quote("{()}") + "]*" + quote(")")).r
|
||||||
|
val variableRegex = (quote("[") + "[^" + quote("{[()]}") + "]*" + quote("]")).r
|
||||||
|
|
||||||
|
sealed abstract class PatternPart(val effectiveStart: Int, val effectiveEnd: Int) extends Product with Serializable {
|
||||||
|
require(effectiveStart <= effectiveEnd)
|
||||||
|
def start = effectiveStart
|
||||||
|
def end = effectiveEnd
|
||||||
|
|
||||||
|
// FIXME Some kind of validation should be used here, to report all the missing variables,
|
||||||
|
// not only the first one missing.
|
||||||
|
def apply(content: String): Map[String, String] => String \/ String
|
||||||
|
}
|
||||||
|
object PatternPart {
|
||||||
|
case class Literal(override val effectiveStart: Int, override val effectiveEnd: Int) extends PatternPart(effectiveStart, effectiveEnd) {
|
||||||
|
def apply(content: String): Map[String, String] => String \/ String = {
|
||||||
|
assert(content.length == effectiveEnd - effectiveStart)
|
||||||
|
val matches = variableRegex.findAllMatchIn(content).toList
|
||||||
|
|
||||||
|
variables =>
|
||||||
|
@tailrec
|
||||||
|
def helper(idx: Int, matches: List[Regex.Match], b: StringBuilder): String \/ String =
|
||||||
|
if (idx >= content.length)
|
||||||
|
\/-(b.result())
|
||||||
|
else {
|
||||||
|
assert(matches.headOption.forall(_.start >= idx))
|
||||||
|
matches.headOption.filter(_.start == idx) match {
|
||||||
|
case Some(m) =>
|
||||||
|
val variableName = content.substring(m.start + 1, m.end - 1)
|
||||||
|
variables.get(variableName) match {
|
||||||
|
case None => -\/(s"Variable not found: $variableName")
|
||||||
|
case Some(value) =>
|
||||||
|
b ++= value
|
||||||
|
helper(m.end, matches.tail, b)
|
||||||
|
}
|
||||||
|
case None =>
|
||||||
|
val nextIdx = matches.headOption.fold(content.length)(_.start)
|
||||||
|
b ++= content.substring(idx, nextIdx)
|
||||||
|
helper(nextIdx, matches, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
helper(0, matches, new StringBuilder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case class Optional(start0: Int, end0: Int) extends PatternPart(start0 + 1, end0 - 1) {
|
||||||
|
override def start = start0
|
||||||
|
override def end = end0
|
||||||
|
|
||||||
|
def apply(content: String): Map[String, String] => String \/ String = {
|
||||||
|
assert(content.length == effectiveEnd - effectiveStart)
|
||||||
|
val inner = Literal(effectiveStart, effectiveEnd).apply(content)
|
||||||
|
|
||||||
|
variables =>
|
||||||
|
\/-(inner(variables).fold(_ => "", x => x))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def substituteProperties(s: String, properties: Map[String, String]): String =
|
||||||
|
propertyRegex.findAllMatchIn(s).toVector.foldRight(s) { case (m, s0) =>
|
||||||
|
val key = s0.substring(m.start + "${".length, m.end - "}".length)
|
||||||
|
val value = properties.getOrElse(key, "")
|
||||||
|
s0.take(m.start) + value + s0.drop(m.end)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
case class Pattern(
|
||||||
|
pattern: String,
|
||||||
|
properties: Map[String, String]
|
||||||
|
) {
|
||||||
|
|
||||||
|
import Pattern._
|
||||||
|
|
||||||
|
private val pattern0 = substituteProperties(pattern, properties)
|
||||||
|
|
||||||
|
val parts = {
|
||||||
|
val optionalParts = optionalPartRegex.findAllMatchIn(pattern0).toList.map { m =>
|
||||||
|
PatternPart.Optional(m.start, m.end)
|
||||||
|
}
|
||||||
|
|
||||||
|
val len = pattern0.length
|
||||||
|
|
||||||
|
@tailrec
|
||||||
|
def helper(
|
||||||
|
idx: Int,
|
||||||
|
opt: List[PatternPart.Optional],
|
||||||
|
acc: List[PatternPart]
|
||||||
|
): Vector[PatternPart] =
|
||||||
|
if (idx >= len)
|
||||||
|
acc.toVector.reverse
|
||||||
|
else
|
||||||
|
opt match {
|
||||||
|
case Nil =>
|
||||||
|
helper(len, Nil, PatternPart.Literal(idx, len) :: acc)
|
||||||
|
case (opt0 @ PatternPart.Optional(start0, end0)) :: rem =>
|
||||||
|
if (idx < start0)
|
||||||
|
helper(start0, opt, PatternPart.Literal(idx, start0) :: acc)
|
||||||
|
else {
|
||||||
|
assert(idx == start0, s"idx: $idx, start0: $start0")
|
||||||
|
helper(end0, rem, opt0 :: acc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
helper(0, optionalParts, Nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(pattern0.isEmpty == parts.isEmpty)
|
||||||
|
if (pattern0.nonEmpty) {
|
||||||
|
for ((a, b) <- parts.zip(parts.tail))
|
||||||
|
assert(a.end == b.start)
|
||||||
|
assert(parts.head.start == 0)
|
||||||
|
assert(parts.last.end == pattern0.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val substituteHelpers = parts.map { part =>
|
||||||
|
part(pattern0.substring(part.effectiveStart, part.effectiveEnd))
|
||||||
|
}
|
||||||
|
|
||||||
|
def substitute(variables: Map[String, String]): String \/ String =
|
||||||
|
substituteHelpers.foldLeft[String \/ String](\/-("")) {
|
||||||
|
case (acc0, helper) =>
|
||||||
|
for {
|
||||||
|
acc <- acc0
|
||||||
|
s <- helper(variables)
|
||||||
|
} yield acc + s
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -115,20 +115,22 @@ object FromSbt {
|
||||||
|
|
||||||
case sbt.FileRepository(_, _, patterns)
|
case sbt.FileRepository(_, _, patterns)
|
||||||
if patterns.ivyPatterns.lengthCompare(1) == 0 &&
|
if patterns.ivyPatterns.lengthCompare(1) == 0 &&
|
||||||
patterns.ivyPatterns == patterns.artifactPatterns =>
|
patterns.artifactPatterns.lengthCompare(1) == 0 =>
|
||||||
|
|
||||||
Some(IvyRepository(
|
Some(IvyRepository(
|
||||||
"file://" + patterns.ivyPatterns.head,
|
"file://" + patterns.artifactPatterns.head,
|
||||||
|
metadataPatternOpt = Some("file://" + patterns.ivyPatterns.head),
|
||||||
changing = Some(true),
|
changing = Some(true),
|
||||||
properties = ivyProperties
|
properties = ivyProperties
|
||||||
))
|
))
|
||||||
|
|
||||||
case sbt.URLRepository(_, patterns)
|
case sbt.URLRepository(_, patterns)
|
||||||
if patterns.ivyPatterns.lengthCompare(1) == 0 &&
|
if patterns.ivyPatterns.lengthCompare(1) == 0 &&
|
||||||
patterns.ivyPatterns == patterns.artifactPatterns =>
|
patterns.artifactPatterns.lengthCompare(1) == 0 =>
|
||||||
|
|
||||||
Some(IvyRepository(
|
Some(IvyRepository(
|
||||||
patterns.ivyPatterns.head,
|
patterns.artifactPatterns.head,
|
||||||
|
metadataPatternOpt = Some(patterns.ivyPatterns.head),
|
||||||
changing = None,
|
changing = None,
|
||||||
properties = ivyProperties
|
properties = ivyProperties
|
||||||
))
|
))
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue