From 354d9083a70235b9575c4d671d38c0591b03be6b Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Sun, 6 Sep 2026 16:18:27 +0800 Subject: [PATCH] =?UTF-8?q?scripts:=20validate-changelog-frontmatter=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20--files=20=E6=8F=90=E4=BA=A4=E5=89=8D?= =?UTF-8?q?=E8=87=AA=E6=A3=80=E6=A8=A1=E5=BC=8F=EF=BC=88=E4=B8=8E=20pre-pu?= =?UTF-8?q?sh=20=E9=97=A8=E7=A6=81=E5=90=8C=E8=A7=84=E5=88=99=EF=BC=8C?= =?UTF-8?q?=E4=BE=9B=E4=BD=9C=E8=80=85/Agent=20=E4=B8=8E=20hl-workflow=20l?= =?UTF-8?q?int=20=E5=A4=8D=E7=94=A8=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01XYL5S9SsBtkg7aGyFAbrrQ --- scripts/validate-changelog-frontmatter.mjs | 43 ++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/scripts/validate-changelog-frontmatter.mjs b/scripts/validate-changelog-frontmatter.mjs index 74cbb9ae..54bf11cc 100644 --- a/scripts/validate-changelog-frontmatter.mjs +++ b/scripts/validate-changelog-frontmatter.mjs @@ -447,8 +447,51 @@ export function runFrontmatterValidation(records, root = process.cwd()) { 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)) { 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 records = parseNameStatusZ(diffFromOptions(options)); const result = runFrontmatterValidation(records);