refactor(#2266): single-source golden-parity manifest builder + fixture correction (#2273)

Phase 1 of golden-install-parity redesign (epic #2264). Consolidates buildParityManifest + exclusion constants into tests/helpers/install-shared.cjs (fixes realRoot divergence), adds anti-divergence guard, corrects 12 stale golden fixtures to portable values. Closes #2266.
This commit is contained in:
Tom Boucher
2026-07-14 17:10:04 -04:00
committed by GitHub
parent ef5a5bc15d
commit 6a474db3aa
14 changed files with 279 additions and 184 deletions

View File

@@ -3,60 +3,28 @@
/**
* Standalone golden-fixture generator for tests/golden-install-parity.
*
* This is a BUILD-TIME generation script — NOT a test run. It replicates the
* buildParityManifest logic from tests/golden-install-parity.test.cjs and
* captures the zcode fixture so the parity test (which the gsd-test gate runs)
* has a committed artifact to compare against. The authoritative test gate
* remains `gsd-test run`, never a local `node --test`.
* This is a BUILD-TIME generation script — NOT a test run. It imports the
* canonical buildParityManifest builder from tests/helpers/install-shared.cjs
* (issue #2266 — single source of truth shared with
* tests/golden-install-parity.test.cjs) and captures the zcode fixture so the
* parity test (which the gsd-test gate runs) has a committed artifact to
* compare against. The authoritative test gate remains `gsd-test run`, never
* a local `node --test`.
*
* Usage: node scripts/gen-golden-install-parity-zcode.cjs
*/
const fs = require('node:fs');
const path = require('node:path');
const crypto = require('node:crypto');
const ROOT = path.resolve(__dirname, '..');
const { walk, runMinimalInstall, RUNTIME_META } = require(path.join(ROOT, 'tests', 'helpers', 'install-shared.cjs'));
const PKG_VERSION = require(path.join(ROOT, 'package.json')).version;
// buildParityManifest (and its exclusion constants) is the canonical single
// source of truth in tests/helpers/install-shared.cjs (issue #2266) — the
// generator no longer keeps its own inline copy, which had drifted from the
// test harness's copy (missing the realpath/`<HOME>` normalization) and
// mis-generated the claude-local fixture (#2100).
const { runMinimalInstall, RUNTIME_META, buildParityManifest } = require(path.join(ROOT, 'tests', 'helpers', 'install-shared.cjs'));
const FIXTURE_DIR = path.join(ROOT, 'tests', 'fixtures', 'golden-install-parity');
const VOLATILE_FILES = new Set([
'gsd-file-manifest.json',
'gsd-install-state.json',
'.gsd-source',
'gsd-core/CHANGELOG.md',
]);
// Must match tests/golden-install-parity.test.cjs exactly — settings.local.json
// (Claude LOCAL hook surface, #338/#2086) embeds the same platform-varying
// node-runner command and is excluded there; omitting it here mis-generated the
// claude-local fixture (#2100).
const HOOK_CONFIG_FILES = new Set(['settings.json', 'settings.local.json', 'hooks.json']);
// Kimi's native config.toml (#2095) — see tests/golden-install-parity.test.cjs'
// HOOK_CONFIG_RELATIVE_PATHS comment for why this is an exact relative-path
// exclusion rather than a HOOK_CONFIG_FILES basename entry (a basename entry
// would also blind Codex's stable, platform-independent config.toml fixture).
const HOOK_CONFIG_RELATIVE_PATHS = new Set(['.kimi/config.toml']);
const EXCLUDED_PREFIXES = ['gsd-core/bin/lib/'];
function buildParityManifest(configDir, root) {
const allFiles = walk(configDir);
const unsorted = {};
for (const full of allFiles) {
const rel = path.relative(configDir, full).split(path.sep).join('/');
if (VOLATILE_FILES.has(rel)) continue;
if (HOOK_CONFIG_FILES.has(path.basename(rel))) continue;
if (HOOK_CONFIG_RELATIVE_PATHS.has(rel)) continue;
if (EXCLUDED_PREFIXES.some((p) => rel.startsWith(p))) continue;
const content = fs.readFileSync(full);
const normalized = content.toString('utf8').split(root).join('<HOME>').split(PKG_VERSION).join('<VERSION>');
const hash = crypto.createHash('sha256').update(normalized).digest('hex').slice(0, 16);
unsorted[rel] = hash;
}
const sorted = {};
for (const key of Object.keys(unsorted).sort()) sorted[key] = unsorted[key];
return sorted;
}
function cleanup(root) {
try { fs.rmSync(root, { recursive: true, force: true }); } catch { /* best effort */ }
}