Skip to content

Conversation

mohsenari
Copy link
Collaborator

@mohsenari mohsenari commented Jun 1, 2023

Summary

I added a --pure flag for devbox shell to create an isolated env without variables leaking from the parent shell.

Q: Should I add a --pure flag for devbox run and devbox shellenv?
A: Yes
Q2: Because the created shell is "pure" the devbox command is also not available inside the pure shell. Should that be an exception?
A: Yes

Note: in pure mode, the $HOME env variable has to leak in because the devbox binary relies on it.

How was it tested?

  • compile
  • export FOO=bar
  • ./devbox shell --pure
  • echo $FOO should show no value
  • Also ./devbox run --pure -- echo $FOO should show no value

@mohsenari mohsenari requested review from gcurtis and savil June 6, 2023 01:31
@mohsenari mohsenari marked this pull request as ready for review June 6, 2023 01:32
Copy link
Collaborator

@savil savil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Yes, I do think we need the flag for shellenv and run
  2. And yes, I do think we need to ensure devbox binary is included.

Could we add some testscript unit-tests for pure versus impure?

Some prelim comments. Need to go to a meeting....

Copy link
Collaborator

@savil savil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice!

  1. Yes, I do think we need the flag for shellenv and run
  2. And yes, I do think we need to ensure devbox binary is included.

Could we add some testscript unit-tests for pure versus impure?

Some prelim comments. Need to go to a meeting....

@mohsenari mohsenari force-pushed the mohsen--pure-shell branch from 79aa6a9 to f2d3b84 Compare June 7, 2023 19:52
@mohsenari mohsenari requested a review from savil June 7, 2023 20:01
Copy link
Collaborator

@savil savil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, this is looking more promising. I had a few thoughts inline, and below:

1.CreateWrappers sets up the wrapper.sh.tmpl which calls shellenv: https://github.com/jetpack-io/devbox/blob/main/internal/wrapnix/wrapper.sh.tmpl#L29.

I tried reasoning thru whether we need to invoke --pure here as well, or not. But this is pretty confusing when I think of various scenarios (devbox add within a pure shell etc.), but my head is hurting now.

@mikeland86 thoughts?

  1. I think we need the .envrc file that is generated for direnv to also incorporate --pure? Maybe in a follow up PR...

  2. When I start devbox shell --pure, I get:

 (mohsen--pure-shell)> devbox shell --pure
Starting a devbox shell...

Error: We found a /nix directory but nix binary is not in your PATH and we were not able to find it in the usual locations. Your nix installation might be broken. If restarting your terminal or reinstalling nix doesn't work, please create an issue at https://github.com/jetpack-io/devbox/issues

My nix is in /nix/var/nix/profiles/default/bin (IIRC its from the default devbox install). Do we need to ensure that this is in PATH? One solution is to make the following edit inside computeNixEnv function:

        currentEnvPath := env["PATH"]
+       if currentEnvPath == "" {
+               currentEnvPath = "/nix/var/nix/profiles/default/bin"
+       }
  1. devbox add doesn't seem to work for me inside a pure shell:
(devbox) Savil-Srivastavas-MacBook-Pro:devbox savil$ devbox add ripgrep

Error: There was an error installing nix packages
source: exec: "git": executable file not found in $PATH

(devbox) Savil-Srivastavas-MacBook-Pro:devbox savil$

@savil savil requested a review from mikeland73 June 8, 2023 04:34
Copy link
Contributor

@mikeland73 mikeland73 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR as is makes me a bit nervous. Like @savil said, it's difficult to reason about what needs pure vs impure environments. Additionally adding boolean params everywhere makes code more difficult to read (and 1 boolean leads to 2, 3.....)

Open to ideas, but I think we can make 2 changes that will reduce the size of this PR a bunch.

  1. Remove pure from CreateWrappers and just create the symlink unconditionally. I'm not sure this is the right place, but it's simple.
  2. Change devbox.Open(dir string, writer io.Writer) to be devbox.Open(dir string, writer io.Writer, devbox.Opts{}) and add pure as a field in the devbox struct. This way you don't need to pass pure everywhere. You have it when you need it. The only downside to this is that you can't have a single devbox object produce pure and impure outcomes, but I think thats OK.

env["PATH"] = JoinPathLists(nixEnvPath, originalPath)
debug.Log("computed environment PATH is: %s", env["PATH"])

d.setCommonHelperEnvVars(env)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm unsure if these should go in pure or not. cc: @gcurtis

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we still want LD_LIBRARY_PATH and LIBRARY_PATH, but without the current environment's values appended. Since it looks like env["LIBRARY_PATH"] and env["LD_LIBRARY_PATH"] will always be empty, I think this is okay as-is.

@mohsenari mohsenari requested review from savil and mikeland73 June 8, 2023 21:47
@mohsenari
Copy link
Collaborator Author

mohsenari commented Jun 8, 2023

@mikeland86 @savil
I made an overhaul to the PR and here is the list of changes to make it easier to review:

  • I removed pure being passed as a function argument. I added it to the Devbox struct and set it when devbox.Open() is called.
    But that means I had to update all devbox.Open() calls, most of them get an empty devbox.Opts{}. So I hope that's ok.
  • In NewDevboxShell() I had to pass it as a function argument because of the way ShellOptions is implemented.
  • I also removed --pure-eval from nix eval since I couldn't find justification to have it added.
  • I added $USER and $DISPLAY along with $HOME as env variables being passed through to match what Nix does.
  • Finally, added Nix's bin path (/nix/var/nix/profiles/default/bin) to the path in pure shell because it was needed for commands like devbox add to work inside a pure shell. It is hardcoded but I think it's ok because it's the same path in all unix operating systems.


currentEnvPath := env["PATH"]
if d.pure { // make nix available inside pure shell - necessary for devbox add to work
currentEnvPath = "/nix/var/nix/profiles/default/bin"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed? devbox should not need nix in path to work

Copy link
Collaborator Author

@mohsenari mohsenari Jun 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mikeland86 It is! you can test it. In a pure shell, devbox add throws this error:

Error: We found a /nix directory but nix binary is not in your PATH and we were not 
able to find it in the usual locations. Your nix installation might be broken. 
If restarting your terminal or reinstalling nix doesn't work, please create an issue at 
https://github.com/jetpack-io/devbox/issues

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, OK. I'm not 100% sure this works in all cases (e.g. single user installs).

I just tested and single user on ubuntu installed:

$ which nix
/home/mike/.nix-profile/bin/nix

See https://github.com/jetpack-io/devbox/blob/main/internal/nix/source.go#L32-L32

For some reason, we are not sourcing nix correctly. In the pure shell, is __ETC_PROFILE_NIX_SOURCED=1 set?

If that is set, then we may need a different approach.

One possible approach is to look in $PATH and try to find:

/nix/var/nix/profiles/default/bin
OR
$HOME/.nix-profile/bin/nix

and keep them around.

There's also :

xdg.StateSubpath("nix/profile/etc/profile.d/nix.sh"),
xdg.StateSubpath("nix/profiles/profile/etc/profile.d/nix.sh"),

I'm not sure if nix also stores bins there.


So in short, I think you need to look in path for any of these:

/nix/var/nix/profiles/default/bin
$HOME/.nix-profile/bin
xdg.StateSubpath(/nix/profile/bin)
xdg.StateSubpath(nix/profiles/profile/bin)

If any of those are in PATH, then they need to be retained. In practice it will only be one of them.

I'm not 100% sure there are no other nix paths, but this should cover all the cases we currently consider.

Comment on lines 98 to 103
if !pure {
// First, check the SHELL environment variable.
path = os.Getenv(envir.Shell)
if path != "" {
debug.Log("Using SHELL env var for shell binary path: %s\n", path)
return path, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not 100% sure we want to ignore the SHELL variable. Which shell we use is does not modify the environment. If a user wants to do SHELL=zsh devbox run something in CICD it won't work.

Thoughts @gcurtis @Lagoja @loreto ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think here we should ignore it and always use Nix's bash shell. IMO the shell can have a pretty big impact on the environment.

I agree with the use-case of letting the user override the shell, but it might make more sense to do that through a new devbox.json field. That way it can be a Nix-installed shell that's guaranteed to be the same for everyone.

@mohsenari
Copy link
Collaborator Author

@mikeland86 Addressed the nit feedback. Not sure about the SHELL variable and about d.setCommonHelperEnvVars(env)

@mohsenari mohsenari requested a review from mikeland73 June 8, 2023 22:28
Copy link
Contributor

@mikeland73 mikeland73 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think all my last round of comments can be done in a follow up. The only semi-urgent one is to search PATH for a few places nix binary can be and not just a single hard coded one. This gives us a better chance of finding it in all types of installations.

Since this PR is already really large, you should merge and do a fast follow with a fix for that. Other nits can be addressed after that.


currentEnvPath := env["PATH"]
if d.pure { // make nix available inside pure shell - necessary for devbox add to work
currentEnvPath = "/nix/var/nix/profiles/default/bin"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, OK. I'm not 100% sure this works in all cases (e.g. single user installs).

I just tested and single user on ubuntu installed:

$ which nix
/home/mike/.nix-profile/bin/nix

See https://github.com/jetpack-io/devbox/blob/main/internal/nix/source.go#L32-L32

For some reason, we are not sourcing nix correctly. In the pure shell, is __ETC_PROFILE_NIX_SOURCED=1 set?

If that is set, then we may need a different approach.

One possible approach is to look in $PATH and try to find:

/nix/var/nix/profiles/default/bin
OR
$HOME/.nix-profile/bin/nix

and keep them around.

There's also :

xdg.StateSubpath("nix/profile/etc/profile.d/nix.sh"),
xdg.StateSubpath("nix/profiles/profile/etc/profile.d/nix.sh"),

I'm not sure if nix also stores bins there.


So in short, I think you need to look in path for any of these:

/nix/var/nix/profiles/default/bin
$HOME/.nix-profile/bin
xdg.StateSubpath(/nix/profile/bin)
xdg.StateSubpath(nix/profiles/profile/bin)

If any of those are in PATH, then they need to be retained. In practice it will only be one of them.

I'm not 100% sure there are no other nix paths, but this should cover all the cases we currently consider.

Copy link
Collaborator

@savil savil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much nicer!

As some followups, I think we will want to enable a direnv user to opt-into pure mode

@savil
Copy link
Collaborator

savil commented Jun 9, 2023

It would also be great to add some testscript unit-tests under testscripts/ for the --pure case. Not blocking landing this PR, can be done in a followup.

@mohsenari mohsenari force-pushed the mohsen--pure-shell branch from d13545a to 5cd0413 Compare June 9, 2023 22:04
@mohsenari
Copy link
Collaborator Author

Merging this now, will followup with another PR addressing nix in PATH issue.

@mohsenari mohsenari merged commit 3764425 into main Jun 9, 2023
@mohsenari mohsenari deleted the mohsen--pure-shell branch June 9, 2023 22:19
mohsenari added a commit that referenced this pull request Jun 13, 2023
## 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`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

4 participants