cachix / devenv

Fast, Declarative, Reproducible, and Composable Developer Environments
https://devenv.sh
Apache License 2.0
3.56k stars 259 forks source link

Question: How can I create a pure shell? #330

Closed jason-riddle closed 1 month ago

jason-riddle commented 1 year ago

Describe the bug Running devenv shell prepends the current $PATH with the devenv-profile bin directory. However, I would like a pristine $PATH with nothing inherited. This would be something similar to nix-shell --pure.

The reason is I don't want to accidentally use a binary outside of the nix store, which has happened a few times now.

To Reproduce

Run devenv init and then devenv shell. Run echo $PATH and notice the inherited path.

Version

$ devenv version
devenv: 0.5
domenkozar commented 1 year ago

Hey! I'm still thinking about adding support for --pure.

One issue I have with it is that it removes all the tooling from your system, for example the editor, etc.

Could you provide an example of the binaries that got in the env?

jason-riddle commented 1 year ago

One issue I have with it is that it removes all the tooling from your system, for example the editor, etc.

Ah, good point. Could a compromise be only known critical system paths are included for $PATH? So /usr/sbin, /usr/bin, /sbin, and /bin?

Could you provide an example of the binaries that got in the env?

Sure, it's only two binaries causing problems, python and virtualenv.

domenkozar commented 1 year ago

Could you explain exactly what happened and what you expected to happen? That would allow me to design this to prevent such kind of mistakes :)

jason-riddle commented 1 year ago

I expect the devenv.nix file, as defined below, to not let me execute virtualenv while in devenv shell.

{ pkgs, ... }:

{
  # https://devenv.sh/packages/
  packages = [];

  # https://devenv.sh/pre-commit-hooks/
  pre-commit.hooks.shellcheck.enable = false;
}

However, it was executed because it was found in my $PATH at /Users/jason/Library/Python/3.9/bin/virtualenv. I don't want /Users/jason/Library/Python/3.9/bin/virtualenv to be in my path while running devenv shell.

arturkow2000 commented 1 year ago

We also would like to see --pure implemented. This feature comes handy when we want to determine which tools/libraries are needed to build some project. Otherwise libraries missing from devenv.nix would be taken from host, such a build environment is not reproducible and build could fail on another OS. Otherwise there may be other reasons to build an isolated environment.

domenkozar commented 1 year ago

This one is easy to achieve once we tackle #240

willjr commented 1 year ago

(Hope you don't mind my commenting)

Hey! I'm still thinking about adding support for --pure.

One issue I have with it is that it removes all the tooling from your system, for example the editor, etc.

I personally see this as a positive option, TBH.

Everywhere else I've worked that aspires to hermetic builds / build-env expects the editor to be able to run things (tests, etc) inside a kind-of "dev-env sandbox" anyway, so you get the nearest thing to your CI systems result(s) as well.

Those places can use things like a dev container, but sometimes that's difficult or inappropriate, so it's really helpful that devenv (and Nix!) can provide this without much extra work.

risicle commented 1 year ago

The advantage of using nix-shell --pure is that you can normalize a lot of differences between platforms. No longer do you have to worry about the differences between macos sed and gnu sed, which variant of nc you're working with, shasum vs sha256sum, macos mktemp vs coreutils mktemp etc.

herwig-hochleitner-gravie commented 10 months ago

I'm using this approach:

    enterShell = lib.mkMerge [
      ''
        export IMPURE_PATH="$PATH"
      ''
      ## if we're running in pure mode, reset PATH to be _just_ from devenv
      (lib.mkIf config.eval.pure ''
        export PATH="$DEVENV_PROFILE/bin"
      '')
    ];

and for commands that should call out into system tools

      # for example for firewall setup, restore original path
      firewall-setup.exec = ''
        export PATH=$IMPURE_PATH
        case $(uname) in
          Linux)
            sudo iptables -t nat -I OUTPUT -p tcp -d 127.0.0.1 --dport 80  -j REDIRECT --to-ports 8000
            sudo iptables -t nat -I OUTPUT -p tcp -d 127.0.0.1 --dport 443 -j REDIRECT --to-ports 4430
          ;;
          Darwin)
            echo "
              rdr pass inet proto tcp from any to any port 80 -> 127.0.0.1 port 8080
              rdr pass inet proto tcp from any to any port 443 -> 127.0.0.1 port 4430" | \
            sudo pfctl -ef -
          ;;
         esac
      '';

this "pure-lite" mode is already very useful for catching missed packages early.

minhuw commented 7 months ago

Another reason I want a pure shell is that glibc linked by installed packages may be much newer than the system's default one. So I always encounter errors like the one below.

/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_ABI_DT_RELR' not found (required by /nix/store/aw2fw9ag10wr9pf0qk4nk5sxi0q0bn56-glibc-2.37-8/lib/libpthread.so.0)
domenkozar commented 7 months ago

That one should be solved for most cases in #745

gonzaloetjo commented 5 months ago

Is there any update on this issue? or does it remain a non-priority?

domenkozar commented 5 months ago

This might be much easier using #745

domenkozar commented 2 months ago

I'm implementing this in #745 using devenv shell --clean.

I wonder if we should add a setting to devenv.yaml what variables to preserve:

clean-keep-vars:
- EDITOR
- ...

More control over $PATH:

clean-keep:
  vars:
    - EDITOR
  executables:
    - vim
kalekseev commented 2 months ago

Another reason I want a pure shell is that glibc linked by installed packages may be much newer than the system's default one. So I always encounter errors like the one below.

/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_ABI_DT_RELR' not found (required by /nix/store/aw2fw9ag10wr9pf0qk4nk5sxi0q0bn56-glibc-2.37-8/lib/libpthread.so.0)

I think devenv could use LIBRARY_PATH instead of LD_LIBRARY_PATH to mitigate this problem, I switch it in enter shell:

  export LIBRARY_PATH="${lib.getLib pkgs.glibc}/lib"
  unset LD_LIBRARY_PATH
domenkozar commented 1 month ago

1.0.1 now implements:

clean:
  enable: true
  keep:
    - EDITOR
domenkozar commented 1 month ago

I'm going to close this, please reopen if you'd like devenv to support keeping some executables around.