devshell icon indicating copy to clipboard operation
devshell copied to clipboard

Different shell?

Open yajo opened this issue 3 years ago • 22 comments

Is your feature request related to a problem? Please describe.

I'm used to fish, makes me more productive.

I'm using startup hooks, so direnv doesn't solve the problem.

Describe the solution you'd like

Respect user's $SHELL if it's available after running nix develop.

Describe alternatives you've considered

TBH I have not found any 😕

Additional context

yajo avatar Feb 21 '22 12:02 yajo

How do you handle when there are two shells loading the same environment, is the second one detecting that all the processes have started?

For now, I have worked with the assumption that it's best to start the processes manually to give the user more control. And startup hooks would only be used for initialization.

zimbatm avatar Feb 25 '22 08:02 zimbatm

Usually it's not a problem because the services use either a pidfile or a port, so they'll just fail to boot in that case.

yajo avatar Mar 11 '22 06:03 yajo

For now I have found this workaround, but I guess it would be awesome if it could be added by default:

nix-shell --command "$SHELL"

yajo avatar Mar 29 '22 10:03 yajo

If you use direnv, then it also acts as an interface between your shell and whatever environment variables nix-shell is providing.

zimbatm avatar Mar 31 '22 13:03 zimbatm

I've found out that you can get current user's shell with:

grep ^$USER: /etc/passwd | cut -f 7 -d :

So it would be great if that can be added as a hook and then that is the shell that gets spawned when doing nix-shell or nix develop.

The main problem with https://github.com/numtide/devshell/issues/171#issuecomment-1081722373 is that services do not get reaped after exiting the shell.

yajo avatar Apr 11 '22 09:04 yajo

I think $SHELL is more reliable because sometimes the shell gets overridden in the Terminal emulator configuration. On Linux you can also look at /proc/$PPID/exe. But yeah, those are all heuristics

zimbatm avatar Apr 11 '22 11:04 zimbatm

The problem with $SHELL is that it's already overriden by devshell. Or you mean not overriding it?

yajo avatar Apr 11 '22 16:04 yajo

I mean as a source of information. But devshell would have to be modified to take that into account. It also opens a bunch of questions like what to do with --pure if the original shell is not available in the PATH.

zimbatm avatar Apr 11 '22 17:04 zimbatm

I don't think --pure has much sense while developing. In any case, IMHO it shouldn't be very hard: just search for the variable and set it only if missing. Isn't it?

El lun, 11 abr 2022 18:12, Jonas Chevalier @.***> escribió:

I mean as a source of information. But devshell would have to be modified to take that into account. It also opens a bunch of questions like what to do with --pure if the original shell is not available in the PATH.

— Reply to this email directly, view it on GitHub https://github.com/numtide/devshell/issues/171#issuecomment-1095317258, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAHNXDLTAJ4F3EXYGAJY7MLVERMQLANCNFSM5O6MOHFA . You are receiving this because you authored the thread.Message ID: @.***>

yajo avatar Apr 12 '22 03:04 yajo

Tangentially I'm also currently trying to find ways how to add proper multi-shell support. I'd be interested in exploring. My current thinking is just: don't at all:

{
      devshell.startup.completion = l.stringsWithDeps.noDepEntry ''
        # bash | zsh | oil
        source <(std _carapace)

        # elvish
        # eval (std _carapace | slurp)

        # fish
        # std _carapace | source

        # nushell
        # std _carapace nushell | save out.nu
        # source out.nu


        # powershell
        # Set-PSReadLineOption -Colors @{ "Selection" = "`e[7m" }
        # Set-PSReadlineKeyHandler -Key Tab -Function MenuComplete
        # std _carapace | Out-String | Invoke-Expression

        # tcsh
        # set autolist
        # eval `std _carapace tcsh`

        # xonsh
        # COMPLETIONS_CONFIRM=True
        # exec($(std _carapace))
      '';
}

blaggacao avatar May 30 '22 19:05 blaggacao

Here are my findings, so far while trying to get shell completion to work across different shells:

  • nix develop --command "$SHELL" is potentially slow since it reloads the entire shell again.
  • nix develop in general is not a good interface to load a devshell since it fixes the shell to bash and foregoes pretty much all the features of the host shell.
  • result/entrypoint - when sourced loads the environment efficiently, when run, switches hard-coded to bash.

direnv

  • when direnv sources result/entrypoint it's not interactive so the interactive commands don't load and the sourcing environment is also bash, therefore shell-specific completion scripts aren't loaded either.

blaggacao avatar May 30 '22 22:05 blaggacao

So this PR is a direct conclusion of the above analysis: https://github.com/numtide/devshell/pull/205

blaggacao avatar May 30 '22 22:05 blaggacao

After furthed research, I conclude that it would be best to use nix print-dev-env --json ... which returns something link the following JSON

JSON
{
  "bashFunctions": {
    "runHook":" \n    eval \"$shellHook\";\n    unset runHook\n"
  },
  "variables": {
    "BASH": {"type": "var", "value": "/noshell"},
    "BASHOPTS": {"type": "unknown"},
    "BASHPID": {"type": "unknown"},
    "COMP_WORDBREAKS": {"type": "var", "value": " \t\n\"'@><=;|&(:"},
    "EPOCHREALTIME": {"type": "var", "value": "1653957355.162904"},
    "EPOCHSECONDS": {"type": "var", "value": "1653957355"},
    "HOME": {"type": "exported", "value": "/homeless-shelter"},
    "HOSTTYPE": {"type": "var", "value": "x86_64"},
    "IFS": {"type": "var", "value": " \t\n"},
    "IN_NIX_SHELL": {"type": "exported", "value": "impure"},
    "LINENO": {"type": "var", "value": "73"},
    "MACHTYPE": {"type": "var", "value": "x86_64-pc-linux-gnu"},
    "NIX_BUILD_CORES": {"type": "exported", "value": "4"},
    "NIX_BUILD_TOP": {"type": "exported", "value": "/build"},
    "NIX_LOG_FD": {"type": "exported", "value": "2"},
    "NIX_STORE": {"type": "exported", "value": "/nix/store"},
    "OLDPWD": {"type": "exported", "value": ""},
    "OPTERR": {"type": "var", "value": "1"},
    "OPTIND": {"type": "unknown"},
    "OSTYPE": {"type": "var", "value": "linux-gnu"},
    "PATH": {"type": "exported", "value": "/path-not-set"},
    "PS4": {"type": "var", "value": "+ "},
    "SHELL": {"type": "var", "value": "/noshell"},
    "SHELLOPTS": {"type": "unknown"},
    "SRANDOM": {"type": "unknown"},
    "TEMP": {"type": "exported", "value": "/build"},
    "TEMPDIR": {"type": "exported", "value": "/build"},
    "TERM": {"type": "exported", "value": "xterm-256color"},
    "TMP": {"type": "exported", "value": "/build"},
    "TMPDIR": {"type": "exported", "value": "/build"},
    "builder": {"type": "exported", "value": "/nix/store/d60gkg5dkw4y5kc055n4m0xyvcjz65im-bash-interactive-5.1-p16/bin/bash"},
    "dontAddDisableDepTrack": {"type": "exported", "value": "1"},
    "name": {"type": "exported", "value": "Standard"},
    "out": {"type": "exported", "value": "/nix/store/wg3jq1c0y99vx35pl2p1a9py83z1qwsj-Standard-env"},
    "outputs": {"type": "var", "value": "out"},
    "shellHook": {"type": "exported", "value": "# Remove all the unnecessary noise that is set by the build env\nunset NIX_BUILD_TOP NIX_BUILD_CORES NIX_STORE\nunset TEMP TEMPDIR TMP TMPDIR\nunset builder name out shellHook stdenv system\n# Flakes stuff\nunset dontAddDisableDepTrack outputs\n\n# For `nix develop`\nif [[ \"$SHELL\" == \"/noshell\" ]]; then\n  export SHELL=/nix/store/d60gkg5dkw4y5kc055n4m0xyvcjz65im-bash-interactive-5.1-p16/bin/bash\nfi\n\n# Load the environment\nsource \"/nix/store/2qpifmmkcrqn7nmj9bh718ah1xq9x1xv-devshell-dir/env.bash\"\n"},
    "stdenv": {"type": "exported", "value": "/nix/store/bq3ply37g51zxb6qqh04rhb2gawhii4l-naked-stdenv"},
    "system": {"type": "exported", "value": "x86_64-linux"}
  }
}

It should be relatively straight forward to write a rendered that would render this for:

  • bash (already built-in)
  • zsh, et al. -- maybe keep on par with: https://rsteube.github.io/carapace/carapace/gen/hiddenSubcommand.html

Then a new devshell would be launched with something a long these lines:

nix print-dev-env --json ... | devshell render --elvish > ./tempfile
# will/should properly increment SHLVL
elvish -c "source ./tempfile" # or whatever the right syntax would be

and that could be shortened to magic, still:

devshell apply  # detecting the right shell based on `SHELL`

blaggacao avatar May 31 '22 02:05 blaggacao

Looks like we can have this a lot easier:

nix build "${nix_args[@]}" --profile "$profile_path/shell-profile"
bash -c "source $profile_path/shell-profile/env.bash; SHLVL=$SHLVL; exec $SHELL -i"
  • your current shell (required to have the -i flag, but could be shimmed easily)
  • with the devshell's env additions
  • and the correct SHLVL
  • correct $SHELL as opposed to after invoking nix develop ...
  • CTRL+D drops you one shell deeper and reverts the env (as expected)

In context: https://github.com/divnix/std/commit/37529d6110f2b4b536b82acabe370b3803e9078d

blaggacao avatar May 31 '22 03:05 blaggacao

Sub-shells are the only way to go if we want something more than env vars (ie direnv).

The easiest for that is to start the sub-shell using the entry point script:

out=$(nix-build ...)
"$out/entrypoint" "$SHELL"

(while at it, you might want to sandbox the whole user environment so the project cannot leak or extract user credentials.)

Of course, the issue then is how to keep the user's configuration in the sub-shell. And what parts are fair game to be overwritten?

zimbatm avatar May 31 '22 07:05 zimbatm

FWIW in the mean time I've been able to get to a comfortable dev experience using a combination of direnv and some vscode extensions.

  1. Avoid starting services when entering the shell. Direnv doesn't support it, and Bash is not very helpful on reaping services.

  2. vscode is also no good at reaping. I just wrap services with tini and it works as expected. To configure for example the postgres server, I did:

    pkgs.devshell.mkShell {
      imports = [
        # Includes postgres command, env and initial db setup
        "${pkgs.devshell.extraModulesDir}/services/postgres.nix"
      ];
      devshell.packages = with pkgs; [
        tini
    
        # Service helpers
        (writeScriptBin "service-postgres" ''
          #!/usr/bin/env bash
          exec tini -sp SIGTERM -- postgres
        '')
      ];
      # ...
    }
    
  3. Recommend pinage404.nix-extension-pack. It includes the excellent mkhl.direnv extension which makes all definitions from the devshell available to the IDE.

  4. Recommend too actboy168.tasks and configure some tasks, so the dev has a button in the nav bar to start services.

  5. Wire those tasks with launch configurations.

At the end, the result is that the dev just opens the workspace on vscode and gets recommended extensions. Installs them, and automatically direnv makes all bins and env available. Then you get a taskbar button to start background services, and can also just hit F5 to start a debugging session which starts those services before.

yajo avatar May 31 '22 08:05 yajo

I'm doing something similar but use a Procfile executed by hivemind to start the services.

@yajo the last commit changed postgres so make sure to replace postgres with start-postgres

zimbatm avatar May 31 '22 09:05 zimbatm

I never used hivemind, but maybe there could be some extra module that solves this issue. One shouldn't care so much about what process manager is used as long as things get reaped as expected. That's probably a separate issue.

yajo avatar May 31 '22 12:05 yajo

I wonder what let to the choice or hivemind over something like arion

blaggacao avatar May 31 '22 17:05 blaggacao

Arion uses containers, right? Hivemind or Tini are only process managers AFAICS. I am ditching containers on dev because they add almost no value in that scenario once you use nix IMHO.

yajo avatar Jun 01 '22 08:06 yajo

Especially on mac where docker has to sync the files between the host and the docker VM, it adds quite a bit of overhead. In exchange for a bit of port-mapping and state folder discipline, you get a great increment in speed and debugging.

zimbatm avatar Jun 03 '22 07:06 zimbatm

General recommendation, nix develop -c fish doesn't print the welcome message. Any simple workaround to fix that?

Animeshz avatar Jan 04 '24 12:01 Animeshz