Nix Overrides That Expire Themselves

Nix Overrides That Expire Themselves

会自动过期的 Nix Overrides

July 30th, 2026 | Warsaw, Poland 2026年7月30日 | 波兰华沙

I recently published yesod-form-1.7.10 to Hackage. My project gets all of its dependencies from nixpkgs, and nixpkgs gradually adopts Haskell packages from Hackage. That means that there’s some time between when the package lands in nixpkgs, and when I need to use the package in my project, which is right now. 我最近向 Hackage 发布了 yesod-form-1.7.10。我的项目所有依赖项都来自 nixpkgs,而 nixpkgs 会逐步采纳 Hackage 上的 Haskell 包。这意味着从包进入 nixpkgs 到我需要在项目中使用它(也就是现在)之间,会存在一段滞后时间。

You can write an override in your flake.nix to get the newer package you need, but what happens when you eventually bump the nixpkgs version and the override is made redundant? There’s nothing by default which warns you that the override should be removed. A colleague of mine showed me this pattern which makes the override announce its own obsolescence. 你可以在 flake.nix 中编写一个 override 来获取所需的较新版本包,但当你最终升级 nixpkgs 版本,导致该 override 变得多余时会发生什么呢?默认情况下,没有任何机制会提醒你该移除这个 override。我的一位同事向我展示了一种模式,可以让 override 自动宣告其过时。

When Nix evaluates the flake, it compares package versions and warns when the version is greater than or equal to the version in your override. 当 Nix 对 flake 进行求值(evaluate)时,它会比较包版本,并在版本大于或等于你 override 中指定的版本时发出警告。

packageOverrides = prev.lib.composeExtensions prev.haskell.packageOverrides (hfinal: hprev: {
  # we require 1.7.10 for runFormPRG; the pinned nixpkgs only has
  # 1.7.9.2.
  yesod-form = let
    noOverride = prev.lib.versionAtLeast hprev.yesod-form.version "1.7.10";
  in prev.lib.warnIf noOverride ''
    yesod-form >= 1.7.10 is now in nixpkgs, the override can be removed.
  '' (if noOverride then hprev.yesod-form else prev.haskell.lib.dontCheck (hfinal.callHackageDirect {
    pkg = "yesod-form";
    ver = "1.7.10";
    sha256 = "sha256-9TqA7c2djVaLvrj/rj47LiJ7D1rLbrGhi585FvD9zRE=";
  } { }));
});

In this example, hprev.yesod-form.version is whatever the pinned nixpkgs provides. The lib.versionAtLeast function compares version strings, and lib.warnIf is the part that prints a message during evaluation when the condition is true. So, when it’s time to remove the override, you’ll see something like this: 在这个例子中,hprev.yesod-form.version 是当前锁定的 nixpkgs 所提供的版本。lib.versionAtLeast 函数用于比较版本字符串,而 lib.warnIf 则负责在条件为真时,于求值过程中打印出提示信息。因此,当需要移除该 override 时,你会看到类似这样的提示:

evaluation warning: yesod-form >= 1.7.10 is now in nixpkgs, the override can be removed.

This approach isn’t specific to package versions. You can do this anywhere you have an override where the justification for the override can be expressed conditionally. For example, we also use this to clean up overrides where a package was marked as broken in nixpkgs, and is subsequently un-marked. 这种方法并不局限于包版本。任何当你拥有一个 override,且其存在的理由可以被条件化表达时,都可以使用这种方法。例如,我们也用它来清理那些因在 nixpkgs 中被标记为“损坏”(broken)而添加的 override,当该标记随后被移除时,我们就能及时清理。

markUnbrokenWithWarning = p: nixpkgs.lib.warnIfNot p.meta.broken ''
  Package ${p.meta.name} is no longer broken. The corresponding override can be removed.
'' (markUnbroken p);

When someone eventually fixes the package upstream and the broken flag is removed, the warning tells us to delete the workaround. 当上游最终修复了该包并移除了 broken 标记后,警告信息就会提醒我们删除这个临时补丁。