Software patches in NixOS for fun and productivity

5 min read Original article ↗
, in

After using NixOS for more than a year, one aspect that I didn't expect to enjoy was software patching. In previous systems, my patches usually became stale (since I never manually pulled the latest release to re-apply my patch). With NixOS, I "just" have to integrate the patches into my config, to ensure that the patch is re-applied to every upgrade.

I hope that this shortlist of patching techniques may help you maintain your own patches! (or at least help my future self when facing such situations)

Patching code when I am impatient

I daily drive a git TUI called Lazygit. I initially couldn't push/pull over ssh, because the PIN-prompt of my tpm-based ssh-key wasn't properly handled.

I opened a PR in the upstream repo, but didn't want to wait for release cascade (review, merge, lazygit release, nixos release...). So instead I applied this patch to my lazygit install:

programs.lazygit =
  let
    patchedPackage = pkgs.lazygit.overrideAttrs
      (finalAttrs: previousAttrs: {
        # downloaded from https://github.com/jesseduffield/lazygit/pull/4018.patch
        # or locally generated with `git format-patch origin/main`
        patches = [ ./assets/lazygit-4018.patch ];
      });
  in
  {
    enable = true;
    package = patchedPackage;
    settings = {...};
  };

With this config, I was able to use the "officially packaged lazygit + my patch" immediately. On every NixOS update, the packaged lazygit version would be seamlessly patched.

My pull request was reviewed but then forgotten by the maintainers; I didn't really care since I wasn't impacted anymore!

Pull Request to add detection of TPM Pin prompt to lazygit

At some point another identical PR was proposed and merged. When the release cascade reached my system, Nix helpfully refused to apply the update, complaining in the pre-build phase that my patch could not be applied. I then removed the `patchedPackage` from above (which means I am now using the default pkgs.lazygit) and performed the update. I am now using the unpatched release of lazygit with my ssh-tpm-key!

Fun fact: I recently tried jujutsu with jjui as TUI frontend, and the latter did not prompt for my TPM pin. I made a pull request using a (hopefully) more robust approach than lazygit (which uses pty and regexes). The pull request was quickly reviewed and merged, so no patching needed here.

Pull Request to hijack ssh askpass to jjui

Patching build script when retro-computing

I revived an iPod, for a child to autonomously choose its music/stories with an intuitive interface (but without internet access).

The strawberry music player was able to manage it correctly¹, but it recently showed a warning when plugging an iPod, because the package maintainer removed the libgpod dependency ("only relevant for hardware many years out of sale/warranty"). I can totally understand the maintainer. The beauty of NixOS is that I can revert the dependency removal:

packages =
  let
    strawberryPatched = pkgs.strawberry.overrideAttrs (finalAttrs: previousAttrs: {
      # revert https://github.com/NixOS/nixpkgs/commit/652ce1f95b1142108814536dfee721b949cec4f9
      buildInputs = [
        pkgs.libgpod
      ] ++ previousAttrs.buildInputs;
      cmakeFlags = lib.lists.remove (lib.cmakeBool "ENABLE_GPOD" false) previousAttrs.cmakeFlags;
    });
  in
  with pkgs;[
    strawberryPatched
    ...
  ];

NixOS Pull Request removing the libgpod dependency from strawberry

Patching code for fun

I am running niri as window manager. I recently felt overwhelmed by the number of open windows (I would have 3+ instances of lazygit running...). I shortly switched back to sway, but got frustrated by the window titles that I couldn't hide.

So back to niri, I decided to hack a kind of scratchpad. Moving a window to a different workspace was too hacky (I could switch to this workspace), so I instead decided to use a floating window, that would move outside of the screen.

Unfortunately, niri helpfully ensures that all floating windows stay visible. So I wrote a one-line rust patch in `src/layout/floating.rs` to lift this restriction on the y-axis.

Such patch would not be accepted upstream for good reasons, but until an official solution (like hidden workspaces #2997) is released, I can have my scratchpads.

Applying this patch to my config is exactly the same as for lazygit.

Fearless (but not easy) patching

I must admit that the config adjustments were quite a steep learning curve (the patches themselves were quite straightforward, the integration within NixOS wasn't). However this is balanced by three important facts:

  • If my patch isn't compatible with the official package, I am informed during a (failed) rebuild. Which means no broken system and an automatic "notification" when I should reconsider my patch.
  • I get (patched) new releases automatically.
  • Removing a patch that has become useless is very straightforward.

The third point particularly resonates with me, since I think that we should strive to write "code that is easy to delete".

———

Footnote ¹ regarding iPod synchronization on linux

After a lengthy investigation, I was able to find out that a `iPod_Control/Device/SysInfo` text file is required for successful iPod synchronization (lipgpod could probably be fixed, but I do not trust my C skills for that):

FirewireGuid: 000A27001XXXXXXX
ModelNumStr: MB754
In Strawberry, you can find the `ModelNumStr` under Settings > Information > 3rd screen. For the `FirewireGuid`, look at the properties of the device or the logs of `strawberry --verbose`, it reads `Device added: "Udisks2/000A27001XXXXXXX/Apple/iPod` at some point.

Comment on the issue tracker, where it helped (at least) one other user to synchronize music to an iPod

Do you have a comment or want to share some feedback?
Please contact me

Back to the index View on Gemini [?]

Unless otherwise noted, site content by Olivier, licensed under CC BY-SA 4.0.
Source code (mainly AGPL-3.0-or-later).