From 5a24c71fe06f0e1955241d8e1b634d3405389499 Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Sun, 13 Sep 2026 13:04:20 +0800 Subject: [PATCH] =?UTF-8?q?feat(scripts):=20=E6=A0=A1=E9=AA=8C=E5=99=A8?= =?UTF-8?q?=E5=8A=A0=20--verbose=EF=BC=8C=E6=88=90=E5=8A=9F=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E4=B9=9F=E6=89=93=E5=8D=B0=E9=80=90=E7=AB=AF=E7=82=B9?= =?UTF-8?q?=E5=91=BD=E4=B8=AD=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 背景:changelog 校验器的端点解析结果原本只在「清单与详情对不上」时才进 errors, 成功路径零输出(只有一行 PASS)。于是「每个端点都被校验过」这件事**无法取证**—— #7439 AC-19 要求「贴校验器输出里逐端点的命中行、端点数对得上」,判据字面不可达成。 改法:模块级开关 VERBOSE_ENDPOINTS,main 识别 --verbose 后从 argv 剔除再走原解析 (避免打乱 argv[0] === '--files' 的判断)。打印格式: [endpoint] # [endpoint-count] 硬约束:**默认行为一个字节都不变**。它是提交门禁,加日志不得影响判定。 已验:同一组文件在改前/改后不带 --verbose 跑,stdout+stderr 逐字节 diff 为空; 仓内 14 条测试改前改后均 14 pass / 0 fail。 为什么值得改而不是改判据:逐端点证据对所有 changelog 都有用, 而「手工用同一条正则再枚举一遍」产出的不是校验器输出,等于用一个未经检验的 复制品去证明原件跑过了。 Co-Authored-By: Claude Opus 5 (1M context) --- scripts/validate-changelog-frontmatter.mjs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/scripts/validate-changelog-frontmatter.mjs b/scripts/validate-changelog-frontmatter.mjs index 5f2ebac8..fcaa1214 100644 --- a/scripts/validate-changelog-frontmatter.mjs +++ b/scripts/validate-changelog-frontmatter.mjs @@ -110,6 +110,15 @@ function sectionByPrefix(body, prefix, level = 2) { return lines.slice(start + 1, end).join('\n'); } +// --verbose 时把逐端点命中行打到 stdout。默认 false ⇒ 输出与加它之前逐字节相同。 +// 为什么需要它:本校验器的端点解析结果原本只在「清单与详情对不上」时才进 errors, +// 成功路径零输出,于是「每个端点都被校验过」这件事无法取证(#7439 AC-19)。 +let VERBOSE_ENDPOINTS = false; + +export function setVerboseEndpoints(on) { + VERBOSE_ENDPOINTS = Boolean(on); +} + function apiEndpointKey(method, endpointPath) { return `${method.trim().toUpperCase()} ${endpointPath.trim()}`; } @@ -166,6 +175,12 @@ function validateApiTemplate(file, metadata, body) { ); const detailMatches = [...detailSection.matchAll(detailPattern)]; const detailed = detailMatches.map((match) => apiEndpointKey(match[1], match[2])); + if (VERBOSE_ENDPOINTS) { + detailMatches.forEach((match, index) => { + console.log(`[endpoint] ${file} #${index + 1} ${match[1]} ${match[2].trim()}`); + }); + console.log(`[endpoint-count] ${file} ${detailMatches.length}`); + } if (detailMatches.length === 0) { errors.push(ruleError( 'E_API_TEMPLATE', @@ -477,6 +492,11 @@ export function runFilesValidation(files, root = process.cwd()) { } export function main(argv = process.argv.slice(2)) { + // --verbose 可出现在任意位置;剔除后再走原有解析,避免打乱 argv[0] === '--files' 的判断。 + if (argv.includes('--verbose')) { + setVerboseEndpoints(true); + argv = argv.filter((item) => item !== '--verbose'); + } try { if (argv[0] === '--files') { const files = argv.slice(1).flatMap((item) => item.split(',')).filter(Boolean);