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
:extra-env does not respect case insensitivy of Windows environment variables #79
Comments
@lread I'll have to think about this a little bit. What would your fix for 3 look like? Also pinging @bobisageek here since I consider him a Windows expert (relative to me at the very least). |
Not sure exactly @borkdude, but I was thinking it would be pretty straightforward alternate handling for Windows around process/src/babashka/process.cljc Lines 82 to 88 in 4392dff
Could do a PR to explore what it could look like. |
@lread So e.g. if |
Yeah, but to be super clear: change only the value for |
That's what I intended to say, thanks for making this extra clear. |
Quick skim: other cases of path do all end up in the process's environment on both Windows and Linux, but as said, Windows seems to have some sort of preference. Based on some quick experiments, I think it might prioritize system-level environment variables over user-level, regardless of case, but I'm not confident in that - need a bit more testing (and Path is a weird corner case that happens at both the system and user-level, so it just gets weirder there). I think a con (but not necessarily a disqualifier) for the merging of environments is that we would need to "process" the underlying environment, which adds a bit of time to those process calls, but obviously that's a trade-off for what might be more desirable behavior. The merge would be consistent with Windows shell behavior. It would eliminate the possibility of using two environment variables that vary in case only, but that's not really an option on Windows in general (as demonstrated by System/getenv). I'm not sure if there would be there anyone out there "working around" the case-insensitive nature of Windows by using One other consideration that might be of interest... this is (sort of) re-createable using (require '[clojure.java.shell :as shell])
(-> (shell/sh "deps" "-M" "-e" "(println (System/getenv \\\"PATH\\\"))"
:env
(merge (into {} (System/getenv))
{"PATH" (str "some-path" (System/getProperty "path.separator") (System/getenv "PATH"))}))
:out
(subs 0 50)
println)
(shutdown-agents) ; lol futures Running this (I'm using With possible performance implications, should we trial it? As in, we can run some perf testing to make sure it's not adding like a second to each shell call, and then maybe put it out provisionally and solicit user feedback? I don't know if some use cases on Windows might have large numbers of env vars and could experience a noticeable impact. I sort of doubt it, but I'm also not immediately coming up with a more elegant/efficient strategy than a case-insensitive key comparison across the entire environment map. |
Wow, thanks for the time and interest @bobisageek! Disclaimer: I am using Path and PATH in envYou inspired me to have a peek with Sysinternal Process Explorer (to take the JVM out of the equation) and yah... I see both Clojure core shThanks for comparing! And ya user=> (some-> (get (System/getenv) "PATH") (subs 0 50))
nil
user=> (some-> (get (System/getenv) "Path") (subs 0 50))
"C:\\Program Files\\Parallels\\Parallels Tools\\Applica" So you are saying on Windows Clojure's PerformanceI did not expect any real performance hit relative to the cost of spawning out, but you are right, we don't know until we measure. |
Observation: we have already gone beyond "Option 1 - Do nothing" by raising and talking about this issue. This issue does serve as a form of documentation for the extra curious. I'll go ahead and address this |
Here's the example compensation from babashka/fs: :tasks
{:requires ([babashka.fs :as fs]
[clojure.string :as str])
test (clojure {:extra-env {(if (fs/windows?) "Path" "PATH") (str "on-path" fs/path-separator (System/getenv "PATH"))}}
"-M:test") So maybe not too horrible? Am willing to PR Option 2 (Document) with a small tip if that's the current preference. |
I think adding docs is a no-brainer. If this issue keeps bugging people we could consider more drastic measures. |
Repro
On Windows 10, given a
Path
environment variable:And a
fiddle.clj
(which I've plunked in a fresh clone of babashka/process):From your shell, run:
Expected Behaviour
As a Windows user, I would expect
:extra-env
to respect Windows environment variable name case insensitivity when overriding existing environment variables.This means any case of
path
should overridePath
(includingPATH
).If this works, I expect to see
some-path
printed as the first path returned by PATH.Actual Behaviour
We do not see out
some-path
prefixed:Diagnosis
If I alter
fiddle.clj
to changePATH
toPath
for:extra-env
:I will get the expected result:
>clojure -M fiddle.clj some-path;C:\Program Files\Parallels\Parallels Too
(Notice I did not alter
PATH
forgetenv
calls).On Windows, environment variables are case insensitive.
The JDK behaves accordingly for
System/getenv
on Windows, butProcessBuilder.environment
does not.Any case variant of
path
passed toSystem/getenv
returns value forPath
.I think
ProcessBuilder.environment
holds bothPath
andPATH
. I'm not sure how one gets chosen over the other when the env is set for a Windows process.Side Note: To keep things confusing, Windows does show
Path
forcmd.exe
set
, butPATH
forenv
, butPath
is the underlying environment variable name.Option 1: Do nothing
If we take the stance that:
Then maybe we will do nothing.
Pros:
Cons:
Option 2: Document Peculiarity
Add a tip to docs about
:extra-env
on Windows stating that although Windows environment variable names are case insensitive, an environment variable will not be overridden unless the provided name matches the original case of the variable name.Questions: Is the original name always
Path
on Windows? Can it bepath
orPATH
?Pros:
Cons:
Option 3: Tweak env merging for Windows only
If an
:extra-env
variable name is a case insensitive match for an existing variable name it is a match and the variable's value's entry should be replaced.But what if someone specifies an
:extra-env
of{"PATH" "boo" "path" "foo"}
?Well, that's probably unlikely enough to ignore as undefined behaviour.
Pros:
PATH
setsPATH
on Linux/macOS but appropriately any case ofPath
on Windows.:extra-env
behaves like the Windows shells and therefore matches what Windows users would expect.Concerns:
Cons:
Proposal
I like option 3 the for pros it lists.
But, I'm sure I've not thought of everything, please do chime in with your thoughts.
Next steps
Happy to PR if you decide option 3 is the path (pun intended) as well.
The text was updated successfully, but these errors were encountered: