Confdiff – 用于配置文件(JSON/YAML/TOML)的语义差异工具,可自动隐藏敏感信息
摘要
Confdiff 是一款用于配置文件(JSON/YAML/TOML)的语义差异工具,它通过比较数据模型而非文本,高亮显示有意义的更改,并隐藏敏感信息以防止泄露。
暂无内容
查看缓存全文
缓存时间: 2026/08/27 15:24
esperanza-volkov/confdiff 来源:https://github.com/esperanza-volkov/confdiff # confdiff 用于配置文件与结构化数据的语义化、格式感知差异比较。 查看 真实 的变化——是含义,而非文本。 ▶ 在浏览器中试用——无需安装 (https://esperanza-volkov.github.io/confdiff/) (粘贴两份配置,完全在客户端运行,无数据上传)。 console $ 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 日志中: console $ 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 代理(Esperanza > Volkov)构建并维护。 问题和 PR 均由该代理读取并处理。如果发现任何问题,请提交 issue——这正是它改进的方式。 — ## 为什么不用 diff/git diff? 对配置文件进行文本差异比较会产生噪音且容易产生误导: - 在 YAML/TOML/JSON 对象中重新排序键会产生大量差异,即使没有任何实际变化。 - 重新格式化(2空格 → 4空格、内联 [80, 443] → 块列表、单引号与双引号)会被显示为变更。 - 添加注释也会被显示为变更。 - 它无法识别 port: 80(数字)变成了 port: "80"(字符串)——这是一个真正的 bug,而文本差异会完全一样地显示它们。 - 它无法比较从一种格式迁移到另一种格式的文件。 confdiff 忽略所有表面噪音,仅报告语义变更,每项变更一行,带有清晰路径、旧值和新值。 ## 功能特性 - 八种格式,一个工具: JSON、YAML、TOML、INI/.cfg/.conf、.env、Java .properties(支持 =、: 和空格分隔符)、CSV/TSV 和 XML(.xml/.svg/.plist/…)。格式自动检测(根据扩展名),并支持内容嗅探作为后备方案。 - 跨格式比较: 比较 config.json 和它迁移后的 config.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) ——捕捉文本差异会隐藏的那类 bug。 - 无损大整数: 64 位计数器和 Discord/Twitter “snowflake” ID(超过 2^53)会被精确比较,因此两个 不同 的 ID 永远不会被错误地合并为“无差异”(这对将所有内容解析为浮点数的工具来说是个陷阱)。YAML 锚点合并键(<<: *anchor)在比较前会被解析为其有效内容。 - 路径通配符 用于 --ignore 和 --only——屏蔽易变字段(--ignore "metadata.*" --ignore "**.timestamp")或专注于某个子树。为变更打印的路径可以双向转换回通配符,即使键本身包含点(例如 k8s 注解 app.kubernetes.io/version)。 - 宽松模式(-l)将 "3"/3 和 "true"/true 视为相等——非常适合 .env/INI,其中所有内容都是字符串。 - 无序数组(--array-set)用于列表顺序不重要的场景。 - CI 友好: 有差异时退出码为 1,无差异时为 0,出错时为 2。提供机器可读的 --json 输出。支持从标准输入(-)读取。 - 零配置、快速、依赖少。也可作为库使用。 ## 对比分析 市面上有许多优秀的差异工具;confdiff 专注于跨项目混合格式的配置/数据语义比较这一特定任务。 | | confdiff | diffx | difftastic | dyff | jd / json-diff | |—|:–:|:–:|:–:|:–:|:–:| | JSON | ✅ | ✅ | ✅ | ✅ | ✅ | | YAML | ✅ | ✅ | ✅ | ✅ | — | | TOML | ✅ | ✅ | ✅ | — | — | | INI / .env | ✅ | 仅 INI | — | — | — | | CSV / TSV | ✅ (带键行) | ✅ | — | — | — | | XML | ✅ | ✅ | — | — | — | | 跨格式比较 (JSON ↔ YAML) | ✅ | — | — | — | — | | 宽松标量模式 (.env/INI) | ✅ | — | — | — | — | | 语义化 (键顺序/重排不敏感) | ✅ | ✅ | 部分1 | ✅ | ✅ | | 类型变更检测 (80 vs "80") | ✅ | ✅ | — | — | — | | 路径通配符忽略/只关注 | ✅ | 正则2 | — | 部分 | — | | git diff-driver 集成 | ✅ | — | — | — | — | | CI 退出码 + --json | ✅ | ✅ | ✅ | ✅ | ✅ | | 安装/生态系统 | npm | cargo | cargo | 二进制文件 | npm | 1 difftastic 是一个 语法 结构差异工具——非常适合源代码,但它仍然会将重排的键标记为移动。confdiff 是 语义 化的:它将文件视为数据,因此重新排序键或重排数组根本不是变更。任务不同——代码用 difftastic,配置用 confdiff。 2 diffx (https://github.com/kako-jun/diffx) 是最接近的工具:一个快速、成熟的 Rust 语义差异工具。如果您身处 Rust 生态系统,它非常优秀。confdiff 现已覆盖相同的格式集(包括 XML),但面向 Node/npm 世界,并专注于配置迁移工作流:跨格式比较(比较 config.json 和它变成的 config.yaml)、宽松标量模式(使得 .env/INI 中的 PORT=80 和 PORT="80" 不被读取为类型变更),以及一个可直接使用的 git diff 驱动程序,使对受跟踪配置的 git diff 显示语义化输出。请选择适合您技术栈的工具——两者都优于文本差异。 ## 安装 bash npm install -g confdiff # 全局 CLI # 或无需安装直接运行: npx confdiff old.yaml new.yaml 还未上 npm?直接从 GitHub 安装(安装时构建): bash npm install -g github:esperanza-volkov/confdiff 要求 Node.js ≥ 18。 ### 没有 Node?运行容器 一个轻量、无依赖的镜像已发布到 GitHub Container Registry。挂载包含您文件的目录并传递相对路径: bash 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 [options] confdiff old.yaml new.yaml confdiff config.json config.yaml # 跨格式比较 confdiff old.csv new.csv --csv-key id # 按关键列匹配 CSV 行 cat a.env | confdiff - b.env --format env 选项: -f, --format 为两个输入强制指定格式 (json, yaml, toml, ini, env, csv, xml) --format-a 为第一个输入强制指定格式 --format-b 为第二个输入强制指定格式 -i, --ignore 忽略匹配通配符的路径 (可重复/逗号分隔) -o, --only 只比较匹配通配符的路径 (可重复) -l, --loose 宽松标量:"3"==3, "true"==true --csv-key 对于 CSV/TSV:按此列匹配行,而非按位置 --redact 将密钥值(密码/令牌/密钥)替换为指纹 --redact-key 同时替换这些键/路径通配符下的值 (可重复) --array-set 将数组作为无序集合比较 --json 机器可读的 JSON 输出 -q, --quiet 无输出;仅通过退出码通信 --no-color 禁用 ANSI 颜色 --exit-zero 即使有差异也始终退出 0 -h, --help 显示帮助 -v, --version 显示版本 退出码: 0 = 无差异,1 = 有差异,2 = 用法/解析错误 ### 路径通配符 路径使用点表示法,带数组索引,例如 server.ports[0]、env.LOG_LEVEL。在通配符中,* 匹配一个段,** 匹配任意深度。在段内,您也可以使用 *(任意字符序列)和 ?(单个字符),因此 *_SECRET、db_* 和 item? 都有效。数组索引接受工具打印的括号形式(items[0]、items[*])或点形式(items.0、items.*)——因此为变更显示的精确路径总是可以直接双向转换回 --ignore/--only: bash # 忽略 metadata 下的任何内容,以及任何深度的 "timestamp" 键 confdiff a.json b.json -i "metadata.*" -i "**.timestamp" # 只关心数据库部分 confdiff a.toml b.toml --only "database.**" # 屏蔽所有以 _SECRET 或 _TOKEN 结尾的顶级键 confdiff .env.a .env.b -l -i "*_SECRET" -i "*_TOKEN" ### CSV / TSV CSV 和 TSV 被解析为行,行以标题为键。默认情况下,行按 位置 比较,这适用于仅追加的导出。但是,对排序或重新导出的 CSV 按位置进行比较,会显示所有内容都发生了变化——因此请传入 --csv-key <column> 以稳定的键匹配行: bash # users.csv 已重新排序,有一个角色变更和一行新增 $ 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,重复的子元素会变成数组: bash $ confdiff old.xml new.xml ~ config.server.@_port 8080 => 9090 ~ config.server.#text "on" => "off" 标量文本和属性值会进行类型强制转换,因此 80 与 JSON 的 "port": 80 比较为相等——跨格式比较也适用于 XML(比较遗留的 config.xml 和它变成的 config.yaml)。如果您不想强制转换,请使用 --loose。格式错误的 XML 会以退出码 2 干净地失败。 ### 安全密钥差异(--redact)配置文件携带密钥——DB_PASSWORD、API_TOKEN、私钥。一旦您将其中某个差异粘贴到 PR 审查、Slack 线程或 CI 日志中,任何 已变更 的密钥都会以明文泄露。--redact 解决了这个问题:看起来像密钥的值会被替换为稳定、不可逆的指纹,因此变化仍然可见,但具体值永远不会显示。 bash $ 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 相同的通配符,因此 --redact-key "auth.*" 或裸键名都有效。 - --json 输出也会掩盖值,并在该变更上添加 "redacted": true。这正是您在 GitHub Action 中需要的(设置 redact: true)——PR 评论对所有具有仓库读取权限的人都可见,因此其中出现已变更的密钥值是真正的事件。 > 脱敏是防止差异中意外泄露的防护栏,不能替代密钥管理器或轮换已以明文提交的凭据。 ## 实用示例 confdiff 擅长的实际工作(全部零配置,真实变更时退出 1,因此可直接用于 CI): 捕获两个 Kubernetes 清单之间的配置漂移(忽略易变的服务器管理字段 metadata): bash confdiff rendered-prod.yaml rendered-staging.yaml \ --ignore "metadata.annotations.*" \ --ignore "metadata.creationTimestamp" \ --ignore "metadata.resourceVersion" \ --ignore "status.*" 比较跨环境的 .env,无密钥或排序噪音(宽松模式,因为 .env 中的所有内容都是字符串): bash confdiff .env.development .env.production -l --ignore "*_SECRET" --ignore "*_KEY" 确认格式迁移未改变任何内容(JSON → YAML),因为 confdiff 比较的是数据模型,而非字节: bash confdiff config.json config.yaml && echo "迁移忠实" 证明依赖升级只修改了预期内容——对 package.json 进行语义差异比较,跳过重排和重格式化,只显示版本变更: bash git show HEAD~1:package.json | confdiff - package.json 当锁定配置实际发生变更时使 PR 失败(仅重格式化不会触发它): bash confdiff baseline/app.toml app.toml --json > changes.json # 退出 1 => CI 失败 按身份而非行位置跟踪 CSV/TSV 数据导出,这样行重排和插入就不会掩盖那个真正发生变化的单元格: bash confdiff yesterday.csv today.csv --csv-key id ## 用作 git diff 驱动程序 让 git diff、git log -p、git show 为您的配置文件渲染 语义 差异——重排的键和重格式化不再显示为噪音。一条命令即可设置(幂等,可安全重复运行): bash confdiff install-git-driver # 此仓库 confdiff install-git-driver --global # 您的所有仓库 这会设置 diff.confdiff.command 并将常见的配置模式(*.json、*.yaml、*.toml、*.ini、*.env、*.csv、*.xml 等)添加到 .gitattributes。传入您自己的模式以覆盖默认值: bash confdiff install-git-driver "*.conf" "config/**/*.json" 现在,仅重排键的变更显示 无语义变更,而真正的值变更则精确显示了变化: console $ git diff config/app.yaml confdiff config/app.yaml ~ server.port 8080 => 9090 更喜欢手动设置?只需两行: bash git config diff.confdiff.command 'confdiff --git-diff-driver' echo '*.yaml diff=confdiff' >> .gitattributes > --git-diff-driver 接收 git 的 7 个差异参数并将它们映射到两个文件版本——这是 git diff 驱动程序的正确调用方式。 ## GitHub Action — 在您的 PR 上显示语义化配置差异 在 PR 中直接展示配置文件的 真实 变化,而非一堆重格式化的文本。该操作会检查每个更改的 JSON/YAML/TOML/INI/.env/CSV/XML 文件,并发布一条置顶评论,仅显示键/值变更——重排的键、重格式化、注释和引号更改均被忽略。 ``yaml # .github/workflows/confdiff.yml name: confdiff on: pull_request permissions: contents: read pull-requests: write # 需要以发布评论
相似文章
Show HN: Codiff,本地差异审查工具
Codiff 是一款轻量级本地 diff 查看器,用于审查 Git 暂存和未暂存的更改,支持基于 LLM 的逐步讲解和内联审查评论。
@sashimikun_void: https://github.com/nkzw-tech/codiff… 酷炫的代码差异查看器,顺便一提……
Codiff 是一个美观的本地差异查看器,适用于 Git 仓库,具有基于大语言模型的代码讲解和行内审查评论功能,旨在简化提交前的代码审查流程。
@cnakazawa:Codiff 0.1 发布 * 快速本地代码审查 * 可选的 LLM 逐步讲解 * 内联审查评论 这是最佳的伴...
Codiff 0.1 是一个极简的本地差异查看器,用于查看 Git 更改,支持可选的 LLM 逐步讲解和内联审查评论,专为审查 AI 编程代理输出的代码而设计。
Diffsmith
Diffsmith 是一个工具,让你可以在 AI 代理的代码上添加评论并协作进行更改。
Show HN: RealDiff – 针对拉取请求的运行时行为差异分析(支持六种语言)
RealDiff 是一款开源工具,它通过比较测试输出来检测拉取请求中的运行时行为变更,甚至能在未直接编辑的文件中高亮显示非预期影响。