fix(reference): verify station coverage and rollback inputs
This commit is contained in:
@@ -339,7 +339,7 @@ export class ReferenceCompiler {
|
||||
if (!manifest.active || !manifest.previous) throw new Error('no previous reference artifact is available for rollback');
|
||||
const previousPath = artifactDatabasePath(this.directory, manifest.previous.database);
|
||||
if (!await exists(previousPath)) throw new Error('previous reference database is missing');
|
||||
validateRollbackArtifact(previousPath, manifest.previous, this.profile, this.importerVersion, this.thresholds);
|
||||
await validateRollbackArtifact(this.directory, previousPath, manifest.previous, this.profile, this.importerVersion, this.thresholds);
|
||||
const next: ReferenceManifest = { ...manifest, active: manifest.previous, previous: manifest.active, lastAttempt: { startedAt: iso(this.now()), finishedAt: iso(this.now()), status: 'success', profile: manifest.previous.profile, version: manifest.previous.version } };
|
||||
await atomicWriteJson(this.directory, MANIFEST_FILE, next);
|
||||
return { activeVersion: next.active!.version, previousVersion: next.previous?.version };
|
||||
@@ -347,19 +347,28 @@ export class ReferenceCompiler {
|
||||
}
|
||||
}
|
||||
|
||||
function validateRollbackArtifact(path: string, artifact: ActiveManifest, expectedProfile: BuildProfile, expectedImporterVersion: string, thresholds: ValidationThresholds): void {
|
||||
if (artifact.profile !== expectedProfile || artifact.importerVersion !== expectedImporterVersion || artifact.stationCoverage !== thresholds.stationCoverage) throw new Error('previous reference artifact is incompatible with the current compiler/profile');
|
||||
async function validateRollbackArtifact(directory: string, path: string, artifact: ActiveManifest, expectedProfile: BuildProfile, expectedImporterVersion: string, thresholds: ValidationThresholds): Promise<void> {
|
||||
if (artifact.profile !== expectedProfile || artifact.importerVersion !== expectedImporterVersion || artifact.stationCoverage !== thresholds.stationCoverage || basename(path) !== `${artifact.version}.sqlite`) throw new Error('previous reference artifact is incompatible with the current compiler/profile');
|
||||
if (!artifact.sourceInputs || !artifact.counts) throw new Error('previous reference artifact is missing compatibility metadata');
|
||||
let db: Database | undefined;
|
||||
try {
|
||||
for (const source of REFERENCE_SOURCES) {
|
||||
const expected = artifact.sourceInputs[source];
|
||||
if (!expected) throw new Error(`previous reference artifact is missing source input metadata for ${source}`);
|
||||
await readSourceCache(directory, source, expected.cacheFile, cacheExpectation(source, expected, expectedImporterVersion, expectedProfile));
|
||||
}
|
||||
db = new Database(path, { readonly: true, strict: true });
|
||||
const counts = validateCandidate(db, thresholds, artifact.profile);
|
||||
for (const [table, expected] of Object.entries(artifact.counts)) if (counts[table] !== expected) throw new Error(`previous reference artifact count mismatch for ${table}: expected ${expected}, got ${counts[table]}`);
|
||||
for (const key of Object.keys(counts)) {
|
||||
if (!(key in artifact.counts)) throw new Error(`previous reference artifact is missing count metadata for ${key}`);
|
||||
const expected = artifact.counts[key];
|
||||
if (counts[key] !== expected) throw new Error(`previous reference artifact count mismatch for ${key}: expected ${expected}, got ${counts[key]}`);
|
||||
}
|
||||
const rows = db.query<{ source: string; fingerprint: string; local_sha256: string }, []>('SELECT source,fingerprint,local_sha256 FROM source_provenance').all();
|
||||
const bySource = new Map(rows.map((row) => [row.source, row]));
|
||||
for (const source of REFERENCE_SOURCES) {
|
||||
const expected = artifact.sourceInputs[source]; const row = bySource.get(source);
|
||||
if (!expected || !row || row.fingerprint !== expected.fingerprint || row.local_sha256 !== expected.localSha256) throw new Error(`previous reference artifact provenance mismatch for ${source}`);
|
||||
if (!row || row.fingerprint !== expected.fingerprint || row.local_sha256 !== expected.localSha256) throw new Error(`previous reference artifact provenance mismatch for ${source}`);
|
||||
}
|
||||
} finally { try { db?.close(); } catch { /* best effort */ } }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user