mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-27 04:37:48 +00:00
fix(update): make pnpm preflight resolution deterministic
This commit is contained in:
@@ -385,6 +385,129 @@ describe("runGatewayUpdate", () => {
|
||||
expect(calls).not.toContain("pnpm ui:build");
|
||||
});
|
||||
|
||||
it("uses pnpm highest resolution mode for update installs", async () => {
|
||||
await setupGitCheckout({ packageManager: "pnpm@8.0.0" });
|
||||
await setupUiIndex();
|
||||
const stableTag = "v1.0.1-1";
|
||||
const installEnvs: NodeJS.ProcessEnv[] = [];
|
||||
const doctorNodePath = await resolveStableNodePath(process.execPath);
|
||||
const { runCommand } = createGitInstallRunner({
|
||||
stableTag,
|
||||
installCommand: "pnpm install",
|
||||
buildCommand: "pnpm build",
|
||||
uiBuildCommand: "pnpm ui:build",
|
||||
doctorCommand: `${doctorNodePath} ${path.join(tempDir, "openclaw.mjs")} doctor --non-interactive --fix`,
|
||||
onCommand: (key, options) => {
|
||||
if (key === "pnpm install") {
|
||||
installEnvs.push(options?.env ?? {});
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
});
|
||||
|
||||
const result = await runWithCommand(runCommand, { channel: "stable" });
|
||||
|
||||
expect(result.status).toBe("ok");
|
||||
expect(installEnvs).toHaveLength(1);
|
||||
expect(installEnvs[0]).toMatchObject({
|
||||
PNPM_CONFIG_RESOLUTION_MODE: "highest",
|
||||
npm_config_resolution_mode: "highest",
|
||||
pnpm_config_resolution_mode: "highest",
|
||||
});
|
||||
});
|
||||
|
||||
it("uses pnpm highest resolution mode for dev preflight installs", async () => {
|
||||
await setupGitPackageManagerFixture();
|
||||
const upstreamSha = "upstream123";
|
||||
const installEnvs: NodeJS.ProcessEnv[] = [];
|
||||
const doctorNodePath = await resolveStableNodePath(process.execPath);
|
||||
const doctorCommand = `${doctorNodePath} ${path.join(tempDir, "openclaw.mjs")} doctor --non-interactive --fix`;
|
||||
|
||||
const runCommand = async (
|
||||
argv: string[],
|
||||
options?: { env?: NodeJS.ProcessEnv; cwd?: string; timeoutMs?: number },
|
||||
) => {
|
||||
const key = argv.join(" ");
|
||||
if (key === `git -C ${tempDir} rev-parse --show-toplevel`) {
|
||||
return { stdout: tempDir, stderr: "", code: 0 };
|
||||
}
|
||||
if (key === `git -C ${tempDir} rev-parse HEAD`) {
|
||||
return { stdout: "abc123", stderr: "", code: 0 };
|
||||
}
|
||||
if (key === `git -C ${tempDir} rev-parse --abbrev-ref HEAD`) {
|
||||
return { stdout: "main", stderr: "", code: 0 };
|
||||
}
|
||||
if (key === `git -C ${tempDir} status --porcelain -- :!dist/control-ui/`) {
|
||||
return { stdout: "", stderr: "", code: 0 };
|
||||
}
|
||||
if (key === `git -C ${tempDir} rev-parse --abbrev-ref --symbolic-full-name @{upstream}`) {
|
||||
return { stdout: "origin/main", stderr: "", code: 0 };
|
||||
}
|
||||
if (key === `git -C ${tempDir} fetch --all --prune --tags`) {
|
||||
return { stdout: "", stderr: "", code: 0 };
|
||||
}
|
||||
if (key === `git -C ${tempDir} rev-parse @{upstream}`) {
|
||||
return { stdout: upstreamSha, stderr: "", code: 0 };
|
||||
}
|
||||
if (key === `git -C ${tempDir} rev-list --max-count=10 ${upstreamSha}`) {
|
||||
return { stdout: `${upstreamSha}\n`, stderr: "", code: 0 };
|
||||
}
|
||||
if (key === "pnpm --version") {
|
||||
return { stdout: "10.0.0", stderr: "", code: 0 };
|
||||
}
|
||||
if (
|
||||
key.startsWith(`git -C ${tempDir} worktree add --detach /tmp/`) &&
|
||||
key.endsWith(` ${upstreamSha}`) &&
|
||||
preflightPrefixPattern.test(key)
|
||||
) {
|
||||
return { stdout: `HEAD is now at ${upstreamSha}`, stderr: "", code: 0 };
|
||||
}
|
||||
if (
|
||||
key.startsWith("git -C /tmp/") &&
|
||||
preflightPrefixPattern.test(key) &&
|
||||
key.includes(" checkout --detach ") &&
|
||||
key.endsWith(upstreamSha)
|
||||
) {
|
||||
return { stdout: "", stderr: "", code: 0 };
|
||||
}
|
||||
if (key === "pnpm install") {
|
||||
installEnvs.push(options?.env ?? {});
|
||||
return { stdout: "", stderr: "", code: 0 };
|
||||
}
|
||||
if (key === "pnpm build" || key === "pnpm ui:build") {
|
||||
return { stdout: "", stderr: "", code: 0 };
|
||||
}
|
||||
if (
|
||||
key.startsWith(`git -C ${tempDir} worktree remove --force /tmp/`) &&
|
||||
preflightPrefixPattern.test(key)
|
||||
) {
|
||||
return { stdout: "", stderr: "", code: 0 };
|
||||
}
|
||||
if (key === `git -C ${tempDir} worktree prune`) {
|
||||
return { stdout: "", stderr: "", code: 0 };
|
||||
}
|
||||
if (key === `git -C ${tempDir} rebase ${upstreamSha}`) {
|
||||
return { stdout: "", stderr: "", code: 0 };
|
||||
}
|
||||
if (key === doctorCommand) {
|
||||
return { stdout: "", stderr: "", code: 0 };
|
||||
}
|
||||
return { stdout: "", stderr: "", code: 0 };
|
||||
};
|
||||
|
||||
const result = await runWithCommand(runCommand, { channel: "dev" });
|
||||
|
||||
expect(result.status).toBe("ok");
|
||||
expect(installEnvs).toHaveLength(2);
|
||||
for (const env of installEnvs) {
|
||||
expect(env).toMatchObject({
|
||||
PNPM_CONFIG_RESOLUTION_MODE: "highest",
|
||||
npm_config_resolution_mode: "highest",
|
||||
pnpm_config_resolution_mode: "highest",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it("returns error and stops early when build fails", async () => {
|
||||
await setupGitCheckout({ packageManager: "pnpm@8.0.0" });
|
||||
const stableTag = "v1.0.1-1";
|
||||
|
||||
@@ -529,6 +529,21 @@ function resolveBuildEnv(env?: NodeJS.ProcessEnv): NodeJS.ProcessEnv | undefined
|
||||
};
|
||||
}
|
||||
|
||||
function resolveInstallEnv(
|
||||
manager: "pnpm" | "bun" | "npm",
|
||||
env?: NodeJS.ProcessEnv,
|
||||
): NodeJS.ProcessEnv | undefined {
|
||||
if (manager !== "pnpm") {
|
||||
return env;
|
||||
}
|
||||
return {
|
||||
...env,
|
||||
PNPM_CONFIG_RESOLUTION_MODE: env?.PNPM_CONFIG_RESOLUTION_MODE ?? "highest",
|
||||
npm_config_resolution_mode: env?.npm_config_resolution_mode ?? "highest",
|
||||
pnpm_config_resolution_mode: env?.pnpm_config_resolution_mode ?? "highest",
|
||||
};
|
||||
}
|
||||
|
||||
function isSupersededInstallFailure(
|
||||
step: UpdateStepResult,
|
||||
steps: readonly UpdateStepResult[],
|
||||
@@ -1006,9 +1021,8 @@ export async function runGatewayUpdate(opts: UpdateRunnerOptions = {}): Promise<
|
||||
const depsStepName = preflightIgnoreScripts
|
||||
? `preflight deps install (ignore scripts) (${shortSha})`
|
||||
: `preflight deps install (${shortSha})`;
|
||||
const depsStep = await runStep(
|
||||
step(depsStepName, depsStepArgv, worktreeDir, manager.env),
|
||||
);
|
||||
const installEnv = resolveInstallEnv(manager.manager, manager.env);
|
||||
const depsStep = await runStep(step(depsStepName, depsStepArgv, worktreeDir, installEnv));
|
||||
steps.push(depsStep);
|
||||
let finalDepsStep = depsStep;
|
||||
if (
|
||||
@@ -1023,7 +1037,7 @@ export async function runGatewayUpdate(opts: UpdateRunnerOptions = {}): Promise<
|
||||
`preflight deps install (ignore scripts) (${shortSha})`,
|
||||
retryArgv,
|
||||
worktreeDir,
|
||||
manager.env,
|
||||
installEnv,
|
||||
),
|
||||
);
|
||||
steps.push(retryStep);
|
||||
@@ -1200,6 +1214,7 @@ export async function runGatewayUpdate(opts: UpdateRunnerOptions = {}): Promise<
|
||||
};
|
||||
}
|
||||
try {
|
||||
const installEnv = resolveInstallEnv(manager.manager, manager.env);
|
||||
const depsStep = await runStep(
|
||||
step(
|
||||
"deps install",
|
||||
@@ -1207,7 +1222,7 @@ export async function runGatewayUpdate(opts: UpdateRunnerOptions = {}): Promise<
|
||||
compatFallback: manager.fallback && manager.manager === "npm",
|
||||
}),
|
||||
gitRoot,
|
||||
manager.env,
|
||||
installEnv,
|
||||
),
|
||||
);
|
||||
steps.push(depsStep);
|
||||
@@ -1216,7 +1231,7 @@ export async function runGatewayUpdate(opts: UpdateRunnerOptions = {}): Promise<
|
||||
const retryArgv = managerInstallIgnoreScriptsArgs(manager.manager);
|
||||
if (retryArgv) {
|
||||
const retryStep = await runStep(
|
||||
step("deps install (ignore scripts)", retryArgv, gitRoot, manager.env),
|
||||
step("deps install (ignore scripts)", retryArgv, gitRoot, installEnv),
|
||||
);
|
||||
steps.push(retryStep);
|
||||
finalDepsStep = retryStep;
|
||||
|
||||
Reference in New Issue
Block a user