关于 nix-flake-organization
面向 Nix flake 仓库的审查与重构 Skill,指导将 `flake/` 作为公共输出层、将 `src/` 用作实现层,并提供迁移和验证流程。
主要功能
- 01分离公共输出与实现
- 02保持输出转接层精简
- 03按功能组织代码
- 04迁移时保留输出名称
- 05验证 flake 输出与构建
适用场景
- 01重构内容拥挤的 `flake.nix`
- 02将模块逻辑移出输出配置
- 03梳理 flake 公共 API
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ darkmatter/ skills --skill nix-flake-organization安装后在 Claude Code 中输入/nix-flake-organization确认已加载。
详细介绍
Nix flake 组织结构
将 flake/ 保留为公共输出层,将 src/ 保留为实现层。flake 目录树负责命名、路由和重新导出输出;功能行为、模块主体、软件包派生项、应用脚本和辅助逻辑则放在 src/<name>/... 下。
在这些边界内应用 codebase-design:
将每项功能的实现集中在一起,仅在隐藏复杂性或实现有用复用时才进行拆分。Nix 输出适配器是有意义的公共入口;额外的内部转发文件则必须有其独立用途。保留 Nix 的职责划分,不要强行套用 TypeScript 的 models/ 和 services/ 目录。
适用场景
- 用户要求重新组织或重构 Nix flake 布局。
- flake 输出过于集中在
flake.nix中,或与实现代码混杂。 - NixOS、nix-darwin、Home Manager 或 flake-parts 模块直接在输出接线文件中包含业务逻辑。
- 仓库需要明确区分公共 flake API 和内部实现。
不适用场景
- 任务只是添加一个软件包、应用或模块,不涉及更改仓库布局。
- 仓库不是 Nix flake。
- 用户明确要求保留其他既定的项目布局。
标准提示词
将请求整理为实施计划时,使用这段改进后的提示词:
重新组织此仓库,使其公共 flake 接口位于顶层
flake/目录中:适用时包含flake/apps、flake/packages、flake/lib和flake/modules/{flake-parts,home-manager,darwin,nixos}。将flake.nix保持为精简入口,并确保flake/下的每个文件都足够精简:它可以声明输出名称、导入实现文件、组合 flake-parts 模块,以及适配公共调用约定,但不得包含派生项逻辑、应用脚本、选项/配置主体、服务行为或辅助实现。将这些实现细节集中在面向功能的src/<name>/...路径中。结构调整期间保留现有输出属性名称;更改公共接口时遵循仓库的兼容性政策。使用nix flake show、nix flake check,以及具有代表性的软件包/模块求值或构建进行验证。
目标结构
优先采用此结构,并根据现有仓库调整名称:
flake.nix
flake/default.nix
flake/apps/default.nix
flake/packages/default.nix
flake/lib/default.nix
flake/checks/default.nix # if checks exist
flake/devShells/default.nix # if devShells exist
flake/overlays/default.nix # if overlays exist
flake/modules/flake-parts/default.nix
flake/modules/home-manager/default.nix
flake/modules/darwin/default.nix
flake/modules/nixos/default.nix
src/<name>/app.nix
src/<name>/package.nix
src/<name>/lib.nix
src/<name>/modules/flake-parts.nix
src/<name>/modules/home-manager.nix
src/<name>/modules/darwin.nix
src/<name>/modules/nixos.nix
如果存在 flake-parts 模块,flake/modules/flake-parts 应与各平台模块目录并列,即使用户简略的示例中未提及它。
面向功能意味着按领域概念分组,而不是按输出类型分组。如果某个概念(如 vpn)同时包含软件包、应用和 NixOS 模块,就将它们放在 src/vpn/ 下。如果某个软件包没有关联模块,使用 src/<pkg-name>/package.nix 即可。如果仓库已经按平台组织,可以在 src/ 下保留这种结构,但不要机械地在 src/ 下重建整个 flake/ 目录树。
薄层规则
| 位置 | 允许 | 不允许 |
|---|---|---|
flake.nix | 输入、最简 outputs、导入 ./flake | 包/模块实现 |
flake/apps | App 输出名称和导入 | Shell 脚本、封装器、运行时行为 |
flake/packages | 包输出名称和导入 | mkDerivation、覆盖层、构建逻辑 |
flake/lib | 公共辅助函数重新导出 | 辅助函数实现 |
flake/checks | 检查输出名称和导入 | 测试框架实现 |
flake/devShells | 开发 Shell 输出名称和导入 | 工具设置逻辑、Shell 钩子 |
flake/overlays | 覆盖层输出名称和导入 | 包覆盖和构建逻辑 |
flake/modules/flake-parts | flake-parts 导入和模块组合 | perSystem 构建逻辑、派生项、App 脚本 |
flake/modules/* | 公共模块导出和导入 | 选项、config、断言、服务 |
src/<name>/... | 所有实现细节 | 公共输出架构决策 |
薄层不等于空文件。薄文件可以适配调用约定、传递 inputs、self 或 pkgs,并保留公共属性名称。无需阅读实现细节也应能理解它。
遵守配置的文件长度限制。如果一个内聚模块超出限制,应先寻找可独立拆分的职责;否则,记录范围明确的限制提高或例外情况。若选项、默认值、断言和配置有助于说明某项功能,即使这会让模块变长,也应将它们保留在模块内。
对于模块输出,薄层意味着 flake/modules/<platform>/default.nix 是一个重新导出点。它会从 src/ 导入完整模块,并将其接入公共输出属性。这并不意味着要在单个模块内拆分选项和配置;选项和配置应在 src/<name>/modules/<platform>.nix 中保持在一起。
# flake/modules/nixos/default.nix
{ ... }:
{
flake.nixosModules.vpn = import ../../../src/vpn/modules/nixos.nix;
}
# src/vpn/modules/nixos.nix
{ config, lib, pkgs, ... }:
{
options.services.vpn = { ... };
config = lib.mkIf config.services.vpn.enable { ... };
}
在 flake-parts 仓库中,perSystem 通常负责 packages、apps、checks 和 devShells。应保持 flake/modules/flake-parts 中的 perSystem 组合精简;包的派生项仍应放在 src/<name>/package.nix 中,App 行为放在 src/<name>/app.nix 中,开发 Shell/检查实现放在 src/<name>/... 中。不要同时通过直接的 flake/packages 接线和 flake-parts perSystem 接线定义同一个输出。
迁移工作流
- 清点当前的公共输出:
packages、apps、lib、checks、devShells、overlays、nixosModules、darwinModules、homeManagerModules,以及 flake-parts 导入项。 - 首先保留公共输出架构。移动文件时不要重命名输出属性,除非用户要求进行破坏性变更。
- 如果当前输出交错定义在同一个文件中,先将各输出系列拆分到足以独立移动的程度;避免一次性大规模重写。
- 创建
flake/目录,将其作为输出垫片,并一次移动一个输出系列。 - 将实现移至面向功能的
src/<name>/...;除非仓库本身已采用面向平台的结构,否则不要在src/下重建flake/目录树。 - 将共享实现放在
src/<name>/lib.nix或src/shared/<name>.nix,而不是flake/lib。 - 对重命名后的输出遵循仓库的兼容性策略。仅在该策略要求时保留别名;否则更新使用方并移除过时路径。
- 对于没有示例目录的输出(如
formatter),为其创建独立且精简的flake/<output>/垫片,或将其留在最近的现有组合文件中;不要把它们并入无关目录。 - 每移动一个系列后,都使用
nix flake show、nix flake check和针对性的构建/求值进行验证。
常见错误
- 将
flake.nix移入flake/;Nix 要求flake.nix位于仓库根目录。 - 把
flake/当作新的实现目录,而不是精简的公共层。 - 因为觉得
flake/lib像是共享代码的集中目录,就把实现藏在其中。 - 将选项声明、
config、断言或服务放入flake/modules/*。 - 忘记
packages.${system}和apps.${system}是系统专属的,而模块输出通常不是。 - 把
perSystem构建逻辑放进flake/modules/flake-parts,而不是从src/<name>/...导入实现。 - 在没有明确兼容性理由的情况下,为同一个派生包同时创建直接输出接线和 flake-parts
perSystem接线。 - 在
flake/lib、flake/modules和src之间创建循环导入。 - 在仅移动文件的重构中更改公共输出名称。
- 因为改动看起来只是调整路径,就跳过验证。
模板化脚本
打包 shell 脚本时,将可复用的脚本逻辑保存在独立源文件中,而不是内嵌在较长的 Nix 字符串中。仅使用 Nix 替换特定于软件包的默认值。
优先使用 nixpkgs 风格的 @name@ 占位符和 replaceVars:
writeTextFile {
name = "tool";
destination = "/bin/tool";
executable = true;
text = builtins.readFile (replaceVars ./tool.sh {
default_config = builtins.toJSON config;
});
}
在脚本中,将所有 Nix 替换项集中放在顶部,并确保运行时值可通过参数或环境变量覆盖:
default_config() {
cat <<'JSON'
@default_config@
JSON
}
: "${TOOL_JQ:=jq}"
tool_config() {
if [ -n "${TOOL_CONFIG_JSON:-}" ]; then
printf '%s\n' "$TOOL_CONFIG_JSON"
else
default_config
fi
}
这样可以让脚本在 Nix 之外也能使用,使 Nix 耦合关系易于审查,避免充斥引号的内嵌 shell 字符串,并符合当前 nixpkgs 约定;如今 substituteAll 已由 replaceVars 取代。
审查清单
flake/文件主要包含导入、属性名称、重新导出和公共调用约定适配器。src/<name>/...包含派生包、脚本、选项、配置、服务、断言和辅助工具。- 现有公共输出名称仍可使用,或已有明确的迁移说明。
- 模块系列彼此分离:
home-manager、darwin、nixos和flake-parts不会相互泄漏平台专属逻辑。 - 验证命令同时涵盖系统专属输出和模块求值路径。
- 模板化脚本保存在独立文件中,通过
replaceVars使用@name@占位符,并将 Nix 专属默认值集中放在顶部,同时为可复用行为提供环境变量/参数覆盖。
工具
无。这是一项纯提示词与审查技能。请使用仓库现有的 Nix 命令进行验证。
参考
没有单独的参考文件。请使用上面的规范提示词、精简层规则、迁移工作流和审查清单。