Skip to content

Commit 30e9e41

Browse files
committed
Add a global offline config key
1 parent 442facc commit 30e9e41

File tree

20 files changed

+85
-22
lines changed

20 files changed

+85
-22
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, cacheLoggerPrefix = "Downloading Bloop"),
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, cacheLoggerPrefix = "Downloading Bloop"), // 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-4
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,12 @@ object BloopStart extends ScalaCommand[BloopStartOptions] {
2323
import opts.*
2424
val buildOptions = BuildOptions(
2525
javaOptions = JvmUtils.javaOptions(jvm).orExit(global.logging.logger),
26-
internal = InternalOptions(
27-
cache = Some(coursier.coursierCache(global.logging.logger.coursierLogger("")))
28-
)
26+
internal = InternalOptions(cache = Some(coursier.coursierCache(global.logging.logger)))
2927
)
3028

3129
compilationServer.bloopRifleConfig(
3230
global.logging.logger,
33-
coursier.coursierCache(global.logging.logger.coursierLogger("Downloading Bloop")),
31+
coursier.coursierCache(global.logging.logger, cacheLoggerPrefix = "Downloading Bloop"),
3432
global.logging.verbosity,
3533
buildOptions.javaHome().value.javaCommand,
3634
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)
5050
val secKeyEntry = Keys.pgpSecretKey
5151
val pubKeyEntry = Keys.pgpPublicKey
5252

modules/cli/src/main/scala/scala/cli/commands/default/Default.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class Default(actualHelp: => RuntimeCommandsHelp)
4343
Version.runCommand(
4444
options = VersionOptions(
4545
global = options.shared.global,
46-
offline = options.shared.coursier.getOffline().getOrElse(false)
46+
offline = options.shared.coursier.getOffline(logger).getOrElse(false)
4747
),
4848
args = args,
4949
logger = logger

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)
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),
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)
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)
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)
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

+12-4
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@ import caseapp.*
44
import com.github.plokhotnyuk.jsoniter_scala.core.*
55
import com.github.plokhotnyuk.jsoniter_scala.macros.*
66
import coursier.cache.{CacheLogger, CachePolicy, FileCache}
7+
import coursier.util.Task
78

9+
import scala.build.Logger
810
import scala.build.internals.EnvVar
911
import scala.cli.commands.tags
12+
import scala.cli.config.Keys
13+
import scala.cli.util.ConfigDbUtils
1014
import scala.concurrent.duration.Duration
1115

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

42-
def coursierCache(logger: CacheLogger) = {
43-
var baseCache = FileCache().withLogger(logger)
46+
def coursierCache(logger: Logger, cacheLogger: CacheLogger): FileCache[Task] = {
47+
var baseCache = FileCache().withLogger(cacheLogger)
4448
if (!validateChecksums)
4549
baseCache = baseCache.withChecksums(Nil)
4650
val ttlOpt = ttl.map(_.trim).filter(_.nonEmpty).map(Duration(_))
4751
for (ttl0 <- ttlOpt)
4852
baseCache = baseCache.withTtl(ttl0)
4953
for (loc <- cache.filter(_.trim.nonEmpty))
5054
baseCache = baseCache.withLocation(loc)
51-
for (isOffline <- getOffline() if isOffline)
55+
for (isOffline <- getOffline(logger) if isOffline)
5256
baseCache = baseCache.withCachePolicies(Seq(CachePolicy.LocalOnly))
5357

5458
baseCache
5559
}
5660

57-
def getOffline(): Option[Boolean] = offline
61+
def coursierCache(logger: Logger, cacheLoggerPrefix: String = ""): FileCache[Task] =
62+
coursierCache(logger, logger.coursierLogger(cacheLoggerPrefix))
63+
64+
def getOffline(logger: Logger): Option[Boolean] = offline
5865
.orElse(EnvVar.Coursier.coursierMode.valueOpt.map(_ == "offline"))
5966
.orElse(Option(System.getProperty("coursier.mode")).map(_ == "offline"))
67+
.orElse(ConfigDbUtils.getConfigDbOpt(logger).flatMap(_.get(Keys.offline).toOption.flatten))
6068
}
6169

6270
object CoursierOptions {

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,7 @@ final case class SharedOptions(
431431
strictBloopJsonCheck = strictBloopJsonCheck,
432432
interactive = Some(() => interactive),
433433
exclude = exclude.map(Positioned.commandLine),
434-
offline = coursier.getOffline()
434+
offline = coursier.getOffline(logger)
435435
),
436436
notForBloopOptions = bo.PostBuildOptions(
437437
scalaJsLinkerOptions = linkerOptions(js),
@@ -603,11 +603,11 @@ final case class SharedOptions(
603603
options => bloopRifleConfig(Some(options)),
604604
threads.bloop,
605605
strictBloopJsonCheckOrDefault,
606-
coursier.getOffline().getOrElse(false)
606+
coursier.getOffline(logger).getOrElse(false)
607607
)
608608
else SimpleScalaCompilerMaker("java", Nil)
609609

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

612612
def inputs(
613613
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)
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

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class LauncherCliTest extends munit.FunSuite {
1212

1313
test("resolve nightly version".flaky) {
1414
val logger = TestLogger()
15-
val cache = CoursierOptions().coursierCache(logger.coursierLogger(""))
15+
val cache = CoursierOptions().coursierCache(logger)
1616
val scalaParameters = ScalaParameters(Constants.defaultScalaVersion)
1717

1818
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.IMPLEMENTATION,
77+
description = "Globally enables offline mode (the '--offline' flag)."
78+
)
79+
7380
val suppressDirectivesInMultipleFilesWarning =
7481
new Key.BooleanEntry(
7582
prefix = Seq("suppress-warning"),
@@ -172,6 +179,7 @@ object Keys {
172179
suppressDirectivesInMultipleFilesWarning,
173180
suppressOutdatedDependenciessWarning,
174181
suppressExperimentalFeatureWarning,
182+
offline,
175183
pgpPublicKey,
176184
pgpSecretKey,
177185
pgpSecretKeyPassword,

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", "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
@@ -663,6 +663,7 @@ Available keys:
663663
- interactive Globally enables interactive mode (the '--interactive' flag).
664664
- interactive-was-suggested Setting indicating if the global interactive mode was already suggested.
665665
- java.properties Java properties for Scala CLI's execution.
666+
- offline Globally enables offline mode (the '--offline' flag).
666667
- pgp.public-key The PGP public key, used for signing.
667668
- pgp.secret-key The PGP secret key, used for signing.
668669
- pgp.secret-key-password The PGP secret key password, used for signing.

0 commit comments

Comments
 (0)