I create a separate flake.nix
file for every repository, and add a .envrc
file with the content use flake
to enable direnv to automatically enable the environment when I cd into the directory.
There is also a direnv plugin for vscode and one for IntellJ, so using direnv solves problems 4 and 5. You might find some info online about installing direnv-nix integration, but this isn’t necessary anymore, direnv supports nix-shell and nix flake out-of-the-box these days.
Problems 1-3 sound like you want some sort of basic inheritance, but you could achieve that with functions quite easily.
With flakes, you can have a separate repository (let’s say “env-templates”) from which every repository imports its base configuration.
You can get an idea for these in the official templates repository.
These templates are designed to be inserted as a file into your repository on initialization (with nix flake init -t
or nix flake new -t
), but I assume you will want to update the base configurations centrally, so a better idea is to add the flakes in your “env-templates” as inputs to your environments.
A base configuration might look like this:
{
inputs.nixpkgs.url = "github:NixOS/nixpkgs/23.05";
outputs = { self, nixpkgs }:
let
supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ];
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
pkgs = forAllSystems (system: nixpkgs.legacyPackages.${system});
in
{
mkShell = { addPackages ? [], addShellHook ? "", overridePackages ? [], overrideShellHook ? "" }:
forAllSystems (system: {
pkgs.${system}.mkShellNoCC {
packages = with pkgs.${system}; (if overridePackages == [] then [
python3
] else overridePackages) ++ extraPackages;
shellHook = (if overrideShellHook == "" then ''
MY_ENV_VAR=1
'' else overrideShellHook) ++ addShellHook;
};
});
};
}
Which then (if it has the path python/flake.nix
in a repository env-templates
belonging to louchen
on gitlab) can be imported in a project like this:
{
inputs.nixpkgs.url = "github:NixOS/nixpkgs/23.05";
inputs.python-env.url = "gitlab:louchen/env-templates?dir=python";
inputs.python-env.inputs.nixpkgs.follows "nixpkgs";
outputs = { self, nixpkgs, python-env }:
let
supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ];
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
pkgs = forAllSystems (system: nixpkgs.legacyPackages.${system});
in
{
devShells = forAllSystems (system: {
default = python-env.mkShell.${system} {
extraPackages = with pkgs; [ jq ];
};
});
};
}
devShells
is the output that nix develop
and direnv use to set up the environment.
This only shows one level; we’re building the environment for the project flake from the language flake directly, but you can extrapolate from this and add another layer for frameworks.
You can also simplify this code quite a bit with flake-utils, and the setup for different languages might not be as simple as what I presented here (for example, with python you need to use python3.withPackages
to install python packages into your environment), but a general structure like this should work quite well.