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"), mimaPreviousArtifacts := Set(), // Some(organization.value %% moduleName.value % "1.0.0"),
publishArtifact in Compile := true, publishArtifact in Compile := true,
publishArtifact in Test := false, publishArtifact in Test := false,
parallelExecution in Test := false,
commands += Command.command("scalafmtCheck") { state => commands += Command.command("scalafmtCheck") { state =>
sys.process.Process("git diff --name-only --exit-code").! match { sys.process.Process("git diff --name-only --exit-code").! match {
case 0 => // ok case 0 => // ok

View File

@ -634,7 +634,8 @@
{ "name": "retrieve", "type": "sbt.internal.librarymanagement.RetrieveConfiguration?" }, { "name": "retrieve", "type": "sbt.internal.librarymanagement.RetrieveConfiguration?" },
{ "name": "missingOk", "type": "boolean" }, { "name": "missingOk", "type": "boolean" },
{ "name": "logging", "type": "sbt.librarymanagement.UpdateLogging" }, { "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": "resolvers", "type": "sbt.librarymanagement.Resolver*" },
{ "name": "otherResolvers", "type": "sbt.librarymanagement.Resolver*" }, { "name": "otherResolvers", "type": "sbt.librarymanagement.Resolver*" },
{ "name": "moduleConfigurations", "type": "sbt.librarymanagement.ModuleConfiguration*" }, { "name": "moduleConfigurations", "type": "sbt.librarymanagement.ModuleConfiguration*" },
{ "name": "localOnly", "type": "boolean" },
{ "name": "checksums", "type": "String*" }, { "name": "checksums", "type": "String*" },
{ "name": "resolutionCacheDir", "type": "java.io.File?" } { "name": "resolutionCacheDir", "type": "java.io.File?" }
], ],
@ -738,7 +738,6 @@
" resolvers: Vector[sbt.librarymanagement.Resolver],", " resolvers: Vector[sbt.librarymanagement.Resolver],",
" otherResolvers: Vector[sbt.librarymanagement.Resolver],", " otherResolvers: Vector[sbt.librarymanagement.Resolver],",
" moduleConfigurations: Vector[sbt.librarymanagement.ModuleConfiguration],", " moduleConfigurations: Vector[sbt.librarymanagement.ModuleConfiguration],",
" localOnly: Boolean,",
" lock: Option[xsbti.GlobalLock],", " lock: Option[xsbti.GlobalLock],",
" checksums: Vector[String],", " checksums: Vector[String],",
" resolutionCacheDir: Option[java.io.File],", " resolutionCacheDir: Option[java.io.File],",
@ -746,7 +745,7 @@
" log: xsbti.Logger", " log: xsbti.Logger",
") =", ") =",
" this(lock, paths.baseDirectory, log, updateOptions, paths, resolvers, otherResolvers,", " 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 resolver
} }
case repo: ChainedResolver => 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 case repo: RawRepository => repo.resolver
} }
} }

View File

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

View File

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

View File

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

View File

@ -68,7 +68,8 @@ class DefaultLibraryManagement(ivyConfiguration: IvyConfiguration, log: Logger)
Some(retrieveConfiguration), Some(retrieveConfiguration),
true, true,
UpdateLogging.DownloadOnly, UpdateLogging.DownloadOnly,
artifactFilter artifactFilter,
false
) )
log.debug(s"Attempting to fetch ${dependenciesNames(module)}. This operation may fail.") 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 paths = IvyPaths(currentBase, Some(currentTarget))
val other = Vector.empty val other = Vector.empty
val moduleConfs = Vector(ModuleConfiguration("*", chainResolver)) val moduleConfs = Vector(ModuleConfiguration("*", chainResolver))
val off = false
val check = Vector.empty val check = Vector.empty
val resCacheDir = currentTarget / "resolution-cache" val resCacheDir = currentTarget / "resolution-cache"
new InlineIvyConfiguration(paths, new InlineIvyConfiguration(paths,
resolvers, resolvers,
other, other,
moduleConfs, moduleConfs,
off,
None, None,
check, check,
Some(resCacheDir), Some(resCacheDir),
@ -69,18 +67,19 @@ trait BaseIvySpecification extends UnitSpec {
log) log)
} }
def makeUpdateConfiguration: UpdateConfiguration = { def makeUpdateConfiguration(offline: Boolean): UpdateConfiguration = {
val retrieveConfig = val retrieveConfig =
RetrieveConfiguration(currentManaged, Resolver.defaultRetrievePattern).withSync(false) RetrieveConfiguration(currentManaged, Resolver.defaultRetrievePattern).withSync(false)
UpdateConfiguration(Some(retrieveConfig), UpdateConfiguration(Some(retrieveConfig),
false, false,
UpdateLogging.Full, UpdateLogging.Full,
ArtifactTypeFilter.forbid(Set("src", "doc"))) ArtifactTypeFilter.forbid(Set("src", "doc")),
offline)
} }
def ivyUpdateEither(module: IvySbt#Module): Either[UnresolvedWarning, UpdateReport] = { def ivyUpdateEither(module: IvySbt#Module): Either[UnresolvedWarning, UpdateReport] = {
// IO.delete(currentTarget) // IO.delete(currentTarget)
val config = makeUpdateConfiguration val config = makeUpdateConfiguration(false)
IvyActions.updateEither(module, IvyActions.updateEither(module,
config, config,
UnresolvedWarningConfiguration(), UnresolvedWarningConfiguration(),

View File

@ -19,7 +19,6 @@ class CustomPomParserTest extends UnitSpec {
Vector(local), Vector(local),
Vector.empty, Vector.empty,
Vector.empty, Vector.empty,
false,
None, None,
Vector("sha1", "md5"), Vector("sha1", "md5"),
None, 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 val m = makeModuleForDepWithSources
// the "default" configuration used in updateEither. // the "default" configuration used in updateEither.
val c = makeUpdateConfiguration val c = makeUpdateConfiguration(false)
val ivyScala = m.moduleSettings.ivyScala val ivyScala = m.moduleSettings.ivyScala
val srcTypes = Set("src") val srcTypes = Set("src")

View File

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