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 UPDATE = args.has("--update");
const ACCEPT_GLOSSARY_RESETS =
process.env.ACCEPT_GLOSSARY_ANCHOR_RESETS === "1";
// Ex: 0.2 => 20%
const THRESHOLD = Number(getArg("--threshold", process.env.ANCHORS_THRESHOLD ?? "0.2"));
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));
}
function loadAcceptedResets() {
function loadAnchorChurnAllowlist() {
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();
if (!raw) return {};
if (!raw) return { acceptedResets: {}, acceptedPrefixes: {} };
const data = JSON.parse(raw);
if (!data || typeof data !== "object" || Array.isArray(data)) {
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");
}
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 ACCEPTED_RESETS = loadAcceptedResets();
const { acceptedResets: ACCEPTED_RESETS, acceptedPrefixes: ACCEPTED_PREFIXES } =
loadAnchorChurnAllowlist();
async function buildSnapshot() {
const absDist = path.resolve(DIST_DIR);
@@ -190,7 +211,11 @@ function diffPage(prevIds, curIds) {
const prevN = prevIds.length || 1;
const churn = (added.length + 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(
`~ ${p} prev=${prevIds.length} now=${curIds.length}` +