fix(docs-sync): prune orphan locale docs whose English source no longer exists

The publish workflow rsyncs source docs/ into the publish repo with --delete,
but explicitly protects locale directories so translation files survive
non-translation-pipeline syncs. When an English source file is renamed (for
example install/migrating-matrix.md -> channels/matrix-migration.md), the
locale copies at <locale>/install/migrating-matrix.md become orphans:
deleted from the English nav but still present on disk.

Mintlify's hosted build appears to silently fall back to the previous
deployment when nav references a path with mixed locale availability, so
recent docs changes (the migration hub rework, matrix-migration move) are
not propagating to docs.openclaw.ai even though every CI run reports
success and the publish repo has the right English content.

Add a pruneOrphanLocaleDocs() pass that walks every generated-locale
directory in the publish target and removes any .md/.mdx file whose
matching English path no longer exists in source docs. Runs after rsync
and before composing docs.json so the regenerated nav and the on-disk
files stay consistent. Verified the logic against the live publish repo:
identifies all ja-JP/es/pt-BR/ko/de/fr/ar/it/tr/uk/id/pl/zh-CN orphans of
install/migrating-matrix.md (12 entries) and would also catch any future
renames the same way.
This commit is contained in:
Vincent Koc
2026-04-27 03:34:52 -07:00
parent ca88daad1e
commit 3a73826e28

View File

@@ -289,6 +289,32 @@ function composeDocsConfig() {
};
}
function pruneOrphanLocaleDocs(targetDocsDir) {
let pruned = 0;
for (const locale of GENERATED_LOCALES) {
const localeDir = path.join(targetDocsDir, locale.dir);
if (!fs.existsSync(localeDir)) {
continue;
}
for (const filePath of walkMarkdownFiles(localeDir)) {
const relativeToLocale = path.relative(localeDir, filePath);
// The English source file lives at docs/<relativeToLocale> with either .md or .mdx.
const englishBase = path.join(SOURCE_DOCS_DIR, relativeToLocale);
const englishMd = englishBase.replace(/\.mdx?$/i, ".md");
const englishMdx = englishBase.replace(/\.mdx?$/i, ".mdx");
if (fs.existsSync(englishMd) || fs.existsSync(englishMdx)) {
continue;
}
fs.rmSync(filePath, { force: true });
pruned += 1;
}
}
if (pruned > 0) {
console.log(`Pruned ${pruned} orphan localized doc(s) with no matching English source file.`);
}
}
function repairGeneratedLocaleDocs(targetDocsDir) {
let repaired = 0;
for (const locale of GENERATED_LOCALES) {
@@ -345,6 +371,7 @@ function syncDocsTree(targetRoot) {
}
}
pruneOrphanLocaleDocs(targetDocsDir);
repairGeneratedLocaleDocs(targetDocsDir);
writeJson(path.join(targetDocsDir, "docs.json"), composeDocsConfig());
}