如何在 flake.nix 的 inputs 中定义和引用版本变量?
不能直接在标准 Nix flakes 的 inputs 中定义一个普通变量,再让两个 URL 引用它。inputs 有静态结构限制。outputs 函数内部可以正常使用 let ... in,但其中的变量不能反过来决定输入依赖。
如果需要让三处共享一个版本值,可以在运行 Nix 之前,用模板生成静态的 flake.nix。对于这样的配置,直接维护两处分支声明通常也不难。
为什么普通 Nix 写法在这里不适用?
示例中的三处 25.11 分别用于两个输入分支和一个模块参数。前两处是依赖声明,第三处是 outputs 内部的普通 Nix 表达式,处理阶段不同。
Nix 需要先读取输入声明、解析依赖并处理锁文件,然后才能将输入传给 outputs。因此,不能用下面这种写法让输入 URL 依赖外部变量:
# 这种写法不能作为标准 flake.nix 使用。
let
release = "25.11";
in {
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-${release}";
# ...
}
把 let ... in 移到 inputs 内部,或者用 import 读取版本文件,也不能将输入声明变成任意可求值的 Nix 表达式。这是 flakes 对输入和元数据的限制,nix.dev 的说明提到了这一点。
原配置中的:
inputs.nixpkgs.follows = "nixpkgs";
可以继续保留。它使 Home Manager 的 nixpkgs 输入使用顶层的同一个输入,但不会提取版本号,也不会自动改写 Home Manager 的分支名称。follows 引用的是依赖图中的输入路径,详见 Nix flake 输入文档。
方案一:保留静态分支声明
最直接的做法是继续显式声明两个分支:
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
home-manager = {
url = "github:nix-community/home-manager/release-25.11";
inputs.nixpkgs.follows = "nixpkgs";
};
};
升级时一起修改这两个分支。Home Manager 的稳定分支应与使用的 Nixpkgs 发布分支对应,Home Manager 升级指南采用的也是这种配置方式。
第三处 release 是否应该一起修改,要先看 home.nix 如何使用它。仅凭 extraSpecialArgs 的声明,无法确定它与输入分支版本是否具有相同含义。
方案二:用模板实现版本号只维护一次
如果确认三个位置都表示同一个发布版本,可以增加一个生成步骤。
先将原配置复制为 flake.nix.in,把三处版本值替换成 @RELEASE@。两个输入 URL 变为:
nixpkgs.url = "github:nixos/nixpkgs/nixos-@RELEASE@";
home-manager = {
url = "github:nix-community/home-manager/release-@RELEASE@";
inputs.nixpkgs.follows = "nixpkgs";
};
outputs 中的声明变为:
release = "@RELEASE@";
其余配置保持原样。接着,在同一目录创建 generate-flake.py:
from pathlib import Path
release = "25.11"
placeholder = "@RELEASE@"
template = Path("flake.nix.in").read_text(encoding="utf-8")
if template.count(placeholder) != 3:
raise SystemExit("模板中应包含三处 @RELEASE@,请检查后再生成。")
Path("flake.nix").write_text(
template.replace(placeholder, release),
encoding="utf-8",
)
在该目录运行:
python3 generate-flake.py
nix flake update
版本号只需在生成脚本里维护一次。Nix 实际读取的仍然是包含静态 URL 的 flake.nix,输入限制仍然适用。
脚本会覆盖 flake.nix,后续应修改模板,再重新生成。模板、生成脚本、生成后的 flake.nix 和 flake.lock 应保持同步。生成步骤只负责替换文本,不会验证对应分支是否存在,也不会证明配置能够成功构建。
注意:不要把 home.stateVersion 自动绑定到输入版本
原问题没有提供 home.nix。如果传入的 release 最终用于:
home.stateVersion = release;
就不适合把三处版本号统一为一个会随升级变化的值。
home.stateVersion 控制与持久状态相关的兼容默认值,升级 Nixpkgs 或 Home Manager 时通常应保持原值。只有阅读相关发布说明并完成必要迁移后,才考虑调整它。具体说明见 Home Manager 的状态版本说明。
如果 release 只是供模块使用的当前发布版本标签,可以用模板统一维护;如果它承担的是 home.stateVersion 的作用,就应单独保留。
备注:内容仅供参考。
