mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-09-06 18:20:52 +03:00
test(check-inline-drift): гард видит копии во всех скриптах навыка
Индекс брал первый файл навыка по алфавиту, поэтому второй скрипт был для гарда невидим. Единственный такой навык — epf-build (epf-build + stub-db-create), и в невидимой копии прожили три расхождения с эталонами, включая потерянную POSIX-ветку run_v8. Теперь сверяются все копии, в сообщении указывается файл. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013MQkqXxErBepYmoUycfkbn
This commit is contained in:
co-authored by
Claude Opus 5
parent
448f05ed11
commit
368d6bb278
@@ -281,9 +281,7 @@ const FAMILIES = [
|
|||||||
{ id: 'base', authority: 'db-create',
|
{ id: 'base', authority: 'db-create',
|
||||||
consumers: ['db-cfe-admin', 'db-dump-cf', 'db-dump-dt', 'db-dump-xml', 'db-load-cf', 'db-load-dt',
|
consumers: ['db-cfe-admin', 'db-dump-cf', 'db-dump-dt', 'db-dump-xml', 'db-load-cf', 'db-load-dt',
|
||||||
'db-load-git', 'db-load-xml', 'db-repo', 'db-run', 'db-update', 'epf-build', 'epf-dump'] },
|
'db-load-git', 'db-load-xml', 'db-repo', 'db-run', 'db-update', 'epf-build', 'epf-dump'] },
|
||||||
// NB: stub-db-create.py внутри epf-build держит свою копию с выводом в stderr, но гарду она
|
// В epf-build два скрипта, и копия семьи есть в каждом — гард сверяет обе.
|
||||||
// не видна: индекс берёт первый файл навыка по алфавиту (epf-build.py). Правя эту семью,
|
|
||||||
// не забывать про стаб — автоматически он не проверяется.
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -636,8 +634,12 @@ function hashBody(body, lang) {
|
|||||||
|
|
||||||
// ─── Индекс: навык+порт → функции ───────────────────────────────────────────
|
// ─── Индекс: навык+порт → функции ───────────────────────────────────────────
|
||||||
|
|
||||||
|
// Навык может держать несколько скриптов (epf-build: epf-build + stub-db-create), и копия
|
||||||
|
// семьи живёт в каждом из них. Раньше индекс брал ПЕРВУЮ копию по алфавиту, и вторая
|
||||||
|
// была гарду невидима: в stub-db-create.py так прожили три расхождения, одно из них —
|
||||||
|
// потерянная POSIX-ветка run_v8. Теперь сверяются ВСЕ копии.
|
||||||
function buildIndex() {
|
function buildIndex() {
|
||||||
const index = new Map(); // `${skill}|${lang}` -> Map(name -> body)
|
const index = new Map(); // `${skill}|${lang}` -> Map(name -> [{ file, body }])
|
||||||
for (const skill of readdirSync(SKILLS)) {
|
for (const skill of readdirSync(SKILLS)) {
|
||||||
const dir = join(SKILLS, skill, 'scripts');
|
const dir = join(SKILLS, skill, 'scripts');
|
||||||
if (!existsSync(dir)) continue;
|
if (!existsSync(dir)) continue;
|
||||||
@@ -648,13 +650,18 @@ function buildIndex() {
|
|||||||
const fns = lang === 'py' ? extractPy(text) : extractPs1(text);
|
const fns = lang === 'py' ? extractPy(text) : extractPs1(text);
|
||||||
const key = `${skill}|${lang}`;
|
const key = `${skill}|${lang}`;
|
||||||
const acc = index.get(key) || new Map();
|
const acc = index.get(key) || new Map();
|
||||||
for (const [n, b] of fns) if (!acc.has(n)) acc.set(n, b);
|
for (const [n, b] of fns) acc.set(n, [...(acc.get(n) || []), { file, body: b }]);
|
||||||
index.set(key, acc);
|
index.set(key, acc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Все копии функции в навыке: [{ file, body }]
|
||||||
|
function copiesOf(index, skill, lang, name) {
|
||||||
|
return index.get(`${skill}|${lang}`)?.get(name) || [];
|
||||||
|
}
|
||||||
|
|
||||||
// ─── Проверка ───────────────────────────────────────────────────────────────
|
// ─── Проверка ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
const index = buildIndex();
|
const index = buildIndex();
|
||||||
@@ -687,21 +694,27 @@ for (const family of FAMILIES) {
|
|||||||
// 1. Каждый объявленный потребитель содержит функцию, и внутри варианта тела совпадают.
|
// 1. Каждый объявленный потребитель содержит функцию, и внутри варианта тела совпадают.
|
||||||
const hashByVariant = new Map();
|
const hashByVariant = new Map();
|
||||||
for (const { v, members } of effective) {
|
for (const { v, members } of effective) {
|
||||||
const hashes = new Map();
|
const hashes = new Map(); // `${skill}` или `${skill} (${file})` -> хеш
|
||||||
for (const skill of members) {
|
for (const skill of members) {
|
||||||
const body = index.get(`${skill}|${lang}`)?.get(fnName);
|
const copies = copiesOf(index, skill, lang, fnName);
|
||||||
if (!body) {
|
if (!copies.length) {
|
||||||
errors.push(`${family.name} [${lang}]: ${skill} объявлен в варианте '${v.id}', но функции ${fnName} в нём нет`);
|
errors.push(`${family.name} [${lang}]: ${skill} объявлен в варианте '${v.id}', но функции ${fnName} в нём нет`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
hashes.set(skill, hashBody(body, lang));
|
for (const { file, body } of copies) {
|
||||||
|
// Эталон берётся из главного скрипта навыка (`<навык>.ps1`/`.py`), остальные
|
||||||
|
// его скрипты — такие же копии и сверяются наравне.
|
||||||
|
const main = file.startsWith(`${skill}.`);
|
||||||
|
const label = main ? skill : `${skill} (${file})`;
|
||||||
|
hashes.set(label, { hash: hashBody(body, lang), main, skill });
|
||||||
}
|
}
|
||||||
const authorityHash = hashes.get(v.authority);
|
}
|
||||||
|
const authorityHash = hashes.get(v.authority)?.hash;
|
||||||
if (authorityHash === undefined) continue;
|
if (authorityHash === undefined) continue;
|
||||||
hashByVariant.set(v.id, authorityHash);
|
hashByVariant.set(v.id, authorityHash);
|
||||||
for (const [skill, h] of hashes) {
|
for (const [label, { hash }] of hashes) {
|
||||||
if (h !== authorityHash) {
|
if (hash !== authorityHash) {
|
||||||
errors.push(`${family.name} [${lang}]: ${skill} разошёлся с эталоном ${v.authority} (вариант '${v.id}')`);
|
errors.push(`${family.name} [${lang}]: ${label} разошёлся с эталоном ${v.authority} (вариант '${v.id}')`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -746,9 +759,11 @@ for (const family of DRIFTED) {
|
|||||||
for (const [key, fns] of index) {
|
for (const [key, fns] of index) {
|
||||||
const [skill, l] = key.split('|');
|
const [skill, l] = key.split('|');
|
||||||
if (l !== lang || !fns.has(fnName)) continue;
|
if (l !== lang || !fns.has(fnName)) continue;
|
||||||
const h = hashBody(fns.get(fnName), lang);
|
for (const { file, body } of fns.get(fnName)) {
|
||||||
|
const h = hashBody(body, lang);
|
||||||
if (!groups.has(h)) groups.set(h, []);
|
if (!groups.has(h)) groups.set(h, []);
|
||||||
groups.get(h).push(skill);
|
groups.get(h).push(file.startsWith(`${skill}.`) ? skill : `${skill} (${file})`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!groups.size) continue;
|
if (!groups.size) continue;
|
||||||
const limit = family.maxVariants[lang];
|
const limit = family.maxVariants[lang];
|
||||||
|
|||||||
Reference in New Issue
Block a user