Link scripts to bin home-manager

I’m trying to integrate my custom scripts into the system’s PATH on NixOS with WSL, using Home Manager (unstable). My file structure includes:

  • configuration.nix
  • flake.lock
  • flake.nix
  • home.nix
  • README.md
  • scripts/
    • myscript.sh

I want to symbolically link my scripts or add the path to the scripts directory to $PATH. I’ve searched extensively but haven’t found a clear solution. This NixOS Discourse post discusses a similar issue but uses Git to fetch scripts. It references a Reddit comment that also didn’t solve my problem.

My configurations are available in this GitHub repo.

You can use the nixpkgs function writeShellScriptBin. Here’s an example:

home.packages = with pkgs; [
  (writeShellScriptBin "hi" ''
    echo "hi!"
  '')
];

This script will automatically be put in your path with the name hi

Edit:

If you want to read it from a separate .sh file, I think this should work.

home.packages = with pkgs; [
  (writeShellScriptBin "hi" (builtins.readFile ./relpath/to/script.sh)
];
3 Likes

I see a few ways of doing this:

1 - You can add you repository’s scripts dir to path and install dependencies manually:

{ pkgs, }: {
  home.sessionPath = [ "/home/sul/your-repository-path-here/scripts" ];
  home.packages = with pkgs; [ lazydocker ];
}

But this has the downside of not being very reproducible, since you will need to have your repository cloned at the declared location for this to work.

2 - You can install each script as a package:

{ pkgs, ... }: {
  home.packages = with pkgs; [
    (writeShellScriptBin {
      name = "lzd";
      runtimeInputs = [ lazydocker ];
      text = import ./lzd;
    })
  ];
}

The downside here is that you will need to run home-manager switch to tinker with your scripts.

3 - You can also do a mix of the two, trying the cloned repo first, and falling back to the installed scripts.

{ pkgs, ... }: {
  home.sessionPath = [ "/home/sul/your-repository-path-here/scripts" ];
  home.packages = with pkgs; [
    lazydocker
    (writeShellScriptBin {
      name = "lzd";
      text = import ./lzd;
    })
  ];
}
3 Likes

What about:

  home-manager.users.username = {
    home.stateVersion = "23.11";

    home.file = {
      ".local/bin" = {
        source = ./scripts;
        recursive = true;
      };
    };

Which should make a ~/.local/bin directory, and copy over the contents from sources/scripts/.

For more info, see: NixOS for the Impatient.

1 Like

it created copy of scipts in .local/bin but I can’t call it from terminal for some reason, it’s look like .local/bin not have in $PATH :grinning:. I first thought it don’t have execution access but it has

can I pass some arguments while using it through file? like ${home.username}

Can you add ~/.local/bin to PATH? Or, can you change .local/bin to a path available in PATH?

If you write the script inside a nix file you can you ${home.username} inside the script.

I used something like

{ pkgs, ... }: {
  home.sessionPath = [ "/home/sul/your-repository-path-here/scripts" ];
  home.packages = with pkgs; [
    lazydocker
    (writeShellScriptBin {
      name = "lzd";
      text = import ./lzd;
    })
  ];
}

in my flake but this leads to this error.

A definition for option home-manager.users.<me>.home.packages."[definition 1-entry 1]"' is not of type package’. Definition values:

apparently writeShellScriptBin does not create a package?

@jnfrd I ran into the same error and fixed it with:

{ pkgs, ... }: {
  home.packages = with pkgs; [
    (writeShellApplication {
      name = "lzd";
      runtimeInputs = [ lazydocker ];
      text = builtins.readFile ./lzd;
    })
  ];
}
2 Likes

I have the same issue. Should we not be able to set the path in home-manager? I am doing:

  home.file = {
    "bin2" = {
      source = ./mybin;
      recursive = true;
    };
  };
  home.sessionPath = [
    "$HOME/bin2"
  ];

I was expecting this to set the PATH variable to include the bin2 folder…

The file is copied, it’s just that the folder in not in the PATH…

Turns out there is an __HM_SESS_VARS_SOURCED=1 that requires me to logout/login for this change to get picked up. I was using a GUI desktop and was trying to start new terminals, therefore the changes were not being picked up due to this variable being set…