From 7072f77f884891fcd49bca364fb686ee2a0a18b3 Mon Sep 17 00:00:00 2001 From: HIsheR Date: Fri, 23 Oct 2026 18:07:37 +0800 Subject: [PATCH] =?UTF-8?q?feat(cli):=20=E6=96=B0=E5=A2=9E=20commit-lint?= =?UTF-8?q?=20=E6=8F=90=E4=BA=A4=E4=BF=A1=E6=81=AF=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/tools/cli/bin/ua.js | 39 +++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/packages/tools/cli/bin/ua.js b/packages/tools/cli/bin/ua.js index 8faf0e8..e6aa9c1 100644 --- a/packages/tools/cli/bin/ua.js +++ b/packages/tools/cli/bin/ua.js @@ -638,6 +638,40 @@ function cmdVerify() { console.log('verify OK (manifest ↔ docs ↔ demo 一致,皮肤无硬编码颜色,README 组件清单同步)'); } +/*** 校验 git 提交信息是否符合 COMMITTING.md 规范(参数为 message 文件路径或直接 message 文本),违规退出码 1 ***/ +function cmdCommitLint(args) { + // 输入来源:文件路径(commit-msg 钩子传参)或参数拼接 + let msg = ''; + if (args[0] && fs.existsSync(args[0])) msg = fs.readFileSync(args[0], 'utf8'); + else msg = args.join(' '); + if (!msg.trim()) { console.error('commit-lint: 提交信息为空'); process.exit(1); } + + const head = msg.split('\n')[0].trimEnd(); + // git 自动生成的合并/回滚信息直接放行 + if (/^(Merge |Revert |fixup!|squash!)/.test(head)) return console.log('commit-lint OK (git 自动信息,跳过校验)'); + + const TYPES = ['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore', 'iter']; + const m = head.match(/^(\w+)(?:\(([^)]*)\))?: (.+)$/); + if (!m) { console.error('commit-lint: 首行须为 (): ,规范见 COMMITTING.md'); process.exit(1); } + const [, type, scope, subject] = m; + const problems = []; + // type 枚举 + if (!TYPES.includes(type)) problems.push(`type「${type}」不在枚举:${TYPES.join('/')}`); + // iter 提交 scope 必须是 backlog 条目 id + if (type === 'iter' && !/^[a-z]\d{2,}$/.test(scope ?? '')) problems.push('iter 提交的 scope 必须是 backlog 条目 id(如 b02/e03)'); + // 非 iter 提交 scope 仅允许小写字母与连字符 + if (type !== 'iter' && scope && !/^[a-z][a-z-]*$/.test(scope)) problems.push(`scope「${scope}」仅允许小写字母与连字符`); + // subject 长度与标点 + if ([...subject].length > 50) problems.push(`subject 超过 50 字符(当前 ${[...subject].length})`); + if (/[。..!!??]$/.test(subject.trim())) problems.push('subject 结尾不加句号'); + // body 行宽 + for (const line of msg.split('\n').slice(1)) { + if (line.length > 80) problems.push(`body 行超过 80 字符:${line.slice(0, 20)}…`); + } + if (problems.length) { for (const p of problems) console.error('commit-lint: ' + p); process.exit(1); } + console.log(`commit-lint OK (${type}${scope ? `(${scope})` : ''})`); +} + // ---- 入口 ---- const [cmd, ...args] = process.argv.slice(2); @@ -664,10 +698,13 @@ switch (cmd) { case 'verify': cmdVerify(); break; + case 'commit-lint': + cmdCommitLint(args); + break; case 'mcp': cmdMcp(); break; default: - console.log('usage: ua |docs|style|verify|mcp>'); + console.log('usage: ua |docs|style|verify|commit-lint|mcp>'); process.exit(cmd ? 1 : 0); }