Skip to content

Commit 7bb5b01

Browse files
committed
Add a global config key for --offline mode
1 parent cc81249 commit 7bb5b01

File tree

19 files changed

+83
-20
lines changed

19 files changed

+83
-20
lines changed

modules/cli/src/main/scala/scala/cli/commands/bloop/BloopExit.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ object BloopExit extends ScalaCommand[BloopExitOptions] {
1818
import opts.*
1919
compilationServer.bloopRifleConfig(
2020
global.logging.logger,
21-
coursier.coursierCache(global.logging.logger.coursierLogger("Downloading Bloop")),
21+
coursier.coursierCache(global.logging.logger.coursierLogger("Downloading Bloop"), global.logging.logger),
2222
global.logging.verbosity,
2323
"java", // shouldn't be used…
2424
Directories.directories

modules/cli/src/main/scala/scala/cli/commands/bloop/BloopOutput.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ object BloopOutput extends ScalaCommand[BloopOutputOptions] {
2020
override def runCommand(options: BloopOutputOptions, args: RemainingArgs, logger: Logger): Unit = {
2121
val bloopRifleConfig = options.compilationServer.bloopRifleConfig(
2222
logger,
23-
CoursierOptions().coursierCache(logger.coursierLogger("Downloading Bloop")), // unused here
23+
CoursierOptions().coursierCache(logger.coursierLogger("Downloading Bloop"), logger), // unused here
2424
options.global.logging.verbosity,
2525
"unused-java", // unused here
2626
Directories.directories

modules/cli/src/main/scala/scala/cli/commands/bloop/BloopStart.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ object BloopStart extends ScalaCommand[BloopStartOptions] {
2424
val buildOptions = BuildOptions(
2525
javaOptions = JvmUtils.javaOptions(jvm).orExit(global.logging.logger),
2626
internal = InternalOptions(
27-
cache = Some(coursier.coursierCache(global.logging.logger.coursierLogger("")))
27+
cache = Some(coursier.coursierCache(global.logging.logger.coursierLogger(""), global.logging.logger))
2828
)
2929
)
3030

3131
compilationServer.bloopRifleConfig(
3232
global.logging.logger,
33-
coursier.coursierCache(global.logging.logger.coursierLogger("Downloading Bloop")),
33+
coursier.coursierCache(global.logging.logger.coursierLogger("Downloading Bloop"), logger),
3434
global.logging.verbosity,
3535
buildOptions.javaHome().value.javaCommand,
3636
Directories.directories

modules/cli/src/main/scala/scala/cli/commands/config/Config.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ object Config extends ScalaCommand[ConfigOptions] {
4646
)
4747
sys.exit(1)
4848
}
49-
val coursierCache = options.coursier.coursierCache(logger.coursierLogger(""))
49+
val coursierCache = options.coursier.coursierCache(logger.coursierLogger(""), logger)
5050
val secKeyEntry = Keys.pgpSecretKey
5151
val pubKeyEntry = Keys.pgpPublicKey
5252

modules/cli/src/main/scala/scala/cli/commands/github/SecretCreate.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ object SecretCreate extends ScalaCommand[SecretCreateOptions] {
157157
).orExit(logger)
158158
}
159159

160-
val cache = options.coursier.coursierCache(logger.coursierLogger(""))
160+
val cache = options.coursier.coursierCache(logger.coursierLogger(""), logger)
161161
val archiveCache = ArchiveCache().withCache(cache)
162162

163163
LibSodiumJni.init(cache, archiveCache, logger)

modules/cli/src/main/scala/scala/cli/commands/new/New.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ object New extends ScalaCommand[NewOptions] {
2424
Seq.empty,
2525
Some(scalaParameters),
2626
logger,
27-
CoursierOptions().coursierCache(logger.coursierLogger("")),
27+
CoursierOptions().coursierCache(logger.coursierLogger(""), logger),
2828
None
2929
) match {
3030
case Right(value) => value

modules/cli/src/main/scala/scala/cli/commands/pgp/PgpExternalCommand.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ abstract class PgpExternalCommand extends ExternalCommand {
106106

107107
val logger = options.global.logging.logger
108108

109-
val cache = options.coursier.coursierCache(logger.coursierLogger(""))
109+
val cache = options.coursier.coursierCache(logger.coursierLogger(""), logger)
110110
val retCode = tryRun(
111111
cache,
112112
remainingArgs,

modules/cli/src/main/scala/scala/cli/commands/pgp/PgpPush.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ object PgpPush extends ScalaCommand[PgpPushOptions] {
3131
sys.exit(1)
3232
}
3333

34-
lazy val coursierCache = options.coursier.coursierCache(logger.coursierLogger(""))
34+
lazy val coursierCache = options.coursier.coursierCache(logger.coursierLogger(""), logger)
3535

3636
for (key <- all) {
3737
val path = os.Path(key, os.pwd)

modules/cli/src/main/scala/scala/cli/commands/publish/PublishSetup.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ object PublishSetup extends ScalaCommand[PublishSetupOptions] {
4242
): Unit = {
4343
Publish.maybePrintLicensesAndExit(options.publishParams)
4444

45-
val coursierCache = options.coursier.coursierCache(logger.coursierLogger(""))
45+
val coursierCache = options.coursier.coursierCache(logger.coursierLogger(""), logger)
4646
val directories = Directories.directories
4747

4848
lazy val configDb = ConfigDbUtils.configDb.orExit(logger)

modules/cli/src/main/scala/scala/cli/commands/shared/CoursierOptions.scala

+8-4
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@ import com.github.plokhotnyuk.jsoniter_scala.core.*
55
import com.github.plokhotnyuk.jsoniter_scala.macros.*
66
import coursier.cache.{CacheLogger, CachePolicy, FileCache}
77

8+
import scala.build.Logger
89
import scala.build.internals.EnvVar
910
import scala.cli.commands.tags
11+
import scala.cli.config.Keys
12+
import scala.cli.util.ConfigDbUtils
1013
import scala.concurrent.duration.Duration
1114

1215
// format: off
@@ -39,24 +42,25 @@ final case class CoursierOptions(
3942
private def validateChecksums =
4043
coursierValidateChecksums.getOrElse(true)
4144

42-
def coursierCache(logger: CacheLogger) = {
43-
var baseCache = FileCache().withLogger(logger)
45+
def coursierCache(cacheLogger: CacheLogger, cliLogger: Logger) = {
46+
var baseCache = FileCache().withLogger(cacheLogger)
4447
if (!validateChecksums)
4548
baseCache = baseCache.withChecksums(Nil)
4649
val ttlOpt = ttl.map(_.trim).filter(_.nonEmpty).map(Duration(_))
4750
for (ttl0 <- ttlOpt)
4851
baseCache = baseCache.withTtl(ttl0)
4952
for (loc <- cache.filter(_.trim.nonEmpty))
5053
baseCache = baseCache.withLocation(loc)
51-
for (isOffline <- getOffline() if isOffline)
54+
for (isOffline <- getOffline(cliLogger) if isOffline)
5255
baseCache = baseCache.withCachePolicies(Seq(CachePolicy.LocalOnly))
5356

5457
baseCache
5558
}
5659

57-
def getOffline(): Option[Boolean] = offline
60+
def getOffline(logger: Logger): Option[Boolean] = offline
5861
.orElse(EnvVar.Coursier.coursierMode.valueOpt.map(_ == "offline"))
5962
.orElse(Option(System.getProperty("coursier.mode")).map(_ == "offline"))
63+
.orElse(ConfigDbUtils.getConfigDbOpt(logger).flatMap(_.get(Keys.offline).toOption.flatten))
6064
}
6165

6266
object CoursierOptions {

modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala

+3-3
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ final case class SharedOptions(
442442
strictBloopJsonCheck = strictBloopJsonCheck,
443443
interactive = Some(() => interactive),
444444
exclude = exclude.map(Positioned.commandLine),
445-
offline = coursier.getOffline()
445+
offline = coursier.getOffline(logger)
446446
),
447447
notForBloopOptions = bo.PostBuildOptions(
448448
scalaJsLinkerOptions = linkerOptions(js),
@@ -594,11 +594,11 @@ final case class SharedOptions(
594594
options => bloopRifleConfig(Some(options)),
595595
threads.bloop,
596596
strictBloopJsonCheckOrDefault,
597-
coursier.getOffline().getOrElse(false)
597+
coursier.getOffline(logger).getOrElse(false)
598598
)
599599
else SimpleScalaCompilerMaker("java", Nil)
600600

601-
lazy val coursierCache = coursier.coursierCache(logging.logger.coursierLogger(""))
601+
lazy val coursierCache = coursier.coursierCache(logging.logger.coursierLogger(""), logger)
602602

603603
def inputs(
604604
args: Seq[String],

modules/cli/src/main/scala/scala/cli/launcher/LauncherCli.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ object LauncherCli {
2121
def runAndExit(version: String, options: LauncherOptions, remainingArgs: Seq[String]): Nothing = {
2222

2323
val logger = LoggingOptions().logger
24-
val cache = CoursierOptions().coursierCache(logger.coursierLogger(""))
24+
val cache = CoursierOptions().coursierCache(logger.coursierLogger(""), logger)
2525
val scalaVersion = options.cliScalaVersion.getOrElse(scalaCliScalaVersion(version))
2626
val scalaParameters = ScalaParameters(scalaVersion)
2727
val snapshotsRepo = Seq(Repositories.central, Repositories.sonatype("snapshots"))

modules/cli/src/test/scala/cli/tests/LauncherCliTest.scala

+4-2
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@ import dependency.ScalaParameters
55
import scala.build.internal.Constants
66
import scala.build.tests.TestLogger
77
import scala.cli.commands.shared.CoursierOptions
8+
import scala.cli.internal.CliLogger
89
import scala.cli.launcher.LauncherCli
910

1011
class LauncherCliTest extends munit.FunSuite {
1112
override def munitFlakyOK: Boolean = TestUtil.isCI
1213

1314
test("resolve nightly version".flaky) {
14-
val logger = TestLogger()
15-
val cache = CoursierOptions().coursierCache(logger.coursierLogger(""))
15+
val cacheLogger = TestLogger()
16+
val cliLogger = CliLogger.default
17+
val cache = CoursierOptions().coursierCache(cacheLogger.coursierLogger(""), cliLogger)
1618
val scalaParameters = ScalaParameters(Constants.defaultScalaVersion)
1719

1820
val nightlyCliVersion = LauncherCli.resolveNightlyScalaCliVersion(cache, scalaParameters)

modules/config/src/main/scala/scala/cli/config/Keys.scala

+8
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,13 @@ object Keys {
7070
description = "Globally enables power mode (the '--power' launcher flag)."
7171
)
7272

73+
val offline = new Key.BooleanEntry(
74+
prefix = Seq.empty,
75+
name = "offline",
76+
specificationLevel = SpecificationLevel.EXPERIMENTAL,
77+
description = "Globally enables offline mode (the '--offline' flag)."
78+
)
79+
7380
val suppressDirectivesInMultipleFilesWarning =
7481
new Key.BooleanEntry(
7582
prefix = Seq("suppress-warning"),
@@ -176,6 +183,7 @@ object Keys {
176183
pgpSecretKey,
177184
pgpSecretKeyPassword,
178185
power,
186+
offline,
179187
proxyAddress,
180188
proxyPassword,
181189
proxyUser,

modules/integration/src/test/scala/scala/cli/integration/ConfigTests.scala

+41
Original file line numberDiff line numberDiff line change
@@ -560,4 +560,45 @@ class ConfigTests extends ScalaCliSuite {
560560
}
561561
}
562562

563+
for {
564+
offlineSetting <- Seq(true, false)
565+
prefillCache <- if (offlineSetting) Seq(true, false) else Seq(false)
566+
caption = s"offline mode: $offlineSetting, " +
567+
(offlineSetting -> prefillCache match {
568+
case (true, true) => "build should succeed when cache was pre-filled"
569+
case (true, false) => "build should fail when cache is empty"
570+
case _ => "dependencies should be downloaded as normal"
571+
})
572+
}
573+
test(caption) {
574+
TestInputs(
575+
os.rel / "simple.sc" -> "println(dotty.tools.dotc.config.Properties.versionNumberString)"
576+
)
577+
.fromRoot { root =>
578+
val configFile = os.rel / "config" / "config.json"
579+
val localRepoPath = root / "local-repo"
580+
val envs = Map(
581+
"COURSIER_CACHE" -> localRepoPath.toString,
582+
"SCALA_CLI_CONFIG" -> configFile.toString
583+
)
584+
os.proc(TestUtil.cli, "bloop", "exit", "--power").call(cwd = root)
585+
os.proc(TestUtil.cli, "config", "--power", "offline", offlineSetting.toString)
586+
.call(cwd = root, env = envs)
587+
if (prefillCache) for {
588+
artifactName <- Seq(
589+
"scala3-compiler_3",
590+
"scala3-staging_3",
591+
"scala3-tasty-inspector_3",
592+
"scala3-sbt-bridge"
593+
)
594+
artifact = s"org.scala-lang:$artifactName:${Constants.scala3Next}"
595+
} os.proc(TestUtil.cs, "fetch", "--cache", localRepoPath, artifact).call(cwd = root)
596+
val buildExpectedToSucceed = !offlineSetting || prefillCache
597+
val r = os.proc(TestUtil.cli, "run", "simple.sc", "--with-compiler")
598+
.call(cwd = root, env = envs, check = buildExpectedToSucceed)
599+
if (buildExpectedToSucceed) expect(r.out.trim() == Constants.scala3Next)
600+
else expect(r.exitCode == 1)
601+
}
602+
}
603+
563604
}

website/docs/guides/power/offline.md

+5
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ or
3939
scala-cli -Dcoursier.mode=offline run Main.scala
4040
```
4141

42+
Finally, it's possible to enable offline mode via global config:
43+
```bash ignore
44+
scala-cli --power config offline true
45+
```
46+
4247
## Changes in behaviour
4348

4449
### Scala artifacts

website/docs/reference/commands.md

+1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ Available keys:
5656
- interactive Globally enables interactive mode (the '--interactive' flag).
5757
- interactive-was-suggested Setting indicating if the global interactive mode was already suggested.
5858
- java.properties Java properties for Scala CLI's execution.
59+
- offline Globally enables offline mode (the '--offline' flag).
5960
- pgp.public-key The PGP public key, used for signing.
6061
- pgp.secret-key The PGP secret key, used for signing.
6162
- pgp.secret-key-password The PGP secret key password, used for signing.

website/docs/reference/scala-command/commands.md

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ Available keys:
5555
- interactive Globally enables interactive mode (the '--interactive' flag).
5656
- interactive-was-suggested Setting indicating if the global interactive mode was already suggested.
5757
- java.properties Java properties for Scala CLI's execution.
58+
- offline Globally enables offline mode (the '--offline' flag).
5859
- pgp.public-key The PGP public key, used for signing.
5960
- pgp.secret-key The PGP secret key, used for signing.
6061
- pgp.secret-key-password The PGP secret key password, used for signing.

website/docs/reference/scala-command/runner-specification.md

+1
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,7 @@ Available keys:
659659
- interactive Globally enables interactive mode (the '--interactive' flag).
660660
- interactive-was-suggested Setting indicating if the global interactive mode was already suggested.
661661
- java.properties Java properties for Scala CLI's execution.
662+
- offline Globally enables offline mode (the '--offline' flag).
662663
- pgp.public-key The PGP public key, used for signing.
663664
- pgp.secret-key The PGP secret key, used for signing.
664665
- pgp.secret-key-password The PGP secret key password, used for signing.

0 commit comments

Comments
 (0)