Add custom checksum checks to ivy

This commit is contained in:
jvican 2017-05-25 22:09:12 +02:00
parent 4d9013a3a3
commit 80ee35b01c
No known key found for this signature in database
GPG Key ID: 42DAFA0F112E8050
4 changed files with 70 additions and 9 deletions

View File

@ -5,10 +5,15 @@ package sbt.internal.librarymanagement
import java.net.URL
import java.util.Collections
import org.apache.ivy.core.module.descriptor.DependencyDescriptor
import org.apache.ivy.core.resolve.ResolveData
import org.apache.ivy.core.settings.IvySettings
import org.apache.ivy.plugins.repository.{ RepositoryCopyProgressListener, TransferEvent }
import org.apache.ivy.plugins.repository.{
RepositoryCopyProgressListener,
Resource,
TransferEvent
}
import org.apache.ivy.plugins.resolver.{
BasicResolver,
DependencyResolver,
@ -24,9 +29,10 @@ import org.apache.ivy.plugins.resolver.{
URLResolver
}
import org.apache.ivy.plugins.repository.url.{ URLRepository => URLRepo }
import org.apache.ivy.plugins.repository.file.{ FileRepository => FileRepo, FileResource }
import java.io.{ IOException, File }
import org.apache.ivy.util.{ FileUtil, ChecksumHelper }
import org.apache.ivy.plugins.repository.file.{ FileResource, FileRepository => FileRepo }
import java.io.{ File, IOException }
import org.apache.ivy.util.{ ChecksumHelper, FileUtil, Message }
import org.apache.ivy.core.module.descriptor.{ Artifact => IArtifact }
import sbt.io.IO
import sbt.util.Logger
@ -156,6 +162,7 @@ private[sbt] object ConvertResolver {
extends IBiblioResolver
with ChecksumFriendlyURLResolver
with DescriptorRequired {
override def getResource(resource: Resource, dest: File): Long = get(resource, dest)
def setPatterns(): Unit = {
// done this way for access to protected methods.
setArtifactPatterns(pattern)
@ -170,7 +177,9 @@ private[sbt] object ConvertResolver {
resolver
}
case repo: SshRepository => {
val resolver = new SshResolver with DescriptorRequired
val resolver = new SshResolver with DescriptorRequired {
override def getResource(resource: Resource, dest: File): Long = get(resource, dest)
}
initializeSSHResolver(resolver, repo, settings)
repo.publishPermissions.foreach(perm => resolver.setPublishPermissions(perm))
resolver
@ -187,6 +196,7 @@ private[sbt] object ConvertResolver {
// in local files for non-changing revisions.
// This will be fully enforced in sbt 1.0.
setRepository(new WarnOnOverwriteFileRepo())
override def getResource(resource: Resource, dest: File): Long = get(resource, dest)
}
resolver.setName(repo.name)
initializePatterns(resolver, repo.patterns, settings)
@ -196,7 +206,9 @@ private[sbt] object ConvertResolver {
resolver
}
case repo: URLRepository => {
val resolver = new URLResolver with ChecksumFriendlyURLResolver with DescriptorRequired
val resolver = new URLResolver with ChecksumFriendlyURLResolver with DescriptorRequired {
override def getResource(resource: Resource, dest: File): Long = get(resource, dest)
}
resolver.setName(repo.name)
initializePatterns(resolver, repo.patterns, settings)
resolver
@ -208,11 +220,59 @@ private[sbt] object ConvertResolver {
}
private sealed trait DescriptorRequired extends BasicResolver {
// Works around implementation restriction to access protected method `get`
def getResource(resource: Resource, dest: File): Long
override def getAndCheck(resource: Resource, dest: File): Long = {
// Follows the same semantics that private method `check` as defined in ivy `BasicResolver`
def check(resource: Resource, destination: File, algorithm: String) = {
if (!ChecksumHelper.isKnownAlgorithm(algorithm)) {
throw new IllegalArgumentException(s"Unknown checksum algorithm: $algorithm")
}
val checksumResource = resource.clone(s"${resource.getName}.$algorithm")
if (checksumResource.exists) {
Message.debug(s"$algorithm file found for $resource: checking...")
val checksumFile = File.createTempFile("ivytmp", algorithm)
try {
getResource(checksumResource, checksumFile)
try {
ChecksumHelper.check(dest, checksumFile, algorithm)
Message.verbose(s"$algorithm OK for $resource")
true
} catch {
case e: IOException =>
dest.delete()
throw e
}
} finally {
checksumFile.delete()
}
} else false
}
val size = getResource(resource, dest)
val checksums = getChecksumAlgorithms
checksums.foldLeft(false) { (failed, checksum) =>
// Continue checking until we hit a failure
if (failed) failed
else check(resource, dest, checksum)
}
size
}
var i = 0
override def getDependency(dd: DependencyDescriptor, data: ResolveData) = {
val moduleID = IvyRetrieve.toModuleID(dd.getDependencyRevisionId)
print(" " * i)
println(s"Downloading and checking for $moduleID")
i += 2
val prev = descriptorString(isAllownomd)
setDescriptor(descriptorString(hasExplicitURL(dd)))
try super.getDependency(dd, data)
val t = try super.getDependency(dd, data)
finally setDescriptor(prev)
i -= 2
print(" " * i)
println(s"End $moduleID")
t
}
def descriptorString(optional: Boolean) =
if (optional) BasicResolver.DESCRIPTOR_OPTIONAL else BasicResolver.DESCRIPTOR_REQUIRED

View File

@ -31,6 +31,7 @@ private[sbt] case class SbtChainResolver(
updateOptions: UpdateOptions,
log: Logger
) extends ChainResolver {
override def setCheckmodified(check: Boolean): Unit = super.setCheckmodified(check)
override def equals(o: Any): Boolean = o match {
case o: SbtChainResolver =>

View File

@ -53,8 +53,8 @@ trait BaseIvySpecification extends UnitSpec {
def mkIvyConfiguration(uo: UpdateOptions): IvyConfiguration = {
val paths = IvyPaths(currentBase, Some(currentTarget))
val other = Vector.empty
val moduleConfs = Vector(ModuleConfiguration("*", chainResolver))
val check = Vector.empty
val moduleConfs = Vector(ModuleConfiguration("*", chainResolver))
val resCacheDir = currentTarget / "resolution-cache"
new InlineIvyConfiguration(paths,
resolvers,

View File

@ -44,7 +44,7 @@ class OfflineModeSpec extends BaseIvySpecification with DependencyBuilders {
val offlineResolution =
IvyActions.updateEither(toResolve, offlineConf, warningConf, noClock, targetDir, log)
assert(offlineResolution.isRight)
assert(offlineResolution.isRight, s"Offline resolution has failed with $offlineResolution.")
val resolveTime = offlineResolution.right.get.stats.resolveTime
// Only check the estimate for the non cached resolution, otherwise resolution is cached