Skip to content

Commit 5d40418

Browse files
authored
[pure-shell] making sure nix is in path in pure mode (#1143)
## Summary This is a follow up PR from as a result of [this discussion](#1084 (comment)). Note, xdg dirs don't keep any nix executables as far as I know. Single user and multi user nix installation put the nix executable in `~/.nix-profile/bin/` and `/nix/var/nix/profiles/default/bin` respectively. Also added tests for devbox run pure mode. For `devbox shellenv`, pure mode doesn't really make sense to write tests for, and for `devbox shell` it's not possible to write testscript tests. ## How was it tested? - in a single user nix installed env, run `./devbox shell --pure` - `devbox add which` - `which nix` should point to `~/.nix-profile/bin/nix` - in a multi-user nix, same test should point to `/nix/var/nix/profiles/default/bin/nix`
1 parent 543a73f commit 5d40418

File tree

3 files changed

+75
-18
lines changed

3 files changed

+75
-18
lines changed

internal/impl/devbox.go

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -740,22 +740,11 @@ func (d *Devbox) computeNixEnv(ctx context.Context, usePrintDevEnvCache bool) (m
740740

741741
// Append variables from current env if --pure is not passed
742742
currentEnv := os.Environ()
743-
env := make(map[string]string, len(currentEnv))
744-
745-
for _, kv := range currentEnv {
746-
key, val, found := strings.Cut(kv, "=")
747-
if !found {
748-
return nil, errors.Errorf("expected \"=\" in keyval: %s", kv)
749-
}
750-
if ignoreCurrentEnvVar[key] {
751-
continue
752-
}
753-
// Passing HOME USER and DISTPLAY for pure shell to leak through
754-
// otherwise devbox binary won't work - this matches nix
755-
if !d.pure || key == "HOME" || key == "USER" || key == "DISPLAY" {
756-
env[key] = val
757-
}
743+
env, err := d.convertEnvToMap(currentEnv)
744+
if err != nil {
745+
return nil, err
758746
}
747+
759748
// check if contents of .envrc is old and print warning
760749
if !usePrintDevEnvCache {
761750
err := d.checkOldEnvrc()
@@ -765,9 +754,6 @@ func (d *Devbox) computeNixEnv(ctx context.Context, usePrintDevEnvCache bool) (m
765754
}
766755

767756
currentEnvPath := env["PATH"]
768-
if d.pure { // make nix available inside pure shell - necessary for devbox add to work
769-
currentEnvPath = "/nix/var/nix/profiles/default/bin"
770-
}
771757
debug.Log("current environment PATH is: %s", currentEnvPath)
772758
// Use the original path, if available. If not available, set it for future calls.
773759
// See https://github.com/jetpack-io/devbox/issues/687
@@ -1195,3 +1181,35 @@ func (d *Devbox) addHashToEnv(env map[string]string) error {
11951181
}
11961182
return err
11971183
}
1184+
1185+
func (d *Devbox) convertEnvToMap(currentEnv []string) (map[string]string, error) {
1186+
env := make(map[string]string, len(currentEnv))
1187+
for _, kv := range currentEnv {
1188+
key, val, found := strings.Cut(kv, "=")
1189+
if !found {
1190+
return nil, errors.Errorf("expected \"=\" in keyval: %s", kv)
1191+
}
1192+
if ignoreCurrentEnvVar[key] {
1193+
continue
1194+
}
1195+
// handling special cases to for pure shell
1196+
// Passing HOME USER and DISPLAY for pure shell to leak through
1197+
// otherwise devbox binary won't work - this matches nix
1198+
// We include PATH to find the nix installation. It is cleaned for pure mode below
1199+
if !d.pure || key == "HOME" || key == "USER" || key == "DISPLAY" || key == "PATH" {
1200+
env[key] = val
1201+
}
1202+
}
1203+
1204+
// handling special case for PATH
1205+
if d.pure {
1206+
// Finding nix executables in path and passing it through
1207+
// Needed for devbox commands inside pure shell to work
1208+
nixInPath, err := findNixInPATH(env)
1209+
if err != nil {
1210+
return nil, err
1211+
}
1212+
env["PATH"] = nixInPath
1213+
}
1214+
return env, nil
1215+
}

internal/impl/shell.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,3 +411,21 @@ func filterPathList(pathList string, keep func(string) bool) string {
411411
}
412412
return strings.Join(filtered, string(filepath.ListSeparator))
413413
}
414+
415+
func findNixInPATH(env map[string]string) (string, error) {
416+
defaultSingleUserNixBin := fmt.Sprintf("%s/.nix-profile/bin", env["HOME"])
417+
defaultMultiUserNixBin := "/nix/var/nix/profiles/default/bin"
418+
xdgNixBin := xdg.StateSubpath("/nix/profile/bin")
419+
pathElements := strings.Split(env["PATH"], ":")
420+
debug.Log("path elements: %v", pathElements)
421+
for _, el := range pathElements {
422+
if el == xdgNixBin ||
423+
el == defaultSingleUserNixBin ||
424+
el == defaultMultiUserNixBin {
425+
return el, nil
426+
}
427+
}
428+
429+
// did not find nix executable in PATH, return error
430+
return "", errors.New("could not find any nix executable in PATH. Make sure Nix is installed and in PATH, then try again")
431+
}

testscripts/run/pure.test.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Tests related to having devbox run in pure mode.
2+
3+
env FOO=bar
4+
env FOO2=bar2
5+
6+
exec devbox run --pure echo '$FOO'
7+
stdout 'baz'
8+
9+
exec devbox run --pure echo '$FOO2'
10+
stdout ''
11+
12+
exec devbox run --pure hello
13+
stdout 'Hello, world!'
14+
15+
-- devbox.json --
16+
{
17+
"packages": ["hello"],
18+
"env": {
19+
"FOO": "baz"
20+
}
21+
}

0 commit comments

Comments
 (0)