mirror of
https://github.com/callstack/agent-device.git
synced 2026-09-14 20:06:34 +08:00
04e4c23b95
* dx(doctor): flag a worktree whose node_modules lags the lockfile Add a doctor probe and a check:affected preflight that compare node_modules/.pnpm/lock.yaml (the exact lockfile snapshot pnpm installed from) against pnpm-lock.yaml via a content hash — no subprocess. On mismatch both surfaces report the same one-liner: "node_modules was installed from a different lockfile; run pnpm install", so a stale install names its own cause instead of surfacing as bogus format diffs on files a change never touched (the #1956 incident). Closes #1963 * fix(doctor): scope the node-modules probe to a local source checkout The probe ran unconditionally from findProjectRoot(), so it fired in two contexts it cannot diagnose: - Packaged installs. Published packages ship neither pnpm-lock.yaml (not in the package.json `files` allowlist) nor an installed snapshot, so every end user's `doctor` gained a spurious node-modules line and a degraded overall status. - `--remote`, where the daemon's own root describes the server deployment rather than the caller's worktree, so the answer could not address #1963 at all. Whether a root is a source checkout is now decided by the presence of pnpm-lock.yaml itself rather than a heuristic about install location, and 'no-source-checkout' is a distinct result rather than a warning, so the packaged case cannot be represented as a defect. The probe returns undefined there and the route appends no check, matching how doctor already models an out-of-scope question (the device family is likewise absent under --remote). The fresh-worktree catch is preserved: a lockfile with no installed snapshot is still a hard failure. The check:affected preflight is unchanged in behavior. Route-level assertions cover all three contexts (source, packaged, remote); each was verified to fail against the pre-fix wiring. * refactor(check): keep stale-install probe worktree-local
40 lines
1.4 KiB
TypeScript
40 lines
1.4 KiB
TypeScript
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
|
|
const LOCKFILE_BASENAME = 'pnpm-lock.yaml';
|
|
const INSTALLED_SNAPSHOT_RELATIVE_PATH = ['node_modules', '.pnpm', 'lock.yaml'];
|
|
|
|
export const STALE_NODE_MODULES_MESSAGE =
|
|
"node_modules does not match this worktree's pnpm-lock.yaml";
|
|
|
|
export type LockfileInstallSyncResult =
|
|
| { readonly status: 'no-source-checkout' }
|
|
| { readonly status: 'in-sync' }
|
|
| {
|
|
readonly status: 'out-of-sync';
|
|
readonly reason: 'install-missing' | 'stale';
|
|
};
|
|
|
|
/**
|
|
* Compares pnpm's installed lockfile snapshot with this worktree's lockfile.
|
|
* This is intentionally synchronous and subprocess-free: it runs before any gate.
|
|
*/
|
|
export function checkLockfileInstallSync(repoRoot: string): LockfileInstallSyncResult {
|
|
const lockfile = readFileIfExists(path.join(repoRoot, LOCKFILE_BASENAME));
|
|
if (!lockfile) return { status: 'no-source-checkout' };
|
|
|
|
const installedSnapshot = readFileIfExists(
|
|
path.join(repoRoot, ...INSTALLED_SNAPSHOT_RELATIVE_PATH),
|
|
);
|
|
if (!installedSnapshot) return { status: 'out-of-sync', reason: 'install-missing' };
|
|
|
|
return lockfile.equals(installedSnapshot)
|
|
? { status: 'in-sync' }
|
|
: { status: 'out-of-sync', reason: 'stale' };
|
|
}
|
|
|
|
function readFileIfExists(filePath: string): Buffer | undefined {
|
|
if (!fs.existsSync(filePath)) return undefined;
|
|
return fs.readFileSync(filePath);
|
|
}
|