Merge pull request #92 from scalacenter/offline-mode

Add offline mode to `UpdateConfiguration`
This commit is contained in:
eugene yokota 2017-05-10 14:30:24 -04:00 committed by GitHub
commit 498367dbf4
12 changed files with 110 additions and 50 deletions

View File

@ -19,6 +19,7 @@ def commonSettings: Seq[Setting[_]] = Seq(
mimaPreviousArtifacts := Set(), // Some(organization.value %% moduleName.value % "1.0.0"),
publishArtifact in Compile := true,
publishArtifact in Test := false,
parallelExecution in Test := false,
commands += Command.command("scalafmtCheck") { state =>
sys.process.Process("git diff --name-only --exit-code").! match {
case 0 => // ok

View File

@ -634,7 +634,8 @@
{ "name": "retrieve", "type": "sbt.internal.librarymanagement.RetrieveConfiguration?" },
{ "name": "missingOk", "type": "boolean" },
{ "name": "logging", "type": "sbt.librarymanagement.UpdateLogging" },
{ "name": "artifactFilter", "type": "sbt.librarymanagement.ArtifactTypeFilter" }
{ "name": "artifactFilter", "type": "sbt.librarymanagement.ArtifactTypeFilter" },
{ "name": "offline", "type": "boolean" }
]
},
{
@ -728,7 +729,6 @@
{ "name": "resolvers", "type": "sbt.librarymanagement.Resolver*" },
{ "name": "otherResolvers", "type": "sbt.librarymanagement.Resolver*" },
{ "name": "moduleConfigurations", "type": "sbt.librarymanagement.ModuleConfiguration*" },
{ "name": "localOnly", "type": "boolean" },
{ "name": "checksums", "type": "String*" },
{ "name": "resolutionCacheDir", "type": "java.io.File?" }
],
@ -738,7 +738,6 @@
" resolvers: Vector[sbt.librarymanagement.Resolver],",
" otherResolvers: Vector[sbt.librarymanagement.Resolver],",
" moduleConfigurations: Vector[sbt.librarymanagement.ModuleConfiguration],",
" localOnly: Boolean,",
" lock: Option[xsbti.GlobalLock],",
" checksums: Vector[String],",
" resolutionCacheDir: Option[java.io.File],",
@ -746,7 +745,7 @@
" log: xsbti.Logger",
") =",
" this(lock, paths.baseDirectory, log, updateOptions, paths, resolvers, otherResolvers,",
" moduleConfigurations, localOnly, checksums, resolutionCacheDir)"
" moduleConfigurations, checksums, resolutionCacheDir)"
]
},
{

View File

@ -202,7 +202,7 @@ private[sbt] object ConvertResolver {
resolver
}
case repo: ChainedResolver =>
IvySbt.resolverChain(repo.name, repo.resolvers, false, settings, log)
IvySbt.resolverChain(repo.name, repo.resolvers, settings, log)
case repo: RawRepository => repo.resolver
}
}

View File

@ -88,16 +88,10 @@ final class IvySbt(val configuration: IvyConfiguration) { self =>
case i: InlineIvyConfiguration =>
is.setVariable("ivy.checksums", i.checksums mkString ",")
i.paths.ivyHome foreach is.setDefaultIvyUserDir
IvySbt.configureCache(is, i.localOnly, i.resolutionCacheDir)
IvySbt.setResolvers(
is,
i.resolvers,
i.otherResolvers,
i.localOnly,
configuration.updateOptions,
configuration.log
)
IvySbt.setModuleConfigurations(is, i.moduleConfigurations, configuration.log)
val log = configuration.log
IvySbt.configureCache(is, i.resolutionCacheDir)
IvySbt.setResolvers(is, i.resolvers, i.otherResolvers, configuration.updateOptions, log)
IvySbt.setModuleConfigurations(is, i.moduleConfigurations, log)
}
is
}
@ -342,13 +336,12 @@ private[sbt] object IvySbt {
settings: IvySettings,
resolvers: Seq[Resolver],
other: Seq[Resolver],
localOnly: Boolean,
updateOptions: UpdateOptions,
log: Logger
): Unit = {
def makeChain(label: String, name: String, rs: Seq[Resolver]) = {
log.debug(label + " repositories:")
val chain = resolverChain(name, rs, localOnly, settings, updateOptions, log)
val chain = resolverChain(name, rs, settings, updateOptions, log)
settings.addResolver(chain)
chain
}
@ -362,18 +355,17 @@ private[sbt] object IvySbt {
module.revision endsWith "-SNAPSHOT"
private[sbt] def isChanging(mrid: ModuleRevisionId): Boolean =
mrid.getRevision endsWith "-SNAPSHOT"
def resolverChain(
name: String,
resolvers: Seq[Resolver],
localOnly: Boolean,
settings: IvySettings,
log: Logger
): DependencyResolver =
resolverChain(name, resolvers, localOnly, settings, UpdateOptions(), log)
): DependencyResolver = resolverChain(name, resolvers, settings, UpdateOptions(), log)
def resolverChain(
name: String,
resolvers: Seq[Resolver],
localOnly: Boolean,
settings: IvySettings,
updateOptions: UpdateOptions,
log: Logger
@ -446,19 +438,12 @@ private[sbt] object IvySbt {
)
}
}
private def configureCache(
settings: IvySettings,
localOnly: Boolean,
resCacheDir: Option[File]
): Unit = {
configureResolutionCache(settings, localOnly, resCacheDir)
configureRepositoryCache(settings, localOnly)
private def configureCache(settings: IvySettings, resCacheDir: Option[File]): Unit = {
configureResolutionCache(settings, resCacheDir)
configureRepositoryCache(settings)
}
private[this] def configureResolutionCache(
settings: IvySettings,
localOnly: Boolean,
resCacheDir: Option[File]
): Unit = {
private[this] def configureResolutionCache(settings: IvySettings, resCacheDir: Option[File]) = {
val base = resCacheDir getOrElse settings.getDefaultResolutionCacheBasedir
settings.setResolutionCacheManager(new ResolutionCache(base, settings))
}
@ -485,7 +470,7 @@ private[sbt] object IvySbt {
)
}
private[this] def configureRepositoryCache(settings: IvySettings, localOnly: Boolean): Unit = {
private[this] def configureRepositoryCache(settings: IvySettings): Unit = {
val cacheDir = settings.getDefaultRepositoryCacheBasedir()
val manager = new DefaultRepositoryCacheManager("default-cache", settings, cacheDir) {
override def findModuleInCache(
@ -529,12 +514,8 @@ private[sbt] object IvySbt {
manager.setDataFilePattern(PluginPattern + manager.getDataFilePattern)
manager.setIvyPattern(PluginPattern + manager.getIvyPattern)
manager.setUseOrigin(true)
if (localOnly)
manager.setDefaultTTL(java.lang.Long.MAX_VALUE)
else {
manager.setChangingMatcher(PatternMatcher.REGEXP)
manager.setChangingPattern(".*-SNAPSHOT")
}
manager.setChangingMatcher(PatternMatcher.REGEXP)
manager.setChangingPattern(".*-SNAPSHOT")
settings.addRepositoryCacheManager(manager)
settings.setDefaultRepositoryCacheManager(manager)
}

View File

@ -467,6 +467,7 @@ object IvyActions {
val resolveId = ResolveOptions.getDefaultResolveId(moduleDescriptor)
resolveOptions.setResolveId(resolveId)
resolveOptions.setArtifactFilter(updateConfiguration.artifactFilter)
resolveOptions.setUseCacheOnly(updateConfiguration.offline)
resolveOptions.setLog(ivyLogLevel(logging))
ResolutionCache.cleanModule(
moduleDescriptor.getModuleRevisionId,
@ -519,6 +520,7 @@ object IvyActions {
val resolveId = ResolveOptions.getDefaultResolveId(descriptor)
resolveOptions.setResolveId(resolveId)
resolveOptions.setArtifactFilter(updateConfiguration.artifactFilter)
resolveOptions.setUseCacheOnly(updateConfiguration.offline)
resolveOptions.setLog(ivyLogLevel(updateConfiguration.logging))
val acceptError = updateConfiguration.missingOk
resolver.customResolve(descriptor, acceptError, logicalClock, resolveOptions, cache, log)

View File

@ -108,7 +108,6 @@ class IvyCache(val ivyHome: Option[File]) {
Vector(local),
Vector.empty,
Vector.empty,
false,
lock,
IvySbt.DefaultChecksums,
None,

View File

@ -68,7 +68,8 @@ class DefaultLibraryManagement(ivyConfiguration: IvyConfiguration, log: Logger)
Some(retrieveConfiguration),
true,
UpdateLogging.DownloadOnly,
artifactFilter
artifactFilter,
false
)
log.debug(s"Attempting to fetch ${dependenciesNames(module)}. This operation may fail.")

View File

@ -54,14 +54,12 @@ trait BaseIvySpecification extends UnitSpec {
val paths = IvyPaths(currentBase, Some(currentTarget))
val other = Vector.empty
val moduleConfs = Vector(ModuleConfiguration("*", chainResolver))
val off = false
val check = Vector.empty
val resCacheDir = currentTarget / "resolution-cache"
new InlineIvyConfiguration(paths,
resolvers,
other,
moduleConfs,
off,
None,
check,
Some(resCacheDir),
@ -69,18 +67,19 @@ trait BaseIvySpecification extends UnitSpec {
log)
}
def makeUpdateConfiguration: UpdateConfiguration = {
def makeUpdateConfiguration(offline: Boolean): UpdateConfiguration = {
val retrieveConfig =
RetrieveConfiguration(currentManaged, Resolver.defaultRetrievePattern).withSync(false)
UpdateConfiguration(Some(retrieveConfig),
false,
UpdateLogging.Full,
ArtifactTypeFilter.forbid(Set("src", "doc")))
ArtifactTypeFilter.forbid(Set("src", "doc")),
offline)
}
def ivyUpdateEither(module: IvySbt#Module): Either[UnresolvedWarning, UpdateReport] = {
// IO.delete(currentTarget)
val config = makeUpdateConfiguration
val config = makeUpdateConfiguration(false)
IvyActions.updateEither(module,
config,
UnresolvedWarningConfiguration(),

View File

@ -19,7 +19,6 @@ class CustomPomParserTest extends UnitSpec {
Vector(local),
Vector.empty,
Vector.empty,
false,
None,
Vector("sha1", "md5"),
None,

View File

@ -0,0 +1,79 @@
package sbt.librarymanagement
import org.scalatest.Assertion
import sbt.internal.librarymanagement._
import sbt.internal.librarymanagement.impl.DependencyBuilders
import sbt.io.IO
class OfflineModeSpec extends BaseIvySpecification with DependencyBuilders {
private final def targetDir = Some(currentDependency)
private final def onlineConf = makeUpdateConfiguration(false)
private final def offlineConf = makeUpdateConfiguration(true)
private final def warningConf = UnresolvedWarningConfiguration()
private final def normalOptions = UpdateOptions()
private final def cachedOptions = UpdateOptions().withCachedResolution(true)
private final def noClock = LogicalClock.unknown
def avro177 = ModuleID("org.apache.avro", "avro", "1.7.7")
def dataAvro1940 = ModuleID("com.linkedin.pegasus", "data-avro", "1.9.40")
def netty320 = ModuleID("org.jboss.netty", "netty", "3.2.0.Final")
final def dependencies: Vector[ModuleID] =
Vector(avro177, dataAvro1940, netty320).map(_.withConfigurations(Some("compile")))
def cleanAll(): Unit = {
cleanIvyCache()
IO.delete(currentTarget)
IO.delete(currentManaged)
IO.delete(currentDependency)
}
def checkOnlineAndOfflineResolution(updateOptions: UpdateOptions): Assertion = {
cleanAll()
val toResolve = module(defaultModuleId, dependencies, None, updateOptions)
if (updateOptions.cachedResolution)
cleanCachedResolutionCache(toResolve)
val onlineResolution =
IvyActions.updateEither(toResolve, onlineConf, warningConf, noClock, targetDir, log)
assert(onlineResolution.isRight)
assert(onlineResolution.right.exists(report => report.stats.resolveTime > 0))
// Compute an estimate to ensure that the second resolution does indeed use the cache
val originalResolveTime = onlineResolution.right.get.stats.resolveTime
val estimatedCachedTime = originalResolveTime * 0.15
val offlineResolution =
IvyActions.updateEither(toResolve, offlineConf, warningConf, noClock, targetDir, log)
assert(offlineResolution.isRight)
val resolveTime = offlineResolution.right.get.stats.resolveTime
// Only check the estimate for the non cached resolution, otherwise resolution is cached
assert(resolveTime <= estimatedCachedTime,
"Offline resolution took more than 15% of normal resolution's running time.")
}
"Offline update configuration" should "reuse the caches when offline is enabled" in {
checkOnlineAndOfflineResolution(normalOptions)
}
it should "reuse the caches when offline and cached resolution are enabled" in {
checkOnlineAndOfflineResolution(cachedOptions)
}
def checkFailingResolution(updateOptions: UpdateOptions): Assertion = {
cleanAll()
val toResolve = module(defaultModuleId, dependencies, None, updateOptions)
if (updateOptions.cachedResolution) cleanCachedResolutionCache(toResolve)
val failedOfflineResolution =
IvyActions.updateEither(toResolve, offlineConf, warningConf, noClock, targetDir, log)
assert(failedOfflineResolution.isLeft)
}
it should "fail when artifacts are missing in the cache" in {
checkFailingResolution(normalOptions)
}
it should "fail when artifacts are missing in the cache for cached resolution" in {
checkFailingResolution(cachedOptions)
}
}

View File

@ -44,7 +44,7 @@ class IvyRepoSpec extends BaseIvySpecification with DependencyBuilders {
val m = makeModuleForDepWithSources
// the "default" configuration used in updateEither.
val c = makeUpdateConfiguration
val c = makeUpdateConfiguration(false)
val ivyScala = m.moduleSettings.ivyScala
val srcTypes = Set("src")

View File

@ -51,7 +51,7 @@ object Dependencies {
def addSbtUtilCache(p: Project): Project = addSbtModule(p, sbtUtilPath, "utilCache", utilCache)
val launcherInterface = "org.scala-sbt" % "launcher-interface" % "1.0.0"
val ivy = "org.scala-sbt.ivy" % "ivy" % "2.3.0-sbt-48dd0744422128446aee9ac31aa356ee203cc9f4"
val ivy = "org.scala-sbt.ivy" % "ivy" % "2.3.0-sbt-a3314352b638afbf0dca19f127e8263ed6f898bd"
val jsch = "com.jcraft" % "jsch" % "0.1.46" intransitive ()
val scalaReflect = Def.setting { "org.scala-lang" % "scala-reflect" % scalaVersion.value }
val scalaXml = scala211Module("scala-xml", "1.0.5")