chisui / zsh-nix-shell

zsh plugin that lets you use zsh in nix-shell shells.
BSD 3-Clause "New" or "Revised" License
373 stars 19 forks source link

shellHook aliases do not show up in zsh #6

Closed lovesegfault closed 6 years ago

lovesegfault commented 6 years ago

Issue description

Currently, if you define aliases in the shellHook portion of your shell.nix/default.nix file, they do not get passed to zsh.

Steps to reproduce

  1. Create a shell.nix with a shellHook section like such: shellHook=''alias taco=echo\ "tacos!"''
  2. Run nix-shell --pure, and then tun taco inside the shell to verify the alias works.
  3. Run nix-shell and then run taco inside the shell; the alias no longer works

Technical details

- system: `"x86_64-linux"`
 - host os: `Linux 4.4.0-62-generic, Ubuntu, 16.04.2 LTS (Xenial Xerus)`
 - multi-user?: `yes`
 - sandbox: `no`
 - version: `nix-env (Nix) 1.11.9`
 - channels(root): `"nixpkgs-17.09.2875.c2b668ee726"`
 - channels(ogle): `"nixpkgs-17.09.2875.c2b668ee726"`
warning: Nix search path entry ‘/home/bemeurer/.nix-defexpr/channels/nixpkgs’ does not exist, ignoring
 - nixpkgs: `/nix/var/nix/profiles/per-user/root/channels/nixpkgs`
zsh 5.4.2 (x86_64-unknown-linux-gnu)
chisui commented 6 years ago

Minimal nix file to reproduce

{}:
with import <nixpkgs> {};
stdenv.mkDerivation {
  name = "zsh-nix-shell-6";
  shellHook=''alias taco=echo\ "tacos!"'';
}
chisui commented 6 years ago

This feature would be extremely hard to support.

The shim has to evaluate the commands that nix-shell passes to it using bash since they aren't valid zsh commands. Inside that bash shell the shim starts a zsh shell. That means that the shellHook will run inside bash before the zsh shell is spawned.

Aliases aren't propagated from bash to zsh

> bash
$ alias taco=echo\ "tacos!"
$ zsh
> taco
zsh: command not found: taco

The only way to support shellHooks completely would be a bash script interpreter for zsh. Alternatively compgen -a provides a way to enumerate all aliases currently defined. So you could theoretically recreate them and pass them to zsh.

For now I will update the readme that this isn't currently supported. Sorry :(

Could you provide a litte more information on your usecase? Maybe we can figure out some workaround.

edit: according to the folks on #nixos IRC it's pretty uncommon to define aliases inside a shellHook though.

lovesegfault commented 6 years ago

Yeah I figured this would be rather hard, but thought was worth reporting anyway.

Regarding use case, it's a bit contrived, but here's what I'm doing. My OS is on version X of glibc. I have a nix shell which I need to be on version Y>X of glibc. I can't simply add ${glibc}/lib to LD_LIBRARY_PATH since that will then break all of my OS-tools, and yet I need the internal glibc on LD_LIBRARY_PATH when running a particular command foo.

My solution was to create an alias foo=LD_LIBRARY_PATH=${glibc}/lib:${LD_LIBRARY_PATH}\ foo and call it a day; and I wanted this to be always available in the shell, so I added it to shellHook.

chisui commented 6 years ago

Yeah, that could be solved pretty easily without an alias in a shellHook.

I'll close this issue now as wont fix. I hope this isn't a dealbreaker for you. If it is you are free to provide a fix yourself ;)

damianbaar commented 5 years ago

For now I will update the readme that this isn't currently supported. Sorry :(

Hey,

I believe I found a workaround for limitation above. For me it works when I will define shellScript first, like so:

with import <nixpkgs> {};

let
  initEnv = pkgs.writeShellScriptBin "init-environment" ''
    export ZDOTDIR=$PWD
  '';
in
stdenv.mkDerivation {
    name = "env";
    buildInputs = [
      initEnv
    ];
    shellHook = ''
      source init-environment
    '';
}

In root directory I have created a symlink to user configuration ln -s ~/.zshrc .zshrc (to get global setup) and added additional .zshenv file for project specific aliases.

Without introducing init-environment I was not able to define ENV vars at all.

chisui commented 5 years ago

How did you try to set environment variables in the first place? As far as I can see your solution should be equivalent to setting the variable inside the shellHook directly. In both scenarios the actual code that sets the variable is executed before you are dropped into zsh.

The following works for me:

with import <nixpkgs> {};
stdenv.mkDerivation {
  name = "env";
  shellHook = "export SOME_VAR=VALUE";
}

The idea of setting ZDOTDIR inside of the shellHook is pretty cool. Although I wouldn't use PWD. I would create a directory with all the relevant files and let nix take care of linking stuff together correctly:

.
├── zdotdir
│   └── .zshrc
├── default.nix

.zshrc

source ~/.zshrc
alias taco=echo\ "tacos!"

default.nix

with import <nixpkgs> {};
stdenv.mkDerivation {
  name = "withCustomEnv";
  shellHook = "export ZDOTDIR=${./zdotdir}";
}
damianbaar commented 5 years ago

actually you are right in case of exporting var, I was blind and forgot about export, sorry for confusions 👍

Your version is much cleaner and going to use it! Thanks!

chisui commented 5 years ago

I wrote a little nix expression that should take care of all edgecases I could come across.

Usage would be like this:

{ pkgs ? import <nixpkgs> {}
, zdotdir ? import (builtins.fetchurl {
    url = "https://gist.githubusercontent.com/chisui/bba90fccc930f614743dc259fbadae6d/raw/4108222addc1d646c1b0a6d12130083e2219ad28/zdotdir.nix";
  }) { inherit pkgs; }
}:
pkgs.stdenv.mkDerivation {
  name = "withZshEnv";
  shellHook = zdotdir {
    zshenv = ''
      alias taco=echo\ "tacos!";
    '';
  };
}

If that works for you @damianbaar and @bemeurer I'll include it in the readme.

damianbaar commented 5 years ago

Hey, looks cool, however getting this, if I'm going to nix-shell from zsh image but if I will run nix-shell within nix-shell then all good.

chisui commented 5 years ago

Does it work now if you use this version? https://gist.githubusercontent.com/chisui/bba90fccc930f614743dc259fbadae6d/raw/b29f5ed299825f16162de5fcb43aae95ab3ddd46/zdotdir.nix

damianbaar commented 5 years ago

no difference, but after changing to

in ''
  if [ -n ''${ZDOTDIR} ]; // from zsh way to posix way
  then
    export ZDOTDIR_PARENT=$ZDOTDIR;
  fi
  export ZDOTDIR=${zDotDir};
''

echo for ZDOTDIR gives me /nix/store/6yxiszpzndk52ar21rzyrw63j873p75g-ZDOTDIR and got taco in scope.

damianbaar commented 5 years ago

i've changed a bit your piece, with less assumptions about env vars, so you can pass string or path to file,

here you can find details: https://gist.github.com/damianbaar/57aff242d06c75444dbd36bf5400060e

chisui commented 5 years ago

Hm, in your version the users dotfiles aren't loaded. Is that by design? In my setup I have a custom theme that includes the current nix-shell environment in the prompt, which isn't loaded if you simple set the ZDOTDIR var.