scripts: validate-changelog-frontmatter 增加 --files 提交前自检模式(与 pre-push 门禁同规则,供作者/Agent 与 hl-workflow lint 复用)
changelog-filename-gate / validate (push) Successful in 2s
changelog-filename-gate / validate (push) Successful in 2s
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XYL5S9SsBtkg7aGyFAbrrQ
这个提交包含在:
@@ -447,8 +447,51 @@ export function runFrontmatterValidation(records, root = process.cwd()) {
|
|||||||
return { checkedCount: documents.length, errors };
|
return { checkedCount: documents.length, errors };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提交前自检模式:`--files a.md b.md`(或逗号分隔)按「新增文件」口径校验工作区里的 changelog,
|
||||||
|
* 让作者/Agent 不用先 commit 就能跑到与 pre-push 门禁完全相同的规则(hl-workflow changelog_workflow.py lint 也走这里)。
|
||||||
|
*/
|
||||||
|
export function runFilesValidation(files, root = process.cwd()) {
|
||||||
|
const errors = [];
|
||||||
|
let checkedCount = 0;
|
||||||
|
for (const raw of files) {
|
||||||
|
const absolute = path.resolve(root, raw);
|
||||||
|
const relative = path.relative(root, absolute).split(path.sep).join('/');
|
||||||
|
if (!controlledRootForPath(relative) || !relative.endsWith('.md')) {
|
||||||
|
errors.push(ruleError('E_PATH', relative, '不在受控 changelog 目录内(changelogs/ 或 changelogs-v2/)'));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let text;
|
||||||
|
try {
|
||||||
|
text = readFileSync(absolute, 'utf8');
|
||||||
|
} catch (error) {
|
||||||
|
errors.push(ruleError('E_READ', relative, `无法读取文件: ${error.message}`));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
checkedCount += 1;
|
||||||
|
errors.push(...validateV2Document(relative, text, { requireV2: true }));
|
||||||
|
}
|
||||||
|
return { checkedCount, errors };
|
||||||
|
}
|
||||||
|
|
||||||
export function main(argv = process.argv.slice(2)) {
|
export function main(argv = process.argv.slice(2)) {
|
||||||
try {
|
try {
|
||||||
|
if (argv[0] === '--files') {
|
||||||
|
const files = argv.slice(1).flatMap((item) => item.split(',')).filter(Boolean);
|
||||||
|
if (files.length === 0) {
|
||||||
|
throw new Error('--files 需要至少一个文件路径');
|
||||||
|
}
|
||||||
|
const result = runFilesValidation(files);
|
||||||
|
if (result.errors.length > 0) {
|
||||||
|
for (const error of result.errors) {
|
||||||
|
console.error(`[${error.code}] ${error.path}: ${error.message}`);
|
||||||
|
}
|
||||||
|
console.error(`FAIL: ${result.errors.length} frontmatter error(s) in ${result.checkedCount} changelog file(s).`);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
console.log(`PASS: validated frontmatter for ${result.checkedCount} changelog file(s) (--files).`);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
const options = parseArguments(argv);
|
const options = parseArguments(argv);
|
||||||
const records = parseNameStatusZ(diffFromOptions(options));
|
const records = parseNameStatusZ(diffFromOptions(options));
|
||||||
const result = runFrontmatterValidation(records);
|
const result = runFrontmatterValidation(records);
|
||||||
|
|||||||
在新工单中引用
屏蔽一个用户