Make the World your Nixpkgs

Electron microscope image of rime ice.

Reading time: 7 minute

Back Home.

This isn’t a tutorial, it’s more a “here’s what that thing I’ve been working on is about.”1

I like Nix a lot. It’s a so called “obvious good idea”. Even with much of its baggage, some technical, some cultural, it’s probably one of the most interesting things currently happening in software, and you’re making a mistake if you’re not paying attention to it.

My desktop, laptop, build server, and even my IRC-bounce server in the “cloud” runs Nix. This makes it easy for me to provision my systems from a single source of version controlled truth. What has made that even better is that all the software projects I maintain include a so called Nix flake, and I make sure to upstream the projects I could imagine people would find some value in to nixpkgs every week on release.

One particular project I maintain is called eza, it’s that fork of exa, that you may or may not even be aware that you’re running if you’re a former exa user2.

Eza relies heavily on Nix. The first way Nix showed up was to help us remove vagrant, a product from HashiCorp, the company famous for making terraform. This was partially because I was uncomfortable with the vagrant business license, but further because Nix could replace it while providing unique benefits. But we do so much more with Nix.

The Nix flake sets up the development environment for contributors, in a way that matches the same environment as I myself develop on effortlessly for devs. It helps ensure that commits follow conventional commits, and checks if the code conforms to a battery of linters. It further makes it easy to format all source, and helps build eza in a declarative way.

In general, I like to think that we at eza run a tight ship. This is necessary, because we’re working in a surprisingly hard domain, where there is a lot of side-effects — what else would you expect from something so closely tied to the file system.

This means that we enforce semantic versioning as well, and generate changelogs that make breaking changes clear.

Now with all that in mind, let me quickly explain the lifecycle of a eza version.

We start on Thursday, after a release was just made. For most of the week, we slowly and steadily merge pull requests. Then, when Thursday comes, based on whether or not a breaking change was introduced (as is easily parsed from our commit summaries thanks to conventional commits), we determine the next version of eza automatically, and justfile recipes (think make files but less pain) cross compiles binaries for the platforms we support, and cuts and tags the new release, as well as making a “proper” github release.

Then, after all that is done, there’s only two things left. First, I run cargo publish, to send the newest eza version to crates.io, from which it is immediately available for download via cargo install eza3. Secondly, I update the nixpkgs package (that I, reasonably enough, am the maintainer of), and await someone with commit bit set to merge it.

Now, it is often merged quite quickly these days, I think that there is a small group of commiters who’s gotten used to looking for my batch of software updates and merge them without much fuss. But even with a same day merge, due to the nature of nixpkgs, this newest version will actually first be available in some days, usually four to five.

This is because the nixpkgs unstable branch lags behind the main branch4 by a bit. The reasons for why this is are overly technical, but sufficient to say that even while nixpkgs-unstable is the most up to date package distribution in aggregate, compared to arch-extra, which usually has the newest version of eza within a day, for packages like eza, it lags behind.

There of course is a potential solution to this, one that I did use for bit. See, since eza has a flake.nix file, one can just refer to that directly in their NixOS config, by making it a flake input (if one does use flakes that is).

This is great, that way, you don’t have to wait for nixpkgs-unstable to catch up with our latest release, but there is a catch.

Flake inputs have no notion of versioning.

Now, if you’ve been keeping your ear to the ground, there has, withing the last few month been two major improvements on this front. Firstly came flakehub, a proprietary platform by many NixOS veterans. Later came the, seemingly spiteful flakestry.dev, by other NixOS veteran. Between the lines, it’s farily clear that this is a direct reaction to flakehub being closed source.

The selling point of both of these platforms is the following: you add a github action, and whenever you make a proper github release, that action will push your flake to the respective platform.

The problem with both however, at least to me, as someone that distributes software to a fair few amount of users, but even further, just as someone that likes to tinker with my system and loves to make it all neat is the following:

And I tried to see if these limitations weren’t too annoying for me to use for some months. But aside from the technical limitations, the closed nature of flakehub got to me (at the time, flakestry.dev didn’t exist).

Having had this in the back of my head lead to many shower thoughts, which lead to additional shower thoughts, which lead to generally very long showers. Some weeks ago, those shower thoughts collided into a sudden, immediate hacking run, and rime was born.

Now, three weeks later, we’re sorta at a place where all the essential features we want are in place.

Those things include supporting not just github, but gitlab, codeberg, forgejo, gitea, sourcehut, and even flakehub5, supporting semver requirements, whether that be getting any version that is a version=\(\le0.3.48\), or just only getting minor and patch updates, but not major version bumps without manual intervention. And like, a lot more features. But really, what makes it great is that I can just specify:

  description = "My Configuration";

  inputs = {
    eza.url = "http://rime.cx/v1/github/cafkafk/eza.tar.gz";

    ... your other inputs

  outputs = {
    ... all your cool outputs

And like that, the eza flake input will always be the latest released (excluding pre-releases) version.

This really puts nixpkgs into perspective for me. For projects that already have their own flakes, the value of nixpkgs mainly becomes it being a hand crafted manually updated package whenever there is a new version.

And while I’m determined to make eza available to as many users as possible, and having it in nixpkgs will obviously help that… for my own use at least, it’s just so much more neat to have it as a flake input, but without having to include unreleased, potentially unstable changes.

It clearly to me also opens some routes to a more decentralized NixOS landscape, since it adds a sort of layer between the latest raw flake and the users computer, reminding me of the layer of snow that build up when you place something sturdy in a hostile, cold climate with a lot of snow(flakes). A layer called rime.



Although much of the work has been done by Gergely Nagy, who I’ve been able to successfully nerdsnipe.


Since distributions shim in eza in the place of exa nowadays.


Although, sadly, it’s impossible to include man pages and completions via crates.io.


Technically, the “master” branch, but let’s not indulge that anachronism.


And it’s written to be easily extendible, so adding new forges is trivial.

Date: 2023-11-09

Created: 2024-04-14 Sun 10:06