mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-24 03:15:46 -06:00
fix(e2e): resolve macOS Parallels VM
This commit is contained in:
@@ -45,11 +45,12 @@ import {
|
||||
import { MacosGuest } from "./guest-transports.ts";
|
||||
import { runSmokeLane, type SmokeLane, type SmokeLaneStatus } from "./lane-runner.ts";
|
||||
import { MacosDiscordSmoke } from "./macos-discord.ts";
|
||||
import { waitForVmStatus } from "./parallels-vm.ts";
|
||||
import { resolveMacosVmName, waitForVmStatus } from "./parallels-vm.ts";
|
||||
import { PhaseRunner } from "./phase-runner.ts";
|
||||
|
||||
interface MacosOptions {
|
||||
vmName: string;
|
||||
vmNameExplicit: boolean;
|
||||
snapshotHint: string;
|
||||
mode: Mode;
|
||||
provider: Provider;
|
||||
@@ -129,6 +130,7 @@ const defaultOptions = (): MacosOptions => ({
|
||||
snapshotHint: "macOS 26.5 latest",
|
||||
targetPackageSpec: "",
|
||||
vmName: "macOS Tahoe",
|
||||
vmNameExplicit: false,
|
||||
});
|
||||
|
||||
function usage(): string {
|
||||
@@ -170,6 +172,7 @@ export function parseArgs(argv: string[]): MacosOptions {
|
||||
break parseArgv;
|
||||
case "--vm":
|
||||
options.vmName = ensureValue(args, i, arg);
|
||||
options.vmNameExplicit = true;
|
||||
i++;
|
||||
break;
|
||||
case "--snapshot-hint":
|
||||
@@ -309,6 +312,7 @@ class MacosSmoke {
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
this.options.vmName = resolveMacosVmName(this.options.vmName, this.options.vmNameExplicit);
|
||||
this.runDir = await makeTempDir("openclaw-parallels-macos.");
|
||||
this.phases = new PhaseRunner(this.runDir);
|
||||
this.guest = new MacosGuest(
|
||||
|
||||
@@ -37,13 +37,14 @@ import {
|
||||
} from "./common.ts";
|
||||
import { runWindowsBackgroundPowerShell } from "./guest-transports.ts";
|
||||
import { linuxUpdateScript, macosUpdateScript, windowsUpdateScript } from "./npm-update-scripts.ts";
|
||||
import { ensureVmRunning, resolveUbuntuVmName } from "./parallels-vm.ts";
|
||||
import { ensureVmRunning, resolveMacosVmName, resolveUbuntuVmName } from "./parallels-vm.ts";
|
||||
import { runTimedUpdateJob } from "./update-job-timeout.ts";
|
||||
|
||||
interface NpmUpdateOptions {
|
||||
betaValidation?: string;
|
||||
freshTargetSpec?: string;
|
||||
hostIp?: string;
|
||||
macosVm?: string;
|
||||
packageSpec: string;
|
||||
targetTarball?: string;
|
||||
updateTarget: string;
|
||||
@@ -111,7 +112,7 @@ interface NpmUpdateSummary {
|
||||
}>;
|
||||
}
|
||||
|
||||
const macosVm = "macOS Tahoe";
|
||||
const macosVmDefault = "macOS Tahoe";
|
||||
const windowsVm = "Windows 11";
|
||||
const linuxVmDefault = "Ubuntu 26.04";
|
||||
const updateTimeoutSeconds = readPositiveIntEnv("OPENCLAW_PARALLELS_NPM_UPDATE_TIMEOUT_S", 1200);
|
||||
@@ -298,6 +299,7 @@ Options:
|
||||
Aliases like beta3 resolve to the latest *-beta.3 version.
|
||||
--platform <list> Comma-separated platforms to run: all, macos, windows, linux.
|
||||
Default: all
|
||||
--macos-vm <name> Explicit Parallels macOS VM name.
|
||||
--provider <openai|anthropic|minimax>
|
||||
--model <provider/model> Override the model used for agent-turn smoke checks.
|
||||
--host-ip <ip> Override Parallels host IP.
|
||||
@@ -315,6 +317,7 @@ export function parseArgs(argv: string[]): NpmUpdateOptions {
|
||||
betaValidation: undefined,
|
||||
freshTargetSpec: undefined,
|
||||
json: false,
|
||||
macosVm: undefined,
|
||||
modelId: undefined,
|
||||
packageSpec: "",
|
||||
targetTarball: undefined,
|
||||
@@ -358,6 +361,10 @@ export function parseArgs(argv: string[]): NpmUpdateOptions {
|
||||
options.platforms = parsePlatformList(ensureValue(args, i, arg));
|
||||
i++;
|
||||
break;
|
||||
case "--macos-vm":
|
||||
options.macosVm = ensureValue(args, i, arg);
|
||||
i++;
|
||||
break;
|
||||
case "--provider":
|
||||
options.provider = parseProvider(ensureValue(args, i, arg));
|
||||
i++;
|
||||
@@ -455,6 +462,7 @@ export class NpmUpdateSmoke {
|
||||
private targetTarballPath = "";
|
||||
private targetTarballBuildCommit = "";
|
||||
private targetTarballVersion = "";
|
||||
private macosVm = macosVmDefault;
|
||||
private linuxVm = linuxVmDefault;
|
||||
|
||||
private freshStatus = platformRecord("skip");
|
||||
@@ -507,6 +515,12 @@ export class NpmUpdateSmoke {
|
||||
if (this.options.platforms.has("linux")) {
|
||||
this.linuxVm = resolveUbuntuVmName(linuxVmDefault);
|
||||
}
|
||||
if (this.options.platforms.has("macos")) {
|
||||
this.macosVm = resolveMacosVmName(
|
||||
this.options.macosVm ?? macosVmDefault,
|
||||
Boolean(this.options.macosVm),
|
||||
);
|
||||
}
|
||||
this.preflightRegistryUpdateTarget();
|
||||
|
||||
say(`Run fresh npm baseline: ${this.packageSpec}`);
|
||||
@@ -535,7 +549,7 @@ export class NpmUpdateSmoke {
|
||||
private async runFreshBaselines(): Promise<void> {
|
||||
const jobs: Job[] = [];
|
||||
if (this.options.platforms.has("macos")) {
|
||||
jobs.push(this.spawnFresh("macOS", "macos", []));
|
||||
jobs.push(this.spawnFresh("macOS", "macos", ["--vm", this.macosVm]));
|
||||
}
|
||||
if (this.options.platforms.has("windows")) {
|
||||
jobs.push(this.spawnFresh("Windows", "windows", []));
|
||||
@@ -563,7 +577,16 @@ export class NpmUpdateSmoke {
|
||||
private async runFreshTargetInstalls(): Promise<void> {
|
||||
const jobs: Job[] = [];
|
||||
if (this.options.platforms.has("macos")) {
|
||||
jobs.push(this.spawnFresh("macOS", "macos", [], {}, this.freshTargetSpec, "fresh-target"));
|
||||
jobs.push(
|
||||
this.spawnFresh(
|
||||
"macOS",
|
||||
"macos",
|
||||
["--vm", this.macosVm],
|
||||
{},
|
||||
this.freshTargetSpec,
|
||||
"fresh-target",
|
||||
),
|
||||
);
|
||||
}
|
||||
if (this.options.platforms.has("windows")) {
|
||||
jobs.push(
|
||||
@@ -768,7 +791,7 @@ export class NpmUpdateSmoke {
|
||||
private async runSameGuestUpdates(): Promise<void> {
|
||||
const jobs: Job[] = [];
|
||||
if (this.options.platforms.has("macos")) {
|
||||
ensureVmRunning(macosVm);
|
||||
ensureVmRunning(this.macosVm);
|
||||
jobs.push(this.spawnUpdate("macOS", "macos", (ctx) => this.runMacosUpdate(ctx)));
|
||||
}
|
||||
if (this.options.platforms.has("windows")) {
|
||||
@@ -912,7 +935,7 @@ export class NpmUpdateSmoke {
|
||||
ctx: UpdateJobContext,
|
||||
): Promise<void> {
|
||||
const scriptPath = this.writeGuestScript(
|
||||
macosVm,
|
||||
this.macosVm,
|
||||
script,
|
||||
"openclaw-parallels-npm-update-macos",
|
||||
);
|
||||
@@ -923,14 +946,14 @@ export class NpmUpdateSmoke {
|
||||
? macosExecArgs[sudoUserArgIndex + 1]
|
||||
: "";
|
||||
if (sudoUser) {
|
||||
run("prlctl", ["exec", macosVm, "/usr/sbin/chown", sudoUser, scriptPath], {
|
||||
run("prlctl", ["exec", this.macosVm, "/usr/sbin/chown", sudoUser, scriptPath], {
|
||||
timeoutMs: 30_000,
|
||||
});
|
||||
}
|
||||
try {
|
||||
const status = await this.runStreamingToJobLog(
|
||||
"prlctl",
|
||||
["exec", macosVm, ...macosExecArgs, "/bin/bash", scriptPath],
|
||||
["exec", this.macosVm, ...macosExecArgs, "/bin/bash", scriptPath],
|
||||
timeoutMs,
|
||||
ctx,
|
||||
);
|
||||
@@ -938,14 +961,14 @@ export class NpmUpdateSmoke {
|
||||
throw new Error(`macOS update command failed with exit code ${status}`);
|
||||
}
|
||||
} finally {
|
||||
this.removeGuestScript(macosVm, scriptPath);
|
||||
this.removeGuestScript(this.macosVm, scriptPath);
|
||||
}
|
||||
}
|
||||
|
||||
private resolveMacosUpdateExecArgs(ctx: UpdateJobContext): string[] {
|
||||
const guestPath =
|
||||
"/opt/homebrew/bin:/opt/homebrew/opt/node/bin:/usr/local/bin:/usr/local/sbin:/opt/homebrew/sbin:/usr/bin:/bin:/usr/sbin:/sbin";
|
||||
const currentUser = run("prlctl", ["exec", macosVm, "--current-user", "whoami"], {
|
||||
const currentUser = run("prlctl", ["exec", this.macosVm, "--current-user", "whoami"], {
|
||||
check: false,
|
||||
quiet: true,
|
||||
timeoutMs: 45_000,
|
||||
@@ -980,7 +1003,7 @@ export class NpmUpdateSmoke {
|
||||
|
||||
private resolveMacosDesktopUser(): string {
|
||||
const consoleUser =
|
||||
run("prlctl", ["exec", macosVm, "/usr/bin/stat", "-f", "%Su", "/dev/console"], {
|
||||
run("prlctl", ["exec", this.macosVm, "/usr/bin/stat", "-f", "%Su", "/dev/console"], {
|
||||
check: false,
|
||||
quiet: true,
|
||||
timeoutMs: 30_000,
|
||||
@@ -998,7 +1021,7 @@ export class NpmUpdateSmoke {
|
||||
}
|
||||
const users = run(
|
||||
"prlctl",
|
||||
["exec", macosVm, "/usr/bin/dscl", ".", "-list", "/Users", "NFSHomeDirectory"],
|
||||
["exec", this.macosVm, "/usr/bin/dscl", ".", "-list", "/Users", "NFSHomeDirectory"],
|
||||
{ check: false, quiet: true, timeoutMs: 30_000 },
|
||||
).stdout.replaceAll("\r", "");
|
||||
for (const line of users.split("\n")) {
|
||||
@@ -1019,7 +1042,7 @@ export class NpmUpdateSmoke {
|
||||
private resolveMacosDesktopHome(user: string): string {
|
||||
const output = run(
|
||||
"prlctl",
|
||||
["exec", macosVm, "/usr/bin/dscl", ".", "-read", `/Users/${user}`, "NFSHomeDirectory"],
|
||||
["exec", this.macosVm, "/usr/bin/dscl", ".", "-read", `/Users/${user}`, "NFSHomeDirectory"],
|
||||
{ check: false, quiet: true, timeoutMs: 30_000 },
|
||||
).stdout.replaceAll("\r", "");
|
||||
const match = /NFSHomeDirectory:\s*(\S+)/.exec(output);
|
||||
|
||||
@@ -105,6 +105,22 @@ export function resolveUbuntuVmName(requested: string, explicit = false): string
|
||||
return fallback;
|
||||
}
|
||||
|
||||
export function resolveMacosVmName(requested: string, explicit = false): string {
|
||||
const names = listVmNames();
|
||||
if (names.includes(requested)) {
|
||||
return requested;
|
||||
}
|
||||
if (explicit) {
|
||||
die(`VM not found: ${requested}`);
|
||||
}
|
||||
const fallback = names.find((name) => name === "macOS");
|
||||
if (!fallback) {
|
||||
die(`VM not found: ${requested}; select a macOS VM explicitly`);
|
||||
}
|
||||
warn(`requested VM ${requested} not found; using ${fallback}`);
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function listVms(timeoutMs = PRLCTL_STATUS_TIMEOUT_MS): PrlctlVmListItem[] {
|
||||
return JSON.parse(
|
||||
run("prlctl", ["list", "--all", "--json"], {
|
||||
|
||||
Reference in New Issue
Block a user