nix-flake-organization

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Nix flake organization

Nix flake 组织规范

Keep
flake/
as the public output layer and
src/
as the implementation layer. The flake tree names, routes, and re-exports outputs; feature behavior, module bodies, package derivations, app scripts, and helper logic live under
src/<name>/...
.
Apply codebase-design within those boundaries: keep each feature's implementation together and extract only to hide complexity or enable useful reuse. Nix output adapters are meaningful public entrypoints; additional internal forwarding files need their own purpose. Preserve Nix's roles rather than imposing TypeScript
models/
and
services/
directories.
将
flake/
作为公共输出层,
src/
作为实现层。flake目录树负责输出的命名、路由和重导出;功能行为、模块主体、package derivation、应用脚本和辅助逻辑都存放在
src/<name>/...
目录下。
在上述边界内应用代码库设计规范:将每个功能的实现代码放在一起,仅在需要隐藏复杂度或实现有效复用的时候才进行抽取。Nix输出适配器是有意义的公共入口;额外的内部转发文件需要有明确的用途。保留Nix的角色定位,不要强行套用TypeScript的
models/
和
services/
目录结构。

When to use

适用场景

  • A user asks to reorganize or refactor a Nix flake layout.
  • Flake outputs are crowded into
    flake.nix
    or mixed with implementation code.
  • NixOS, nix-darwin, Home Manager, or flake-parts modules contain business logic directly in output wiring files.
  • A repo needs a clear split between public flake API and implementation internals.
  • 用户要求重组或重构Nix flake的布局。
  • Flake输出都挤在
    flake.nix
    中,或者与实现代码混在一起。
  • NixOS、nix-darwin、Home Manager或flake-parts模块的业务逻辑直接写在输出编排文件中。
  • 代码仓库需要清晰划分公共flake API与内部实现。

When NOT to use

不适用场景

  • The task is only to add one package, app, or module without changing repo layout.
  • The repo is not a Nix flake.
  • The user explicitly wants a different established project layout preserved.
  • 任务只是添加一个包、应用或模块,不需要改变仓库布局。
  • 该仓库不是Nix flake项目。
  • 用户明确要求保留现有的其他项目布局。

Canonical prompt

标准提示词

Use this improved prompt when turning the request into an implementation plan:
Reorganize this repo so its public flake surface lives in a top-level
flake/
directory:
flake/apps
,
flake/packages
,
flake/lib
, and
flake/modules/{flake-parts,home-manager,darwin,nixos}
where applicable. Keep
flake.nix
as a minimal entrypoint and keep every file under
flake/
thin: it may declare output names, import implementation files, compose flake-parts modules, and adapt public calling conventions, but it must not contain derivation logic, app scripts, option/config bodies, service behavior, or helper implementations. Keep those implementation details together in feature-oriented
src/<name>/...
paths. Preserve existing output attribute names during structural moves; follow the repository's compatibility policy when changing the public interface. Verify with
nix flake show
,
nix flake check
, and representative package/module evals or builds.
将需求转化为实现方案时,使用以下优化后的提示词:
重组该仓库,使其公共flake表层位于顶层
flake/
目录下:包括
flake/apps
、
flake/packages
、
flake/lib
,以及适用情况下的
flake/modules/{flake-parts,home-manager,darwin,nixos}
。保持
flake.nix
作为最小入口点,并且
flake/
下的所有文件都保持薄型:这些文件可以声明输出名称、导入实现文件、组合flake-parts模块、适配公共调用约定,但不得包含derivation逻辑、应用脚本、option/config主体、服务行为或辅助工具实现。将这些实现细节统一放在面向功能的
src/<name>/...
路径下。结构移动过程中保留现有的输出属性名称;更改公共接口时遵循仓库的兼容性策略。使用
nix flake show
、
nix flake check
以及有代表性的包/模块求值或构建进行验证。

Target shape

目标结构

Prefer this structure, adapting names to the existing repo:
text
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/modules/flake-parts
belongs beside the platform module families when flake-parts modules exist, even if the user's shorthand example omits it.
Feature-oriented means grouping by domain concept, not by output type. If a concept
vpn
has a package, app, and NixOS module, keep them under
src/vpn/
. If a package has no associated modules,
src/<pkg-name>/package.nix
is fine. When a repo is already organized by platform, preserving that structure under
src/
is acceptable, but do not mechanically recreate the whole
flake/
tree under
src/
.
优先采用以下结构,可根据现有仓库调整名称:
text
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/
目录树。

Thin layer rule

薄层规则

LocationAllowedNot allowed
flake.nix
Inputs, minimal
outputs
, import
./flake
Package/module implementation
flake/apps
App output names and importsShell scripts, wrappers, runtime behavior
flake/packages
Package output names and imports
mkDerivation
, overlays, build logic
flake/lib
Public helper re-exportsHelper implementations
flake/checks
Check output names and importsTest harness implementation
flake/devShells
Dev shell output names and importsTool setup logic, shell hooks
flake/overlays
Overlay output names and importsPackage overrides and build logic
flake/modules/flake-parts
flake-parts imports and module composition
perSystem
build logic, derivations, app scripts
flake/modules/*
Public module exports and importsOptions,
config
, assertions, services
src/<name>/...
All implementation detailsPublic output schema decisions
Thin does not mean empty. A thin file can adapt calling conventions, pass
inputs
,
self
, or
pkgs
, and preserve public attribute names. It should be understandable without reading implementation details.
Keep configured file limits. If a cohesive module exceeds one, first look for an independent responsibility; otherwise document a narrow increase or exception. Keep options, defaults, assertions, and configuration local when they explain one feature, even when that makes a module longer.
For module outputs, thin means the
flake/modules/<platform>/default.nix
file is a re-export point. It imports the full module from
src/
and wires it into the public output attribute. It does not mean splitting options from config inside a single module; options and config stay together in
src/<name>/modules/<platform>.nix
.
nix
undefined
位置允许的内容不允许的内容
flake.nix
输入、最小化的
outputs
、导入
./flake
包/模块实现
flake/apps
应用输出名称和导入Shell脚本、包装器、运行时行为
flake/packages
包输出名称和导入
mkDerivation
、overlays、构建逻辑
flake/lib
公共辅助函数重导出辅助函数实现
flake/checks
检查输出名称和导入测试框架实现
flake/devShells
开发环境输出名称和导入工具配置逻辑、shell钩子
flake/overlays
Overlay输出名称和导入包覆盖和构建逻辑
flake/modules/flake-parts
flake-parts导入和模块组合
perSystem
构建逻辑、derivations、应用脚本
flake/modules/*
公共模块导出和导入Options、
config
、断言、服务
src/<name>/...
所有实现细节公共输出架构决策
薄并不意味着空。薄文件可以适配调用约定、传递
inputs
、
self
或
pkgs
,并保留公共属性名称。无需阅读实现细节就能理解薄文件的内容。
遵守配置的文件大小限制。如果一个内聚模块超过了限制,首先寻找是否有可以独立的职责;否则记录下小范围的放宽或例外情况。当options、默认值、断言和配置仅用于解释单个功能时,将它们放在局部,即使这会让模块变长。
对于模块输出,薄意味着
flake/modules/<platform>/default.nix
文件是一个重导出点。它从
src/
导入完整的模块,并将其接入公共输出属性。这并不意味着在单个模块中将options与config拆分;options和config要一起放在
src/<name>/modules/<platform>.nix
中。
nix
undefined

flake/modules/nixos/default.nix

flake/modules/nixos/default.nix

{ ... }: { flake.nixosModules.vpn = import ../../../src/vpn/modules/nixos.nix; }
{ ... }: { flake.nixosModules.vpn = import ../../../src/vpn/modules/nixos.nix; }

src/vpn/modules/nixos.nix

src/vpn/modules/nixos.nix

{ config, lib, pkgs, ... }: { options.services.vpn = { ... }; config = lib.mkIf config.services.vpn.enable { ... }; }

In flake-parts repos, `perSystem` often owns `packages`, `apps`, `checks`, and `devShells`. Keep `perSystem` composition thin in `flake/modules/flake-parts`; package derivations still belong in `src/<name>/package.nix`, app behavior in `src/<name>/app.nix`, and dev shell/check implementation in `src/<name>/...`. Do not define the same output through both direct `flake/packages` wiring and flake-parts `perSystem` wiring.
{ 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`的组合层很薄;包derivation仍然属于`src/<name>/package.nix`,应用行为属于`src/<name>/app.nix`,开发环境/检查的实现属于`src/<name>/...`。不要同时通过直接的`flake/packages`接线和flake-parts的`perSystem`接线来定义同一个输出。

Migration workflow

迁移工作流

  1. Inventory current public outputs:
    packages
    ,
    apps
    ,
    lib
    ,
    checks
    ,
    devShells
    ,
    overlays
    ,
    nixosModules
    ,
    darwinModules
    ,
    homeManagerModules
    , and flake-parts imports.
  2. Preserve the public output schema first. Move files without renaming output attributes unless the user requested a breaking change.
  3. If current outputs are interleaved in one file, first separate each output family enough that it can move independently; avoid a big-bang rewrite.
  4. Create the
    flake/
    directories as output shims and move one output family at a time.
  5. Move implementation into feature-oriented
    src/<name>/...
    ; avoid recreating the
    flake/
    tree under
    src/
    unless the repo is already platform-oriented.
  6. Keep shared implementation in
    src/<name>/lib.nix
    or
    src/shared/<name>.nix
    , not in
    flake/lib
    .
  7. Follow the repository's compatibility policy for renamed outputs. Keep aliases only when that policy requires them; otherwise update consumers and remove obsolete paths.
  8. Give outputs without an example directory, such as
    formatter
    , their own thin
    flake/<output>/
    shim or leave them in the nearest existing composition file; do not fold them into unrelated directories.
  9. Verify after each family move with
    nix flake show
    ,
    nix flake check
    , and targeted builds/evals.
  1. 盘点当前的公共输出:
    packages
    、
    apps
    、
    lib
    、
    checks
    、
    devShells
    、
    overlays
    、
    nixosModules
    、
    darwinModules
    、
    homeManagerModules
    以及flake-parts导入。
  2. 优先保留公共输出架构。移动文件时不要重命名输出属性,除非用户要求进行破坏性变更。
  3. 如果当前的输出都混在一个文件中,首先将每个输出系列分离到可以独立移动的程度;避免一次性大重构。
  4. 创建
    flake/
    目录作为输出垫片,每次移动一个输出系列。
  5. 将实现代码移动到面向功能的
    src/<name>/...
    中;避免在
    src/
    下重建
    flake/
    目录树,除非仓库本身就是按平台组织的。
  6. 将共享实现代码放在
    src/<name>/lib.nix
    或
    src/shared/<name>.nix
    中,不要放在
    flake/lib
    里。
  7. 对于重命名的输出,遵循仓库的兼容性策略。仅当策略要求时才保留别名;否则更新使用方并移除过时路径。
  8. 对于没有对应示例目录的输出(例如
    formatter
    ),为它们创建单独的薄型
    flake/<output>/
    垫片,或者放在最近的现有组合文件中;不要把它们塞进不相关的目录里。
  9. 每次移动一个输出系列后,使用
    nix flake show
    、
    nix flake check
    以及针对性的构建/求值进行验证。

Common mistakes

常见错误

  • Moving
    flake.nix
    into
    flake/
    ; Nix expects
    flake.nix
    at the repo root.
  • Treating
    flake/
    as the new implementation home instead of a thin public layer.
  • Hiding implementation in
    flake/lib
    because it feels like a shared bucket.
  • Putting option declarations,
    config
    , assertions, or services in
    flake/modules/*
    .
  • Forgetting that
    packages.${system}
    and
    apps.${system}
    are system-specific while module outputs usually are not.
  • Putting
    perSystem
    build logic in
    flake/modules/flake-parts
    instead of importing implementations from
    src/<name>/...
    .
  • Creating both direct output wiring and flake-parts
    perSystem
    wiring for the same derivation without a deliberate compatibility reason.
  • Creating circular imports between
    flake/lib
    ,
    flake/modules
    , and
    src
    .
  • Changing public output names during a move-only refactor.
  • Skipping verification because the change looks like only path shuffling.
  • 将
    flake.nix
    移到
    flake/
    目录下;Nix要求
    flake.nix
    位于仓库根目录。
  • 将
    flake/
    当作新的实现存放目录,而不是薄的公共层。
  • 因为觉得
    flake/lib
    像个共享桶,就把实现代码藏在里面。
  • 将option声明、
    config
    、断言或服务放在
    flake/modules/*
    中。
  • 忘记
    packages.${system}
    和
    apps.${system}
    是系统相关的,而模块输出通常不是。
  • 将
    perSystem
    构建逻辑放在
    flake/modules/flake-parts
    中,而不是从
    src/<name>/...
    导入实现。
  • 没有明确的兼容性理由,就为同一个derivation同时创建直接输出接线和flake-parts的
    perSystem
    接线。
  • 在
    flake/lib
    、
    flake/modules
    和
    src
    之间造成循环导入。
  • 在仅移动的重构中更改公共输出名称。
  • 因为改动看起来只是路径调整,就跳过验证。

Templated scripts

模板化脚本

When packaging shell scripts, keep reusable script logic in its own source file instead of embedding long scripts inline in Nix strings. Use Nix only to substitute package-specific defaults.
Prefer nixpkgs-style
@name@
placeholders with
replaceVars
:
nix
writeTextFile {
  name = "tool";
  destination = "/bin/tool";
  executable = true;
  text = builtins.readFile (replaceVars ./tool.sh {
    default_config = builtins.toJSON config;
  });
}
In the script, group all Nix substitutions at the top and make runtime values overrideable by arguments or environment variables:
sh
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
}
This keeps scripts usable outside Nix, makes the Nix coupling easy to audit, avoids quote-heavy inline shell strings, and matches current nixpkgs conventions now that
substituteAll
has been replaced by
replaceVars
.
打包Shell脚本时,将可复用的脚本逻辑放在单独的源文件中,不要把长脚本内联在Nix字符串里。仅使用Nix来替换包特定的默认值。
推荐使用nixpkgs风格的
@name@
占位符搭配
replaceVars
:
nix
writeTextFile {
  name = "tool";
  destination = "/bin/tool";
  executable = true;
  text = builtins.readFile (replaceVars ./tool.sh {
    default_config = builtins.toJSON config;
  });
}
在脚本中,将所有Nix替换内容集中放在顶部,并让运行时值可以通过参数或环境变量覆盖:
sh
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
取代了。

Review checklist

审查清单

  • flake/
    files are mostly imports, attr names, re-exports, and public calling-convention adapters.
  • src/<name>/...
    contains derivations, scripts, options, config, services, assertions, and helpers.
  • Existing public output names still work or have deliberate migration notes.
  • Module families are separated:
    home-manager
    ,
    darwin
    ,
    nixos
    , and
    flake-parts
    do not leak platform-specific logic into each other.
  • Verification commands cover both system-specific outputs and module evaluation paths.
  • Templated scripts live in standalone files, use
    @name@
    placeholders via
    replaceVars
    , and keep Nix-specific defaults grouped at the top with environment/argument overrides for reusable behavior.
  • flake/
    下的文件主要包含导入、属性名、重导出和公共调用约定适配器。
  • src/<name>/...
    包含derivations、脚本、options、config、服务、断言和辅助函数。
  • 现有的公共输出名称仍然可用,或者有明确的迁移说明。
  • 模块系列相互分离:
    home-manager
    、
    darwin
    、
    nixos
    和
    flake-parts
    之间不会互相泄漏平台特定的逻辑。
  • 验证命令同时覆盖系统相关输出和模块求值路径。
  • 模板化脚本存放在独立文件中,通过
    replaceVars
    使用
    @name@
    占位符,并将Nix特定的默认值集中放在顶部,同时支持通过环境变量/参数覆盖以实现可复用行为。

Tools

工具

None. This is a pure prompt and review skill. Use the repo's existing Nix commands for verification.
无。这是纯提示词和审查技能。使用仓库现有的Nix命令进行验证。

Reference

参考

No separate reference files. Use the canonical prompt, thin layer rule, migration workflow, and review checklist above.
无单独的参考文件。使用上文的标准提示词、薄层规则、迁移工作流和审查清单即可。