nixpkgs 贡献与维护:从 by-name 到 backport

nixpkgs 贡献与维护实战:仓库结构与 pkgs/by-name 自动调用约定、打包一个新包从零到 PR 的完整流程、提交规范与 commit message 格式、review 流程与维护者职责、ofBorg CI 与测试、staging 分支与 backport 机制、包版本更新与长期维护、常见拒绝原因与最佳实践清单。

引言

nixpkgs 是全球最大的包仓库之一——十万多个包、每天上千次合并。它靠的不是少数人的英雄主义,而是一套高度标准化的贡献流程:pkgs/by-name 让新包免写样板、ofBorg 自动跑 CI、staging 分支做大规模重建、backport 把修复送回稳定版。

本文是一份可操作的贡献指南:从仓库结构讲起,说清 by-name 的约定、打包一个新包的完整流程、提交规范与 review 流程、ofBorg 与 staging 的运作,以及维护者日常要做的版本更新与 backport。

前置:包管理实战、源码获取与 fetchers、派生与 store 内幕。


目录


1. nixpkgs 仓库结构与组织

1.1 顶层目录

路径内容
pkgs/所有包定义
pkgs/by-name/新式自动调用包(第 2 节)
pkgs/top-level/包集装配(all-packages.nix 等)
lib/Nix 语言库(attrsets/lists/strings)
nixos/NixOS 模块与测试
maintainers/维护者名单与脚本
doc/文档源码

1.2 两个关键文件

# pkgs/top-level/all-packages.nix:属性名 → 包定义的映射表
# maintainers/maintainer-list.nix:维护者 handle → { name, github, email }

all-packages.nix 是传统方式的入口:一个包的属性名必须在这里显式映射。by-name 就是为消除这一步而生的。

1.3 分支模型

分支用途
master开发主线,一切新特性进这里
staging会触发大规模重建的改动
staging-nextstaging 的验证与合流
nixos-XX.YY稳定发布分支(只收修复)
release-XX.YY发布准备

记忆:nixpkgs 结构的关键是 pkgs/by-name(新式自动调用)、pkgs/top-level/all-packages.nix(传统属性映射)、lib/(语言库)、nixos/(模块与测试);分支上 master 是主线、staging 做大重建、nixos-XX.YY 是稳定版只收修复。


2. pkgs/by-name 自动调用约定

2.1 目录约定

pkgs/by-name/
  he/hello/
    package.nix
  ri/ripgrep/
    package.nix

规则:pkgs/by-name/<前两个字符>/<包名>/package.nix。属性名自动推导为目录名,无需改 all-packages.nix。

2.2 为什么这样设计

传统方式by-name 方式
写 pkgs/xxx/default.nix写 pkgs/by-name/xx/xxx/package.nix
还要在 all-packages.nix 加一行自动暴露为 pkgs.xxx
新包 = 改两个文件新包 = 加一个文件

好处:消除合并冲突(all-packages.nix 是巨型文件,人人改必冲突)、降低新包门槛、属性名与目录强绑定。

2.3 package.nix 的写法

{ lib, stdenv, fetchFromGitHub, openssl }:

stdenv.mkDerivation (finalAttrs: {
  pname = "mytool";
  version = "1.2.3";

  src = fetchFromGitHub {
    owner = "example";
    repo = "mytool";
    rev = "v${finalAttrs.version}";
    hash = "sha256-AAAA...";
  };

  buildInputs = [ openssl ];

  meta = {
    description = "A tool that does something";
    homepage = "https://github.com/example/mytool";
    license = lib.licenses.mit;
    maintainers = [ lib.maintainers.yourhandle ];
    mainProgram = "mytool";
  };
})

注意用 finalAttrs 而非 rec:version 可以被 rev 引用而无需递归属性集。

记忆:by-name 的约定是 pkgs/by-name/<前两字符>/<包名>/package.nix,属性名自动推导、无需改 all-packages.nix——它消除了巨型文件的合并冲突、降低新包门槛;写 package.nix 时用 finalAttrs 而非 rec。


3. 打包一个新包:从零到 PR

3.1 完整流程

# 1) fork nixpkgs 并 clone 到本地
# 2) 建分支:git checkout -b add-mytool
# 3) 创建 pkgs/by-name/my/mytool/package.nix
# 4) 用 nix-build 或 nix build 构建验证
# 5) 运行 nixpkgs-review 或本地构建
# 6) commit(符合提交规范)
# 7) push 并开 PR

3.2 构建与取哈希

# 先用假哈希触发报错,拿到真实哈希
nix-build -A mytool 2>&1 | grep 'got:'
# 或用 nix-prefetch-url / nix store prefetch-file
nix-prefetch-url --unpack https://github.com/example/mytool/archive/v1.2.3.tar.gz

3.3 本地验证

# 直接构建该属性并运行;nixpkgs-review 可检查是否破坏别的包(见第 5 节)
nix-build -A mytool
./result/bin/mytool --version
# nix-init 可交互式生成 package.nix 骨架
nix run nixpkgs#nix-init

记忆:新包流程是「fork → 建分支 → 写 by-name/package.nix → 取哈希 → 构建验证 → 提交 → 开 PR」;取哈希靠「假哈希触发报错」或 nix-prefetch-url;nix-init 可交互式生成骨架。


4. 提交规范与 commit message

4.1 格式

<包名>: <动词> <描述>

<可选正文:为什么这么改>

<可选:Fixes #12345>

示例:

mytool: init at 1.2.3
mytool: 1.2.3 -> 1.2.4
mytool: fix build on aarch64
mytool: add missing openssl dependency

4.2 常用动词

动词场景
init at X新增包
X -> Y版本更新
fix ...修构建/运行问题
add ...增加依赖或功能
remove ...移除不再需要的东西
refactor不改变行为的结构调整

4.3 规则

# ☐ 包名在前、冒号后空格、动词开头
# ☐ 一次 PR 只做一件事(init 就别顺手升级别的包)
# ☐ 版本更新写 "old -> new" 格式
# ☐ 正文说明「为什么」,代码里看不出时才需要

记忆:nixpkgs 提交规范是 <包名>: <动词> <描述>——init at X(新增)、X -> Y(升级)、fix/add/remove;一次 PR 只做一件事、包名与动词必须规范,因为工具与 changelog 依赖这个格式。


5. review 流程与维护者职责

5.1 PR 生命周期

开 PR → ofBorg 自动测试 → 人工 review → 打标签 → 合并
         ↑ 失败则修                ↑ 需要时请求特定维护者

5.2 谁来 review

角色职责
包维护者(meta.maintainers)该包的把关人
领域 reviewer语言/生态专家
提交者本人自测 + 响应反馈

请求 review 的常用方式:在 PR 里 @ 相关维护者,或用 nixpkgs-review 结果作为证据。

5.3 维护者的日常

# ☐ 关注自己维护包的更新(有人 @ 或 CI 报错)
# ☐ 审阅自己包的 PR,给 approve 或修改意见
# ☐ 跟进上游 breaking change
# ☐ 定期检查包的构建状态(hydra 上的 red 状态)

用 nix run nixpkgs#nixpkgs-review pr 123456 可自动构建「受 PR 影响的所有包」,确认没有破坏。

记忆:PR 生命周期是「开 PR → ofBorg 自动测 → 人工 review → 合并」;review 者优先是 meta.maintainers 里登记的包维护者;维护者日常是「盯自己包的更新与 PR、跟进上游 breaking change、看 hydra 构建状态」,nixpkgs-review 用来确认改动没破坏别的包。


6. ofBorg CI 与测试

6.1 ofBorg 是什么

ofBorg 是 nixpkgs 的 CI 机器人,它会自动:

# 1) 在 PR 上评论构建结果(成功/失败/被跳过)
# 2) 检测提交格式是否规范
# 3) 检测是否影响大量包(提示走 staging)
# 4) 提供「哪些属性被构建」的摘要

6.2 常见 ofBorg 反馈

反馈含义应对
ofborg-eval 失败求值出错修表达式语法/属性
构建失败某属性构建不过看日志修
Mass-rebuild 标签影响包数超阈值需走 staging
by-name 检查目录结构不合规按约定放

6.3 测试类型

# 包内 passthru.tests:为包附上测试
{ passthru.tests.version = testers.testVersion { package = mytool; }; }

NixOS 模块类改动则用 nixos/tests/ 下的集成测试。

记忆:ofBorg 是 nixpkgs 的 CI 机器人——自动在 PR 上报告构建结果、检查提交格式、识别 mass-rebuild(提示走 staging);包内可用 passthru.tests 附测试,模块改动用 nixos/tests 集成测试。


7. staging 分支与 backport

7.1 什么时候走 staging

# 触发「大量包重建」的改动必须走 staging,例如:
# - 升级 gcc/glibc/python 等被广泛依赖的包
# - 修改 stdenv 或核心库
# 阈值由 ofBorg 计算,超过就自动打 Mass-rebuild 标签

走 staging 的流程:向 staging 分支开 PR → 通过 staging-next 验证 → 合入 master。

7.2 backport:把修复送回稳定版

# 场景:nixos-24.11 用户需要某个修复
# 做法:把 master 上的 commit cherry-pick 到 release-24.11 分支
# 结果:下个稳定点版本带上该修复

7.3 backport 的两种方式

方式操作
自动在 PR 上评论 @NixOS/nixpkgs-backport-24.11(或对应机器人)
手动直接向 release-24.11 分支开 cherry-pick PR

7.4 原则

# ☐ 只有「修复」才 backport,新功能与新包不进稳定版
# ☐ backport 的 commit 必须与 master 上一致(cherry-pick 不手改)
# ☐ 破坏性变更(需用户改配置)不 backport

记忆:触发大量重建的改动走 staging(ofBorg 自动判 Mass-rebuild);backport 是把 master 的修复 cherry-pick 回 release-XX.YY 分支——只有修复能 backport,新功能/新包/破坏性变更都不进稳定版。


8. 更新包与版本维护

8.1 一次标准的版本更新

# 1) 改 version,把 hash 换成假值
# 2) 构建,从报错里拿真实 hash
nix-build -A mytool 2>&1 | grep 'got:'
# 3) 填回 hash,再次构建
# 4) 运行 nixpkgs-review 确认无破坏
# 5) commit: "mytool: 1.2.3 -> 1.2.4"

8.2 自动化工具与常见维护任务

nix-update 可自动查上游最新版并更新 hash(nix run nixpkgs#nix-update -- --flake mytool),配合 r-ryantm 机器人可在上游发布后自动开 PR。

任务工具
升级版本nix-update
更新 hash假哈希触发或 nix-prefetch-url
修 aarch64/darwin 构建本地或远程 builder 验证
跟进上游 API 变化手改 + 测试

记忆:版本更新的标准动作是「改 version → 假哈希触发取真值 → 填回 → nixpkgs-review → commit pkg: old -> new」;nix-update 与 r-ryantm 机器人可自动化这一流程。


9. 常见拒绝原因与最佳实践

9.1 高频拒绝原因

原因说明
提交信息不规范没用 <pkg>: <动词> 格式
一次 PR 做太多事init + 升级 + 重构混在一起
没跑 nixpkgs-review无法证明没破坏别的包
用 rec 而非 finalAttrs新包应遵循现代写法
没登记 maintainers无人接手维护
用了不该用的 fetch 方式应优先 fetchFromGitHub 等

9.2 最佳实践清单

# ☐ 新包放 pkgs/by-name/xx/name/package.nix
# ☐ 用 finalAttrs 模式,hash 用 SRI 格式(sha256-...)
# ☐ meta 里写全 description/homepage/license/maintainers/mainProgram
# ☐ 一次 PR 一件事,commit message 严格规范
# ☐ 本地跑 nixpkgs-review,附上结果
# ☐ 大改动先问 maintainers 或走 staging

9.3 参与方式

进阶路径是:从「给现有包加 maintainers」「修小 bug」入门,再到「升级版本」「新增包」,最后是「staging 大规模改动」「模块与测试」。

记忆:nixpkgs 拒绝的常见原因是「提交信息不规范、一次做太多事、没跑 nixpkgs-review、没用 finalAttrs、没登记 maintainers」;最佳实践是 by-name + finalAttrs + SRI hash + 完整 meta + 一次一事 + 附 review 结果,从修小 bug 逐步进阶。


10. 速查表与一句话记忆

需求命令 / 约定一句话
新包位置pkgs/by-name/xx/name/package.nix免改 all-packages
写法finalAttrs 模式别用 rec
取哈希假哈希触发 / nix-prefetch-url拿到 got 值
提交pkg: old -> new格式严格
自测nixpkgs-review pr <n>证明没破坏
大改动走 stagingofBorg 判 Mass-rebuild
修复回稳定版backport / cherry-pick只 backport 修复
自动升级nix-update配合 r-ryantm

一句话记忆:nixpkgs 贡献的核心是标准化流程——新包放 pkgs/by-name/<前两字符>/<名>/package.nix(自动推导属性名,免改 all-packages.nix),用 finalAttrs 模式与 SRI 哈希写 package.nix;提交严格遵循 <包名>: <动词> <描述>(init at X / X -> Y / fix),一次 PR 只做一件事并附 nixpkgs-review 结果;ofBorg 自动跑 CI 并在触发大量重建时打 Mass-rebuild 标签(这类改动走 staging 分支);backport 是把 master 的修复 cherry-pick 回 release-XX.YY,只有修复能进稳定版;日常维护用 nix-update 与 r-ryantm 机器人升级版本;meta 里写全 description/homepage/license/maintainers/mainProgram 是「可维护」的门槛——从修小 bug 到 staging 大改,是一条清晰的进阶路径。


延伸阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「nix」更多文章

  1. Nix 中的 CUDA 与机器学习环境:cudaPackages 与 PyTorch
  2. nix-darwin:macOS 的声明式系统配置
  3. Nix 远程构建与分布式构建:builders 协议、ssh-ng 与跨架构