return 12; // good enough for now

Nix-powered developer environments

2023-01-24

I’m addicted to setting up dev environments. A new language, framework, or editor with look interesting and I’ll dedicate myself to learning it. Briefly. Really I’ll just spend ages setting up the tooling in a pleasing way, go through a couple of tutorials and then get distracted by the next thing. It’s the same with reading books. Does this hint at a wider personality flaw? Maybe, but I don’t have the attention span to dig into it further.

Anyhoo, since falling in love with Nix I can scratch this itch a little faster and a little more cleanly. On a per-project basis I can specify the dependencies (language tooling, etc) and have them available to me when (and only when) I’m in the directory of that project.

$ python
zsh: command not found: python
$ cd some-python-project
$ python
Python 3.10.7 (main, Sep 16 2022, 20:44:58) [Clang 11.1.0 ] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>

There are several available tools to help manage this but I’ve not really settled on one yet (I think it’s because they don’t feel idiomatic to Nix enough, even though that might be a good thing) so I’m just using a simple flake (mostly cribbed from this post of Xe’s) plus nix-direnv. The flake looks like this…

# flake.nix

{
  description = "Project name";

  inputs.nixpkgs.url = "nixpkgs/nixos-unstable";

  outputs = { self, nixpkgs }:
    let
      supportedSystems = [
        "x86_64-linux"
        "aarch64-linux"
        "x86_64-darwin"
      ];

      forAllSystems = nixpkgs.lib.genAttrs supportedSystems;

      nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; });
    in
    {

      packages = forAllSystems (system:
        let
          pkgs = nixpkgsFor.${system};
        in
        {
          default = with pkgs;
            stdenv.mkDerivation {
              name = "some-project";
              src = self;
              nativeBuildInputs = [ ]; # List project dependencies here
                                       # eg nativeBuildInputs = [python3]
              dontInstall = true;
            };
        });
        devShells = forAllSystems (system: {
          default = nixpkgsFor.${system}.mkShell {
            name = "some-project-devshell";
            inputsFrom = [self.packages.${system}.default];
          };
        });
    };
}

Then create a simple .envrc file in the same directory, initialise the git repository (a requirement for flakes) and off you go.

$ echo "use flake" > .envrc
$ git init
$ git add flake.nix
$ direnv allow

I’m sure there’s a much more elegant way to do this, but for now I’m happy to copy and paste the above into new projects.