From 3a39ce17674344fc5e7809b15e2d6fc984c31d88 Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Thu, 29 Aug 2024 15:33:35 +0200 Subject: [PATCH] Introduce `os.proc.env` `DynamicVariable` to override default `env` In Mill and any client-server application, it is handy to start processes on the server passing the client environment. This requires to pass the environment manually in all the `os.proc(...).call()`s. An easier alternative is to override the environment as we to for `os.Inherit` `out`, `in` and `err` pipes. So users can forget about the environment and have the right one passed automatically. --- os/src/ProcessOps.scala | 24 ++++++++++++++++-------- os/test/src/SubprocessTests.scala | 16 ++++++++++++++++ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/os/src/ProcessOps.scala b/os/src/ProcessOps.scala index fa8cc973..025159d0 100644 --- a/os/src/ProcessOps.scala +++ b/os/src/ProcessOps.scala @@ -189,6 +189,10 @@ case class proc(command: Shellable*) { def pipeTo(next: proc): ProcGroup = ProcGroup(Seq(this, next)) } +object proc { + val env = new scala.util.DynamicVariable[Map[String, String]](sys.env) +} + /** * A group of processes that are piped together, corresponding to e.g. `ls -l | grep .scala`. * You can create a `ProcGroup` by calling `.pipeTo` on a [[proc]] multiple times. @@ -423,18 +427,22 @@ private[os] object ProcessOps { val builder = new java.lang.ProcessBuilder() val environment = builder.environment() + environment.clear() - if (!propagateEnv) { - environment.clear() - } - - if (env != null) { - for ((k, v) <- env) { - if (v != null) builder.environment().put(k, v) - else builder.environment().remove(k) + def addToProcessEnv(env: Map[String, String]) = + if (env != null) { + for ((k, v) <- env) { + if (v != null) builder.environment().put(k, v) + else builder.environment().remove(k) + } } + + if (propagateEnv) { + addToProcessEnv(os.proc.env.value) } + addToProcessEnv(env) + builder.directory(Option(cwd).getOrElse(os.pwd).toIO) builder diff --git a/os/test/src/SubprocessTests.scala b/os/test/src/SubprocessTests.scala index 16eefe93..2bb9f1c0 100644 --- a/os/test/src/SubprocessTests.scala +++ b/os/test/src/SubprocessTests.scala @@ -146,6 +146,22 @@ object SubprocessTests extends TestSuite { } } } + test("envWithValue") { + if (Unix()) { + def envValue() = os.proc("bash", "-c", "echo \"$TEST_ENV_FOO\"").call().out.lines().head + + val before = envValue() + assert(before == "") + + os.proc.env.withValue(Map("TEST_ENV_FOO" -> "bar")) { + val res = envValue() + assert(res == "bar") + } + + val after = envValue() + assert(after == "") + } + } test("multiChunk") { // Make sure that in the case where multiple chunks are being read from // the subprocess in quick succession, we ensure that the output handler