mirror of https://github.com/sbt/sbt.git
Merge branch '0.13' into follow-deprecation
This commit is contained in:
commit
24f31804af
|
|
@ -10,13 +10,91 @@ import core.module.id.ModuleRevisionId
|
|||
import core.module.descriptor.DependencyDescriptor
|
||||
import core.resolve.ResolveData
|
||||
import core.settings.IvySettings
|
||||
import plugins.resolver.{BasicResolver, DependencyResolver, IBiblioResolver}
|
||||
import plugins.resolver.{BasicResolver, DependencyResolver, IBiblioResolver, RepositoryResolver}
|
||||
import plugins.resolver.{AbstractPatternsBasedResolver, AbstractSshBasedResolver, FileSystemResolver, SFTPResolver, SshResolver, URLResolver}
|
||||
import plugins.repository.url.{URLRepository => URLRepo}
|
||||
import plugins.repository.file.{FileRepository => FileRepo, FileResource}
|
||||
import java.io.File
|
||||
import org.apache.ivy.util.ChecksumHelper
|
||||
import org.apache.ivy.core.module.descriptor.{Artifact=>IArtifact}
|
||||
|
||||
|
||||
private object ConvertResolver
|
||||
{
|
||||
/** This class contains all the reflective lookups used in the
|
||||
* checksum-friendly URL publishing shim.
|
||||
*/
|
||||
private object ChecksumFriendlyURLResolver {
|
||||
// TODO - When we dump JDK6 support we can remove this hackery
|
||||
// import java.lang.reflect.AccessibleObject
|
||||
type AccessibleObject = {
|
||||
def setAccessible(value: Boolean): Unit
|
||||
}
|
||||
private def reflectiveLookup[A <: AccessibleObject](f: Class[_] => A): Option[A] =
|
||||
try {
|
||||
val cls = classOf[RepositoryResolver]
|
||||
val thing = f(cls)
|
||||
import scala.language.reflectiveCalls
|
||||
thing.setAccessible(true)
|
||||
Some(thing)
|
||||
} catch {
|
||||
case (_: java.lang.NoSuchFieldException) |
|
||||
(_: java.lang.SecurityException) |
|
||||
(_: java.lang.NoSuchMethodException) => None
|
||||
}
|
||||
private val signerNameField: Option[java.lang.reflect.Field] =
|
||||
reflectiveLookup(_.getDeclaredField("signerName"))
|
||||
private val putChecksumMethod: Option[java.lang.reflect.Method] =
|
||||
reflectiveLookup(_.getDeclaredMethod("putChecksum",
|
||||
classOf[IArtifact], classOf[File], classOf[String],
|
||||
classOf[Boolean], classOf[String]))
|
||||
private val putSignatureMethod: Option[java.lang.reflect.Method] =
|
||||
reflectiveLookup(_.getDeclaredMethod("putSignature",
|
||||
classOf[IArtifact], classOf[File], classOf[String],
|
||||
classOf[Boolean]))
|
||||
}
|
||||
/**
|
||||
* The default behavior of ivy's overwrite flags ignores the fact that a lot of repositories
|
||||
* will autogenerate checksums *for* an artifact if it doesn't already exist. Therefore
|
||||
* if we succeed in publishing an artifact, we need to just blast the checksums in place.
|
||||
* This acts as a "shim" on RepositoryResolvers so that we can hook our methods into
|
||||
* both the IBiblioResolver + URLResolver without having to duplicate the code in two
|
||||
* places. However, this does mean our use of reflection is awesome.
|
||||
*
|
||||
* TODO - See about contributing back to ivy.
|
||||
*/
|
||||
private trait ChecksumFriendlyURLResolver extends RepositoryResolver {
|
||||
import ChecksumFriendlyURLResolver._
|
||||
private def signerName: String = signerNameField match {
|
||||
case Some(field) => field.get(this).asInstanceOf[String]
|
||||
case None => null
|
||||
}
|
||||
override protected def put(artifact: IArtifact, src: File, dest: String, overwrite: Boolean): Unit = {
|
||||
// verify the checksum algorithms before uploading artifacts!
|
||||
val checksums = getChecksumAlgorithms()
|
||||
val repository = getRepository()
|
||||
for {
|
||||
checksum <- checksums
|
||||
if !ChecksumHelper.isKnownAlgorithm(checksum)
|
||||
} throw new IllegalArgumentException("Unknown checksum algorithm: " + checksum)
|
||||
repository.put(artifact, src, dest, overwrite);
|
||||
// Fix for sbt#1156 - Artifactory will auto-generate MD5/sha1 files, so
|
||||
// we need to overwrite what it has.
|
||||
for (checksum <- checksums) {
|
||||
putChecksumMethod match {
|
||||
case Some(method) => method.invoke(this, artifact, src, dest, true: java.lang.Boolean, checksum)
|
||||
case None => // TODO - issue warning?
|
||||
}
|
||||
}
|
||||
if (signerName != null) {
|
||||
putSignatureMethod match {
|
||||
case None => ()
|
||||
case Some(method) => method.invoke(artifact, src, dest, true: java.lang.Boolean)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Converts the given sbt resolver into an Ivy resolver..*/
|
||||
def apply(r: Resolver, settings: IvySettings, log: Logger) =
|
||||
{
|
||||
|
|
@ -25,7 +103,7 @@ private object ConvertResolver
|
|||
case repo: MavenRepository =>
|
||||
{
|
||||
val pattern = Collections.singletonList(Resolver.resolvePattern(repo.root, Resolver.mavenStyleBasePattern))
|
||||
final class PluginCapableResolver extends IBiblioResolver with DescriptorRequired {
|
||||
final class PluginCapableResolver extends IBiblioResolver with ChecksumFriendlyURLResolver with DescriptorRequired {
|
||||
def setPatterns() { // done this way for access to protected methods.
|
||||
setArtifactPatterns(pattern)
|
||||
setIvyPatterns(pattern)
|
||||
|
|
@ -61,7 +139,13 @@ private object ConvertResolver
|
|||
}
|
||||
case repo: FileRepository =>
|
||||
{
|
||||
val resolver = new FileSystemResolver with DescriptorRequired
|
||||
val resolver = new FileSystemResolver with DescriptorRequired {
|
||||
// Workaround for #1156
|
||||
// Temporarily in sbt 0.13.x we deprecate overwriting
|
||||
// in local files for non-changing revisions.
|
||||
// This will be fully enforced in sbt 1.0.
|
||||
setRepository(new WarnOnOverwriteFileRepo())
|
||||
}
|
||||
resolver.setName(repo.name)
|
||||
initializePatterns(resolver, repo.patterns, settings)
|
||||
import repo.configuration.{isLocal, isTransactional}
|
||||
|
|
@ -71,7 +155,7 @@ private object ConvertResolver
|
|||
}
|
||||
case repo: URLRepository =>
|
||||
{
|
||||
val resolver = new URLResolver with DescriptorRequired
|
||||
val resolver = new URLResolver with ChecksumFriendlyURLResolver with DescriptorRequired
|
||||
resolver.setName(repo.name)
|
||||
initializePatterns(resolver, repo.patterns, settings)
|
||||
resolver
|
||||
|
|
@ -135,7 +219,7 @@ private object ConvertResolver
|
|||
/** A custom Ivy URLRepository that returns FileResources for file URLs.
|
||||
* This allows using the artifacts from the Maven local repository instead of copying them to the Ivy cache. */
|
||||
private[this] final class LocalIfFileRepo extends URLRepo {
|
||||
private[this] val repo = new FileRepo
|
||||
private[this] val repo = new WarnOnOverwriteFileRepo()
|
||||
override def getResource(source: String) = {
|
||||
val url = new URL(source)
|
||||
if(url.getProtocol == IO.FileScheme)
|
||||
|
|
@ -144,4 +228,16 @@ private object ConvertResolver
|
|||
super.getResource(source)
|
||||
}
|
||||
}
|
||||
|
||||
private[this] final class WarnOnOverwriteFileRepo extends FileRepo() {
|
||||
override def put(source: java.io.File, destination: String, overwrite: Boolean): Unit = {
|
||||
try super.put(source, destination, overwrite)
|
||||
catch {
|
||||
case e: java.io.IOException if e.getMessage.contains("destination already exists") =>
|
||||
import org.apache.ivy.util.Message
|
||||
Message.warn(s"Attempting to overwrite $destination\n\tThis usage is deprecated and will be removed in sbt 1.0.")
|
||||
super.put(source, destination, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ object CustomPomParser
|
|||
val JarPackagings = Set("eclipse-plugin", "hk2-jar", "orbit")
|
||||
val default = new CustomPomParser(PomModuleDescriptorParser.getInstance, defaultTransform)
|
||||
|
||||
private[this] val TransformedHashKey = "sbtTransformHash"
|
||||
private[this] val TransformedHashKey = "e:sbtTransformHash"
|
||||
// A hash of the parameters transformation is based on.
|
||||
// If a descriptor has a different hash, we need to retransform it.
|
||||
private[this] val TransformHash: String = hash((unqualifiedKeys ++ JarPackagings).toSeq.sorted)
|
||||
|
|
@ -57,8 +57,14 @@ object CustomPomParser
|
|||
|
||||
private[this] def transformedByThisVersion(md: ModuleDescriptor): Boolean =
|
||||
{
|
||||
val oldTransformedHashKey = "sbtTransformHash"
|
||||
val extraInfo = md.getExtraInfo
|
||||
extraInfo != null && extraInfo.get(TransformedHashKey) == TransformHash
|
||||
// sbt 0.13.1 used "sbtTransformHash" instead of "e:sbtTransformHash" until #1192 so read both
|
||||
Option(extraInfo).isDefined &&
|
||||
((Option(extraInfo get TransformedHashKey) orElse Option(extraInfo get oldTransformedHashKey)) match {
|
||||
case Some(TransformHash) => true
|
||||
case _ => false
|
||||
})
|
||||
}
|
||||
|
||||
private[this] def defaultTransformImpl(parser: ModuleDescriptorParser, md: ModuleDescriptor): ModuleDescriptor =
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import Resolver.PluginPattern
|
|||
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
import java.text.ParseException
|
||||
import java.util.concurrent.Callable
|
||||
import java.util.{Collection, Collections => CS}
|
||||
import CS.singleton
|
||||
|
|
@ -24,9 +23,7 @@ import core.settings.IvySettings
|
|||
import plugins.latest.LatestRevisionStrategy
|
||||
import plugins.matcher.PatternMatcher
|
||||
import plugins.parser.m2.PomModuleDescriptorParser
|
||||
import plugins.repository.ResourceDownloader
|
||||
import plugins.resolver.{ChainResolver, DependencyResolver}
|
||||
import plugins.resolver.util.ResolvedResource
|
||||
import util.{Message, MessageLogger}
|
||||
import util.extendable.ExtendableItem
|
||||
|
||||
|
|
@ -99,6 +96,8 @@ final class IvySbt(val configuration: IvyConfiguration)
|
|||
def withIvy[T](log: MessageLogger)(f: Ivy => T): T =
|
||||
withDefaultLogger(log)
|
||||
{
|
||||
// See #429 - We always insert a helper authenticator here which lets us get more useful authentication errors.
|
||||
ivyint.ErrorMessageAuthenticator.install()
|
||||
ivy.pushContext()
|
||||
ivy.getLoggerEngine.pushLogger(log)
|
||||
try { f(ivy) }
|
||||
|
|
@ -356,41 +355,8 @@ private object IvySbt
|
|||
case pr: ProjectResolver => true
|
||||
case _ => false
|
||||
}
|
||||
/** This is overridden to delete outofdate artifacts of changing modules that are not listed in the metadata.
|
||||
* This occurs for artifacts with classifiers, for example. */
|
||||
@throws(classOf[ParseException])
|
||||
override def cacheModuleDescriptor(resolver: DependencyResolver, mdRef: ResolvedResource, dd: DependencyDescriptor, moduleArtifact: IArtifact, downloader: ResourceDownloader, options: CacheMetadataOptions): ResolvedModuleRevision =
|
||||
{
|
||||
val rmrRaw = super.cacheModuleDescriptor(null, mdRef, dd, moduleArtifact, downloader, options)
|
||||
val rmr = resetArtifactResolver(rmrRaw)
|
||||
val mrid = moduleArtifact.getModuleRevisionId
|
||||
def shouldClear(): Boolean = rmr != null &&
|
||||
( (rmr.getReport != null && rmr.getReport.isSearched && isChanging(dd, mrid)) ||
|
||||
isProjectResolver(rmr.getResolver) )
|
||||
// only handle changing modules whose metadata actually changed.
|
||||
// Typically, the publication date in the metadata has to change to get here.
|
||||
if(shouldClear()) {
|
||||
// this is the locally cached metadata as originally retrieved (e.g. the pom)
|
||||
val original = rmr.getReport.getOriginalLocalFile
|
||||
if(original != null) {
|
||||
// delete all files in subdirectories that are older than the original metadata file's publication date
|
||||
// The publication date is used because the metadata will be redownloaded for changing files,
|
||||
// so the last modified time changes, but the publication date doesn't
|
||||
val pubDate = rmrRaw.getPublicationDate
|
||||
val lm = if(pubDate eq null) original.lastModified else pubDate.getTime
|
||||
val indirectFiles = PathFinder(original.getParentFile).*(DirectoryFilter).**(-DirectoryFilter).get.toList
|
||||
val older = indirectFiles.filter(f => f.lastModified < lm).toList
|
||||
Message.verbose("Deleting additional old artifacts from cache for changed module " + mrid + older.mkString(":\n\t", "\n\t", ""))
|
||||
IO.delete(older)
|
||||
}
|
||||
}
|
||||
rmr
|
||||
}
|
||||
// ignore the original resolver wherever possible to avoid issues like #704
|
||||
override def saveResolvers(descriptor: ModuleDescriptor, metadataResolverName: String, artifactResolverName: String) {}
|
||||
|
||||
def isChanging(dd: DependencyDescriptor, requestedRevisionId: ModuleRevisionId): Boolean =
|
||||
!localOnly && (dd.isChanging || requestedRevisionId.getRevision.contains("-SNAPSHOT"))
|
||||
}
|
||||
manager.setArtifactPattern(PluginPattern + manager.getArtifactPattern)
|
||||
manager.setDataFilePattern(PluginPattern + manager.getDataFilePattern)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,11 @@ import core.resolve.ResolveOptions
|
|||
import plugins.resolver.{BasicResolver, DependencyResolver}
|
||||
|
||||
final class DeliverConfiguration(val deliverIvyPattern: String, val status: String, val configurations: Option[Seq[Configuration]], val logging: UpdateLogging.Value)
|
||||
final class PublishConfiguration(val ivyFile: Option[File], val resolverName: String, val artifacts: Map[Artifact, File], val checksums: Seq[String], val logging: UpdateLogging.Value)
|
||||
final class PublishConfiguration(val ivyFile: Option[File], val resolverName: String, val artifacts: Map[Artifact, File], val checksums: Seq[String], val logging: UpdateLogging.Value,
|
||||
val overwrite: Boolean) {
|
||||
def this(ivyFile: Option[File], resolverName: String, artifacts: Map[Artifact, File], checksums: Seq[String], logging: UpdateLogging.Value) =
|
||||
this(ivyFile, resolverName, artifacts, checksums, logging, false)
|
||||
}
|
||||
|
||||
final class UpdateConfiguration(val retrieve: Option[RetrieveConfiguration], val missingOk: Boolean, val logging: UpdateLogging.Value)
|
||||
final class RetrieveConfiguration(val retrieveDirectory: File, val outputPattern: String)
|
||||
|
|
@ -86,11 +90,11 @@ object IvyActions
|
|||
import configuration._
|
||||
module.withModule(log) { case (ivy, md, default) =>
|
||||
val resolver = ivy.getSettings.getResolver(resolverName)
|
||||
if(resolver eq null) error("Undefined resolver '" + resolverName + "'")
|
||||
if(resolver eq null) sys.error("Undefined resolver '" + resolverName + "'")
|
||||
val ivyArtifact = ivyFile map { file => (MDArtifact.newIvyArtifact(md), file) }
|
||||
val cross = crossVersionMap(module.moduleSettings)
|
||||
val as = mapArtifacts(md, cross, artifacts) ++ ivyArtifact.toList
|
||||
withChecksums(resolver, checksums) { publish(md, as, resolver, overwrite = true) }
|
||||
val as = mapArtifacts(md, cross, artifacts) ++ ivyArtifact.toSeq
|
||||
withChecksums(resolver, checksums) { publish(md, as, resolver, overwrite = overwrite) }
|
||||
}
|
||||
}
|
||||
private[this] def withChecksums[T](resolver: DependencyResolver, checksums: Seq[String])(act: => T): T =
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ class MakePom(val log: Logger)
|
|||
<dependency>
|
||||
<groupId>{mrid.getOrganisation}</groupId>
|
||||
<artifactId>{mrid.getName}</artifactId>
|
||||
<version>{mrid.getRevision}</version>
|
||||
<version>{makeDependencyVersion(mrid.getRevision)}</version>
|
||||
{ scopeElem(scope) }
|
||||
{ optionalElem(optional) }
|
||||
{ classifierElem(classifier) }
|
||||
|
|
@ -197,6 +197,44 @@ class MakePom(val log: Logger)
|
|||
</dependency>
|
||||
}
|
||||
|
||||
|
||||
|
||||
def makeDependencyVersion(revision: String): String = {
|
||||
def plusRange(s:String, shift:Int = 0) = {
|
||||
def pow(i:Int):Int = if (i>0) 10 * pow(i-1) else 1
|
||||
val (prefixVersion, lastVersion) = (s+"0"*shift).reverse.split("\\.",2) match {
|
||||
case Array(revLast,revRest) =>
|
||||
( revRest.reverse + ".", revLast.reverse )
|
||||
case Array(revLast) => ("", revLast.reverse)
|
||||
}
|
||||
val lastVersionInt = lastVersion.toInt
|
||||
s"[${prefixVersion}${lastVersion},${prefixVersion}${lastVersionInt+pow(shift)})"
|
||||
}
|
||||
val startSym=Set(']','[','(')
|
||||
val stopSym=Set(']','[',')')
|
||||
try {
|
||||
if (revision endsWith ".+") {
|
||||
plusRange(revision.substring(0,revision.length-2))
|
||||
} else if (revision endsWith "+") {
|
||||
val base = revision.take(revision.length-1)
|
||||
// This is a heuristic. Maven just doesn't support Ivy's notions of 1+, so
|
||||
// we assume version ranges never go beyond 5 siginificant digits.
|
||||
(0 to 5).map(plusRange(base,_)).mkString(",")
|
||||
} else if (startSym(revision(0)) && stopSym(revision(revision.length-1))) {
|
||||
val start = revision(0)
|
||||
val stop = revision(revision.length-1)
|
||||
val mid = revision.substring(1,revision.length-1)
|
||||
(if (start == ']') "(" else start) + mid + (if (stop == '[') ")" else stop)
|
||||
} else revision
|
||||
} catch {
|
||||
case e: NumberFormatException =>
|
||||
// TODO - if the version doesn't meet our expectations, maybe we just issue a hard
|
||||
// error instead of softly ignoring the attempt to rewrite.
|
||||
//sys.error(s"Could not fix version [$revision] into maven style version")
|
||||
revision
|
||||
}
|
||||
}
|
||||
|
||||
@deprecated("No longer used and will be removed.", "0.12.1")
|
||||
def classifier(dependency: DependencyDescriptor, includeTypes: Set[String]): NodeSeq =
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,128 @@
|
|||
package sbt
|
||||
package ivyint
|
||||
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.Method
|
||||
import java.net.Authenticator
|
||||
import java.net.PasswordAuthentication
|
||||
import org.apache.ivy.util.Credentials
|
||||
import org.apache.ivy.util.Message
|
||||
import org.apache.ivy.util.url.IvyAuthenticator
|
||||
import org.apache.ivy.util.url.CredentialsStore
|
||||
|
||||
/**
|
||||
* Helper to install an Authenticator that works with the IvyAuthenticator to provide better error messages when
|
||||
* credentials don't line up.
|
||||
*/
|
||||
object ErrorMessageAuthenticator {
|
||||
private var securityWarningLogged = false
|
||||
|
||||
private def originalAuthenticator: Option[Authenticator] = {
|
||||
try {
|
||||
val f = classOf[Authenticator].getDeclaredField("theAuthenticator");
|
||||
f.setAccessible(true);
|
||||
Option(f.get(null).asInstanceOf[Authenticator])
|
||||
} catch {
|
||||
// TODO - Catch more specific errors.
|
||||
case t: Throwable =>
|
||||
Message.debug("Error occurred while getting the original authenticator: " + t.getMessage)
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
private lazy val ivyOriginalField = {
|
||||
val field = classOf[IvyAuthenticator].getDeclaredField("original")
|
||||
field.setAccessible(true)
|
||||
field
|
||||
}
|
||||
// Attempts to get the original authenticator form the ivy class or returns null.
|
||||
private def installIntoIvy(ivy: IvyAuthenticator): Option[Authenticator] = {
|
||||
// Here we install ourselves as the IvyAuthenticator's default so we get called AFTER Ivy has a chance to run.
|
||||
def installIntoIvyImpl(original: Option[Authenticator]): Unit = {
|
||||
val newOriginal = new ErrorMessageAuthenticator(original)
|
||||
ivyOriginalField.set(ivy, newOriginal)
|
||||
}
|
||||
|
||||
try Option(ivyOriginalField.get(ivy).asInstanceOf[Authenticator]) match {
|
||||
case Some(alreadyThere: ErrorMessageAuthenticator) => // We're already installed, no need to do the work again.
|
||||
case originalOpt => installIntoIvyImpl(originalOpt)
|
||||
} catch {
|
||||
case t: Throwable =>
|
||||
Message.debug("Error occurred will trying to install debug messages into Ivy Authentication" + t.getMessage)
|
||||
}
|
||||
Some(ivy)
|
||||
}
|
||||
|
||||
/** Installs the error message authenticator so we have nicer error messages when using java's URL for downloading. */
|
||||
def install() {
|
||||
// Actually installs the error message authenticator.
|
||||
def doInstall(original: Option[Authenticator]): Unit =
|
||||
try Authenticator.setDefault(new ErrorMessageAuthenticator(original))
|
||||
catch {
|
||||
case e: SecurityException if !securityWarningLogged =>
|
||||
securityWarningLogged = true;
|
||||
Message.warn("Not enough permissions to set the ErorrMessageAuthenticator. "
|
||||
+ "Helpful debug messages disabled!");
|
||||
}
|
||||
// We will try to use the original authenticator as backup authenticator.
|
||||
// Since there is no getter available, so try to use some reflection to
|
||||
// obtain it. If that doesn't work, assume there is no original authenticator
|
||||
def doInstallIfIvy(original: Option[Authenticator]): Unit =
|
||||
original match {
|
||||
case Some(installed: ErrorMessageAuthenticator) => // Ignore, we're already installed
|
||||
case Some(ivy: IvyAuthenticator) => installIntoIvy(ivy)
|
||||
case original => doInstall(original)
|
||||
}
|
||||
doInstallIfIvy(originalAuthenticator)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* An authenticator which just delegates to a previous authenticator and issues *nice*
|
||||
* error messages on failure to find credentials.
|
||||
*
|
||||
* Since ivy installs its own credentials handler EVERY TIME it resolves or publishes, we want to
|
||||
* install this one at some point and eventually ivy will capture it and use it.
|
||||
*/
|
||||
private[sbt] final class ErrorMessageAuthenticator(original: Option[Authenticator]) extends Authenticator {
|
||||
|
||||
protected override def getPasswordAuthentication(): PasswordAuthentication = {
|
||||
// We're guaranteed to only get here if Ivy's authentication fails
|
||||
if (!isProxyAuthentication) {
|
||||
val host = getRequestingHost
|
||||
// TODO - levenshtein distance "did you mean" message.
|
||||
Message.error(s"Unable to find credentials for [${getRequestingPrompt} @ ${host}].")
|
||||
val configuredRealms = IvyCredentialsLookup.realmsForHost.getOrElse(host, Set.empty)
|
||||
if(!configuredRealms.isEmpty) {
|
||||
Message.error(s" Is one of these realms mispelled for host [${host}]:")
|
||||
configuredRealms foreach { realm =>
|
||||
Message.error(s" * ${realm}")
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO - Maybe we should work on a helpful proxy message...
|
||||
|
||||
// TODO - To be more maven friendly, we may want to also try to grab the "first" authentication that shows up for a server and try it.
|
||||
// or maybe allow that behavior to be configured, since maven users aren't used to realms (which they should be).
|
||||
|
||||
// Grabs the authentication that would have been provided had we not been installed...
|
||||
def originalAuthentication: Option[PasswordAuthentication] = {
|
||||
Authenticator.setDefault(original.getOrElse(null))
|
||||
try Option(Authenticator.requestPasswordAuthentication(
|
||||
getRequestingHost,
|
||||
getRequestingSite,
|
||||
getRequestingPort,
|
||||
getRequestingProtocol,
|
||||
getRequestingPrompt,
|
||||
getRequestingScheme))
|
||||
finally Authenticator.setDefault(this)
|
||||
}
|
||||
originalAuthentication.getOrElse(null)
|
||||
}
|
||||
|
||||
/** Returns true if this authentication if for a proxy and not for an HTTP server.
|
||||
* We want to display different error messages, depending.
|
||||
*/
|
||||
private def isProxyAuthentication: Boolean =
|
||||
getRequestorType == Authenticator.RequestorType.PROXY
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
package sbt
|
||||
package ivyint
|
||||
|
||||
import org.apache.ivy.util.url.CredentialsStore
|
||||
import collection.JavaConverters._
|
||||
|
||||
/** A key used to store credentials in the ivy credentials store. */
|
||||
private[sbt] sealed trait CredentialKey
|
||||
/** Represents a key in the ivy credentials store that is only specific to a host. */
|
||||
private[sbt] case class Host(name: String) extends CredentialKey
|
||||
/** Represents a key in the ivy credentials store that is keyed to both a host and a "realm". */
|
||||
private[sbt] case class Realm(host: String, realm: String) extends CredentialKey
|
||||
|
||||
/**
|
||||
* Helper mechanism to improve credential related error messages.
|
||||
*
|
||||
* This evil class exposes to us the necessary information to warn on credential failure and offer
|
||||
* spelling/typo suggestions.
|
||||
*/
|
||||
private[sbt] object IvyCredentialsLookup {
|
||||
|
||||
/** Helper extractor for Ivy's key-value store of credentials. */
|
||||
private object KeySplit {
|
||||
def unapply(key: String): Option[(String,String)] = {
|
||||
key.indexOf('@') match {
|
||||
case -1 => None
|
||||
case n => Some(key.take(n) -> key.drop(n+1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Here we cheat runtime private so we can look in the credentials store.
|
||||
*
|
||||
* TODO - Don't bomb at class load time...
|
||||
*/
|
||||
private val credKeyringField = {
|
||||
val tmp = classOf[CredentialsStore].getDeclaredField("KEYRING")
|
||||
tmp.setAccessible(true)
|
||||
tmp
|
||||
}
|
||||
|
||||
/** All the keys for credentials in the ivy configuration store. */
|
||||
def keyringKeys: Set[CredentialKey] = {
|
||||
val map = credKeyringField.get(null).asInstanceOf[java.util.HashMap[String, Any]]
|
||||
// make a clone of the set...
|
||||
(map.keySet.asScala.map {
|
||||
case KeySplit(realm, host) => Realm(host, realm)
|
||||
case host => Host(host)
|
||||
})(collection.breakOut)
|
||||
}
|
||||
|
||||
/**
|
||||
* A mapping of host -> realms in the ivy credentials store.
|
||||
*/
|
||||
def realmsForHost: Map[String, Set[String]] =
|
||||
keyringKeys collect {
|
||||
case x: Realm => x
|
||||
} groupBy { realm =>
|
||||
realm.host
|
||||
} mapValues { realms =>
|
||||
realms map (_.realm)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package sbt
|
||||
|
||||
import java.io.File
|
||||
import org.specs2._
|
||||
import mutable.Specification
|
||||
|
||||
object MakePomTest extends Specification
|
||||
{
|
||||
val mp = new MakePom(ConsoleLogger())
|
||||
import mp.{makeDependencyVersion=>v}
|
||||
"MakePom makeDependencyVersion" should {
|
||||
"Handle .+ in versions" in {
|
||||
v("1.+") must_== "[1,2)"
|
||||
v("1.2.3.4.+") must_== "[1.2.3.4,1.2.3.5)"
|
||||
v("12.31.42.+") must_== "[12.31.42,12.31.43)"
|
||||
}
|
||||
/* TODO - do we care about this case?
|
||||
* 1+ --> [1,2),[10,20),[100,200),[1000,2000),[10000,20000),[100000,200000)
|
||||
*/
|
||||
"Handle ]* bracket in version ranges" in {
|
||||
v("]1,3]") must_== "(1,3]"
|
||||
v("]1.1,1.3]") must_== "(1.1,1.3]"
|
||||
}
|
||||
"Handle *[ bracket in version ranges" in {
|
||||
v("[1,3[") must_== "[1,3)"
|
||||
v("[1.1,1.3[") must_== "[1.1,1.3)"
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue