Files
WYF-koubo/tools/oem-batch-builder/kill-stale-build-processes.ps1
2026-06-19 18:45:55 +08:00

210 lines
7.9 KiB
PowerShell

param(
[string]$ProjectRoot = ""
)
$ErrorActionPreference = "SilentlyContinue"
if ([string]::IsNullOrWhiteSpace($ProjectRoot)) {
$ProjectRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).Path
} else {
$ProjectRoot = (Resolve-Path $ProjectRoot).Path
}
$toolDir = Join-Path $ProjectRoot "tools\oem-batch-builder"
$activeRunPath = Join-Path $toolDir "active-run.json"
$runsDir = Join-Path $toolDir "runs"
$currentPid = $PID
$killed = New-Object System.Collections.Generic.List[int]
function Write-CleanupLog([string]$Message) {
$stamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Write-Output "[$stamp] $Message"
}
function Write-JsonNoBom($Path, $Value) {
$json = $Value | ConvertTo-Json -Depth 10
$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
[System.IO.File]::WriteAllText($Path, $json, $utf8NoBom)
}
function Get-ProcByPid([int]$ProcId) {
Get-CimInstance Win32_Process -Filter "ProcessId=$ProcId"
}
function Test-IsProjectBuildProcess($Proc) {
if (-not $Proc) { return $false }
if ([int]$Proc.ProcessId -eq [int]$currentPid) { return $false }
$name = [string]$Proc.Name
$cmd = [string]$Proc.CommandLine
$exe = [string]$Proc.ExecutablePath
$text = "$cmd`n$exe"
$lower = $text.ToLowerInvariant()
$rootLower = $ProjectRoot.ToLowerInvariant()
if ($name -ieq "OEMBatchBuilderTool.exe") { return $false }
if ($lower.Contains("tools\oem-batch-builder\worker.cjs")) { return $true }
if ($lower.Contains("tools\oem-batch-builder\main.cjs")) { return $true }
if ($lower.Contains("electron-builder") -and $lower.Contains("--win") -and $lower.Contains("--x64")) { return $true }
if ($lower.Contains("npm.cmd run build:win") -or $lower.Contains("npm run build:win")) { return $true }
if (-not $lower.Contains($rootLower)) {
return $false
}
if ($lower.Contains("\dist-release-final\")) { return $true }
if ($lower.Contains("\build\oem-batch-temp\")) { return $true }
if ($lower.Contains("dist-release-final\win-unpacked")) { return $true }
if ($lower.Contains("node_modules\.bin\electron-builder")) { return $true }
if ($lower.Contains("electron-builder.cmd")) { return $true }
if ($lower.Contains("electron-builder --win")) { return $true }
if ($lower.Contains("npm.cmd run build:win")) { return $true }
if ($lower.Contains("npm run build:win")) { return $true }
if ($lower.Contains("npm.cmd run prepare-package")) { return $true }
if ($lower.Contains("npm run prepare-package")) { return $true }
if ($lower.Contains("npm.cmd run build")) { return $true }
if ($lower.Contains("npm run build")) { return $true }
if ($lower.Contains("vite build")) { return $true }
if ($lower.Contains("apply-oem.cjs")) { return $true }
if ($lower.Contains("scripts\prepare-package.cjs")) { return $true }
if ($lower.Contains("scripts\build-resource-artifacts.cjs")) { return $true }
if ($lower.Contains("scripts\fix-latest-yml.cjs")) { return $true }
if ($lower.Contains("scripts\assemble-release-artifacts.cjs")) { return $true }
if ($lower.Contains("scripts\clean-build-artifacts.cjs")) { return $true }
return $false
}
function Test-IsWorkerPidProcess($Proc) {
if (-not $Proc) { return $false }
$cmd = ([string]$Proc.CommandLine).ToLowerInvariant()
return $cmd.Contains("tools\oem-batch-builder\worker.cjs")
}
function Test-ToolInstanceIsAlive() {
$instances = @(Get-CimInstance Win32_Process | Where-Object {
if (-not $_) { return $false }
if ([int]$_.ProcessId -eq [int]$currentPid) { return $false }
if ([string]$_.Name -ine "OEMBatchBuilderTool.exe") { return $false }
$cmd = ([string]$_.CommandLine).ToLowerInvariant()
$cmd.Contains("tools\oem-batch-builder")
})
if ($instances.Count -gt 0) {
$ids = ($instances | ForEach-Object { $_.ProcessId }) -join ","
Write-CleanupLog "OEM batch builder is already running. Skip cleanup and let single-instance focus it. PIDs=$ids"
return $true
}
return $false
}
function Test-ActiveWorkerIsAlive() {
if (-not (Test-Path $activeRunPath)) { return $false }
try {
$active = Get-Content -Path $activeRunPath -Raw -Encoding UTF8 | ConvertFrom-Json
if (-not $active.running) { return $false }
$candidatePids = New-Object System.Collections.Generic.List[int]
if ($active.pid) { $candidatePids.Add([int]$active.pid) | Out-Null }
if ($active.sessionDir) {
$workerPidPath = Join-Path ([string]$active.sessionDir) "worker.pid"
if (Test-Path $workerPidPath) {
$pidText = (Get-Content -Path $workerPidPath -Raw).Trim()
if ($pidText -match "^\d+$") { $candidatePids.Add([int]$pidText) | Out-Null }
}
}
foreach ($candidatePid in $candidatePids) {
$proc = Get-ProcByPid $candidatePid
if ($proc -and (Test-IsWorkerPidProcess $proc)) {
Write-CleanupLog "Active OEM build is still running. Skip cleanup. PID=$candidatePid Session=$($active.sessionDir)"
return $true
}
}
} catch {
Write-CleanupLog "Failed to inspect active-run.json: $($_.Exception.Message)"
}
return $false
}
function Stop-Tree([int]$ProcId, [string]$Reason, [bool]$WorkerPidOnly = $false) {
if ($ProcId -le 0 -or $ProcId -eq $currentPid) { return }
if ($killed.Contains($ProcId)) { return }
$proc = Get-ProcByPid $ProcId
if (-not $proc) { return }
if ($WorkerPidOnly -and -not (Test-IsWorkerPidProcess $proc)) {
Write-CleanupLog "Skip PID $ProcId ($($proc.Name)): $Reason points to a non-worker process"
return
}
if (-not (Test-IsProjectBuildProcess $proc)) { return }
Write-CleanupLog "Killing PID $ProcId ($($proc.Name)): $Reason"
& taskkill.exe /F /T /PID $ProcId | Out-Null
$killed.Add($ProcId) | Out-Null
}
function Stop-ActiveRunState([string]$Message) {
if (-not (Test-Path $activeRunPath)) { return }
try {
$state = Get-Content -Path $activeRunPath -Raw -Encoding UTF8 | ConvertFrom-Json
if ($state.running -or $killed.Count -gt 0) {
$state | Add-Member -NotePropertyName "running" -NotePropertyValue $false -Force
$state | Add-Member -NotePropertyName "stoppedAt" -NotePropertyValue ((Get-Date).ToUniversalTime().ToString("o")) -Force
$state | Add-Member -NotePropertyName "message" -NotePropertyValue $Message -Force
Write-JsonNoBom $activeRunPath $state
Write-CleanupLog "Marked active-run stopped: $Message"
}
} catch {
Write-CleanupLog "Failed to update active-run.json: $($_.Exception.Message)"
}
}
Write-CleanupLog "Cleanup started. ProjectRoot=$ProjectRoot"
if (Test-ToolInstanceIsAlive) {
Write-CleanupLog "Cleanup skipped because the tool process is alive."
exit 0
}
if (Test-ActiveWorkerIsAlive) {
Write-CleanupLog "Cleanup skipped because an active worker is alive."
exit 0
}
if (Test-Path $activeRunPath) {
try {
$active = Get-Content -Path $activeRunPath -Raw -Encoding UTF8 | ConvertFrom-Json
if ($active.pid) { Stop-Tree ([int]$active.pid) "active-run pid" $true }
if ($active.launcherPid) { Stop-Tree ([int]$active.launcherPid) "active-run launcherPid" }
if ($active.sessionDir) {
$workerPidPath = Join-Path ([string]$active.sessionDir) "worker.pid"
if (Test-Path $workerPidPath) {
$pidText = (Get-Content -Path $workerPidPath -Raw).Trim()
if ($pidText -match "^\d+$") { Stop-Tree ([int]$pidText) "active session worker.pid" $true }
}
}
} catch {
Write-CleanupLog "Failed to read active-run.json: $($_.Exception.Message)"
}
}
Get-CimInstance Win32_Process | Where-Object { Test-IsProjectBuildProcess $_ } | ForEach-Object {
Stop-Tree ([int]$_.ProcessId) "matched OEM/build command"
}
Start-Sleep -Milliseconds 800
$remaining = @(Get-CimInstance Win32_Process | Where-Object { Test-IsProjectBuildProcess $_ })
if ($remaining.Count -gt 0) {
foreach ($proc in $remaining) {
Write-CleanupLog "Still running PID $($proc.ProcessId) ($($proc.Name))"
}
} else {
Write-CleanupLog "Cleanup finished. Killed=$($killed.Count)"
}
if ($killed.Count -gt 0) {
Stop-ActiveRunState "Stale OEM build processes were cleaned before launching the tool. Killed=$($killed.Count)"
}