It's happened, I've drank the punch.

So I got into dev shells a while back, but I've started to run into some issues, mostly with other's nix shell environments. QMK's shell.nix is one such issue, and I believe it has something to do with it being written for NixOS, or at least Nix on Linux. This highlights one of the biggest weaknesses of using "bare" nix as opposed to Nix Flakes, with their ability to build different targets. With that out of the way on the why I bothered, here's my first flake!

This blog!

{
  description = "A flake for developing and building my personal website";

  # It's a flake, may as well try the latest
  inputs.nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";

  # Useful utilities to automatically create targets for different
  # platforms. Just makes it more readable in this case.
  inputs.flake-utils.url = "github:numtide/flake-utils";

  # This builds a package, in this case, containing zola
  
  outputs = { self, nixpkgs, flake-utils }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = nixpkgs.legacyPackages.${system};
      in
      {
        # This uses nix to fully build the site as a nix package.
        packages.website = pkgs.stdenv.mkDerivation rec {
          pname = "static-website";
          version = "2023-08-26";
          src = ./.;
          nativeBuildInputs = [ pkgs.zola ];
          buildPhase = "zola build";
          installPhase = "cp -r public $out";
        };
        # This output is what we use for our dev shell.
        defaultPackage = self.packages.${system}.website;
        devShell = pkgs.mkShell {
          packages = with pkgs; [
            gnumake
            zola
          ];
        };
      }
    );
}

While functionally similar to the previous system without a flake, it will create a flake.lock file. Currently it looks like this, though you should never hand edit this file, I'm just showing what's in it.

{
  "nodes": {
    "flake-utils": {
      "inputs": {
        "systems": "systems"
      },
      "locked": {
        "lastModified": 1692799911,
        "narHash": "sha256-3eihraek4qL744EvQXsK1Ha6C3CR7nnT8X2qWap4RNk=",
        "owner": "numtide",
        "repo": "flake-utils",
        "rev": "f9e7cf818399d17d347f847525c5a5a8032e4e44",
        "type": "github"
      },
      "original": {
        "owner": "numtide",
        "repo": "flake-utils",
        "type": "github"
      }
    },
    "nixpkgs": {
      "locked": {
        "lastModified": 1693355128,
        "narHash": "sha256-+ZoAny3ZxLcfMaUoLVgL9Ywb/57wP+EtsdNGuXUJrwg=",
        "owner": "nixos",
        "repo": "nixpkgs",
        "rev": "a63a64b593dcf2fe05f7c5d666eb395950f36bc9",
        "type": "github"
      },
      "original": {
        "owner": "nixos",
        "ref": "nixpkgs-unstable",
        "repo": "nixpkgs",
        "type": "github"
      }
    },
    "root": {
      "inputs": {
        "flake-utils": "flake-utils",
        "nixpkgs": "nixpkgs"
      }
    },
    "systems": {
      "locked": {
        "lastModified": 1681028828,
        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
        "owner": "nix-systems",
        "repo": "default",
        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
        "type": "github"
      },
      "original": {
        "owner": "nix-systems",
        "repo": "default",
        "type": "github"
      }
    }
  },
  "root": "root",
  "version": 7
}

This will fully lock down all of the sources by revision of git commit, or similar. This insures that if Zola updates, and it breaks the blog, no new install of Zola on a new machine, or a newly spawned shell will seemingly randomly break. This gives us the option of updating only when we are ready, knowing that all machines are on identical versions of the software, and we can update and push the exact same update to all machines at the same time. Updating the flake is as simple as running nix flake update. If everything works as expected, then commit the new flake.lock file to the repository and push it like any other change to the repo.

Other upsides

Now that I have a flake, I can simply reference it from other flakes, even if I don't have it locally installed on the machine. FlakeHub is a common place that people push their flakes for others to use, as is GitHub. Flakes can be layered similar to docker images, so you can use other's flakes to build on top of to make creating tooling easier, as well as deployment of build tools! Nix can, and will, eat your everything if you let it.

Further rambling outro

While I have stated that I won't let Nix eat my everything, I'm coming around to the cool tooling that it has to offer. I doubt that I'll let it completely take over everything, but time will tell. I've still not got amazing things to say about NixOS, and quite like having a system underneath nix to run the show, but I try to give things a try before truly dismissing them. Maybe next I'll try to build docker images with nix as I'm not a huge fan of creating images the "normal" way. Still keeping docker, but changing the build tooling. We'll see I suppose.

Useful snippits that may help you out.

Make a shell package, or basic flake to start a dev environment, all in a zsh/bash function.

nixify() {
  if [ ! -e ./.envrc ]; then
    echo "use nix" > .envrc
    direnv allow
  fi
  if [[ ! -e shell.nix ]] && [[ ! -e default.nix ]]; then
    cat > default.nix <<'EOF'
with import <nixpkgs> {};
mkShell {
  nativeBuildInputs = [
    bashInteractive
  ];
}
EOF
    ${EDITOR:-vim} default.nix
  fi
}
flakify() {
  if [ ! -e flake.nix ]; then
    nix flake new -t github:nix-community/nix-direnv .
  elif [ ! -e .envrc ]; then
    echo "use flake" > .envrc
    direnv allow
  fi
  ${EDITOR:-vim} flake.nix
}

And some more reading if you want to know more. These are the resources that I used to get where I am with this post.

https://ejpcmac.net/blog/migrating-to-a-static-blog/
https://fasterthanli.me/series/building-a-rust-service-with-nix/part-10
https://zero-to-nix.com/concepts/flakes