In Poor Man’s devenv, I wrote a small bash script called use. It finds
project files, maps them to Nix packages, and opens a shell with:
exec nix-shell -p "${packages[@]}"It worked, but every shell still paid the nix-shell setup cost. It was just
slow enough for me to change it.
The new version caches the built environment.
One cache per package set ยถ
use sorts and deduplicates the package names, then hashes the result:
hash_input="$(printf '%s\n' "${shell_packages[@]}" | LC_ALL=C sort -u)"
package_key="$(printf '%s\n' "$hash_input" | md5sum)"Order does not matter. use go jq and use jq go share the same cache.
The hash is only used as a file name. It is not used for security, so MD5 is enough.
Build once, reuse later ยถ
nix print-dev-env evaluates the mkShell expression and prints a bash
script that recreates its build environment. Unlike nix develop, it does not
start a shell. This makes its output easy to save and source later:
nix print-dev-env \
--impure \
--expr "with import <nixpkgs> {}; mkShell { packages = [ ${shell_packages[*]} ]; }" \
--profile "$HOME/.local/state/use/${package_key}.profile" \
> "$HOME/.local/state/use/${package_key}.bash"<hash>.bashis the generated shell script. Sourcing it restores the build environment, including its variables and shell functions, without calling Nix again.<hash>.profileis the Nix profile created by--profile. It keeps the store paths used by that environment alive and available to<hash>.bash.
Later runs source the saved environment and start the user’s shell:
. "$HOME/.local/state/use/${package_key}.bash"
exec "$SHELL" -iConclusion ยถ
This keeps use small and efficient:
- Detect packages.
- Build each package set once.
- Enter it fast.
See the full diff.