mcpField
C
chezmoi

chezmoi

github.com/paulnsorensen开发158 次安装1 天前更新
代码仓库

关于 chezmoi

面向使用 chezmoi 管理多台机器配置文件的开发者,提供从初始化、添加文件到安全应用变更的操作指导。内容重点包括 Go 模板、加密、文件属性和应用前检查。

主要功能

  1. 01跨机器管理配置文件
  2. 02使用 Go 模板按环境生成文件
  3. 03通过属性控制文件行为与权限
  4. 04加密存储敏感文件
  5. 05运行一次性或变更触发脚本
  6. 06应用前检查差异与执行计划

适用场景

  1. 01在新机器上初始化配置文件
  2. 02按操作系统或主机定制配置
  3. 03安全纳入令牌等秘密
  4. 04排查配置应用造成的意外更改

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/paulnsorensen/skillz-that-grillz --skill chezmoi

安装后在 Claude Code 中输入/chezmoi确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
bootstrap.md
pitfalls.md
scripts.md
secrets.md
templating.md

详细介绍

chezmoi

封装 chezmoi — 使用源状态语义、Go 模板和静态加密,在多台机器间管理 dotfiles。

适用场景

  • 在新机器上初始化 dotfiles
  • 向现有的 chezmoi 管理的 dotfiles 添加新文件
  • 按操作系统、主机或角色(工作与个人)为 dotfile 创建模板
  • 通过 1Password / Bitwarden / age / gpg 添加密钥
  • 编写 run_once_ 安装脚本
  • 排查“chezmoi apply 改了我没想到会改的内容”
  • 为一组机器设计 .chezmoi.toml.tmpl 配置模板

基本概念

chezmoi 将源目录(~/.local/share/chezmoi,一个 git 仓库)映射到 目标目录(用户的主目录)。源文件名包含决定目标名称和行为的属性前缀。带有 .tmpl 后缀的源文件会作为 Go text/template 进行渲染,并写入对应的目标路径。.chezmoitemplates/ 下的文件是可供其他模板包含的可复用模板片段,不会自行生成目标文件。位于 ~/.config/chezmoi/chezmoi.toml 的配置文件(通常在首次 init 时由 .chezmoi.toml.tmpl 生成)保存每台机器各自的变量。

人们遇到问题通常有两个原因:

  1. 他们直接编辑 ~/.local/share/chezmoi/ 下的文件,而不使用 chezmoi edit $TARGET。这会破坏模板和解密往返过程。
  2. 他们因为“仓库是私有的”就以明文提交密钥。仓库会泄露,fork 会泄露,历史记录会永久留存。请使用密钥后端。

配置文件的位置

有两个容易混淆的文件。两者都可以使用 toml、yaml、json 或 jsonc 格式(根据扩展名自动检测;同时存在两种格式会报错,而不是“优先使用第一个”):

文件路径创建者
chezmoi.<fmt>(配置)$XDG_CONFIG_HOME/chezmoi/chezmoi.<fmt>,默认为 ~/.config/chezmoi/chezmoi.<fmt>(最常见的是 .toml)。不在源仓库中。由 chezmoi init(或 apply --init)根据下方模板生成。按机器区分。保存密钥、API 令牌、机器专属设置 — 永远不要提交。
.chezmoi.<fmt>.tmpl(配置模板)源状态根目录:~/.local/share/chezmoi/.chezmoi.<fmt>.tmpl(最常见的是 .toml)。提交到 dotfiles 仓库。由你编写。使用 promptBoolOnce / promptChoiceOnce / promptStringOnce,以便新机器在首次 init 时生成正确的配置。

使用 --config <path> 覆盖位置(如果扩展名不常见,请使用 --config-format <fmt>)。通过 chezmoi cat-config 和 chezmoi dump-config 查看生效的配置值。

协议

1. 确认用户的需求
用户说转到
“从头设置 chezmoi” / “新机器”§2 初始化
“把这个文件添加到我的 dotfiles”§3 添加文件
“让这个 dotfile 根据操作系统 / 主机变化”references/templating.md
“加密这个” / “存储这个令牌”references/secrets.md
“首次 apply 时运行脚本”references/scripts.md
“chezmoi 做了奇怪的事”references/pitfalls.md
“设计我的完整配置”references/bootstrap.md
2. 初始化(新机器)

一行命令即可完成克隆、渲染模板和应用:

sh -c "$(curl -fsLS get.chezmoi.io)" -- init --apply $GITHUB_USERNAME

有关完整的 .chezmoi.toml.tmpl 配置方案,其中使用 promptBoolOnce 区分工作、个人和无头环境,请参阅 references/bootstrap.md。

3. 添加文件
chezmoi add ~/.zshrc                  # plain copy
chezmoi add --template ~/.gitconfig   # add as a template (.tmpl suffix added)
chezmoi add --encrypt ~/.ssh/config   # encrypted at rest
chezmoi chattr +template ~/.zshrc     # turn an existing managed file into a template
chezmoi edit ~/.zshrc                 # edit the SOURCE — handles decrypt + .tmpl correctly

然后执行安全应用流程(§5)。

4. 文件命名参考

这是用户最容易弄错的表格。前缀必须按特定顺序排列,且允许使用的前缀集合取决于目标类型—— /reference/source-state-attributes/ 是权威来源。

前缀对目标的影响
dot_在开头添加 .(dot_zshrc → ~/.zshrc)
private_移除组和其他用户的权限(模式 0600 / 0700)
readonly_移除所有写权限位
executable_添加可执行权限位
empty_允许空文件(默认情况下会将其移除)
encrypted_写入目标前,使用 age/gpg 解密源文件
symlink_创建符号链接而不是常规文件(文件形式,目标是符号链接目标)
exact_(目录)删除目标目录中源文件里不存在的内容
create_仅在目标不存在时创建——绝不覆盖
modify_将源文件视为生成目标内容的脚本(或模板,见下文)
run_将源文件视为要执行的脚本(而非目标文件)
before_ / after_为脚本相对于应用文件的执行排序
once_(脚本)仅在此前未成功运行过该内容时运行
onchange_(脚本)仅当此文件名对应的内容发生变化时运行
remove_如果目标存在,则将其移除
external_(目录)将目录标记为外部目录——与 .chezmoiexternal.<fmt> / .chezmoiexternals/ 配套使用;chezmoi 会忽略子条目的属性
literal_(任意位置)从此处起停止解析后续属性

各目标类型允许的前缀顺序(顺序固定;调整顺序会导致解析错误):

目标类型允许的前缀(按顺序)可选后缀
目录remove_、external_、exact_、private_、readonly_、dot_—
常规文件encrypted_、private_、readonly_、empty_、executable_、dot_.tmpl
创建文件create_、encrypted_、private_、readonly_、empty_、executable_、dot_.tmpl
修改文件modify_、encrypted_、private_、readonly_、executable_、dot_.tmpl
移除文件remove_、dot_—
脚本run_、once_ 或 onchange_、before_ 或 after_.tmpl
符号链接symlink_、dot_.tmpl

后缀 .tmpl 表示该文件是模板。配置了相应加密方式后,.age / .asc 会自动移除。

修改文件详解。 modify_ 源文件默认是脚本——它从标准输入读取当前目标,并将新目标写入标准输出。如果文件包含字符串 chezmoi:modify-template,则会移除包含该标记的行,并将其余内容视为模板;现有目标可通过 .chezmoi.stdin 访问。需要精细修改结构化文件(JSON、TOML、YAML)时,请使用模板形式;需要使用真正的解释器(jq、awk、sed)时,请使用脚本形式。

5. 安全应用流程

应用任何会触及重要文件的更改前,请先运行检查三件套:

chezmoi status              # one line per changed file
chezmoi diff                # unified diff of pending changes
chezmoi apply --dry-run -v  # full plan, including scripts that would run
chezmoi apply               # only after the above looks right

在你信任的机器上进行常规拉取时:

chezmoi update                                          # = git pull --autostash --rebase + apply
chezmoi git pull -- --autostash --rebase && chezmoi diff # preview only — no apply
6. 安全编辑
chezmoi edit ~/.zshrc       # opens source in $EDITOR, handles .tmpl + encryption
chezmoi cd                  # subshell in the source directory (for git operations)
chezmoi re-add              # refresh source from current target after editing target
chezmoi merge ~/.zshrc      # 3-way merge target ↔ source ↔ destination
chezmoi merge-all           # 3-way merge every file whose actual ≠ target (add --init to re-render config first)

编辑器的选择顺序:edit.command → $VISUAL → $EDITOR → vi(Windows 上为 notepad.exe)。如果编辑器的运行时间短于 edit.minDuration(默认值为 1s),chezmoi 会打印警告——这可以捕捉到“我打开了文件,但实际上并未编辑”的情况。设为 0 可关闭警告。

切勿直接在编辑器中打开 ~/.local/share/chezmoi/encrypted_dot_ssh/... 或 *.tmpl 文件——你看到的文件并不是 chezmoi 看到的文件。

7. 应用顺序(心智模型)

chezmoi apply 是确定性的。每次运行都会:

  1. 读取源状态,读取目标状态,并计算目标状态。
  2. 按目标名称的 ASCII 顺序运行 run_before_ 脚本。
  3. 按目标名称的 ASCII 顺序更新条目。目录(包括由 externals 实体化的目录)会先于其包含的文件写入。externals 会在此阶段获取并展开。
  4. 按 ASCII 顺序运行 run_after_ 脚本。

影响:

  • run_before_ 脚本不得读取由 external 提供的文件——externals 会在第 3 阶段应用,而不是之前。
  • 比较目标名称时,会去掉所有属性前缀。对于 create_alpha 和 modify_dot_beta,.beta 会先于 alpha 更新,因为在 ASCII 顺序中 .beta < alpha。
  • chezmoi 假定源和目标在运行期间不会发生变化。向其中任一处写入的 run_before_ 脚本都违反了这一假设——行为未定义。
8. 部分文件管理

当 chezmoi 不应管理整个文件时,根据文件的归属方选择一种模式:

modify_——用户拥有文件,chezmoi 写入一个区块。 源会从标准输入读取现有目标,并将新目标写入标准输出。

脚本形式:modify_dot_zshrc:

#!/usr/bin/env bash
set -euo pipefail
# Strip any prior chezmoi block from the existing file …
awk '/# chezmoi:begin/{s=1;next} /# chezmoi:end/{s=0;next} !s'
# … then append the current managed block.
cat <<'EOF'

# chezmoi:begin
export EDITOR=nvim
alias g=git
# chezmoi:end
EOF

模板形式(用于结构化文件):modify_dot_config_private_app.yaml.tmpl:

{{- /* chezmoi:modify-template */ -}}
{{- $cfg := dict -}}
{{- if .chezmoi.stdin -}}{{- $cfg = .chezmoi.stdin | fromYaml -}}{{- end -}}
{{- $_ := set $cfg "telemetry" false -}}
{{- $_ := set $cfg "user" (dict "email" .email) -}}
{{ toYaml $cfg }}

if .chezmoi.stdin 条件用于处理首次应用时目标尚不存在的情况。使用 fromJsonc / fromToml / fromYaml + toPrettyJson / toToml / toYaml 完成往返转换。

.chezmoitemplates/——chezmoi 拥有文件,片段可共享。 可复用区块存放在 .chezmoitemplates/ 下,通过 {{ template "name" . }} 引入常规模板(如果需要将结果通过管道传给函数,则使用 includeTemplate "name" .):

.chezmoitemplates/zsh-prompt          # one file
dot_zshrc.tmpl                        # {{ template "zsh-prompt" . }}
dot_config/fish/config.fish.tmpl      # {{ template "zsh-prompt" . }} too

根据归属方选择模式。 用户可以在托管区块之外自由编辑文件时,使用 modify_。chezmoi 渲染整个文件但你希望复用共享部分时,使用 .chezmoitemplates/。在同一个目标中混用两种模式通常不是好做法。

硬性规则

这些规则不可妥协,每一条都对应一个真实的隐患:

  1. 绝不提交明文密钥,即使是私有仓库也不行。仓库可能会泄露、被派生、被记录日志或被备份。从 references/secrets.md 中选择一种后端。
  2. 绝不直接编辑加密或模板化的源文件。 使用 chezmoi edit $TARGET,以确保完成解密和模板往返转换。
  3. 在应用到共享或重要文件之前,务必检查——使用 chezmoi diff 或 chezmoi apply --dry-run -v。目录上的 exact_ 会删除非托管条目;应提前确认这一点。
  4. prompt* 模板函数只能用于配置文件模板(.chezmoi.toml.tmpl)。在常规点文件模板中使用它们,会导致每次 apply / diff / status 都弹出提示,从而无法自动化。

密钥决策树(摘要)

完整详情请参阅 references/secrets.md。每个仓库只选择一种。

  • 公共仓库 + 云端密码管理器 → 1Password CLI / Bitwarden 模板函数。密钥在应用时获取,不会写入磁盘。
  • 私有仓库 + 支持离线使用 → age(encrypted_ 前缀)。身份文件位于 ~/.config/chezmoi/key.txt,存放在仓库之外。
  • 团队共享 → 使用 SOPS 加密的 .chezmoidata/secrets.yaml。
  • 隔离网络 / 首次引导 → 被 gitignore 忽略的明文 .chezmoidata/local.yaml(过渡方案,不适合作为长期方案)。

混用后端只会增加需要维护的部分,并不会让任何内容更安全。

模板快速参考

完整详情请参阅 references/templating.md。

{{- if eq .chezmoi.os "darwin" }}
export HOMEBREW_PREFIX="/opt/homebrew"
{{- else if eq .chezmoi.os "linux" }}
export PATH="/usr/local/bin:$PATH"
{{- end }}

{{- if and (eq .chezmoi.os "darwin") (lookPath "brew") }}
# brew is installed
{{- end }}

email = {{ .email | quote }}

内置模板变量包括 .chezmoi.os、.chezmoi.arch、 .chezmoi.hostname、.chezmoi.username、.chezmoi.homeDir、 .chezmoi.sourceDir。用户自定义数据存放在 chezmoi.toml 的 [data] 下,或任意 .chezmoidata/*.{toml,yaml,json} 文件中。

chezmoi 使用 Go 的 missingkey=error 选项运行模板,因此像 {{ .gitub.user }} 这样的拼写错误会导致应用失败,而不是渲染出空值。 只有确有充分理由时才通过 [template] options = ["missingkey=zero"] 覆盖此设置——失去拼写错误保护通常得不偿失。

对于符号链接目标,会移除渲染结果开头和结尾的空白;如果结果为空,则会移除符号链接。对于文件目标,空渲染结果会移除文件(使用 empty_ 前缀可保留文件)。

特殊文件和目录

源目录中所有以 . 开头的条目都会被忽略,以下特殊项除外。每次操作时都会按以下顺序处理: .chezmoiroot → .chezmoi.<fmt>.tmpl → 数据文件 → .chezmoitemplates/ → .chezmoiignore → .chezmoiremove → 外部资源 → .chezmoiversion。

路径作用
.chezmoiroot单行文件,指向作为源根目录的子目录
.chezmoi.<fmt>.tmpl配置文件模板,用于首次执行 chezmoi init 时(以及使用 --init 时)
.chezmoidata.<fmt> / .chezmoidata/在渲染任何模板之前,合并到模板 . 命名空间中的数据
.chezmoitemplates/可复用的模板片段({{ template "name" . }})——不是目标文件
.chezmoiscripts/不与目标文件绑定的脚本;遵循 run_before_ / run_after_
.chezmoiignore按目标设置的忽略列表——相对于源目录,可使用模板
.chezmoiremove应用时要移除的目标——可使用模板
.chezmoiexternal.<fmt> / .chezmoiexternals/应用时从 URL 获取文件或归档
.chezmoiversion此源代码仓库所需的 chezmoi 最低版本

.chezmoiignore 匹配的是相对于源目录的路径(例如 dot_zshrc),而不是 目标路径。很多人总会弄错这一点。

<fmt> 表示 toml、yaml、json 或 jsonc。目录形式 (.chezmoidata/、.chezmoiexternals/)会按字典顺序合并其内容,并与同前缀的文件形式合并——这对于将大型配置拆分到多个文件中,或按角色组合外部资源很有用。

不要做的事

  • 编写与之竞争的点文件管理器,或为 chezmoi apply 编写封装层
  • 当 ~/.local/share/chezmoi/ 下的文件是模板或已加密时,直接编辑这些文件
  • 将 .env / secrets.yaml 提交到“私有”仓库
  • 在共享机器上未先运行 chezmoi diff 就建议执行 chezmoi apply
  • 在 .chezmoi.toml.tmpl 之外使用 prompt* 模板函数
  • 未给出明确的书面理由,就在同一仓库中混用多个密钥后端(age + 1Password + SOPS)

注意事项

  • 运行模板脚本时出现 fork/exec ...: exec format error,表示 #! 前有换行符。在第一行模板的结束 }} 内添加 -,即可抑制该换行符。
  • 脚本出现 fork/exec ...: permission denied,表示 $TMPDIR 挂载时启用了 noexec。在 chezmoi.toml 中设置 scriptTempDir。
  • 模板中的 Windows 换行符(CRLF)会导致 shell 脚本在 Linux/macOS 上无法正常运行。在 dotfiles 仓库的 .gitattributes 中添加 * text eol=lf,或设置 core.autocrlf = input。
  • 在新机器上,如果 .chezmoi.toml.tmpl 尚不存在就运行 chezmoi init,会静默跳过逐台机器的提示。添加特定主机的文件前,先提交该模板。
  • 出现异常时,首先运行 chezmoi doctor——它会一次性报告缺失的依赖项(op、age 等)、损坏的路径和 shell 集成问题。
  • Context7 可能不可用。chezmoi CLI 可通过 chezmoi help 和 chezmoi help <command> 自行提供文档;遇到这种情况时改用它们。
  • 在 ~/.config/chezmoi/ 中同时存在两个配置文件(例如 chezmoi.toml 和 chezmoi.yaml)会报错,并非“优先使用第一个”。迁移格式时,在同一次提交中删除旧文件。
  • 配置格式根据扩展名检测。如果通过管道传入来自特殊路径的配置,可使用 --config-format 强制指定格式。
  • chezmoi <unknown> 会在报错前,先在 $PATH 中查找 chezmoi-<unknown>(git 风格的插件约定)。这便于添加仓库本地的辅助工具;但如果拼写错误意外运行了无关的二进制文件,也会令人措手不及。

参考 URL 索引

chezmoi 文档更新很快——每隔几个版本就会新增模板函数、新的配置键和新的前缀。如果问题超出了本技能的范围,请获取实时页面,不要根据训练数据猜测。优先使用 mcp__tavily__tavily_extract(单个 URL,extract_depth: "advanced",format: "markdown")——它会直接返回规范的 /reference/* 内容。仅在无法使用时才改用 Context7。不要使用 tavily_map 或爬取页面——以下 URL 稳定不变;选择确切的 URL 即可。

调用示例:

mcp__tavily__tavily_extract(
  urls: ["https://www.chezmoi.io/reference/templates/functions/jq/"],
  extract_depth: "advanced",
  format: "markdown",
)

内容位置索引:

主题URL
概念(源 / 目标 / 目的地 / 工作树)/reference/concepts/
各目标类型的源状态属性顺序/reference/source-state-attributes/
目标类型(文件 / 目录 / 符号链接 / 脚本 / 修改 / 创建 / 移除)/reference/target-types/
chezmoi apply 的应用顺序/reference/application-order/
所有命令行标志(全局 / 常用 / 开发者)/reference/command-line-flags/
单条命令参考(chezmoi <name>)/reference/commands/<name>/
配置文件结构/reference/configuration-file/
钩子(read-source-state / apply / 等)/reference/configuration-file/hooks/
按扩展名划分的脚本解释器/reference/configuration-file/interpreters/
特殊文件(.chezmoiignore 等)/reference/special-files/<name>/
特殊目录(.chezmoidata/ 等)/reference/special-directories/<name>/
模板变量(.chezmoi.os 等)/reference/templates/variables/
模板指令(chezmoi:template: 等)/reference/templates/directives/
内置模板函数/reference/templates/functions/<name>/
init 模板函数(promptBoolOnce 等)/reference/templates/init-functions/<name>/
密码管理器模板函数/reference/templates/<vendor>-functions/<name>/(1password、bitwarden、dashlane、doppler、ejson、gopass、keepassxc、keeper、keyring、lastpass、pass、passhole、protonpass、vault、aws-secrets-manager、azure-key-vault、secret)
GitHub 模板函数/reference/templates/github-functions/<name>/
插件/reference/plugins/

判断方法:用户问“前缀 X 有什么作用”时 → source-state-attributes;问“chezmoi <cmd> 有什么作用”时 → commands/<cmd>;问“哪个模板变量存放 Y”时 → templates/variables;问“如何从 Z 中读取机密”时 → templates/<vendor>-functions。使用 mcp__tavily__tavily_extract 仅获取所需页面——单个 URL、advanced 深度——这样答案始终基于截至今天的规范文档,而不是本技能上次编辑时掌握的信息。

评论

支持 Markdown