关于 provisioning-with-comtrya
此技能介绍如何使用 Comtrya 为 macOS、Linux 和 Windows 设计并应用配置清单,涵盖系统差异、依赖顺序和权限控制。它强调在团队推广前先验证、试运行并进行试点。
主要功能
- 01按配置职责组织清单
- 02指定操作系统和包管理器变体
- 03显式声明操作依赖
- 04限制权限提升
- 05推广前先验证、试运行和试点
适用场景
- 01跨操作系统配置开发机器
- 02为团队共享点文件和系统设置
- 03在团队设备应用前测试配置清单
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ zenobi-us/ dotfiles --skill provisioning-with-comtrya安装后在 Claude Code 中输入/provisioning-with-comtrya确认已加载。
附带文件references/action-reference.md
详细介绍
使用 Comtrya 进行配置
概述
Comtrya 是一款声明式系统配置工具(YAML/TOML),可在 macOS、Linux 和 Windows 上以幂等方式应用配置。核心原则:清单描述期望状态;可重复应用,且不会产生副作用。
关键要点: 配置系统需要分层思考(OS 检测、包管理器、权限级别、依赖顺序、回滚安全性)。
重要: 文件操作(file.link、file.copy、directory.copy)要求源文件位于相对于清单的 files/ 子目录中。这样可以避免 Comtrya 将配置文件解析为清单。
适用场景
- 设计适用于多种 OS 的配置方案(开发团队、CI/CD、个人设备)
- 构建能够处理 OS 差异的清单
- 管理包管理器冲突
- 同时设置点文件和系统配置
- 大范围应用前测试更改
- 安全处理权限提升
多 OS 清单结构
使用变体检测 OS
使用 where 条件来指定特定的 OS、架构或运行时条件:
actions:
- action: package.install
name: neovim
variants:
- where: os.name == "linux"
provider: apt
- where: os.name == "macos"
provider: brew
- where: os.name == "windows"
provider: winget
更简单的方式: 让 comtrya 自动检测默认包管理器(Linux 上为 apt,macOS 上为 brew,Windows 上为 winget):
- action: package.install
name: git
按职责组织(团队安全)
按职责拆分清单,而非按 OS 拆分:
manifests/
base.yml # Cross-platform: git, dotfiles, shell config
dev-tools.yml # Dev dependencies with OS variants
system.yml # OS-specific: systemd units, defaults, prefs
应用多个清单:
comtrya apply --manifest base.yml --manifest dev-tools.yml
原因: 使用不同 OS 的团队可以共享基础配置,同时覆盖特定工具层。
包管理器安全性
防止冲突
Comtrya 具有幂等性且仅添加内容——package.install 不会卸载或降级软件包。核心行为:
- 如果软件包已安装:跳过(无操作)
- 如果同一软件包使用不同的提供方:分别保留
- 如果使用相同的包管理器:以单个声明为准
团队策略: 记录每种 OS 使用的包管理器:
- “Linux 团队:仅使用 apt”
- “macOS 团队:主要使用 brew,GUI 应用使用 homebrew-cask”
- “例外情况记录在清单注释中”
覆盖默认提供方
团队成员需要使用非标准提供方时(例如,Linux 上使用 nix):
- action: package.install
name: rust
provider: nix
where: user.username == "zenobius"
或者创建团队变体:
- action: package.install
name: rust
variants:
- where: user.username == "zenobius"
provider: nix
- provider: apt # default fallback
依赖排序
Comtrya 不会自动排列操作顺序——请显式声明依赖项:
actions:
- action: group.add
group_name: docker
id: docker_group
- action: package.install
name: docker
depends_on: ["docker_group"] # Wait for group creation
- action: command.run
command: systemctl
args: ["enable", "docker"]
privileged: true
depends_on: ["docker_package"]
重要: 如果没有 depends_on,docker 软件包会在用户组创建之前安装,导致静默失败。
权限提升模式
使用权限提升
对于需要提升权限的操作,请使用 privileged: true:
- action: command.run
command: systemctl
args: ["enable", "nginx"]
privileged: true
where: os.name == "linux"
或者使用 sudo comtrya apply 运行整个清单。
安全性:尽量减少权限提升
尽可能避免提升权限:
# ❌ Avoid: Privileged command for user config
- action: command.run
command: tee
args: ["~/.config/app/config.json"]
privileged: true
# ✅ Better: User-owned file without privileges
- action: file.copy
from: app-config.json
to: ~/.config/app/config.json
应用前:试运行和验证
请勿跳过本节。验证是不可妥协的要求。
在任何团队成员的设备上应用清单之前,必须进行验证。这不是最佳实践,而是硬性要求。跳过验证会导致每位受影响用户需要花费 1–4 小时进行恢复。验证总共需要 15–30 分钟。
强制验证流程(必须按顺序完成):
1. 语法验证(关卡:必须通过)
检查清单语法中是否存在拼写错误和解析错误:
comtrya validate manifest.yml
关卡: 继续之前修复所有错误。如果验证失败,请勿继续。
2. 在测试设备上试运行(关卡:检查输出)
预览所有更改,但不实际应用:
comtrya apply --dry-run --manifest manifest.yml
关卡: 仔细检查输出。如果有任何操作出乎意料(使用了错误的包管理器、路径异常、缺少依赖项),请先排查问题再继续。
3. 让一名团队成员参与试点(关卡:获得批准)
将清单应用到一名志愿团队成员的实际设备上(不是虚拟机):
# Let ONE team member apply while you watch
comtrya apply --manifest manifest.yml
关卡: 确认对方的系统未受影响。扩大部署范围前,必须获得试点用户的明确确认。
4. 部署到团队(仅在通过 Gate 3 后)
试点成功后,部署到团队的其他成员。
为什么不能跳过验证:
- **仅进行语法验证并不够。**它可以发现拼写错误,却无法发现逻辑错误(例如,操作系统对应的包管理器错误、缺少
depends_on)。 - **在你的机器上试运行 ≠ 在团队成员的机器上试运行。**每种操作系统、包管理器和用户配置都不一样。
- **你的配置不具有代表性。**必须先让一位团队成员在其实际环境中进行测试。在部署期间测试 = 一片混乱。
- **跳过此流程会导致:**清单损坏、软件包安装到错误位置、缺少依赖项、系统状态无法恢复。
- **跳过验证的代价:**每位受影响的团队成员需要手动恢复 1–4 小时。
- **验证的代价:**总计 15–30 分钟。
这不是可选事项。请遵循各个关卡。
回滚与恢复(仅限紧急情况)
回滚仅用于紧急情况——成本高且容易出错。通过验证来预防,成本要低得多。
Comtrya 清单描述的是期望状态,而非状态变更。恢复需要:
- 手动移除更改或
- 编写反向操作(package.remove、file.delete)
- 对清单进行版本控制以跟踪更改
紧急回滚示例:
# Only if troubleshooting a broken manifest on live system
# AFTER validation failed: this is a failure mode
actions:
- action: package.remove
name: broken-package
- action: command.run
cmd: "rm ~/.config/broken-app"
**恢复成本:**每位受影响的用户需要 1–4 小时。
**预防成本:**应用更改前进行验证,耗时 15–30 分钟。
Recover.yml 并不能替代验证——它是灭火器。不要依赖灭火器;火灾发生后再使用,而不是用它来替代预防。
常见模式
符号链接配置文件
# Single file symlink (from files/ directory)
- action: file.link
from: nvim/init.lua
to: ~/.config/nvim/init.lua
# Walk directory and symlink all files
- action: file.link
source: shell-configs
target: ~/.config/shell
walk_dir: true
下载并安装二进制文件
- action: file.download
from: https://example.com/tool.tar.gz
to: /tmp/tool.tar.gz
- action: file.unarchive
from: /tmp/tool.tar.gz
to: /usr/local/bin/
- action: file.remove
target: /tmp/tool.tar.gz
将 Git 仓库作为配置的一部分
- action: git.clone
repo_url: "https://github.com/user/dotfiles.git"
directory: ~/dotfiles
- action: file.link
from: dotfiles/zshrc
to: ~/.zshrc
用户和组管理(需要特权)
- action: group.add
group_name: dev
- action: user.add
fullname: Dev User
username: devuser
home_dir: /home/devuser
shell: /bin/bash
group:
- dev
- sudo
版本兼容性说明
Comtrya 版本之间可能存在以下差异:
- 可用操作(较新版本会添加功能)
- where 子句语法
- 特权处理方式
**最佳实践:**在 CI/CD 或团队文档中固定 comtrya 版本:
COMTRYA_VERSION=0.3.0
curl -fsSL https://get.comtrya.dev | INSTALL_VERSION=$COMTRYA_VERSION sh
快速参考:操作(v0.9.2)
文件与目录操作
| 操作 | 必需 | 可选 | 说明 |
|---|---|---|---|
file.copy | from、to | template、chmod、owned_by_user/group | 从 files/ 目录复制文件 |
file.link | from、to | walk_dir、source、target | 为 files/ 目录中的文件创建符号链接 |
file.download | from/source、to/target | owned_by_user/group | 从 URL 下载 |
file.remove | target | - | 移除文件 |
file.chown | path、user、group | - | 更改所有权 |
file.unarchive | from、to | force | 解压 tar.gz |
directory.copy | from、to | - | 从 files/ 目录复制目录 |
重要:file.link、file.copy、directory.copy 要求源文件位于 files/ 子目录中。
软件包操作
| 操作 | 必需 | 可选 | 说明 |
|---|---|---|---|
package.install | name 或 list | provider、repository、file | 安装软件包 |
package.repository | name(url) | key、provider | 添加软件包仓库 |
提供程序:apt、brew、pacman、yay、paru、pkg、pkgin、winget、xbps、zypper、macports、dnf、snapcraft
其他操作
| 操作 | 必需 | 可选 | 说明 |
|---|---|---|---|
command.run(或 cmd.run) | command | args、dir、privileged、env | 执行命令 |
git.clone | repo_url、directory | - | 克隆仓库(v0.9.1+) |
user.add | fullname、home_dir、username | shell、group | 创建用户 |
user.group | username、group | - | 将用户添加到组 |
group.add | group_name | - | 创建组 |
binary.github | name、directory、repository、version | - | 下载 GitHub 二进制文件 |
macos.default | domain、key、kind、value | - | 设置 macOS 默认值 |
上下文变量
| 变量 | 示例 | 描述 |
|---|---|---|
{{ user.home_dir }} | /home/zenobius | 主目录 |
{{ user.config_dir }} | /home/zenobius/.config | 配置目录 |
{{ user.username }} | zenobius | 用户名 |
{{ os.name }} | linux, macos, windows | 操作系统名称 |
常见错误(危险信号)
❌ 禁止:未满足以下条件就应用于任何团队机器:
- 语法验证通过
- 已在你的测试机器上完成并审核试运行
- 一名志愿团队成员已先行成功试用
后果: 每位受影响用户需要 1–4 小时恢复。跳过这些步骤就会发生。
✅ 必须: 语法检查通过 → 试运行通过 → 一人试用通过 → 推广至团队。总耗时:15–30 分钟。结果:系统无故障。
❌ 混用包管理器而不记录: 会造成隐性冲突或效率低下。
✅ 解决方法: 在清单注释中按操作系统记录团队策略。
❌ 未试运行就应用于生产环境: 会破坏现有配置。
✅ 解决方法: 始终先运行 --dry-run,在一台机器上测试,然后再推广。
❌ 假定自定义命令具有幂等性: 某些脚本无法安全重复执行。
✅ 解决方法: 为非幂等命令添加保护措施(检查是否已执行)。
❌ 不加限制地提升权限: 过度使用 sudo 会带来安全风险。
✅ 解决方法: 尽量减少 sudo;改用 file.create 创建用户配置。
实际效果
应用此模式后的结果:
- 多操作系统团队的配置时间:2 小时 → 20 分钟(经过验证的清单)
- 因缺少依赖项导致的隐性故障:每次推广 7 次 → 0 次(明确使用
depends_on) - 包管理器冲突:每月 3 次 → 0 次(记录策略)
- 从故障清单中恢复:手动清理 → 脚本化回滚