-- Leo's gemini proxy

-- Connecting to log.pfad.fr:1965...

-- Connected

-- Sending request

-- Meta line: 20 text/gemini; charset=utf-8

Experimenting with flake.nix


I have read a lot recently about Nix (the OS, the package manager and the language). The reproducibility promise looks promising, so I am giving it a try.


> Disclaimer: all this stuff is pretty new to me, if anything could be improved/simplified, please reach out!


Installing the package manager and flake.nix concept


To start slowly, I chose not to install the NixOS but only the package manager. It was quite straightforward (just note that you should make a "bind mount" instead of a symlink if you want /nix to be "physically" stored somewhere else).


As far as I understand, the main goal of the flake.nix file is to define how to build some binaries (called "outputs") after having defined its dependencies (called "input").


Struggling with flake.nix


I choose a simple go project of mine called devf, which has no dependency outside of the go standard library.


Introducing devf for live reloading

code.pfad.fr/devf (live-reload web server for developers)


The first version of flake.nix looked like this:


{
  description = "livereload webserver for developers";

  inputs = {
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs = { self, nixpkgs, flake-utils }:
    flake-utils.lib.eachDefaultSystem (system:
      let

        pkgs = nixpkgs.legacyPackages.${system};

        # to work with older version of flakes
        lastModifiedDate = self.lastModifiedDate or self.lastModified or "19700101";

        # Generate a user-friendly version number.
        version = builtins.substring 0 8 lastModifiedDate;

      in
      {
        # See https://ryantm.github.io/nixpkgs/languages-frameworks/go/
        packages.default = pkgs.buildGoModule {
          name = "devf";
          inherit version;
          src = ./.;
          vendorHash = null;
        };
        devShells.default = pkgs.mkShell {
          buildInputs = with pkgs; [ go go-tools ];
        };
        formatter = pkgs.nixpkgs-fmt;
      });
}

I more or less blindly copy-pasted this from various sources until it worked.


inputs


Having to specify flake-utils as an input seems to be quite usual. It is used to define multiple outputs depending on the operating system in "flake-utils.lib.eachDefaultSystem (system: ...)".


outputs: packages


To be honest, I don't have much idea what the "version" output is used for... Then the meat of the configuration can be found under "packages". The "default" declares the default package (we only have one "devf") and "buildGoModule" tells nix that the code should be built using the go toolchain:


        packages.default = pkgs.buildGoModule {
          name = "devf";
          inherit version;
          src = ./.;
          vendorHash = null;
        };

Note the required `vendorHash = null;` since this module has no dependency outside of the go standard library (otherwise something like gomod2nix must be used apparently, but it looked quite cumbersome at first glance. If anyone has an example usage inside flake.nix, I would be grateful).


To compile "devf", one can call "nix build" and the resulting binary will be available under "result/bin/devf".


Improving devShells


devShells is another output, that allows setting up a development environment. So anyone who wants to hack on devf can run `nix develop` and have all the dependencies available at the right version (the go toolchain in the first version).


To allow anyone to reproduce all my setup, I also added the "reuse" tool (mainly for SPDX license headers checks) and the "inotify-tools", which I plug into my documentation generator "vanitydoc" to visualize the rendered documentation in realtime.


Adding my documentation generator "vanitydoc" got quite interesting since it is not available in nixpkgs (at least not as I write this ;). I first added a similar flake.nix file to the vanitydoc repository, then imported the repo as input in the devf flake.nix and finally added it to the devShells packages:


vanitydoc/flake.nix


  inputs = {
    flake-utils.url = "github:numtide/flake-utils";

    vanitydoc.url = "git+https://codeberg.org/pfad.fr/vanitydoc.git";
  };

  outputs = { self, nixpkgs, flake-utils, vanitydoc }:
    flake-utils.lib.eachDefaultSystem (system:
      let
    // ...
        vanitydoc-cli = vanitydoc.packages.${system}.default;
      in
      {
    // ...
        devShells.default = pkgs.mkShell {
          packages = [ vanitydoc-cli ];
          buildInputs = with pkgs; [ go go-tools reuse inotify-tools ];
        };
      });

Now the pieces fit nicely together: to get a development environment, vanitydoc gets built according to its flake.nix and exposed in the "devShell" of devf.


So just run "nix develop" to get started!


Going full circle


To finalize my setup I also added "devf" to the devShell of vanitydoc (so the two tools "depend" on each other for their devShell). Nix didn't seem to mind and everything worked fine, so I consider this experiment successful.


I will likely add flake.nix files to my other projects to ease developer quickstart as well as dependency management and package release.


devf/flake.nix

vanitydoc/flake.nix


> Let me know if anything can be improved/simplified/corrected, thanks!

📅 2023-10-23


Back to the index


Send me a comment or feedback

-- Response ended

-- Page fetched on Tue May 14 16:15:32 2024