Supporte les resets d'ancres par préfixe
All checks were successful
SMOKE / smoke (push) Successful in 6s
CI / build-and-anchors (push) Successful in 39s
CI / build-and-anchors (pull_request) Successful in 42s

This commit is contained in:
2026-04-27 22:58:27 +02:00
parent 6ed2cd4284
commit abcba413f5

View File

@@ -14,6 +14,9 @@ const DIST_DIR = getArg("--dist", "dist");
const BASELINE = getArg("--baseline", path.join("tests", "anchors-baseline.json")); const BASELINE = getArg("--baseline", path.join("tests", "anchors-baseline.json"));
const UPDATE = args.has("--update"); const UPDATE = args.has("--update");
const ACCEPT_GLOSSARY_RESETS =
process.env.ACCEPT_GLOSSARY_ANCHOR_RESETS === "1";
// Ex: 0.2 => 20% // Ex: 0.2 => 20%
const THRESHOLD = Number(getArg("--threshold", process.env.ANCHORS_THRESHOLD ?? "0.2")); const THRESHOLD = Number(getArg("--threshold", process.env.ANCHORS_THRESHOLD ?? "0.2"));
const MIN_PREV = Number(getArg("--min-prev", process.env.ANCHORS_MIN_PREV ?? "10")); const MIN_PREV = Number(getArg("--min-prev", process.env.ANCHORS_MIN_PREV ?? "10"));
@@ -74,24 +77,42 @@ function loadAllowMissing() {
return new Set(arr.map(String)); return new Set(arr.map(String));
} }
function loadAcceptedResets() { function loadAnchorChurnAllowlist() {
const p = path.resolve("config/anchor-churn-allowlist.json"); const p = path.resolve("config/anchor-churn-allowlist.json");
if (!fssync.existsSync(p)) return {}; if (!fssync.existsSync(p)) return { acceptedResets: {}, acceptedPrefixes: {} };
const raw = fssync.readFileSync(p, "utf8").trim(); const raw = fssync.readFileSync(p, "utf8").trim();
if (!raw) return {}; if (!raw) return { acceptedResets: {}, acceptedPrefixes: {} };
const data = JSON.parse(raw); const data = JSON.parse(raw);
if (!data || typeof data !== "object" || Array.isArray(data)) { if (!data || typeof data !== "object" || Array.isArray(data)) {
throw new Error("anchor-churn-allowlist.json must be an object"); throw new Error("anchor-churn-allowlist.json must be an object");
} }
const accepted = data.accepted_resets || {};
if (!accepted || typeof accepted !== "object" || Array.isArray(accepted)) { const acceptedResets = data.accepted_resets || {};
if (!acceptedResets || typeof acceptedResets !== "object" || Array.isArray(acceptedResets)) {
throw new Error("anchor-churn-allowlist.json: accepted_resets must be an object"); throw new Error("anchor-churn-allowlist.json: accepted_resets must be an object");
} }
return accepted;
const acceptedPrefixes = data.accepted_prefixes || {};
if (!acceptedPrefixes || typeof acceptedPrefixes !== "object" || Array.isArray(acceptedPrefixes)) {
throw new Error("anchor-churn-allowlist.json: accepted_prefixes must be an object");
}
return { acceptedResets, acceptedPrefixes };
}
function acceptedResetReasonForPage(page) {
if (ACCEPTED_RESETS[page]) return ACCEPTED_RESETS[page];
for (const [prefix, reason] of Object.entries(ACCEPTED_PREFIXES)) {
if (page.startsWith(prefix)) return reason;
}
return null;
} }
const ALLOW_MISSING = loadAllowMissing(); const ALLOW_MISSING = loadAllowMissing();
const ACCEPTED_RESETS = loadAcceptedResets(); const { acceptedResets: ACCEPTED_RESETS, acceptedPrefixes: ACCEPTED_PREFIXES } =
loadAnchorChurnAllowlist();
async function buildSnapshot() { async function buildSnapshot() {
const absDist = path.resolve(DIST_DIR); const absDist = path.resolve(DIST_DIR);
@@ -190,7 +211,11 @@ function diffPage(prevIds, curIds) {
const prevN = prevIds.length || 1; const prevN = prevIds.length || 1;
const churn = (added.length + removed.length) / prevN; const churn = (added.length + removed.length) / prevN;
const removedRatio = removed.length / prevN; const removedRatio = removed.length / prevN;
const acceptedReason = ACCEPTED_RESETS[p] || null; const acceptedReason =
ACCEPTED_RESETS[p] ||
(ACCEPT_GLOSSARY_RESETS && p.startsWith("glossaire/")
? "Reset intentionnel des ancres du glossaire après refonte éditoriale substantielle."
: null);
console.log( console.log(
`~ ${p} prev=${prevIds.length} now=${curIds.length}` + `~ ${p} prev=${prevIds.length} now=${curIds.length}` +