The Complete Computing Environment

Keeping Nix Version Pins in One Place


NixOS is a big fan of reproduceability – indeed that's what i'm using it for. But then I go read READMEs that say to do things like say "hey just download the code here from github's master.tar.gz and use it unconditionally". Nuh-uh, no can-do, bad idea. I'm going to maintain versions in a single Nix module which can be imported and used and updated when necessary.

To update this document:

To understand why/how read on:

This document contains Magic

To get the org-auto-tangle to work with the document I have had to be quite careful in how I construct it. I would like to be able to update all the refs on the page at once, by an affirmative user-action. The process for updating this file involves using modifications I've made of jweigley's nix-update-el and is simple to operate:

There is some scaffolding and nuance required to make this work in the tangle stage.

First of all, org-auto-tangle is smart enough to not execute Org Babel functions by default. It's imperative to customize help:org-auto-tangle-babel-safelist.

Consider nix-update-branch-revs. These are fetched using this code block referenced in the document as prefetch-git-rev, evaluated, and the results inserted in to versions.nix using noweb syntax. If this was evaluated every time I saved the document, the version of home-manager and emacs-overlay which are imported would change every time I save!

(require 's)
  (format "curl -s | jq .commit.sha" REPO BRANCH)))

This require's things which aren't a part of Emacs by default, so I have to modify my AUTO_TANGLE document keyword with a new feature to pass variables in to the async Emacs invocation, thus #+AUTO_TANGLE: vars:load-path. This is, unfortunately, much slower to load and thus tangle now.

You'll note that this prefetch-git-rev is an elisp function, why not just use a shell-script? well, org doesn't load ob-shell by default, and passing org-babel-load-languages in to the async function was not enough to get it to work. oh well. Luckily I don't need anything too special here.

And so we are quite careful in how this document is constructed. Consider the home-manager example. If (NAME) were attached to the inline org-babel CALL, this would update every save. So the results have to be named. The (invocation) looks like a function call but will actually use the cached value.

#+CALL: prefetch-git-rev(REPO="nix-community/home-manager", BRANCH="master")

#+NAME: prefetch-hm                                                          (ref:NAME)
: "60c6bfe322944d04bb38e76b64effcbd01258824"

#+begin_src nix :noweb-ref homeManager :noweb yes
homeManager = _: builtins.fetchGit {
  url = "";
  rev =
   <<prefetch-hm()>>                                                          (ref:invocation)

By structuring these invocations like this it is possible to write a function contained in my nix-update page which will iterate over all the call sections and update the builtins.fetchGit entities, and then update the revisions and sha256 of the rest of the document, and safely tangle the new values out on save. This is probably a useful pattern in developing Hypermedia in org-mode.

NEXT update my NixOS version pins and deploy

{ ... }:

  # fetchGit
  # fetchTarball
  # github
  # tabfs
  # org libraries

Run elisp:(cce/update-nixpkgs-checkout) first

Right now I am running off a branch of nixpkgs which builds vsketch and vpype for my Plotter Art, so it's kind of a pain to update rather than simply checking out the nixpkgs-unstable branch.. Might have to elisp:(magit-status "/home/rrix/Code/nixpkgs") and resolve some merge conflicts… I should upstream these packages!!!

Update home-manager by hand

homeManager = _: builtins.fetchGit {
  url = "";
  rev =

Update emacs-overlay used in Arroyo Emacs by hand

emacsOverlay = _: builtins.fetchGit {
  url = "";
  rev =

^ This is upstream; I also have a checkout which I can manage with elisp:(magit-status "~/Code/emacs-overlay"); I can move the #+NAME keyword on the above code segment to this one to build a local instance:

emacsOverlay = _: /home/rrix/Code/emacs-overlay;

mastodon in Emacs ->

builds from

mastodon = { pkgs, ... }: pkgs.fetchgit {
  url = "";
  rev = "91020f86cf2b19bfb3136a9a92facd77bd0ccda0";
  sha256 = "0xr6kjhxfzchkpma1rj85yhkipn9hlqfshqci8mg94xg8hnlr0q0";
  # date = "2022-11-19T19:24:43+01:00";

nixGL ->

nixGL = { pkgs, ... }: pkgs.fetchFromGitHub {
  owner = "guibou";
  repo = "nixGL";
  rev = "7165ffbccbd2cf4379b6cd6d2edd1620a427e5ae";
  sha256 = "1wc85xqnq2wb008y9acb29jbfkc242m9697g2b8j6q3yqmfhrks1";
  # date = "2022-08-24T20:56:02+02:00";

TabFS ->

tabfs-rev = "09d57f94b507f68ec5e16f53b1cc868fbaf6cceb";
tabfs-fetch = {pkgs, ...}: pkgs.fetchFromGitHub {
  owner = "osnr";
  repo = "TabFS";
  rev = "09d57f94b507f68ec5e16f53b1cc868fbaf6cceb";
  sha256 = "0pnzq44plbs358l8lzxzlx35p2c5izvjaqbff5f1fj093wk92wiw";
  # date = "2022-02-02T00:56:15-05:00";


consult-org-roam-rev = "268f436858e1ea3b263782af466a54e4d603a7d2";
consult-org-roam = {pkgs, ...}: pkgs.fetchFromGitHub {
  owner = "jgru";
  repo = "consult-org-roam";
  rev = "96c95e5a14378cc6fc9c22981f5bfd9e18c419f6";
  sha256 = "1mjz73sv2hkirlsr3hmjfivknwydb4yjf90kk5a4zwmhcs3vlh8q";
  # date = "2022-11-20T08:18:09+01:00";


ox-rss = rec {
  version = "20220704.0450";
  rev = "83dc898fa5493925b01716e5dd495d5e07c3d41a";
  url = "";
  src = { pkgs, ... }: pkgs.fetchgit {
    rev = "83dc898fa5493925b01716e5dd495d5e07c3d41a";
    url = "";
    sha256 = "0513kixv9bgkignmji95m3rskn6px6c0fack4zdl61qq09fg8w6h";
    # date = "2021-06-06T07:25:35-04:00";


org-fc = rec {
  rev = "f64b5336485a42be91cfe77850c02a41575f5984";
  src = { pkgs, ... }: pkgs.fetchFromGitHub {
    owner = "l3kn";
    repo = "org-fc";
    rev = "973a16a9561f1ed2fd7e4c5c614b5e5d15715b12";
    sha256 = "07cjswmfwc1r11m77pp58rcpbxi3hayv2pnfrqz4zk4kvyb2zw2i";
    # date = "2022-09-27T20:31:27+02:00";

  1. This is required because I couldn't get nix-update-decls to update the revs of these without also populating a sha256 key which will not be valid in a builtins.fetchGit invocation. Both nix-community/emacs-overlay and home-manager are loaded in situations where there is no pre-existing nixpkgs to invoke, so they have to use this "impure" invocation.↩︎