ci: 服务端 commit message 校验(Conventional + Feature-Confirmed trailer)
All checks were successful
Push & PR Check / check (push) Successful in 54s
All checks were successful
Push & PR Check / check (push) Successful in 54s
lefthook 在 agent 沙箱(DSH workspace-write)内静默失效的机器层硬补偿, dsh-takeover-readiness T2。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
246d562b15
commit
1c3133c1dd
67
.gitea/scripts/check-commit-msg.mjs
Normal file
67
.gitea/scripts/check-commit-msg.mjs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// 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 校验通过');
|
||||||
@ -12,6 +12,8 @@ jobs:
|
|||||||
runs-on: windows
|
runs-on: windows
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v4
|
||||||
with:
|
with:
|
||||||
@ -23,6 +25,15 @@ jobs:
|
|||||||
node-version: '20'
|
node-version: '20'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|
||||||
|
# 服务端 commit message 校验(lefthook 在 agent 沙箱内会静默失效,此为机器层硬补偿)
|
||||||
|
- name: Commit message 校验
|
||||||
|
env:
|
||||||
|
EVENT_NAME: ${{ github.event_name }}
|
||||||
|
BEFORE_SHA: ${{ github.event.before }}
|
||||||
|
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||||
|
HEAD_SHA: ${{ github.sha }}
|
||||||
|
run: node .gitea/scripts/check-commit-msg.mjs
|
||||||
|
|
||||||
- run: pnpm install --frozen-lockfile
|
- run: pnpm install --frozen-lockfile
|
||||||
- run: pnpm typecheck
|
- run: pnpm typecheck
|
||||||
- run: pnpm test
|
- run: pnpm test
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user