Backfill verification install state

This commit is contained in:
ejclaw
2026-04-08 01:35:34 +09:00
parent a758b81a62
commit f769151f34
4 changed files with 89 additions and 5 deletions

View File

@@ -10,6 +10,7 @@ import {
isVerificationProfile,
runVerificationRequest,
} from './verification.js';
import { hasInstalledNodeModules } from './workspace-package-manager.js';
describe('verification helpers', () => {
it('recognizes only fixed verification profiles', () => {
@@ -82,16 +83,18 @@ describe('verification helpers', () => {
expect(third).not.toBe(first);
});
it('fails verification when node_modules contains only cache noise', async () => {
it('backfills legacy node_modules state before verification', async () => {
const repoDir = fs.mkdtempSync(
path.join(os.tmpdir(), 'ejclaw-verification-noise-'),
path.join(os.tmpdir(), 'ejclaw-verification-legacy-'),
);
fs.mkdirSync(path.join(repoDir, 'node_modules', '.vite'), {
fs.mkdirSync(path.join(repoDir, 'node_modules', '.bin'), {
recursive: true,
});
fs.writeFileSync(
path.join(repoDir, 'package.json'),
JSON.stringify({
name: 'verification-legacy',
packageManager: 'bun@1.3.11',
scripts: {
test: 'vitest run',
typecheck: 'tsc --noEmit',
@@ -99,16 +102,22 @@ describe('verification helpers', () => {
},
}),
);
fs.writeFileSync(path.join(repoDir, 'bun.lock'), '');
fs.writeFileSync(path.join(repoDir, 'node_modules', '.bin', 'tsc'), '');
expect(hasInstalledNodeModules(repoDir)).toBe(false);
const result = await runVerificationRequest(
{
requestId: 'req-noise',
requestId: 'req-legacy-node-modules',
profile: 'typecheck',
expectedSnapshotId: 'fs:mismatch',
},
{ repoDir },
);
expect(hasInstalledNodeModules(repoDir)).toBe(true);
expect(result.ok).toBe(false);
expect(result.error).toContain('installed node_modules tree');
expect(result.error).toContain('Snapshot mismatch before verification');
});
});

View File

@@ -15,6 +15,7 @@ import { resolveGroupIpcPath } from './group-folder.js';
import {
buildWorkspaceScriptCommand,
detectPnpmStorePath,
ensureWorkspaceDependenciesInstalled,
hasInstalledNodeModules,
} from './workspace-package-manager.js';
import {
@@ -271,6 +272,27 @@ export async function runVerificationRequest(
};
}
if (!hasInstalledNodeModules(repoDir)) {
try {
ensureWorkspaceDependenciesInstalled(repoDir);
} catch (error) {
return {
ok: false,
profile: request.profile,
command: command.commandText,
stdout: '',
stderr: '',
exitCode: 1,
snapshotId: 'unknown',
runtimeVersion,
error:
error instanceof Error
? error.message
: String(error),
};
}
}
if (!hasInstalledNodeModules(repoDir)) {
return {
ok: false,

View File

@@ -181,4 +181,32 @@ describe('workspace package manager helpers', () => {
);
expect(hasInstalledNodeModules(repoDir)).toBe(false);
});
it('backfills install state for a legacy runnable node_modules tree', () => {
const repoDir = path.join(tempRoot, 'legacy');
fs.mkdirSync(path.join(repoDir, 'node_modules', '.bin'), {
recursive: true,
});
fs.writeFileSync(
path.join(repoDir, 'package.json'),
JSON.stringify({
name: 'legacy',
packageManager: 'bun@1.3.11',
scripts: { test: 'vitest run' },
}),
);
fs.writeFileSync(path.join(repoDir, 'bun.lock'), '');
fs.writeFileSync(path.join(repoDir, 'node_modules', '.bin', 'vitest'), '');
expect(hasInstalledNodeModules(repoDir)).toBe(false);
const result = ensureWorkspaceDependenciesInstalled(repoDir);
expect(result).toMatchObject({
installed: false,
packageManager: 'bun',
});
expect(hasInstalledNodeModules(repoDir)).toBe(true);
expect(execFileSyncMock).not.toHaveBeenCalled();
});
});

View File

@@ -194,6 +194,27 @@ function hasRunnableNodeModulesTree(repoDir: string): boolean {
}
}
function backfillInstallFingerprintIfPossible(
repoDir: string,
packageManager: WorkspacePackageManager,
): boolean {
if (!hasRunnableNodeModulesTree(repoDir)) {
return false;
}
if (readInstallFingerprint(repoDir)) {
return false;
}
const expectedFingerprint = computeInstallFingerprint(repoDir);
if (!expectedFingerprint) {
return false;
}
writeInstallFingerprint(repoDir, packageManager, expectedFingerprint);
return true;
}
export function detectWorkspacePackageManager(
repoDir: string,
): WorkspacePackageManager | null {
@@ -326,6 +347,10 @@ export function ensureWorkspaceDependenciesInstalled(
return { installed: false, packageManager };
}
if (backfillInstallFingerprintIfPossible(repoDir, packageManager)) {
return { installed: false, packageManager };
}
const command = resolveWorkspaceInstallCommand(repoDir);
if (!command) {
return { installed: false, packageManager };