diff --git a/scripts/sync-sched-publish.mjs b/scripts/sync-sched-publish.mjs index ea37f1a..132bfce 100644 --- a/scripts/sync-sched-publish.mjs +++ b/scripts/sync-sched-publish.mjs @@ -60,13 +60,25 @@ function copyTree(srcFile, dstFile) { function collect() { const out = shSilent(`git -C "${DEV}" ls-files`); if (out === null) { fail("dev не git-репо"); return null; } - const EXCL = /^\.wiki\/|^\.tasks\/|^\.agents\/|^AGENTS\.md$|^CLAUDE\.md$|^branding\/pora-|^pngtree-|^327274997_|^cat-clock-|^MIGRATION\.md$/; + const EXCL = /^\.wiki\/|^\.tasks\/|^\.agents\/|^\.mappa\/|^AGENTS\.md$|^CLAUDE\.md$|^branding\/pora-|^pngtree-|^327274997_|^cat-clock-|^MIGRATION\.md$/; return out.split("\n").filter((f) => f && !EXCL.test(f)); } +// dev-only артефакты, которые не должны жить в pub НИКОГДА (gotcha 15: .mappa/config.yaml +// несёт внутренний реестр — tenant/mappa.vds.kzntsv.site/git.kzntsv.site; в волне 2026-09-19 +// утёк в паблик-коммит, потому что exclusion-list его не знал) +const NEVER = [".mappa"]; +function purge() { + for (const p of NEVER) { + const t = `${PUB}/${p}`; + if (fs.existsSync(t)) { fs.rmSync(t, { recursive: true, force: true }); log(`purge: удалён ${p} (dev-only)`); } + } +} + function copy(files) { for (const f of files) copyTree(`${DEV}/${f}`, `${PUB}/${f}`); log(`copy: ${files.length} файлов`); + purge(); } // выбеливание — точечные замены, идемпотентно (эталон: верифицированный b0afd14) @@ -268,13 +280,32 @@ function gate() { return true; } +// Портативный скан (gotcha 16): прежний `grep -rnE '…'` через cmd.exe на Windows НЕ работал: +// cmd не снимает одинарные кавычки, а `|` для него — оператор pipe → команда падала, +// shSilent возвращал null, проверка молча считалась зелёной (0 хитов). Теперь скан — на Node. function grepCheck() { - const hits = shSilent(`cd "${PUB}" && grep -rnE 'git\\.kzntsv|verdaccio|vds\\.kzntsv|registry\\.kzntsv|VERDACCIO' --exclude-dir=node_modules --exclude-dir=.git .`); - if (hits && hits.length > 0) { - fail(`grep: остались ${hits.split("\n").length} вхождений kzntsv/verdaccio`); + const re = /git\.kzntsv|verdaccio|vds\.kzntsv|registry\.kzntsv|VERDACCIO/i; + const SKIP = new Set(["node_modules", ".git", ".yarn"]); + const BIN = /\.(svg|png|webp|jpg|jpeg|gif|ico|woff2?|ttf|zip|gz)$/i; + const hits = []; + const walk = (dir) => { + for (const e of fs.readdirSync(dir, { withFileTypes: true })) { + const p = `${dir}/${e.name}`; + if (e.isDirectory()) { if (!SKIP.has(e.name)) walk(p); continue; } + if (BIN.test(p)) continue; + let data; + try { data = fs.readFileSync(p, "utf8"); } catch { continue; } + data.split("\n").forEach((line, i) => { + if (re.test(line)) hits.push(`${path.relative(PUB, p)}:${i + 1}: ${line.trim().slice(0, 140)}`); + }); + } + }; + walk(PUB); + if (hits.length > 0) { + fail(`grep: остались ${hits.length} вхождений kzntsv/verdaccio\n` + hits.slice(0, 10).join("\n")); return false; } - log("grep: 0 вхождений kzntsv/verdaccio"); + log("grep: 0 вхождений kzntsv/verdaccio (портативный скан)"); return true; }