查找 sbt 找到插件或 JAR 的存储库
Find repository where sbt finds a plugin or JAR
正在尝试对 sbt
构建进行故障排除,想知道 sbt.repositories
中的哪个模式与给定的三元组匹配。
我有一个插件和两个 sbt
版本。一个构建找到了插件,而另一个没有。两个版本都使用 sbt.repositories
文件来用内部站点(在公司防火墙后面)覆盖外部存储库。
两个版本的 sbt.repositories
文件不同,所以我怀疑其中一个缺少与插件匹配的模式。 sbt.repositories
文件足够大,我不想手动或通过反复试验来完成此操作。
我想使用 sbt
自己的工具来打印消息,例如 "using pattern (some pattern from sbt.repositories
) I have found plugin (org,name,ver) at (some URL)"。
来自 sbt.repositories
的模式具有以下格式。 sbt
必须循环遍历这些模式,计算所有组合并测试结果是否有效 URL。我想自己访问 class,这样我就可以从 sbt.repositories
中找出哪个模式允许一个构建找到插件(或 JAR)。然后我将该模式添加到另一个构建的 sbt.repositories
中。
有谁知道此功能是否可通过构建 DSL(即来自 build.sbt
)或为 sbt
执行此操作的解析器 class 的名称提供?
sbt.repositories
repo-name = http://some.internal.website.com:1234/artifacts/releases,[organisation]/[module](/scala_[scalaVersion])(/sbt_[sbtVersion])/[revision]/[artifact](-[classifier])(-[revision]).[ext]
我试过 sbt -Dsbt.log.noformat=true --debug clean update compile > someLogFile.txt
,这告诉我找到了哪些插件,包括我感兴趣的插件,但不是 在哪里 找到了它们。
写了一些又快又脏的东西,但对我有用。我很高兴看到使用 Ivy 解析器的解决方案,它必须提供相同的功能,而且更可靠。
根据我的经验,因为人类很难理解像 [organisation]/[module](/scala_[scalaVersion])(/sbt_[sbtVersion])/[revision]/[artifact](-[classifier])(-[revision]).[ext]
这样的模式是否匹配他们需要的或复制 sbt.repositories
中已经存在的另一个模式,人们只是复制粘贴模式从一个回购文件到另一个回购文件,直到构建工作。随着时间的推移,这会造成混乱,我们需要工具来处理 "which pattern matches a given dependency" 或 "which two patterns are identical" 或 "which pattern does not match any dependencies" 等问题
import java.io.File
import java.net.URL
import java.util.regex.Pattern
import scala.collection.JavaConversions._
import scala.collection.mutable
import scala.io.{Codec, Source}
import scala.util.control.NonFatal
object FindDependency {
def main(args: Array[String]): Unit = {
val root = new File(".")
val sbtRepos = new File(root, "./build/sbt.repositories")
implicit val dep = Dep(organisation = "com.foo.bar.sbt", module = "baz", artifact = "baz", revision = "1.2.3-SNAPSHOT", ext = "jar"
, scalaVersion = "2.10", sbtVersion = "0.13", artType = "jar", classifier = "")
println(sbtRepos.getCanonicalPath + ": exists?" + sbtRepos.exists)
processSbtRepos(sbtRepos)
}
def processSbtRepos(repoFilePath: File)(implicit dep: Dep): Unit = {
val source = Source.fromFile(repoFilePath)(Codec.ISO8859)
source.getLines.foreach { line =>
if (!ignore(line)) {
println(s"\n$line")
val namePattern = line.split(":") map (_.trim)
if (namePattern.length >= 3) artifactUrl(namePattern)
}
}
source.close
}
def artifactUrl(words: Array[String])(implicit dep: Dep): Unit = {
val artBase = repoUrl(words)
val artifactPattern = if (artBase.indexOf('[') > 0) artBase else artBase + ",[organisation]/[module]/[revision]/[artifact]-[revision].[ext]"
val artUrls = dep.potentialUrls(artifactPattern)
artUrls.foreach { url =>
if (isValidUrl(url)) {
println(s"$artifactPattern -> $url")
}
}
}
def isValidUrl(url: String): Boolean = {
try {
val u = new URL(url)
val contentLength = u.openConnection.getContentLength
val openStream = u.openStream
val binaryData = new Array[Byte](contentLength)
openStream.read(binaryData)
openStream.close
true
} catch {
case NonFatal(_) => false
}
}
def repoUrl(words: Array[String]): String = words.drop(1).mkString(":")
def ignore(line: String): Boolean = {
line.indexOf("http://") < 0
}
}
/**
* Represents a dependency
*
* @param artType artifact type, e.g. "sources", "javadoc", ""
*/
case class Dep(organisation: String, module: String, artifact: String, revision: String
, ext: String, scalaVersion: String, sbtVersion: String, artType: String
, classifier: String) {
/**
* Given a pattern and the known parameters for a dependency, replace the details of the dependency in the pattern.
*
* @return a concrete URL (with optional portions) where the dependency may be located
*/
def replace(pattern: String): String = {
pattern.replaceAll(Pattern.quote("[artifact]"), artifact)
.replaceAll(Pattern.quote("[ext]"), ext)
.replaceAll(Pattern.quote("[module]"), module)
.replaceAll(Pattern.quote("[organisation]"), organisation)
.replaceAll(Pattern.quote("[organization]"), organisation)
.replaceAll(Pattern.quote("[revision]"), revision)
.replaceAll(Pattern.quote("[scalaVersion]"), scalaVersion)
.replaceAll(Pattern.quote("[sbtVersion]"), sbtVersion)
.replaceAll(Pattern.quote("[type]"), artType)
.replaceAll(Pattern.quote("[classifier]"), classifier)
}
def potentialUrls(pattern: String): Seq[String] = {
val urlWithOpts = replace(pattern)
.replaceAll(" ", "")
.replaceAll(",", "/")
.replaceAll("\(", "(?") // mark the optional elements with the prefix "?"
val words = urlWithOpts.split("\(|\)").map(_.trim).filterNot(_.isEmpty).filterNot(_ == "?-")
val urls = mutable.ArrayBuffer.empty[String]
val urlsCrt = mutable.ArrayBuffer.empty[String] // a temp container for urls
urls.add(words(0)) // the first "word" is the repo URL, which is not optional
words.drop(1).foreach { word =>
// transfer the current list of URLs to the temp container
urlsCrt.clear
urlsCrt ++= urls
urls.clear
if (word.startsWith("?")) {
// append all URLs that were there before, unchanged (because this word is optional)
urls ++= urlsCrt
// as well as all URLs that were there before, each concatenated with this word
urls ++= urlsCrt.map(_ + word.substring(1))
} else {
// to each URL that was there before, concatenate the new word
urls ++= urlsCrt.map(_ + word)
}
}
urls
}
}
正在尝试对 sbt
构建进行故障排除,想知道 sbt.repositories
中的哪个模式与给定的三元组匹配。
我有一个插件和两个 sbt
版本。一个构建找到了插件,而另一个没有。两个版本都使用 sbt.repositories
文件来用内部站点(在公司防火墙后面)覆盖外部存储库。
两个版本的 sbt.repositories
文件不同,所以我怀疑其中一个缺少与插件匹配的模式。 sbt.repositories
文件足够大,我不想手动或通过反复试验来完成此操作。
我想使用 sbt
自己的工具来打印消息,例如 "using pattern (some pattern from sbt.repositories
) I have found plugin (org,name,ver) at (some URL)"。
来自 sbt.repositories
的模式具有以下格式。 sbt
必须循环遍历这些模式,计算所有组合并测试结果是否有效 URL。我想自己访问 class,这样我就可以从 sbt.repositories
中找出哪个模式允许一个构建找到插件(或 JAR)。然后我将该模式添加到另一个构建的 sbt.repositories
中。
有谁知道此功能是否可通过构建 DSL(即来自 build.sbt
)或为 sbt
执行此操作的解析器 class 的名称提供?
sbt.repositories
repo-name = http://some.internal.website.com:1234/artifacts/releases,[organisation]/[module](/scala_[scalaVersion])(/sbt_[sbtVersion])/[revision]/[artifact](-[classifier])(-[revision]).[ext]
我试过 sbt -Dsbt.log.noformat=true --debug clean update compile > someLogFile.txt
,这告诉我找到了哪些插件,包括我感兴趣的插件,但不是 在哪里 找到了它们。
写了一些又快又脏的东西,但对我有用。我很高兴看到使用 Ivy 解析器的解决方案,它必须提供相同的功能,而且更可靠。
根据我的经验,因为人类很难理解像 [organisation]/[module](/scala_[scalaVersion])(/sbt_[sbtVersion])/[revision]/[artifact](-[classifier])(-[revision]).[ext]
这样的模式是否匹配他们需要的或复制 sbt.repositories
中已经存在的另一个模式,人们只是复制粘贴模式从一个回购文件到另一个回购文件,直到构建工作。随着时间的推移,这会造成混乱,我们需要工具来处理 "which pattern matches a given dependency" 或 "which two patterns are identical" 或 "which pattern does not match any dependencies" 等问题
import java.io.File
import java.net.URL
import java.util.regex.Pattern
import scala.collection.JavaConversions._
import scala.collection.mutable
import scala.io.{Codec, Source}
import scala.util.control.NonFatal
object FindDependency {
def main(args: Array[String]): Unit = {
val root = new File(".")
val sbtRepos = new File(root, "./build/sbt.repositories")
implicit val dep = Dep(organisation = "com.foo.bar.sbt", module = "baz", artifact = "baz", revision = "1.2.3-SNAPSHOT", ext = "jar"
, scalaVersion = "2.10", sbtVersion = "0.13", artType = "jar", classifier = "")
println(sbtRepos.getCanonicalPath + ": exists?" + sbtRepos.exists)
processSbtRepos(sbtRepos)
}
def processSbtRepos(repoFilePath: File)(implicit dep: Dep): Unit = {
val source = Source.fromFile(repoFilePath)(Codec.ISO8859)
source.getLines.foreach { line =>
if (!ignore(line)) {
println(s"\n$line")
val namePattern = line.split(":") map (_.trim)
if (namePattern.length >= 3) artifactUrl(namePattern)
}
}
source.close
}
def artifactUrl(words: Array[String])(implicit dep: Dep): Unit = {
val artBase = repoUrl(words)
val artifactPattern = if (artBase.indexOf('[') > 0) artBase else artBase + ",[organisation]/[module]/[revision]/[artifact]-[revision].[ext]"
val artUrls = dep.potentialUrls(artifactPattern)
artUrls.foreach { url =>
if (isValidUrl(url)) {
println(s"$artifactPattern -> $url")
}
}
}
def isValidUrl(url: String): Boolean = {
try {
val u = new URL(url)
val contentLength = u.openConnection.getContentLength
val openStream = u.openStream
val binaryData = new Array[Byte](contentLength)
openStream.read(binaryData)
openStream.close
true
} catch {
case NonFatal(_) => false
}
}
def repoUrl(words: Array[String]): String = words.drop(1).mkString(":")
def ignore(line: String): Boolean = {
line.indexOf("http://") < 0
}
}
/**
* Represents a dependency
*
* @param artType artifact type, e.g. "sources", "javadoc", ""
*/
case class Dep(organisation: String, module: String, artifact: String, revision: String
, ext: String, scalaVersion: String, sbtVersion: String, artType: String
, classifier: String) {
/**
* Given a pattern and the known parameters for a dependency, replace the details of the dependency in the pattern.
*
* @return a concrete URL (with optional portions) where the dependency may be located
*/
def replace(pattern: String): String = {
pattern.replaceAll(Pattern.quote("[artifact]"), artifact)
.replaceAll(Pattern.quote("[ext]"), ext)
.replaceAll(Pattern.quote("[module]"), module)
.replaceAll(Pattern.quote("[organisation]"), organisation)
.replaceAll(Pattern.quote("[organization]"), organisation)
.replaceAll(Pattern.quote("[revision]"), revision)
.replaceAll(Pattern.quote("[scalaVersion]"), scalaVersion)
.replaceAll(Pattern.quote("[sbtVersion]"), sbtVersion)
.replaceAll(Pattern.quote("[type]"), artType)
.replaceAll(Pattern.quote("[classifier]"), classifier)
}
def potentialUrls(pattern: String): Seq[String] = {
val urlWithOpts = replace(pattern)
.replaceAll(" ", "")
.replaceAll(",", "/")
.replaceAll("\(", "(?") // mark the optional elements with the prefix "?"
val words = urlWithOpts.split("\(|\)").map(_.trim).filterNot(_.isEmpty).filterNot(_ == "?-")
val urls = mutable.ArrayBuffer.empty[String]
val urlsCrt = mutable.ArrayBuffer.empty[String] // a temp container for urls
urls.add(words(0)) // the first "word" is the repo URL, which is not optional
words.drop(1).foreach { word =>
// transfer the current list of URLs to the temp container
urlsCrt.clear
urlsCrt ++= urls
urls.clear
if (word.startsWith("?")) {
// append all URLs that were there before, unchanged (because this word is optional)
urls ++= urlsCrt
// as well as all URLs that were there before, each concatenated with this word
urls ++= urlsCrt.map(_ + word.substring(1))
} else {
// to each URL that was there before, concatenate the new word
urls ++= urlsCrt.map(_ + word)
}
}
urls
}
}