Home-manager-shell: nix-shell for your home-manager config

home-manager-shell allows you to run any program with your home-manager configuration without installing it in one command.

For the times you ssh into a friend’s or coworker’s machine and don’t want to pollute their environment.

Inspired by nixos-shell and nix develop (or the plain old nix-shell).

Why?

For some programs like neovim it is easy to write a wrapper package that includes your personal configuration. For most others though it is not. Usually programs expect their configuration files at specific paths in $HOME, and writing there is not an option on someone else’s machine.

How does it work?

home-manager-shell generates a temporary flake that pulls in your configuration, builds its profile, references it via $PATH, exports its other environment variables, and finally starts a shell or your program using proot to bind-mount the profile’s paths like ~/.config/git/config over the host’s filesystem. That allows us to layer our own config files on top of the host’s config files without touching them, only from the perspective of the started shell.

Try it

Run my CLI calendar with color scheme and key bindings:

nix run sourcehut:~dermetfan/garnix#home-manager-shell -- -U dermetfan -e programs.wyrd -e programs.kakoune -- wyrd

(Can't open file: /home/you/.local/share/remind is to be expected as you don’t have a calendar file on your machine)

Run my text editor with color scheme, key bindings, and tons of plugins (huge build):

nix run sourcehut:~dermetfan/garnix#home-manager-shell -- -U dermetfan -e programs.kakoune -- kak

Status

It works well enough for my use cases. That includes kakoune, timewarrior, buku, and wyrd. I use this daily so I keep it up to date with home-manager releases. There are release branches for 22.11 and 22.05.

At the moment it’s just a simple bash script. If we want to add more features it might make sense to rewrite in another language though.

There is an open home-manager issue discussing what the flake output for sharing modules should be named by convention. At the moment I use .#homeManagerProfiles but I will update this as soon as the community makes a decision.

Contributing

I do not expect hordes of contributors as this is probably a niche use case. However, since this is currently neither hosted nor mirrored on GitHub, here are some quick links:

Sourcehut unfortunately has no pull request feature. If you do wish to send a patch I do not require you to do it the sourcehut way or native git way though; just post the patch on the issue tracker or post a link to your fork.

14 Likes

Hey Dermetfan, good job!

In a way, it feels like this could overlap with https://devenv.sh/ a bit.

@domenkozar What’s your take on this? Didn’t you discuss wanting to be able to reuse home-manager modules within devenv? It’d be pretty powerful to be able to share editor configuration with your team.

If it wasn’t for the lack of filesystem virtualization on MacOS…

1 Like

Sadly doesn’t seem to build for me on MacOS

error: flake ‘sourcehut:~dermetfan/garnix’ does not provide attribute ‘apps.aarch64-darwin.home-manager-shell’, ‘packages.aarch64-darwin.home-manager-shell’, ‘legacyPackages.aarch64-darwin.home-manager-shell’ or ‘home-manager-shell’

I’ve been toying with the idea of using devenv for things like editors and language servers. Sort of a batteries-included devshell which defines everything you need. But If you’ve got a lot of opinionated developers that puts you on a slippery slope towards nineteen batteries-included devshells among which a newcomer to the project must now chose–many of which will be replicated across projects (at potentially different versions).

I hadn’t considered that you might first start a nix-shell with your editor and other non-project-specific-but-maybe-language-specific stuff, and then work from the project-defined devshell as a subshell therein. Seems like a nice clean separation between my wacky keybindings which most other contributors don’t care about, and my project specific dependencies which they likely do.

I’m curious of anybody is exploring the devenv-as-a-subsubshell approach and whether they like it.

1 Like

@MattRixman

batteries-included devshell which defines everything you need

I’ve actually been doing this for years. I have a messy framework called fornix that kinda does the job of home manager for nix shells. It makes a fake home folder, uses a pure nix-shell, includes all the common cli utils via nix (which, ssh, git, ping, find, etc), builds a zshenv, starts a zsh shell with all the fancy config options (spaceship prompt, syntax highlighting, autocomplete). But also there’s an explicit list of external commands, like sudo/doas which cant/shouldnt be provided by nix but needs to be available. Fornix creates a local bin folder, symlinks “injected” binaries to the user’s system binary (e.g. a symlink to system’s sudo). Not only that but there’s also an injection wrapper for stuff like vi, emacs, and neovim; instead of a symlink it’s a shell script sets HOME back to the real home before calling the users real/external vi binary. That way running vi/vim in the shell with a fake home still gets people their configured setup instead of a vanilla vi/vim. There’s a purge command for clearing out the fake home (really important when using python virtual environments and pip packages), there’s event hooks (ex: before entering nix shell, after nix shell, on purge, git hook events), and there’s a toml file for adding packages (toml file gets read by the shell.nix file).

It took a lot of effort but one those caveats were addressed, the experience actually works really well. And not just for me, I’ve been using this framework for teams of about 20 people for at least two years.

Slippery slope

Actually not really. When people want their own CLI experience (custom git diff tools, editors, file size checks, etc) they simply don’t use the dev shell. When they need to run a should-work-for-everyone command, they use the dev shell.

There is one major caveat though

(not with the slippery slope but with the development experience). Editors that hook into debuggers/formatters/linters etc are problematic. Dev shells don’t have to be slow, but with the current design of nix-shell/nix develop they are really slow to start. It’s a pain to configure an editor to use the dev shell, but even after that when a formatter runs it spins up a dev shell just to run one command. It’s as bad as booting up a VM just to run one command.

Like I said it doesn’t have to be this way, Devbox has gotten around this by just setting ENV vars. I haven’t had the chance to refactor my framework to use their method. Also using devbox isn’t really an option for me because it’s very impure.

_

So yeah. I was actually trying to replace my framework with home-manager to make things more simple and piggy back of all the work done on home manager, but it looks like I might not get the chance to do that.