Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a global --offline config key #3216

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ object BloopExit extends ScalaCommand[BloopExitOptions] {
import opts.*
compilationServer.bloopRifleConfig(
global.logging.logger,
coursier.coursierCache(global.logging.logger.coursierLogger("Downloading Bloop")),
coursier.coursierCache(global.logging.logger, cacheLoggerPrefix = "Downloading Bloop"),
global.logging.verbosity,
"java", // shouldn't be used…
Directories.directories
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@ object BloopOutput extends ScalaCommand[BloopOutputOptions] {
override def runCommand(options: BloopOutputOptions, args: RemainingArgs, logger: Logger): Unit = {
val bloopRifleConfig = options.compilationServer.bloopRifleConfig(
logger,
CoursierOptions().coursierCache(logger.coursierLogger("Downloading Bloop")), // unused here
CoursierOptions().coursierCache(logger, cacheLoggerPrefix = "Downloading Bloop"), // unused here
options.global.logging.verbosity,
"unused-java", // unused here
Directories.directories
Original file line number Diff line number Diff line change
@@ -23,14 +23,12 @@ object BloopStart extends ScalaCommand[BloopStartOptions] {
import opts.*
val buildOptions = BuildOptions(
javaOptions = JvmUtils.javaOptions(jvm).orExit(global.logging.logger),
internal = InternalOptions(
cache = Some(coursier.coursierCache(global.logging.logger.coursierLogger("")))
)
internal = InternalOptions(cache = Some(coursier.coursierCache(global.logging.logger)))
)

compilationServer.bloopRifleConfig(
global.logging.logger,
coursier.coursierCache(global.logging.logger.coursierLogger("Downloading Bloop")),
coursier.coursierCache(global.logging.logger, cacheLoggerPrefix = "Downloading Bloop"),
global.logging.verbosity,
buildOptions.javaHome().value.javaCommand,
Directories.directories
Original file line number Diff line number Diff line change
@@ -46,7 +46,7 @@ object Config extends ScalaCommand[ConfigOptions] {
)
sys.exit(1)
}
val coursierCache = options.coursier.coursierCache(logger.coursierLogger(""))
val coursierCache = options.coursier.coursierCache(logger)
val secKeyEntry = Keys.pgpSecretKey
val pubKeyEntry = Keys.pgpPublicKey

Original file line number Diff line number Diff line change
@@ -43,7 +43,7 @@ class Default(actualHelp: => RuntimeCommandsHelp)
Version.runCommand(
options = VersionOptions(
global = options.shared.global,
offline = options.shared.coursier.getOffline().getOrElse(false)
offline = options.shared.coursier.getOffline(logger).getOrElse(false)
),
args = args,
logger = logger
Original file line number Diff line number Diff line change
@@ -157,7 +157,7 @@ object SecretCreate extends ScalaCommand[SecretCreateOptions] {
).orExit(logger)
}

val cache = options.coursier.coursierCache(logger.coursierLogger(""))
val cache = options.coursier.coursierCache(logger)
val archiveCache = ArchiveCache().withCache(cache)

LibSodiumJni.init(cache, archiveCache, logger)
Original file line number Diff line number Diff line change
@@ -24,7 +24,7 @@ object New extends ScalaCommand[NewOptions] {
Seq.empty,
Some(scalaParameters),
logger,
CoursierOptions().coursierCache(logger.coursierLogger("")),
CoursierOptions().coursierCache(logger),
None
) match {
case Right(value) => value
Original file line number Diff line number Diff line change
@@ -106,7 +106,7 @@ abstract class PgpExternalCommand extends ExternalCommand {

val logger = options.global.logging.logger

val cache = options.coursier.coursierCache(logger.coursierLogger(""))
val cache = options.coursier.coursierCache(logger)
val retCode = tryRun(
cache,
remainingArgs,
Original file line number Diff line number Diff line change
@@ -31,7 +31,7 @@ object PgpPush extends ScalaCommand[PgpPushOptions] {
sys.exit(1)
}

lazy val coursierCache = options.coursier.coursierCache(logger.coursierLogger(""))
lazy val coursierCache = options.coursier.coursierCache(logger)

for (key <- all) {
val path = os.Path(key, os.pwd)
Original file line number Diff line number Diff line change
@@ -42,7 +42,7 @@ object PublishSetup extends ScalaCommand[PublishSetupOptions] {
): Unit = {
Publish.maybePrintLicensesAndExit(options.publishParams)

val coursierCache = options.coursier.coursierCache(logger.coursierLogger(""))
val coursierCache = options.coursier.coursierCache(logger)
val directories = Directories.directories

lazy val configDb = ConfigDbUtils.configDb.orExit(logger)
Original file line number Diff line number Diff line change
@@ -4,9 +4,13 @@ import caseapp.*
import com.github.plokhotnyuk.jsoniter_scala.core.*
import com.github.plokhotnyuk.jsoniter_scala.macros.*
import coursier.cache.{CacheLogger, CachePolicy, FileCache}
import coursier.util.Task

import scala.build.Logger
import scala.build.internals.EnvVar
import scala.cli.commands.tags
import scala.cli.config.Keys
import scala.cli.util.ConfigDbUtils
import scala.concurrent.duration.Duration

// format: off
@@ -39,24 +43,28 @@ final case class CoursierOptions(
private def validateChecksums =
coursierValidateChecksums.getOrElse(true)

def coursierCache(logger: CacheLogger) = {
var baseCache = FileCache().withLogger(logger)
def coursierCache(logger: Logger, cacheLogger: CacheLogger): FileCache[Task] = {
var baseCache = FileCache().withLogger(cacheLogger)
if (!validateChecksums)
baseCache = baseCache.withChecksums(Nil)
val ttlOpt = ttl.map(_.trim).filter(_.nonEmpty).map(Duration(_))
for (ttl0 <- ttlOpt)
baseCache = baseCache.withTtl(ttl0)
for (loc <- cache.filter(_.trim.nonEmpty))
baseCache = baseCache.withLocation(loc)
for (isOffline <- getOffline() if isOffline)
for (isOffline <- getOffline(logger) if isOffline)
baseCache = baseCache.withCachePolicies(Seq(CachePolicy.LocalOnly))

baseCache
}

def getOffline(): Option[Boolean] = offline
def coursierCache(logger: Logger, cacheLoggerPrefix: String = ""): FileCache[Task] =
coursierCache(logger, logger.coursierLogger(cacheLoggerPrefix))

def getOffline(logger: Logger): Option[Boolean] = offline
.orElse(EnvVar.Coursier.coursierMode.valueOpt.map(_ == "offline"))
.orElse(Option(System.getProperty("coursier.mode")).map(_ == "offline"))
.orElse(ConfigDbUtils.getConfigDbOpt(logger).flatMap(_.get(Keys.offline).toOption.flatten))
}

object CoursierOptions {
Original file line number Diff line number Diff line change
@@ -432,7 +432,7 @@ final case class SharedOptions(
strictBloopJsonCheck = strictBloopJsonCheck,
interactive = Some(() => interactive),
exclude = exclude.map(Positioned.commandLine),
offline = coursier.getOffline()
offline = coursier.getOffline(logger)
),
notForBloopOptions = bo.PostBuildOptions(
scalaJsLinkerOptions = linkerOptions(js),
@@ -604,11 +604,11 @@ final case class SharedOptions(
options => bloopRifleConfig(Some(options)),
threads.bloop,
strictBloopJsonCheckOrDefault,
coursier.getOffline().getOrElse(false)
coursier.getOffline(logger).getOrElse(false)
)
else SimpleScalaCompilerMaker("java", Nil)

lazy val coursierCache = coursier.coursierCache(logging.logger.coursierLogger(""))
lazy val coursierCache: FileCache[Task] = coursier.coursierCache(logging.logger)

def inputs(
args: Seq[String],
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@ object LauncherCli {
def runAndExit(version: String, options: LauncherOptions, remainingArgs: Seq[String]): Nothing = {

val logger = LoggingOptions().logger
val cache = CoursierOptions().coursierCache(logger.coursierLogger(""))
val cache = CoursierOptions().coursierCache(logger)
val scalaVersion = options.cliScalaVersion.getOrElse(scalaCliScalaVersion(version))
val scalaParameters = ScalaParameters(scalaVersion)
val snapshotsRepo = Seq(Repositories.central, Repositories.sonatype("snapshots"))
2 changes: 1 addition & 1 deletion modules/cli/src/test/scala/cli/tests/LauncherCliTest.scala
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ class LauncherCliTest extends munit.FunSuite {

test("resolve nightly version".flaky) {
val logger = TestLogger()
val cache = CoursierOptions().coursierCache(logger.coursierLogger(""))
val cache = CoursierOptions().coursierCache(logger)
val scalaParameters = ScalaParameters(Constants.defaultScalaVersion)

val nightlyCliVersion = LauncherCli.resolveNightlyScalaCliVersion(cache, scalaParameters)
8 changes: 8 additions & 0 deletions modules/config/src/main/scala/scala/cli/config/Keys.scala
Original file line number Diff line number Diff line change
@@ -70,6 +70,13 @@ object Keys {
description = "Globally enables power mode (the '--power' launcher flag)."
)

val offline = new Key.BooleanEntry(
prefix = Seq.empty,
name = "offline",
specificationLevel = SpecificationLevel.IMPLEMENTATION,
description = "Globally enables offline mode (the '--offline' flag)."
)

val suppressDirectivesInMultipleFilesWarning =
new Key.BooleanEntry(
prefix = Seq("suppress-warning"),
@@ -172,6 +179,7 @@ object Keys {
suppressDirectivesInMultipleFilesWarning,
suppressOutdatedDependenciessWarning,
suppressExperimentalFeatureWarning,
offline,
pgpPublicKey,
pgpSecretKey,
pgpSecretKeyPassword,
Original file line number Diff line number Diff line change
@@ -560,4 +560,47 @@ class ConfigTests extends ScalaCliSuite {
}
}

for {
offlineSetting <- Seq(true, false)
prefillCache <- if (offlineSetting) Seq(true, false) else Seq(false)
caption = s"offline mode: $offlineSetting, " +
(offlineSetting -> prefillCache match {
case (true, true) => "build should succeed when cache was pre-filled"
case (true, false) => "build should fail when cache is empty"
case _ => "dependencies should be downloaded as normal"
})
}
test(caption) {
TestInputs(
os.rel / "simple.sc" -> "println(dotty.tools.dotc.config.Properties.versionNumberString)"
)
.fromRoot { root =>
val configFile = os.rel / "config" / "config.json"
val localRepoPath = root / "local-repo"
val envs = Map(
"COURSIER_CACHE" -> localRepoPath.toString,
"SCALA_CLI_CONFIG" -> configFile.toString
)
os.proc(TestUtil.cli, "bloop", "exit", "--power").call(cwd = root)
os.proc(TestUtil.cli, "config", "offline", offlineSetting.toString)
.call(cwd = root, env = envs)
if (prefillCache) for {
artifactName <- Seq(
"scala3-compiler_3",
"scala3-staging_3",
"scala3-tasty-inspector_3",
"scala3-sbt-bridge"
)
artifact = s"org.scala-lang:$artifactName:${Constants.scala3Next}"
} os.proc(TestUtil.cs, "fetch", "--cache", localRepoPath, artifact).call(cwd = root)
val buildExpectedToSucceed = !offlineSetting || prefillCache
val r = os.proc(TestUtil.cli, "run", "simple.sc", "--with-compiler")
.call(cwd = root, env = envs, check = buildExpectedToSucceed)
if (buildExpectedToSucceed) expect(r.out.trim() == Constants.scala3Next)
else expect(r.exitCode == 1)
os.proc(TestUtil.cli, "config", "offline", "--unset")
.call(cwd = root, env = envs)
}
}

}
Original file line number Diff line number Diff line change
@@ -38,7 +38,7 @@ trait RunGistTestDefinitions { _: RunTestDefinitions =>
"https://gist.github.com/alexarchambault/f972d941bc4a502d70267cfbbc4d6343/raw/2691c01984c9249936a625a42e29a822a357b0f6/Test.java"
val message = "Hello from Java GitHub Gist"
emptyInputs.fromRoot { root =>
val output = os.proc(TestUtil.cli, extraOptions, escapedUrls(url))
val output = os.proc(TestUtil.cli, extraOptions, "-v", "-v", "-v", escapedUrls(url))
.call(cwd = root)
.out.trim()
expect(output == message)
Original file line number Diff line number Diff line change
@@ -64,7 +64,7 @@ trait RunPipedSourcesTestDefinitions { _: RunTestDefinitions =>
|}
|""".stripMargin
emptyInputs.fromRoot { root =>
val output = os.proc(TestUtil.cli, "_.java", extraOptions)
val output = os.proc(TestUtil.cli, "_.java", "-v", "-v", "-v", extraOptions)
.call(cwd = root, stdin = pipedInput)
.out.trim()
expect(output == expectedOutput)
@@ -88,7 +88,7 @@ trait RunPipedSourcesTestDefinitions { _: RunTestDefinitions =>
|}
|""".stripMargin
emptyInputs.fromRoot { root =>
val output = os.proc(TestUtil.cli, "_.java", extraOptions)
val output = os.proc(TestUtil.cli, "_.java", "-v", "-v", "-v", extraOptions)
.call(cwd = root, stdin = pipedInput)
.out.trim()
expect(output == expectedOutput)
@@ -129,6 +129,9 @@ trait RunPipedSourcesTestDefinitions { _: RunTestDefinitions =>
scalaSnippet,
"--java-snippet",
javaSnippet,
"-v",
"-v",
"-v",
extraOptions
)
.call(cwd = root, stdin = pipedInput)
5 changes: 5 additions & 0 deletions website/docs/guides/power/offline.md
Original file line number Diff line number Diff line change
@@ -39,6 +39,11 @@ or
scala-cli -Dcoursier.mode=offline run Main.scala
```

Finally, it's possible to enable offline mode via global config:
```bash ignore
scala-cli --power config offline true
```

## Changes in behaviour

### Scala artifacts
1 change: 1 addition & 0 deletions website/docs/reference/commands.md
Original file line number Diff line number Diff line change
@@ -56,6 +56,7 @@ Available keys:
- interactive Globally enables interactive mode (the '--interactive' flag).
- interactive-was-suggested Setting indicating if the global interactive mode was already suggested.
- java.properties Java properties for Scala CLI's execution.
- offline Globally enables offline mode (the '--offline' flag).
- pgp.public-key The PGP public key, used for signing.
- pgp.secret-key The PGP secret key, used for signing.
- pgp.secret-key-password The PGP secret key password, used for signing.
1 change: 1 addition & 0 deletions website/docs/reference/scala-command/commands.md
Original file line number Diff line number Diff line change
@@ -55,6 +55,7 @@ Available keys:
- interactive Globally enables interactive mode (the '--interactive' flag).
- interactive-was-suggested Setting indicating if the global interactive mode was already suggested.
- java.properties Java properties for Scala CLI's execution.
- offline Globally enables offline mode (the '--offline' flag).
- pgp.public-key The PGP public key, used for signing.
- pgp.secret-key The PGP secret key, used for signing.
- pgp.secret-key-password The PGP secret key password, used for signing.
Original file line number Diff line number Diff line change
@@ -663,6 +663,7 @@ Available keys:
- interactive Globally enables interactive mode (the '--interactive' flag).
- interactive-was-suggested Setting indicating if the global interactive mode was already suggested.
- java.properties Java properties for Scala CLI's execution.
- offline Globally enables offline mode (the '--offline' flag).
- pgp.public-key The PGP public key, used for signing.
- pgp.secret-key The PGP secret key, used for signing.
- pgp.secret-key-password The PGP secret key password, used for signing.
Loading