dev-manager-tauri/.gitea/scripts/check-commit-msg.mjs
lanrtop 1c3133c1dd
All checks were successful
Push & PR Check / check (push) Successful in 54s
ci: 服务端 commit message 校验(Conventional + Feature-Confirmed trailer)
lefthook 在 agent 沙箱(DSH workspace-write)内静默失效的机器层硬补偿,
dsh-takeover-readiness T2。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-11 21:16:01 +09:00

68 lines
2.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// CI 侧 commit message 校验Conventional Commits + feat 须带 Feature-Confirmed trailer。
// 与 lefthook commit-msg 同一套规则的服务端硬补偿——agent 沙箱(如 DSH workspace-write
// 内 lefthook 会静默失效本脚本保证机器校验层不缺位dsh-takeover-readiness T2
// 环境变量EVENT_NAME / BEFORE_SHA / BASE_SHA / HEAD_SHA由 workflow 从 github context 注入)
import { execSync } from 'node:child_process';
const git = (cmd) => execSync(`git ${cmd}`, { encoding: 'utf8' }).trim();
const CONVENTIONAL =
/^(feat|fix|docs|chore|refactor|ci|perf|style|test|build|revert)(\(.+\))?!?: .+/;
const ZERO_SHA = /^0+$/;
const { EVENT_NAME, BEFORE_SHA, BASE_SHA, HEAD_SHA } = process.env;
let range;
if (EVENT_NAME === 'pull_request' && BASE_SHA) {
range = `${BASE_SHA}..${HEAD_SHA}`;
} else if (BEFORE_SHA && !ZERO_SHA.test(BEFORE_SHA)) {
range = `${BEFORE_SHA}..${HEAD_SHA}`;
} else {
// 新分支首推等场景拿不到 before与 master 比对兜底,再退化为仅查 HEAD
try {
git('fetch origin master --quiet');
range = `origin/master..${HEAD_SHA || 'HEAD'}`;
} catch {
range = `${HEAD_SHA || 'HEAD'} -1`;
}
}
let shas;
try {
shas = git(`rev-list --no-merges ${range}`).split('\n').filter(Boolean);
} catch {
// force push 后旧 before 在 clone 里不存在等场景:退化为与 master 比对,再退化为仅查 HEAD
try {
git('fetch origin master --quiet');
range = `origin/master..${HEAD_SHA || 'HEAD'}`;
shas = git(`rev-list --no-merges ${range}`).split('\n').filter(Boolean);
} catch {
range = 'HEAD -1';
shas = [git('rev-parse HEAD')];
}
}
console.log(`commit message 校验:范围 ${range},共 ${shas.length} 个 commit`);
const errors = [];
for (const sha of shas) {
const subject = git(`log -1 --format=%s ${sha}`);
if (!CONVENTIONAL.test(subject)) {
errors.push(`${sha.slice(0, 8)} 首行不符合 Conventional Commits${subject}`);
continue;
}
if (/^feat/.test(subject)) {
const body = git(`log -1 --format=%B ${sha}`);
if (!body.includes('Feature-Confirmed: true')) {
errors.push(`${sha.slice(0, 8)} feat 提交缺少 "Feature-Confirmed: true" trailer${subject}`);
}
}
}
if (errors.length > 0) {
console.error('\n❌ commit message 校验失败:');
for (const e of errors) console.error(` ${e}`);
console.error('\n 规则:首行须为 <type>(<scope>): <subject>feat 提交末尾须有 Feature-Confirmed: true');
process.exit(1);
}
console.log('✅ commit message 校验通过');