import { DebugLogger, exec, ExtraSpawnOptions, InvalidConfigurationError, log, spawn } from "builder-util" import { ExecFileOptions, SpawnOptions } from "child_process" import { Lazy } from "lazy-val" import * as path from "path" import { ParallelsVm } from "./ParallelsVm" export class VmManager { get pathSep(): string { return path.sep } exec(file: string, args: Array, options?: ExecFileOptions, isLogOutIfDebug = true): Promise { return exec(file, args, options, isLogOutIfDebug) } spawn(file: string, args: Array, options?: SpawnOptions, extraOptions?: ExtraSpawnOptions): Promise { return spawn(file, args, options, extraOptions) } toVmFile(file: string): string { return file } readonly powershellCommand = new Lazy(() => { return this.exec("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", `Get-Command pwsh.exe`]) .then(() => { log.info(null, "identified pwsh.exe") return "pwsh.exe" }) .catch(() => { log.info(null, "unable to find pwsh.exe, falling back to powershell.exe") return "powershell.exe" }) }) } export async function getWindowsVm(debugLogger: DebugLogger): Promise { const parallelsVmModule = await import("./ParallelsVm") let vmList: ParallelsVm[] = [] try { vmList = (await parallelsVmModule.parseVmList(debugLogger)).filter(it => ["win-10", "win-11"].includes(it.os)) } catch (_error) { if ((await isPwshAvailable.value) && (await isWineAvailable.value)) { const vmModule = await import("./PwshVm") return new vmModule.PwshVmManager() } } if (vmList.length === 0) { throw new InvalidConfigurationError("Cannot find suitable Parallels Desktop virtual machine (Windows 10 is required) and cannot access `pwsh` and `wine` locally") } // prefer running or suspended vm return new parallelsVmModule.ParallelsVmManager(vmList.find(it => it.state === "running") || vmList.find(it => it.state === "suspended") || vmList[0]) } const isWineAvailable = new Lazy(async () => { return isCommandAvailable("wine", ["--version"]) }) export const isPwshAvailable = new Lazy(async () => { return isCommandAvailable("pwsh", ["--version"]) }) export const isCommandAvailable = async (command: string, args: string[]) => { try { await exec(command, args) return true } catch { return false } }