chore(scripts): syncDecorations fails loudly on unmatched entries; patch-folds is diagnosable and idempotent
- syncDecorations.mjs tracks which slugs its regex actually removed and exits 1 without writing if that set doesn't match the missing assets, instead of silently no-op'ing on a reformatted catalog. - patch-folds.mjs matches a 4-line context block, reports the installed folds version and expected snippet when the target is missing, and distinguishes "already patched" (exit 0) from "pattern not found" (exit 1). Verified against all three states. Fixes #88 Fixes #55 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
@@ -103,9 +103,38 @@ missing.forEach((r) => console.log(` Removing (HTTP ${r.status}): ${r.slug}`));
|
||||
const missingSet = new Set(missing.map((r) => r.slug));
|
||||
|
||||
// Remove individual entries for missing slugs
|
||||
let updated = catalog.replace(/^[ \t]*\{ slug: '([^']+)', name: .+\},?\r?\n/gm, (match, slug) =>
|
||||
missingSet.has(slug) ? '' : match,
|
||||
);
|
||||
const removedSlugs = new Set();
|
||||
let updated = catalog.replace(/^[ \t]*\{ slug: '([^']+)', name: .+\},?\r?\n/gm, (match, slug) => {
|
||||
if (!missingSet.has(slug)) return match;
|
||||
removedSlugs.add(slug);
|
||||
return '';
|
||||
});
|
||||
|
||||
// Regex-based removal is brittle: if the catalog is reformatted (different
|
||||
// indentation, line-wrapped entries, etc.) the pattern above can silently
|
||||
// match zero entries while HTTP probing still reports slugs missing. Verify
|
||||
// every slug we intended to remove actually got matched — otherwise abort
|
||||
// without writing, so a formatting change fails loudly instead of leaving
|
||||
// stale/dead entries in the catalog (see Gitea #88).
|
||||
const unmatched = [...missingSet].filter((slug) => !removedSlugs.has(slug));
|
||||
if (unmatched.length > 0) {
|
||||
console.error(
|
||||
`Aborting: expected to remove ${missingSet.size} entr${missingSet.size === 1 ? 'y' : 'ies'} ` +
|
||||
`but only matched ${removedSlugs.size}. The catalog's formatting may have changed and the ` +
|
||||
`parser in scripts/syncDecorations.mjs needs updating. Refusing to write a partial result.`,
|
||||
);
|
||||
console.error(` Unmatched slugs: ${unmatched.join(', ')}`);
|
||||
process.exit(1);
|
||||
}
|
||||
if (removedSlugs.size === 0) {
|
||||
// We already exited above when `missing.length === 0`, so reaching here
|
||||
// with zero removals despite `missing.length > 0` means the diff between
|
||||
// "expected" and "actual" itself is broken — fail rather than proceed.
|
||||
console.error(
|
||||
'Aborting: no entries were matched for removal despite missing slugs. Refusing to write.',
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Drop category blocks that now have an empty decorations array
|
||||
updated = updated.replace(
|
||||
@@ -118,6 +147,6 @@ updated = updated.replace(/\n{3,}/g, '\n\n');
|
||||
|
||||
writeFileSync(catalogPath, updated, 'utf8');
|
||||
console.log(
|
||||
`\nDone. Removed ${missing.length} entr${missing.length === 1 ? 'y' : 'ies'} from the catalog.`,
|
||||
`\nDone. Removed ${removedSlugs.size} entr${removedSlugs.size === 1 ? 'y' : 'ies'} from the catalog.`,
|
||||
);
|
||||
console.log('Review with: git diff src/app/features/lotus/avatarDecorations.ts');
|
||||
|
||||
Reference in New Issue
Block a user