Backfill verification install state
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
|||||||
isVerificationProfile,
|
isVerificationProfile,
|
||||||
runVerificationRequest,
|
runVerificationRequest,
|
||||||
} from './verification.js';
|
} from './verification.js';
|
||||||
|
import { hasInstalledNodeModules } from './workspace-package-manager.js';
|
||||||
|
|
||||||
describe('verification helpers', () => {
|
describe('verification helpers', () => {
|
||||||
it('recognizes only fixed verification profiles', () => {
|
it('recognizes only fixed verification profiles', () => {
|
||||||
@@ -82,16 +83,18 @@ describe('verification helpers', () => {
|
|||||||
expect(third).not.toBe(first);
|
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(
|
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,
|
recursive: true,
|
||||||
});
|
});
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.join(repoDir, 'package.json'),
|
path.join(repoDir, 'package.json'),
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
|
name: 'verification-legacy',
|
||||||
|
packageManager: 'bun@1.3.11',
|
||||||
scripts: {
|
scripts: {
|
||||||
test: 'vitest run',
|
test: 'vitest run',
|
||||||
typecheck: 'tsc --noEmit',
|
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(
|
const result = await runVerificationRequest(
|
||||||
{
|
{
|
||||||
requestId: 'req-noise',
|
requestId: 'req-legacy-node-modules',
|
||||||
profile: 'typecheck',
|
profile: 'typecheck',
|
||||||
|
expectedSnapshotId: 'fs:mismatch',
|
||||||
},
|
},
|
||||||
{ repoDir },
|
{ repoDir },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
expect(hasInstalledNodeModules(repoDir)).toBe(true);
|
||||||
expect(result.ok).toBe(false);
|
expect(result.ok).toBe(false);
|
||||||
expect(result.error).toContain('installed node_modules tree');
|
expect(result.error).toContain('Snapshot mismatch before verification');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { resolveGroupIpcPath } from './group-folder.js';
|
|||||||
import {
|
import {
|
||||||
buildWorkspaceScriptCommand,
|
buildWorkspaceScriptCommand,
|
||||||
detectPnpmStorePath,
|
detectPnpmStorePath,
|
||||||
|
ensureWorkspaceDependenciesInstalled,
|
||||||
hasInstalledNodeModules,
|
hasInstalledNodeModules,
|
||||||
} from './workspace-package-manager.js';
|
} from './workspace-package-manager.js';
|
||||||
import {
|
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)) {
|
if (!hasInstalledNodeModules(repoDir)) {
|
||||||
return {
|
return {
|
||||||
ok: false,
|
ok: false,
|
||||||
|
|||||||
@@ -181,4 +181,32 @@ describe('workspace package manager helpers', () => {
|
|||||||
);
|
);
|
||||||
expect(hasInstalledNodeModules(repoDir)).toBe(false);
|
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();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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(
|
export function detectWorkspacePackageManager(
|
||||||
repoDir: string,
|
repoDir: string,
|
||||||
): WorkspacePackageManager | null {
|
): WorkspacePackageManager | null {
|
||||||
@@ -326,6 +347,10 @@ export function ensureWorkspaceDependenciesInstalled(
|
|||||||
return { installed: false, packageManager };
|
return { installed: false, packageManager };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (backfillInstallFingerprintIfPossible(repoDir, packageManager)) {
|
||||||
|
return { installed: false, packageManager };
|
||||||
|
}
|
||||||
|
|
||||||
const command = resolveWorkspaceInstallCommand(repoDir);
|
const command = resolveWorkspaceInstallCommand(repoDir);
|
||||||
if (!command) {
|
if (!command) {
|
||||||
return { installed: false, packageManager };
|
return { installed: false, packageManager };
|
||||||
|
|||||||
Reference in New Issue
Block a user