Patching ruby gems of a nixpkgs bundler app in an overlay

As part of the work sponsored by Niteo, today spent some time figuring out how a nixpkgs package specified with bundlerApp can be changed to have a patched version of a gem. This turned out to be much trickier than anticipated, as it seems that the ruby infrastructure isn’t really made for this. In this post I’m just going to quickly show an example of how I was able to achieve this, in the hopes that it can help somebody in the future.

The overlay specifically fixes jekyll to not throw a libsass error on macOS, see this PR for more info (might be upstreamed later). The overlay has the following simplified structure. I added comments to explain it a bit:

self: super: {
  jekyll = super.jekyll.override (old: {
    # Since bundlerApp's arguments can't be overwritten directly
    # we instead overwrite the function itself
    bundlerApp = attrs: old.bundlerApp (attrs // {
      # Originally just the gemdir argument was passed, containing a gemset.nix
      # But we can also set gemset directly which will override gemdir's gemset.nix
      gemset = let
        # Import the gemdir to get the original gemset.nix
        gems = import (attrs.gemdir + "/gemset.nix");
      # recursiveUpdate instead of // such that we can do deep overriding
      in super.lib.recursiveUpdate gems {
        # This is essentially the same as changing the gemset.nix file directly,
        # except that we can't change it from here and that it would be regenerated for updates
        sassc.patches = [(builtins.toFile "foo.patch" ''
          ...
        '')];
      };
    });
  });
}

All things can be overridden, they just take varying forms of effort :slight_smile:

1 Like