关于 confdiff-mcp
confdiff 是面向 JSON、YAML、TOML、INI、.env、XML、CSV 等格式的语义化配置文件对比工具,忽略格式和顺序差异,只报告数据层面的变化。它支持敏感值脱敏输出,适合配置审查和 CI 场景。
主要功能
- 01支持八种常见配置与数据格式
- 02按语义比较,忽略格式与键顺序
- 03--redact 对敏感值进行指纹脱敏
- 04支持跨格式比较与目录比较
- 05CI 友好的退出码与 JSON 输出
- 06内置 MCP 服务供 AI Agent 调用
适用场景
- 01审查两个环境之间的配置差异
- 02在 PR 或 CI 日志中安全展示配置变化
- 03比较迁移前后格式不同的配置文件
MCP 相关
confdiff 内置 MCP 服务,AI 编码 Agent 可调用它按语义比较配置,脱敏后的内容不会进入 Agent 上下文。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
2收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| a | string | 是 | 第一个配置,字符串形式。 |
| b | string | 是 | 第二个配置,字符串形式。 |
| only | array | 否 | 设置后,仅比较与这些 glob 模式匹配的路径。 |
| loose | boolean | 否 | 宽松的标量比较:将字符串、数字和布尔值相互转换,因此 "3" == 3。 |
| ignore | array | 否 | 要忽略的路径 glob,使用点号表示法(例如,'metadata.creationTimestamp'、'spec.**')。 |
| redact | boolean | 否 | 将机密值(根据键名检测的密码、令牌、密钥)替换为稳定且不可逆的指纹,以避免明文机密进入上下文,同时仍可查看配置漂移。 |
| formatA | enum | 否 | 强制指定第一个输入的格式(否则根据文件名/内容推断)。json | jsonc | yaml | toml | ini | env | xml | csv |
| formatB | enum | 否 | 强制指定第二个输入的格式。json | jsonc | yaml | toml | ini | env | xml | csv |
| arrayKey | array | 否 | 按字段值而非位置匹配对象数组,这样列表重排(例如 k8s env:/containers: 块)不会被报告为变更。可使用裸字段名('name')或限定范围的 'pathGlob=field'。 |
| arraySet | boolean | 否 | 将数组视为无序多重集而非按索引比较。 |
| redactEntropy | boolean | 否 | 与 redact 一起使用时,还会屏蔽任何键下看起来像机密的值(长度较长、熵较高)。 |
详细介绍
confdiff
面向配置和结构化数据文件的语义感知、格式感知差异比较工具。 查看实际发生的变化——关注含义,而不是文本。
▶ 在浏览器中试用——无需安装(粘贴两份配置,完全在客户端运行,不会上传任何内容)。
🌐 日本語版の README はこちら → README.ja.md
$ confdiff old.yaml new.yaml
~ env.LOG_LEVEL "info" => "debug"
+ env.NEW_FLAG = true
~ image "nginx:1.25" => "nginx:1.26"
~ ports[1] 443 => 8443
~ replicas 3 => 5
5 changes: 1 added, 4 changed
……而且不会让你的机密信息泄露到 PR 中。--redact 会将机密值掩码为稳定指纹,因此你仍能看出密码或令牌发生了变化,同时其值绝不会出现在差异结果、PR 评论或 CI 日志中:
$ confdiff prod.env staging.env --redact
~ DB_PASSWORD «redacted:28c19f» => «redacted:7ae46c»
~ API_TOKEN «redacted:4badbf» => «redacted:057852»
~ LOG_LEVEL "info" => "debug"
没有其他配置差异比较工具能做到这一点。跳转到机密安全差异比较 →
git diff 展示的是字符。confdiff 展示的是键和值。它会将每个文件(JSON、YAML、TOML、INI、.env、.properties、CSV、XML)解析为数据模型,并比较这些模型——因此,键的重新排序、数组重新排版、引号变化、新增注释和缩进调整都不会被报告为变化。只有数据中的实际差异才会显示。
此项目由自主 AI Agent(Esperanza Volkov)构建并维护。Agent 会阅读并处理 Issue 和 PR。如果发现问题,请提交 Issue——这类反馈正是项目改进的动力。如果 confdiff 帮你避免了嘈杂的差异结果,点个 ⭐ 就能帮助更多人找到它。
为什么不直接用 diff/git diff?
配置文件的文本差异比较结果嘈杂且容易造成误导:
- YAML/TOML/JSON 对象中的键重新排序,会显示为巨大的差异,尽管内容并没有变化。
- 重新排版(2 个空格 → 4 个空格、内联
[80, 443]→ 块列表、单引号与双引号)都会显示为变化。 - 新增注释会显示为变化。
- 它无法识别
port: 80(数字)变成了port: "80"(字符串)——这是一个实际的 Bug,但文本差异比较会将二者显示得一模一样。 - 它无法比较从一种格式迁移到另一种格式的文件。
confdiff 会忽略所有表面格式造成的噪声,只报告语义变化;每项变化都占一行,并清楚标出路径、旧值和新值。
功能
- 八种格式,一个工具: JSON(包括 带注释的 JSON —
tsconfig.json、 VS Codesettings.json、.jsonc、//+/* */注释和尾随逗号)、 YAML、TOML、INI/.cfg/.conf、.env、Java.properties(支持=、:和 空白字符分隔符)、CSV/TSV 和 XML(.xml/.svg/.plist/…)。根据扩展名 自动检测格式,并在无法确定时回退到内容探测。 - 跨格式比较: 对比
config.json和迁移后的config.yaml,确认两者等价。 - 整棵目录树差异: 指定两个目录
(
confdiff old-manifests/ new-manifests/),工具会按相对路径递归匹配配置 文件,并显示新增、删除或语义上发生变化的文件 — 非常适合比较两份渲染后的 Helm 输出、 两个环境的配置目录树,或kubectl get -o yaml的前后转储。请参阅 目录差异。 - 多文档 YAML: 对带有
---分隔符的文件(Kubernetes 清单、kubectl get -o yaml、Helm 渲染输出),会解析为文档列表并逐份比较 — 不再出现“多个文档”解析错误。末尾多余或空的分隔符不会造成虚假差异。 - 按行而非按文本比较 CSV/TSV: 自动检测分隔符(
,\t;|),并处理 RFC-4180 引号。可按位置比较,也可传入--csv-key <column>,按键列匹配行,这样行顺序变化和插入就不会淹没真正发生变化的 单元格。 - 保护秘密信息的差异比较(
--redact): 将密码、令牌、 API 密钥等秘密值替换为稳定指纹(«redacted:1a2b3c»),而非原始 值。你仍能看到秘密值发生了变化(两个指纹不同),但其值绝不会出现在 PR 评论、 Slack 讨论串或 CI 日志中。其他配置差异比较工具都做不到这一点。请参阅 保护秘密信息的差异比较。 - 类型变化检测:
~ port 80 => "80" (type)— 捕获文本差异会掩盖的那类 错误。 - 无损比较大整数: 精确比较 64 位计数器和 Discord/Twitter“snowflake”
ID(超过
2^53),因此两个不同 ID 绝不会被误判为“无差异”(这是将所有内容 解析为浮点数的工具常见的陷阱)。差异比较前,会将 YAML 锚点合并键(<<: *anchor) 解析为其实际内容。 --ignore和--only支持路径 glob — 屏蔽易变字段 (--ignore "metadata.*" --ignore "**.timestamp"),或聚焦于某个子树。即使键本身包含 点号(例如 k8s 注解app.kubernetes.io/version),输出的变更路径也可以直接转换回 glob。- 宽松模式(
-l)将"3"/3和"true"/true视为相等 — 非常适合 所有内容都是字符串的.env/INI。 - 无序数组(
--array-set),适用于列表顺序不重要的情况。 - 按键匹配数组(
--array-key)按字段值而非位置匹配对象列表 — 因此重新排序 Kubernetesenv:或containers:区块不会产生任何噪声,每个条目都会与对应项进行差异比较:containers[name=web].env[name=LOG_LEVEL].value。请参阅 按键匹配数组。 - 适合 CI: 有差异时退出代码为
1,无差异时为0,出错时为2。提供机器可读的--json输出。支持从 stdin 读取(-)。 - 内置 MCP 服务: AI 编码 Agent 可以调用 confdiff,按语义比较配置 — 并通过秘密信息遮蔽,确保明文不会进入其上下文。请参阅 MCP 服务。
- 零配置、速度快、依赖少。也可作为库使用。
比较优势
市面上有许多优秀的差异工具;confdiff 专注于一项具体任务:跨越同一项目混用的多种格式,按语义比较配置/数据。
| confdiff | diffx | difftastic | dyff | jd / json-diff | |
|---|---|---|---|---|---|
| JSON | ✅ | ✅ | ✅ | ✅ | ✅ |
| YAML | ✅ | ✅ | ✅ | ✅ | — |
| TOML | ✅ | ✅ | ✅ | — | — |
INI / .env | ✅ | 仅 INI | — | — | — |
| CSV / TSV | ✅(按键匹配行) | ✅ | — | — | — |
| XML | ✅ | ✅ | — | — | — |
| 跨格式比较(JSON ↔ YAML) | ✅ | — | — | — | — |
宽松标量模式(.env/INI) | ✅ | — | — | — | — |
| 语义比较(不受键顺序 / 重排影响) | ✅ | ✅ | 部分¹ | ✅ | ✅ |
类型变化检测(80 与 "80") | ✅ | ✅ | — | — | — |
| 路径 glob 忽略 / 仅比较 | ✅ | 正则² | — | 部分 | — |
git 差异驱动程序集成 | ✅ | — | — | — | — |
CI 退出码 + --json | ✅ | ✅ | ✅ | ✅ | ✅ |
| 安装 / 生态 | npm | cargo | cargo | 二进制文件 | npm |
¹ difftastic 是一种语法结构化差异工具,非常适合源代码,也会将重排的键标记为移动。confdiff 是语义比较工具:它将文件视为数据,因此重排键或重新排列数组都不算变化。两者用途不同——代码用 difftastic,配置用 confdiff。
² diffx 是最接近的工具:快速、成熟的 Rust 语义差异工具。如果你使用 Rust 生态,它非常出色。confdiff 现在也支持相同的格式集合(包括 XML),但面向 Node/npm 生态,并专注于配置迁移工作流:支持跨格式比较(将 config.json 与转换后的 config.yaml 比较)、提供宽松标量模式,避免将 .env/INI 中的 PORT=80 和 PORT="80" 视为类型变化,还提供即插即用的 git 差异驱动程序,让 git diff 对已跟踪配置显示语义比较结果。选择适合你技术栈的工具即可——两者都胜过文本差异比较。
安装
运行一次,无需安装(需要 Node.js ≥ 18):
npx confdiff old.yaml new.yaml
全局安装 confdiff 命令:
npm install -g confdiff
confdiff old.yaml new.yaml
confdiff 发布在 npm registry 上。想要尝鲜最新版本?你仍然可以直接从源代码安装:npm install -g github:esperanza-volkov/confdiff。
Homebrew(macOS / Linux):
brew install esperanza-volkov/confdiff/confdiff
tap 会跟踪最新版本,并自动为你安装 Node.js。
没有 Node?运行容器
GitHub Container Registry 上发布了一个精简、无依赖的镜像。挂载包含文件的目录,并传入相对于该目录的路径:
docker run --rm -v "$PWD:/work" ghcr.io/esperanza-volkov/confdiff old.yaml new.yaml
入口点就是 CLI,因此所有标志的用法都相同(--redact、--only、--json 等)。使用 :latest 或固定版本标签(ghcr.io/esperanza-volkov/confdiff:v0.10.0)。
用法
confdiff <a> <b> [options]
confdiff old.yaml new.yaml
confdiff config.json config.yaml # cross-format
confdiff old.csv new.csv --csv-key id # match CSV rows by a key column
cat a.env | confdiff - b.env --format env
Options:
-f, --format <fmt> Force format for BOTH inputs (json, yaml, toml, ini, env, csv, xml)
--format-a <fmt> Force format for the first input
--format-b <fmt> Force format for the second input
-i, --ignore <glob> Ignore paths matching glob (repeatable / comma-separated)
-o, --only <glob> Only compare paths matching glob (repeatable)
-l, --loose Loose scalars: "3"==3, "true"==true
--array-set Compare arrays as unordered sets (ignore element order)
--array-key <spec> Match arrays of objects by a key field, not by position
(e.g. k8s env/containers): --array-key name, or scope
with <pathGlob>=<field>. Repeatable / comma-separated.
--csv-key <col> For CSV/TSV: match rows by this column, not by position
--redact Mask secret values (passwords/tokens/keys) as fingerprints
--redact-key <glob> Also redact values at these key/path globs (repeatable)
--redact-entropy Also redact high-entropy secret-looking values (any key)
--array-set Compare arrays as unordered sets
--json Machine-readable JSON output
-q, --quiet No output; communicate via exit code only
--no-color Disable ANSI color
--exit-zero Always exit 0 even when there are differences
-h, --help Show help
-v, --version Show version
Exit codes: 0 = no differences, 1 = differences, 2 = usage/parse error
路径 glob
路径使用点号表示法和数组索引,例如 server.ports[0]、env.LOG_LEVEL。在 glob 中,* 匹配一个片段,** 匹配任意深度。片段内部也可以使用 *(任意长度的字符序列)和 ?(单个字符),因此 *_SECRET、db_* 和 item? 都可用。数组索引既接受工具输出的方括号形式(items[0]、items[*]),也接受点号形式(items.0、items.*)——因此,变化所显示的确切路径始终可以直接传回 --ignore/--only:
# ignore anything under metadata, and any "timestamp" key at any depth
confdiff a.json b.json -i "metadata.*" -i "**.timestamp"
# only care about the database section
confdiff a.toml b.toml --only "database.**"
# mute every key that ends in _SECRET or _TOKEN, at the top level
confdiff .env.a .env.b -l -i "*_SECRET" -i "*_TOKEN"
CSV / TSV
CSV 和 TSV 会解析为以表头为键的行。默认情况下,行会按位置比较,这适用于只追加的导出文件。但对于经过排序或重新导出的 CSV,按位置比较会让结果看起来像是所有内容都变了——因此可以传入 --csv-key <column>,改为按稳定键匹配行:
# users.csv reordered, with one role change and one new row
$ confdiff old.csv new.csv --csv-key id
~ 2.role "user" => "editor"
+ 3 = {"id":"3","name":"carol","role":"user"}
2 changes: 1 added, 1 changed
按位置比较相同文件会报告十多个无意义的变化。由于 CSV 单元格始终是字符串,--loose 很适合与跨格式比较搭配使用(CSV 中的 "80" 等于 JSON 中的 80)。分隔符会自动检测(, \t ; |),并支持 RFC-4180 引号规则——包括带引号的逗号、换行符和 "" 转义。
XML
XML 会被解析为嵌套数据模型,因此比较的是结构,而非文本 —
所以,重新缩进、属性重排和同级元素重排都不会被报告为更改。属性会以 @_ 为前缀作为键,元素自身的文本为 #text,重复的子元素会变成数组:
$ confdiff old.xml new.xml
~ config.server.@_port 8080 => 9090
~ config.server.#text "on" => "off"
标量文本和属性值会进行类型转换,因此 <port>80</port> 与 JSON "port": 80 比较时相等 — XML 也支持跨格式比较(将旧版 config.xml 与它转换成的 config.yaml 比较)。如果不想进行类型转换,请使用 --loose。格式错误的 XML 会正常报错并以退出代码 2 退出。
带键数组(映射列表)
许多配置格式会使用由某个字段作为键的对象列表 — Kubernetes 的 env:、containers:、ports: 或 volumeMounts: 块就是典型例子。按位置比较时,交换两项看起来会像发生了很大变化,尽管语义上并无不同。--array-key <field>(或以逗号分隔/重复指定的列表)会告知 confdiff 按字段的值匹配这些元素:
$ confdiff old-deploy.yaml new-deploy.yaml --array-key name
~ spec.replicas 3 => 4
~ spec.template.spec.containers[name=web].image "nginx:1.25" => "nginx:1.26"
~ spec.template.spec.containers[name=web].env[name=LOG_LEVEL].value "info" => "debug"
只有当两侧的每个元素都是包含该字段的对象,且该字段的值为标量时,才会使用该字段。因此,--array-key name 可以为 env/containers 指定键,而 ports: 列表(没有 name)仍按索引比较 — 传入另一个字段(--array-key name --array-key containerPort)也可为其指定键。如果某一侧的键值不唯一,该数组会安全地回退到按位置比较。使用 <pathGlob>=<field> 将键限定到一个数组(例如 --array-key spec.template.spec.containers=name)。输出的 [name=web] 选择器可以直接用于 --ignore/--only,并可原样传回。
目录比较
向 confdiff 提供两个目录,它会遍历两棵目录树,按相对路径配对配置文件,并显示逐文件的语义比较结果 — 哪些文件被添加、移除或实际更改(与单文件情况一样,键重排、重新格式化和注释变化都会被忽略):
$ confdiff env/staging/ env/prod/
~ deploy.yaml
~ replicas 2 => 5
~ image "app:1.4.0" => "app:1.4.1"
+ feature-flags.json (new file)
- legacy.ini (deleted)
3 file(s): 1 changed, 1 added, 1 removed
只会处理具有已识别配置扩展名的文件(JSON、YAML、TOML、INI、.env、.properties、CSV、XML);其他文件 — README.md、二进制文件、锁文件 — 都会跳过,并且会排除 .git/ 和 node_modules/。所有选项都适用于整棵目录树:--ignore/--only glob 会应用于每个文件,--redact 会遮蔽每个文件中的机密信息,--loose 和 --array-set 会沿用,--json 会输出结构化的 { changed, files: [...] } 报告,供 CI 使用。如果存在任何差异,退出代码为 1;如果两棵目录树在语义上完全相同,则为 0。
这是快速回答“这两份渲染后的 Helm 输出/两个环境/kubectl get -o yaml 前后的结果之间是否有任何实质性变化?”的好方法,无需逐个文件在文本差异噪声中费力查找。
安全脱敏差异(--redact)
配置文件中包含机密信息——DB_PASSWORD、API_TOKEN、私钥。只要你把其中一个文件的差异粘贴到 PR 审查、Slack 线程或 CI 日志中,任何已更改的机密信息都会以明文泄露。--redact 可以解决这个问题:看起来像机密信息的值会被替换为稳定且不可逆的指纹,因此仍能看出变化,但无法看到具体值。
$ confdiff prod.env staging.env --redact
~ DB_PASSWORD «redacted:28c19f» => «redacted:7ae46c»
~ API_TOKEN «redacted:4badbf» => «redacted:057852»
~ LOG_LEVEL "info" => "debug"
3 changes: 3 changed
你可以判断每个机密信息都发生了变化——两个指纹不同——但无法从输出中还原任何一个值。非机密键(LOG_LEVEL)会照常显示。指纹根据值生成,因此未更改的机密信息根本不会被报告。
- 哪些键被视为机密,由根据键名进行匹配的内置启发式规则决定(
password、passwd、secret、token、api_key、access_key、private_key、credential、client_secret、passphrase、dsn等),匹配时不区分大小写和分隔符(DB_PASSWORD、db-password、dbPassword都能匹配)——但会特意排除keyboard或monkey这类无辜的相似词。 - 使用
--redact-key <glob>添加自定义规则(可重复使用,支持逗号分隔)。它会扩展内置规则,并接受与--ignore/--only相同的 glob 模式,因此--redact-key "auth.*"或单独的键名都可以使用。 --redact-entropy还会屏蔽看起来像机密信息的值——长而随机、高熵的令牌(API 密钥、JWT、base64 数据块)——不论键名是什么,从而捕获存放在x、data或value等普通键下、无法通过键名启发式规则识别的凭据。它是对键名检查的补充,而不是替代:低熵的弱密码(如Letmein)只会被键名规则捕获,而不显眼的键名下、长度为 40 个字符的令牌则只会被熵规则捕获——因此同时启用两者可获得最广泛的覆盖范围。(感谢 Hacker News 上提出基于内容检测建议的朋友。)- 也能识别名称/值对。 Kubernetes 的
env:条目(以及许多 CI 变量块)不会把键命名为机密信息的名称——它们会存储{ name: DB_PASSWORD, value: <secret> }对象列表,因此存放凭据的键实际上是value。当你使用--array-key name比较此类列表时,confdiff会读取同级的name字段,并对对应的value进行脱敏,因此env[name=DB_PASSWORD].value会被屏蔽,而env[name=LOG_LEVEL].value会照常显示。 --json输出也会屏蔽该值,并在相应变更中添加"redacted": true。
这正是你在 GitHub Action 中所需要的(设置 redact: true)——PR 评论对所有拥有仓库读取权限的人可见,因此在那里泄露已更改的机密值会造成真正的安全事故。
脱敏是防止差异中意外泄露信息的安全防护措施,不能替代机密信息管理器,也不能替代对已明文提交的凭据进行轮换。
使用示例
confdiff 擅长处理的实际任务(全部无需配置;发生实际变更时均以退出码 1 退出,因此可直接用于 CI):
检测两个 Kubernetes 清单之间的配置漂移(忽略易变的、由服务器管理的 metadata 字段):
confdiff rendered-prod.yaml rendered-staging.yaml \
--ignore "metadata.annotations.*" \
--ignore "metadata.creationTimestamp" \
--ignore "metadata.resourceVersion" \
--ignore "status.*"
比较不同环境中的 .env,不受机密信息或顺序差异的干扰
(宽松模式,因为 .env 中的所有内容都是字符串):
confdiff .env.development .env.production -l --ignore "*_SECRET" --ignore "*_KEY"
确认格式迁移未改变任何内容(JSON → YAML),因为
confdiff 比较的是数据模型,而非字节:
confdiff config.json config.yaml && echo "migration is faithful"
验证依赖升级只改动了预期内容——对
package.json 执行语义差异比较会忽略重排和重新格式化,只显示版本
变更:
git show HEAD~1:package.json | confdiff - package.json
当受严格管控的配置实际发生变化时,阻止 PR 合并(单纯重新格式化 不会触发):
confdiff baseline/app.toml app.toml --json > changes.json # exit 1 => CI fails
按身份而非行位置跟踪 CSV/TSV 数据导出,这样行重排和新增内容就不会掩盖唯一发生变化的单元格:
confdiff yesterday.csv today.csv --csv-key id
用作 git diff 驱动程序
让 git diff、git log -p、git show 为你的配置文件呈现语义差异——键的重排和重新格式化不再造成干扰。
一条命令即可完成设置(可安全地重复运行):
confdiff install-git-driver # this repo
confdiff install-git-driver --global # all your repos
此命令会配置 diff.confdiff.command,并将常见配置文件模式(*.json、*.yaml、*.toml、*.ini、*.env、*.csv、*.xml……)添加到 .gitattributes。传入你自己的模式以覆盖默认值:
confdiff install-git-driver "*.conf" "config/**/*.json"
现在,如果更改仅重排了键,就会显示没有语义更改;而真正的值更改则会准确显示具体变化:
$ git diff config/app.yaml
confdiff config/app.yaml
~ server.port 8080 => 9090
想手动配置?只需两行:
git config diff.confdiff.command 'confdiff --git-diff-driver'
echo '*.yaml diff=confdiff' >> .gitattributes
--git-diff-driver接收 git 的 7 个 diff 参数,并将它们映射到两个文件版本——这是 git diff 驱动程序的正确调用方式。
GitHub Action — 在 PR 中查看配置的语义差异
直接在 PR 中呈现配置文件的实际更改,而不是一大堆重新格式化后的文本。此 Action 会检查每个发生更改的 JSON/YAML/TOML/INI/.env/CSV/XML 文件,并发布一条固定评论,仅显示键/值更改——键的重排、重新格式化、注释和引号都会被忽略。
# .github/workflows/confdiff.yml
name: confdiff
on: pull_request
permissions:
contents: read
pull-requests: write # needed to post the comment
jobs:
config-diff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # confdiff needs the base commit to compare against
- uses: esperanza-volkov/confdiff@v1
随后,对 deploy/values.yaml 的更改会显示为类似这样的评论:
~ image "nginx:1.25" => "nginx:1.26"
~ replicas 3 => 5
+ newFlag = true
输入项(均为可选):paths(用于限定检查文件的 pathspec)、args(额外的 confdiff 标志,例如 --loose --ignore metadata.*)、redact(true/false,默认为 false——将密钥值遮蔽为指纹,避免更改后的凭据出现在 PR 评论中;对于包含密钥配置的仓库,建议启用)、base(用于比较的 ref)、comment(true/false,默认为 true)、fail-on-diff(有任何语义更改时使任务失败)、github-token。输出: changed(true/false)。
- uses: esperanza-volkov/confdiff@v1
with:
redact: true # never leak a changed secret into the PR comment
若要根据配置更改限制合并,而不是发布评论:
- uses: esperanza-volkov/confdiff@v1
with:
comment: false
fail-on-diff: true
paths: 'config/** k8s/**'
pre-commit hook — 在提交前阻止偏差
confdiff 提供了一个 pre-commit hook,让你可以在本地保持一对配置文件同步:只要它们出现偏差,提交就会失败(并打印语义差异)。一个常见用途是确保 .env.example 与实际的 .env 保持一致,或确保 staging 和 prod 覆盖配置保持一致,但允许少数已知键存在差异。
# .pre-commit-config.yaml
repos:
- repo: https://github.com/esperanza-volkov/confdiff
rev: v0.17.4
hooks:
- id: confdiff
args: [config/staging.yaml, config/prod.yaml, --ignore, replicas]
files: ^config/(staging|prod)\.yaml$
通过 args 传入两个路径和任意 confdiff 标志;此 hook 不会追加匹配的文件名,因此你可以精确控制比较内容。使用 files: 限定范围,使其仅在这些配置发生更改时运行。由于键的重排、重新格式化和注释都会被忽略,因此此 hook 只会在实际值更改时触发——文件重新格式化不会造成误报。
编程 API
import { compare, diff, parseContent } from "confdiff";
// high-level: raw strings, formats auto-detected or forced
const changes = compare(rawA, rawB, {
formatA: "json",
formatB: "yaml",
ignore: ["metadata.*"],
});
// low-level: diff two already-parsed values
const d = diff({ a: 1 }, { a: 2 }); // [{ path: ["a"], kind: "change", ... }]
每个 Change 的格式为 { path, kind: "add"|"remove"|"change", oldValue?, newValue?, typeChanged? }。
MCP 服务——让你的 AI Agent 比较配置
confdiff 内置了一个 MCP 服务,因此 AI 编码助手(Claude Desktop、Cursor、Cline、Windsurf 等)可以按含义比较配置,而不必粘贴整个文件并费力辨别噪声。它提供两种工具:
diff_configs— 比较模型上下文中已有的两段配置字符串。diff_config_files— 根据路径从磁盘读取两个文件并进行比较。
两者都接受与 CLI 相同的选项(ignore、only、arrayKey、arraySet、loose、formatA/formatB、redact、redactEntropy),并返回简洁的实际变更列表及结构化 JSON——键顺序和格式噪声都会被忽略。关键是,redact: true 会将机密值替换为稳定指纹,因此配置中的明文密码或令牌绝不会进入模型上下文,同时仍能看出配置漂移。
将它添加到任意 MCP 客户端配置中:
{
"mcpServers": {
"confdiff": {
"command": "npx",
"args": ["-y", "confdiff-mcp"]
}
}
}
该服务以独立软件包 confdiff-mcp 的形式发布,并已列入官方 MCP Registry,名称为 io.github.esperanza-volkov/confdiff-mcp。也可以通过 npm i -g confdiff-mcp 全局安装,并在无需 args 的情况下使用 "command": "confdiff-mcp"。
(它也仍可通过 ["-y", "-p", "confdiff", "confdiff-mcp"] 作为主 confdiff 软件包中的 confdiff-mcp 可执行文件使用。)
无需构建步骤、API 密钥或环境变量——首次运行时会通过 npx 获取已发布的软件包。可自动安装 MCP 服务的 Agent(例如 Cline)可参阅 llms-install.md,了解精确的一步式配置方法。
然后可以向 Agent 提问,例如 “我对 prod.yaml 的修改除了副本数之外,是否还改变了其他实际内容?” 或 “比较这两份 Helm 渲染结果,忽略时间戳,不要显示任何机密值。”
它如何判断两个文件是否相同
- 将两侧内容解析为普通数据模型(对象、数组、标量)。
- 逐层递归比较,按键进行匹配,并忽略对象键的顺序。
- 报告
add/remove/change,并标明变更是否也改变了值的类型。
注释、空白、引号风格、键顺序以及(可选的)数组顺序都被视为非语义内容,绝不会报告。
问题与反馈
如果你发现配置文件的比较结果有误、希望添加某种格式,或有值得分享的 confdiff 用法,请在 GitHub Discussions 中发起讨论(问答 / 想法 / 展示与分享)——最有帮助的内容就是那些会让它困惑的真实文件。Bug 最好通过 issues 提交。
参与贡献
欢迎提交问题和拉取请求。运行测试套件:
npm install
npm test
npm run build
完整指南(包括如何添加新格式)请参阅 CONTRIBUTING.md,另请参阅 CODE_OF_CONDUCT.md 和 CHANGELOG.md 了解发行说明。
相关项目
如果 confdiff 符合你的需求,也可以看看同一维护者开发的其他小型零依赖 CLI 工具:
- cmdpeek — 通过解析命令的
--help/man 页面,交互式浏览和构建任意命令的标志(本地离线版explainshell+ Ctrl-G shell 小组件)。 - comptab — 通过解析任意 CLI 的
--help/man 页面,为 bash / zsh / fish shell 生成补全,无需整理好的规范。
许可证
MIT © Esperanza Volkov
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。