test: harden parallels npm update runner

This commit is contained in:
Peter Steinberger
2026-03-25 11:13:09 +00:00
parent b497f3cda0
commit 94041f06b4

View File

@@ -12,6 +12,7 @@ JSON_OUTPUT=0
RUN_DIR="$(mktemp -d /tmp/openclaw-parallels-npm-update.XXXXXX)"
MAIN_TGZ_DIR="$(mktemp -d)"
MAIN_TGZ_PATH=""
WINDOWS_UPDATE_SCRIPT_PATH=""
SERVER_PID=""
HOST_IP=""
HOST_PORT=""
@@ -165,6 +166,108 @@ pack_main_tgz() {
cp "$MAIN_TGZ_DIR/$pkg" "$MAIN_TGZ_PATH"
}
write_windows_update_script() {
WINDOWS_UPDATE_SCRIPT_PATH="$MAIN_TGZ_DIR/openclaw-main-update.ps1"
cat >"$WINDOWS_UPDATE_SCRIPT_PATH" <<'EOF'
param(
[Parameter(Mandatory = $true)][string]$TgzUrl,
[Parameter(Mandatory = $true)][string]$HeadShort,
[Parameter(Mandatory = $true)][string]$SessionId,
[Parameter(Mandatory = $true)][string]$LogPath,
[Parameter(Mandatory = $true)][string]$DonePath
)
$ErrorActionPreference = 'Stop'
$PSNativeCommandUseErrorActionPreference = $false
function Invoke-Logged {
param(
[Parameter(Mandatory = $true)][string]$Label,
[Parameter(Mandatory = $true)][scriptblock]$Command
)
$previousErrorActionPreference = $ErrorActionPreference
$previousNativeErrorPreference = $PSNativeCommandUseErrorActionPreference
try {
$ErrorActionPreference = 'Continue'
$PSNativeCommandUseErrorActionPreference = $false
& $Command *>> $LogPath
$exitCode = $LASTEXITCODE
} finally {
$ErrorActionPreference = $previousErrorActionPreference
$PSNativeCommandUseErrorActionPreference = $previousNativeErrorPreference
}
if ($exitCode -ne 0) {
throw "$Label failed with exit code $exitCode"
}
}
function Invoke-CaptureLogged {
param(
[Parameter(Mandatory = $true)][string]$Label,
[Parameter(Mandatory = $true)][scriptblock]$Command
)
$previousErrorActionPreference = $ErrorActionPreference
$previousNativeErrorPreference = $PSNativeCommandUseErrorActionPreference
try {
$ErrorActionPreference = 'Continue'
$PSNativeCommandUseErrorActionPreference = $false
$output = & $Command 2>&1
$exitCode = $LASTEXITCODE
} finally {
$ErrorActionPreference = $previousErrorActionPreference
$PSNativeCommandUseErrorActionPreference = $previousNativeErrorPreference
}
if ($null -ne $output) {
$output | Tee-Object -FilePath $LogPath -Append | Out-Null
}
if ($exitCode -ne 0) {
throw "$Label failed with exit code $exitCode"
}
return ($output | Out-String).Trim()
}
try {
$env:PATH = "$env:LOCALAPPDATA\OpenClaw\deps\portable-git\cmd;$env:LOCALAPPDATA\OpenClaw\deps\portable-git\mingw64\bin;$env:LOCALAPPDATA\OpenClaw\deps\portable-git\usr\bin;$env:PATH"
$tgz = Join-Path $env:TEMP 'openclaw-main-update.tgz'
Remove-Item $tgz, $LogPath, $DonePath -Force -ErrorAction SilentlyContinue
Invoke-Logged 'download current tgz' { curl.exe -fsSL $TgzUrl -o $tgz }
Invoke-Logged 'npm install current tgz' { npm.cmd install -g $tgz --no-fund --no-audit }
$openclaw = Join-Path $env:APPDATA 'npm\openclaw.cmd'
$version = Invoke-CaptureLogged 'openclaw --version' { & $openclaw --version }
if ($version -notmatch [regex]::Escape($HeadShort)) {
throw "version mismatch: expected substring $HeadShort"
}
Invoke-Logged 'openclaw models set' { & $openclaw models set openai/gpt-5.4 }
# Windows can keep the old hashed dist modules alive across in-place global npm upgrades.
# Restart the gateway/service before verifying status or the next agent turn.
Invoke-Logged 'openclaw gateway restart' { & $openclaw gateway restart }
Start-Sleep -Seconds 5
Invoke-Logged 'openclaw gateway status' { & $openclaw gateway status --deep --require-rpc }
Invoke-CaptureLogged 'openclaw agent' { & $openclaw agent --agent main --session-id $SessionId --message 'Reply with exact ASCII text OK only.' --json } | Out-Null
$exitCode = $LASTEXITCODE
if ($null -eq $exitCode) {
$exitCode = 0
}
Set-Content -Path $DonePath -Value ([string]$exitCode)
exit $exitCode
} catch {
if (Test-Path $LogPath) {
Add-Content -Path $LogPath -Value ($_ | Out-String)
} else {
($_ | Out-String) | Set-Content -Path $LogPath
}
Set-Content -Path $DonePath -Value '1'
exit 1
}
EOF
}
start_server() {
HOST_IP="$(resolve_host_ip)"
HOST_PORT="$(allocate_host_port)"
@@ -197,6 +300,7 @@ import sys
text = pathlib.Path(sys.argv[1]).read_text(encoding="utf-8", errors="replace")
matches = re.findall(r"OpenClaw [^\r\n]+", text)
matches = [match for match in matches if re.search(r"OpenClaw \d", match)]
print(matches[-1] if matches else "")
PY
}
@@ -218,35 +322,35 @@ PY
}
run_windows_script_via_log() {
local script_body="$1"
local runner_name log_name done_name done_status
local script_url="$1"
local tgz_url="$2"
local head_short="$3"
local session_id="$4"
local runner_name log_name done_name done_status launcher_state
local start_seconds poll_deadline startup_checked
runner_name="openclaw-update-$RANDOM-$RANDOM.ps1"
log_name="openclaw-update-$RANDOM-$RANDOM.log"
done_name="openclaw-update-$RANDOM-$RANDOM.done"
start_seconds="$SECONDS"
poll_deadline=$((SECONDS + 900))
startup_checked=0
guest_powershell "$(cat <<EOF
\$runner = Join-Path \$env:TEMP '$runner_name'
\$log = Join-Path \$env:TEMP '$log_name'
\$done = Join-Path \$env:TEMP '$done_name'
Remove-Item \$runner, \$log, \$done -Force -ErrorAction SilentlyContinue
@'
\$ErrorActionPreference = 'Stop'
\$PSNativeCommandUseErrorActionPreference = \$false
\$log = Join-Path \$env:TEMP '$log_name'
\$done = Join-Path \$env:TEMP '$done_name'
try {
$script_body
Set-Content -Path \$done -Value ([string]\$LASTEXITCODE)
} catch {
if (Test-Path \$log) {
Add-Content -Path \$log -Value (\$_ | Out-String)
} else {
(\$_ | Out-String) | Set-Content -Path \$log
}
Set-Content -Path \$done -Value '1'
}
'@ | Set-Content -Path \$runner
Start-Process powershell.exe -ArgumentList @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', \$runner) -WindowStyle Hidden | Out-Null
curl.exe -fsSL '$script_url' -o \$runner
Start-Process powershell.exe -ArgumentList @(
'-NoProfile',
'-ExecutionPolicy', 'Bypass',
'-File', \$runner,
'-TgzUrl', '$tgz_url',
'-HeadShort', '$head_short',
'-SessionId', '$session_id',
'-LogPath', \$log,
'-DonePath', \$done
) -WindowStyle Hidden | Out-Null
EOF
)"
@@ -260,6 +364,22 @@ EOF
[[ "$done_status" == "0" ]]
return $?
fi
if [[ "$startup_checked" -eq 0 && $((SECONDS - start_seconds)) -ge 20 ]]; then
launcher_state="$(
guest_powershell "\$runner = Join-Path \$env:TEMP '$runner_name'; \$log = Join-Path \$env:TEMP '$log_name'; \$done = Join-Path \$env:TEMP '$done_name'; 'runner=' + (Test-Path \$runner) + ' log=' + (Test-Path \$log) + ' done=' + (Test-Path \$done)"
)"
launcher_state="${launcher_state//$'\r'/}"
startup_checked=1
if [[ "$launcher_state" == *"runner=False"* && "$launcher_state" == *"log=False"* && "$launcher_state" == *"done=False"* ]]; then
warn "windows update helper failed to materialize guest files"
return 1
fi
fi
if (( SECONDS >= poll_deadline )); then
guest_powershell "\$log = Join-Path \$env:TEMP '$log_name'; if (Test-Path \$log) { Get-Content \$log }"
warn "windows update helper timed out waiting for done file"
return 1
fi
sleep 2
done
}
@@ -293,30 +413,8 @@ EOF
run_windows_update() {
local tgz_url="$1"
local head_short="$2"
run_windows_script_via_log "$(cat <<EOF
\$env:PATH = "\$env:LOCALAPPDATA\OpenClaw\deps\portable-git\cmd;\$env:LOCALAPPDATA\OpenClaw\deps\portable-git\mingw64\bin;\$env:LOCALAPPDATA\OpenClaw\deps\portable-git\usr\bin;\$env:PATH"
\$tgz = Join-Path \$env:TEMP 'openclaw-main-update.tgz'
curl.exe -fsSL '$tgz_url' -o \$tgz >> \$log 2>&1
npm.cmd install -g \$tgz --no-fund --no-audit >> \$log 2>&1
\$openclaw = Join-Path \$env:APPDATA 'npm\openclaw.cmd'
\$version = & \$openclaw --version
\$version | Tee-Object -FilePath \$log -Append
if (\$version -notmatch '$head_short') {
throw 'version mismatch: expected substring $head_short'
}
& \$openclaw models set openai/gpt-5.4 >> \$log 2>&1
# Windows can keep the old hashed dist modules alive across in-place global npm upgrades.
# Restart the gateway/service before verifying status or the next agent turn.
& \$openclaw gateway restart >> \$log 2>&1
Start-Sleep -Seconds 5
& \$openclaw gateway status --deep --require-rpc >> \$log 2>&1
\$output = & \$openclaw agent --agent main --session-id parallels-npm-update-windows-$head_short --message 'Reply with exact ASCII text OK only.' --json 2>&1
if (\$null -ne \$output) {
\$output | ForEach-Object { \$_ } | Tee-Object -FilePath \$log -Append
}
exit \$LASTEXITCODE
EOF
)"
local script_url="$3"
run_windows_script_via_log "$script_url" "$tgz_url" "$head_short" "parallels-npm-update-windows-$head_short"
}
run_linux_update() {
@@ -421,14 +519,16 @@ wait_job "Linux fresh" "$linux_fresh_pid" && LINUX_FRESH_STATUS="pass" || LINUX_
[[ "$LINUX_FRESH_STATUS" == "pass" ]] || die "Linux fresh baseline failed"
pack_main_tgz
write_windows_update_script
start_server
tgz_url="http://$HOST_IP:$HOST_PORT/$(basename "$MAIN_TGZ_PATH")"
windows_update_script_url="http://$HOST_IP:$HOST_PORT/$(basename "$WINDOWS_UPDATE_SCRIPT_PATH")"
say "Run same-guest update to current main"
run_macos_update "$tgz_url" "$CURRENT_HEAD_SHORT" >"$RUN_DIR/macos-update.log" 2>&1 &
macos_update_pid=$!
run_windows_update "$tgz_url" "$CURRENT_HEAD_SHORT" >"$RUN_DIR/windows-update.log" 2>&1 &
run_windows_update "$tgz_url" "$CURRENT_HEAD_SHORT" "$windows_update_script_url" >"$RUN_DIR/windows-update.log" 2>&1 &
windows_update_pid=$!
run_linux_update "$tgz_url" "$CURRENT_HEAD_SHORT" >"$RUN_DIR/linux-update.log" 2>&1 &
linux_update_pid=$!