Trim verification snapshot scope
This commit is contained in:
4
shared/verification-snapshot.d.ts
vendored
4
shared/verification-snapshot.d.ts
vendored
@@ -2,6 +2,10 @@ export declare const VERIFICATION_SNAPSHOT_EXCLUDE_NAMES: ReadonlySet<string>;
|
|||||||
export declare function isVerificationSnapshotExcludedName(
|
export declare function isVerificationSnapshotExcludedName(
|
||||||
name: string,
|
name: string,
|
||||||
): boolean;
|
): boolean;
|
||||||
|
export declare function isVerificationSnapshotExcludedPath(
|
||||||
|
repoDir: string,
|
||||||
|
currentPath: string,
|
||||||
|
): boolean;
|
||||||
export declare function computeVerificationSnapshotId(repoDir: string): string;
|
export declare function computeVerificationSnapshotId(repoDir: string): string;
|
||||||
export declare function resolveVerificationResponsesDir(
|
export declare function resolveVerificationResponsesDir(
|
||||||
hostIpcDir: string,
|
hostIpcDir: string,
|
||||||
|
|||||||
@@ -3,15 +3,46 @@ import fs from 'fs';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
export const VERIFICATION_SNAPSHOT_EXCLUDE_NAMES = new Set([
|
export const VERIFICATION_SNAPSHOT_EXCLUDE_NAMES = new Set([
|
||||||
'.git',
|
|
||||||
'node_modules',
|
'node_modules',
|
||||||
'.env',
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const VERIFICATION_SNAPSHOT_ROOT_EXCLUDE_NAMES = new Set([
|
||||||
|
'.git',
|
||||||
|
'.env',
|
||||||
|
'data',
|
||||||
|
'logs',
|
||||||
|
'cache',
|
||||||
|
'.ejclaw-reviewer-runtime',
|
||||||
|
]);
|
||||||
|
|
||||||
|
const VERIFICATION_SNAPSHOT_ROOT_EXCLUDE_PREFIXES = [
|
||||||
|
'store.local-backup-',
|
||||||
|
];
|
||||||
|
|
||||||
export function isVerificationSnapshotExcludedName(name) {
|
export function isVerificationSnapshotExcludedName(name) {
|
||||||
return VERIFICATION_SNAPSHOT_EXCLUDE_NAMES.has(name);
|
return VERIFICATION_SNAPSHOT_EXCLUDE_NAMES.has(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isVerificationSnapshotExcludedPath(repoDir, currentPath) {
|
||||||
|
const name = path.basename(currentPath);
|
||||||
|
if (isVerificationSnapshotExcludedName(name)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const relPath = path.relative(repoDir, currentPath);
|
||||||
|
const isRepoRootEntry = relPath !== '' && !relPath.includes(path.sep);
|
||||||
|
if (!isRepoRootEntry) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
VERIFICATION_SNAPSHOT_ROOT_EXCLUDE_NAMES.has(name) ||
|
||||||
|
VERIFICATION_SNAPSHOT_ROOT_EXCLUDE_PREFIXES.some((prefix) =>
|
||||||
|
name.startsWith(prefix),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function updateVerificationSnapshotHash(hash, repoDir, currentPath) {
|
function updateVerificationSnapshotHash(hash, repoDir, currentPath) {
|
||||||
const relPath = path.relative(repoDir, currentPath) || '.';
|
const relPath = path.relative(repoDir, currentPath) || '.';
|
||||||
const stat = fs.lstatSync(currentPath);
|
const stat = fs.lstatSync(currentPath);
|
||||||
@@ -21,8 +52,9 @@ function updateVerificationSnapshotHash(hash, repoDir, currentPath) {
|
|||||||
hash.update(`dir\0${relPath}\0`);
|
hash.update(`dir\0${relPath}\0`);
|
||||||
}
|
}
|
||||||
for (const entry of fs.readdirSync(currentPath).sort()) {
|
for (const entry of fs.readdirSync(currentPath).sort()) {
|
||||||
if (isVerificationSnapshotExcludedName(entry)) continue;
|
const nextPath = path.join(currentPath, entry);
|
||||||
updateVerificationSnapshotHash(hash, repoDir, path.join(currentPath, entry));
|
if (isVerificationSnapshotExcludedPath(repoDir, nextPath)) continue;
|
||||||
|
updateVerificationSnapshotHash(hash, repoDir, nextPath);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,6 +83,76 @@ describe('verification helpers', () => {
|
|||||||
expect(third).not.toBe(first);
|
expect(third).not.toBe(first);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('ignores runtime state directories and local backup folders', () => {
|
||||||
|
const repoDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ejclaw-snapshot-'));
|
||||||
|
fs.mkdirSync(path.join(repoDir, 'src'), { recursive: true });
|
||||||
|
fs.mkdirSync(path.join(repoDir, 'data'), { recursive: true });
|
||||||
|
fs.mkdirSync(path.join(repoDir, 'logs'), { recursive: true });
|
||||||
|
fs.mkdirSync(path.join(repoDir, 'cache'), { recursive: true });
|
||||||
|
fs.mkdirSync(path.join(repoDir, '.ejclaw-reviewer-runtime'), {
|
||||||
|
recursive: true,
|
||||||
|
});
|
||||||
|
fs.mkdirSync(path.join(repoDir, 'store.local-backup-20260408_024500'), {
|
||||||
|
recursive: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(repoDir, 'src', 'index.ts'),
|
||||||
|
'export const x = 1;\n',
|
||||||
|
);
|
||||||
|
fs.writeFileSync(path.join(repoDir, 'data', 'state.json'), '{"x":1}\n');
|
||||||
|
fs.writeFileSync(path.join(repoDir, 'logs', 'service.log'), 'line-1\n');
|
||||||
|
fs.writeFileSync(path.join(repoDir, 'cache', 'tmp.txt'), 'cache-a\n');
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(repoDir, '.ejclaw-reviewer-runtime', 'runtime.json'),
|
||||||
|
'{"ok":true}\n',
|
||||||
|
);
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(repoDir, 'store.local-backup-20260408_024500', 'db.sqlite'),
|
||||||
|
'backup-a\n',
|
||||||
|
);
|
||||||
|
|
||||||
|
const first = computeVerificationSnapshot(repoDir).snapshotId;
|
||||||
|
|
||||||
|
fs.writeFileSync(path.join(repoDir, 'data', 'state.json'), '{"x":2}\n');
|
||||||
|
fs.writeFileSync(path.join(repoDir, 'logs', 'service.log'), 'line-2\n');
|
||||||
|
fs.writeFileSync(path.join(repoDir, 'cache', 'tmp.txt'), 'cache-b\n');
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(repoDir, '.ejclaw-reviewer-runtime', 'runtime.json'),
|
||||||
|
'{"ok":false}\n',
|
||||||
|
);
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(repoDir, 'store.local-backup-20260408_024500', 'db.sqlite'),
|
||||||
|
'backup-b\n',
|
||||||
|
);
|
||||||
|
|
||||||
|
const second = computeVerificationSnapshot(repoDir).snapshotId;
|
||||||
|
|
||||||
|
expect(second).toBe(first);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('still includes nested source directories named like excluded roots', () => {
|
||||||
|
const repoDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ejclaw-snapshot-'));
|
||||||
|
fs.mkdirSync(path.join(repoDir, 'src', 'fixtures', 'data'), {
|
||||||
|
recursive: true,
|
||||||
|
});
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(repoDir, 'src', 'fixtures', 'data', 'sample.json'),
|
||||||
|
'{"v":1}\n',
|
||||||
|
);
|
||||||
|
|
||||||
|
const first = computeVerificationSnapshot(repoDir).snapshotId;
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(repoDir, 'src', 'fixtures', 'data', 'sample.json'),
|
||||||
|
'{"v":2}\n',
|
||||||
|
);
|
||||||
|
|
||||||
|
const second = computeVerificationSnapshot(repoDir).snapshotId;
|
||||||
|
|
||||||
|
expect(second).not.toBe(first);
|
||||||
|
});
|
||||||
|
|
||||||
it('backfills legacy node_modules state before verification', async () => {
|
it('backfills legacy node_modules state before verification', async () => {
|
||||||
const repoDir = fs.mkdtempSync(
|
const repoDir = fs.mkdtempSync(
|
||||||
path.join(os.tmpdir(), 'ejclaw-verification-legacy-'),
|
path.join(os.tmpdir(), 'ejclaw-verification-legacy-'),
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
} from './workspace-package-manager.js';
|
} from './workspace-package-manager.js';
|
||||||
import {
|
import {
|
||||||
computeVerificationSnapshotId,
|
computeVerificationSnapshotId,
|
||||||
isVerificationSnapshotExcludedName,
|
isVerificationSnapshotExcludedPath,
|
||||||
resolveVerificationResponsesDir,
|
resolveVerificationResponsesDir,
|
||||||
} from '../shared/verification-snapshot.js';
|
} from '../shared/verification-snapshot.js';
|
||||||
|
|
||||||
@@ -95,8 +95,8 @@ export function buildVerificationCommand(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldExcludePath(name: string): boolean {
|
function shouldExcludePath(repoDir: string, source: string): boolean {
|
||||||
return isVerificationSnapshotExcludedName(name);
|
return isVerificationSnapshotExcludedPath(repoDir, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function computeVerificationSnapshot(
|
export function computeVerificationSnapshot(
|
||||||
@@ -130,7 +130,7 @@ function copyWorkspaceToScratch(repoDir: string, scratchDir: string): void {
|
|||||||
recursive: true,
|
recursive: true,
|
||||||
filter: (source) => {
|
filter: (source) => {
|
||||||
if (source === repoDir) return true;
|
if (source === repoDir) return true;
|
||||||
return !shouldExcludePath(path.basename(source));
|
return !shouldExcludePath(repoDir, source);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user