SQL Formatter 接入 CI/CD:检查、自动修改与失败策略
SQL formatter 接入 CI/CD 的核心不是增加一条泛化流水线,而是明确格式规范、检查模式、自动修改边界和失败策略。开发机可以自动写回,CI 通常只检查并在发现差异时失败;两者使用同一方言和配置,才能让结果可重复。
一、问题概述:格式漂移需要可重复的门禁
同一仓库中的 SQL 如果由不同配置格式化,提交差异会反复出现。CI 需要判断“文件是否已经符合 formatter 输出”,而不是在构建环境中默默覆盖开发者提交;自动修改和只读检查应是两个显式模式。
二、最小复现:check 与 write 的结果不同
下面的函数通过注入 formatter,避免假设某个命令行工具或参数名称。check 只返回差异,write 才产生新内容;CI 可以据此决定退出状态。
type Format = (sql: string) => string;
type Mode = "check" | "write";
function processSql(source: string, mode: Mode, format: Format) {
const formatted = format(source);
const changed = formatted !== source;
return { changed, output: mode === "write" ? formatted : source };
}
const format = (sql: string) => sql.trim() + "\n";
console.log(processSql("SELECT 1", "check", format)); // changed: true, output: original
console.log(processSql("SELECT 1", "write", format)); // output: formatted三、根因:CI 必须固定输入范围与 formatter 配置
没有固定文件 glob、方言、换行符和配置版本时,格式检查会在本地与 CI 之间漂移。还要决定生成文件、迁移脚本、fixture 和嵌入字符串是否属于检查范围;不在范围内的文件不应被隐式改写。
四、推荐方案:本地 write,CI check,差异即失败
开发者可在提交前运行 write 模式查看并审阅修改;CI 使用 check 模式,只要 formatter 输出与仓库文件不同就失败,并打印 diff 或文件列表。formatter、dialect 和配置应锁定,自动修复不应在 CI 中直接提交或覆盖工作区。
五、完整代码:按模式汇总失败与自动修改
以下示例处理一组内存文件,展示 CI 的检查逻辑。实际仓库读取、diff 展示和退出码由项目脚本实现;函数本身不依赖某个 formatter CLI。
type Format = (sql: string) => string;
type Mode = "check" | "write";
type File = { path: string; content: string };
function formatFiles(files: File[], mode: Mode, format: Format) {
const changed: File[] = [];
const output = files.map((file) => {
const next = format(file.content);
if (next !== file.content) changed.push(file);
return mode === "write" ? { ...file, content: next } : file;
});
return { changed, output, ok: mode === "write" || changed.length === 0 };
}
const format = (sql: string) => sql.trim() + "\n";
console.log(formatFiles([{ path: "query.sql", content: "SELECT 1" }], "check", format).ok); // false六、常见错误方案
在 CI 中无条件写回文件会让构建工作区与提交内容不一致;只检查部分 SQL 文件会产生虚假的绿色结果;格式化失败时把原文件静默替换为空文本会掩盖语法或方言问题。把 SQL formatter 检查和无关的部署、监控模板混在一起,也会让失败原因不清晰。
七、边界条件:生成文件、方言和人工例外
明确是否检查迁移文件、快照、fixture、注释中的 SQL 和多方言目录。例外文件应有可审阅的配置或注释,而不是在脚本里散落路径判断。formatter 无法识别的 vendor extension 应失败并给出文件位置,或进入明确的保留列表。
八、如何验证 CI 行为
准备已格式化文件、故意多一个空格、不同换行符、错误 dialect 和 formatter 抛错五类样本。断言 check 在有差异或出错时失败、write 只修改预期文件、失败输出包含路径,并确认 CI 不会自动提交生成结果。
九、FAQ
问:CI 应该自动修复并提交吗?答:通常让 CI 报告差异更可审阅;是否自动提交必须由仓库流程明确决定。
问:格式化失败和格式不一致要区分吗?答:要,前者可能是 parser/dialect 问题,后者是可修复的风格差异。
问:为什么本地和 CI 结果不同?答:检查 formatter 版本、方言、配置、换行符和文件范围是否一致。
十、总结
SQL formatter 的 CI/CD 接入应固定配置和文件范围,开发机使用 write,CI 使用只读 check,差异或格式化错误按明确策略失败。输出路径和 diff,避免 CI 默默改写或提交文件,并把方言例外单独记录。
来源与延伸阅读
技术审校所依据的规范与权威参考资料。
- PostgreSQL Documentation — SQL Syntax
PostgreSQL Global Development Group
- RFC 8259 — The JavaScript Object Notation (JSON) Data Interchange Format
RFC Editor
相关文章
SQL Formatter 方言兼容:关键字、函数与语法保真
说明 SQL formatter 为什么必须声明方言,比较 PostgreSQL、MySQL 与 SQLite 的关键字、函数、引号和语法保真边界。
实现原理SQL Formatter Lexer 与高亮引擎:Token、缩进状态和能力边界
从 lexer/token 角度说明 SQL 高亮和缩进状态机如何工作,并明确语法高亮引擎不是完整 SQL parser。
最佳实践SQL Dump 转 JSON Mock:解析、脱敏、批量与类型边界
从 SQL dump 生成测试 JSON 时,分别处理 INSERT parser、敏感字段脱敏、批量行和 SQL 类型限制,不把 Mock 转换当成数据库迁移。
继续阅读
可打开关联的浏览器工具,使用自己的样本验证文中的处理流程。
打开关联工具