Files
yuqei af057ff165 chore: archive project history and preserve delivery controls
Keep current plans and release controls discoverable without losing historical evidence, and include the reviewed Windows start/stop workflow.
2026-10-08 19:05:30 +08:00

43 lines
3.0 KiB
PowerShell

# Stop only recorded project processes whose creation time and identity still match.
$ErrorActionPreference = 'Stop'
$root = $PSScriptRoot
$stateFile = Join-Path $root '.runtime\dev-logs\processes.json'
if (-not (Test-Path -LiteralPath $stateFile)) { Write-Host 'No recorded local services.'; exit 0 }
$state = Get-Content -Raw -LiteralPath $stateFile | ConvertFrom-Json
if ($state.Root -ne $root) { throw 'Process state belongs to another directory. Nothing stopped.' }
function Get-Identity([int]$ProcessId) {
$item = Get-CimInstance Win32_Process -Filter "ProcessId = $ProcessId" -ErrorAction SilentlyContinue
if (-not $item) { return $null }
return [pscustomobject]@{ Pid = [int]$item.ProcessId; CreatedUtc = $item.CreationDate.ToUniversalTime().ToString('o'); ExecutablePath = [string]$item.ExecutablePath; CommandLine = [string]$item.CommandLine }
}
function Stop-MatchedProcess($Expected, [switch]$VerifiedPythonChild) {
$actual = Get-Identity $Expected.Pid
if (-not $actual) { return }
if ($actual.CreatedUtc -ne $Expected.CreatedUtc -or $actual.ExecutablePath -ne $Expected.ExecutablePath -or $actual.CommandLine -ne $Expected.CommandLine -or (-not $VerifiedPythonChild -and $actual.CommandLine.IndexOf($root, [StringComparison]::OrdinalIgnoreCase) -lt 0)) {
Write-Warning "PID $($Expected.Pid) no longer matches this project; skipped."
return
}
# Only matched project descendants; never kill by process name.
$moduleArguments = if ($actual.CommandLine -match '\s-m\s+(.+)$') { $matches[1] } else { '' }
$children = @(Get-CimInstance Win32_Process -Filter "ParentProcessId = $($actual.Pid)" | Where-Object {
$_.CommandLine -and $_.CreationDate.ToUniversalTime() -ge [datetime]::Parse($actual.CreatedUtc).ToUniversalTime()
})
foreach ($child in $children) {
$identity = Get-Identity $child.ProcessId
if (-not $identity) { continue }
if ($identity.CreatedUtc -ne $child.CreationDate.ToUniversalTime().ToString('o') -or $identity.CommandLine -ne $child.CommandLine) { continue }
if ($identity.CommandLine.IndexOf($root, [StringComparison]::OrdinalIgnoreCase) -ge 0) { Stop-MatchedProcess $identity }
elseif ($moduleArguments -and $actual.ExecutablePath -match '\\python.exe$' -and $identity.ExecutablePath -match '\\python.exe$' -and $identity.CommandLine -match '\s-m\s+(.+)$' -and $matches[1] -eq $moduleArguments) {
# Same module and arguments, live parent link, and later creation time establish venv ownership.
Stop-MatchedProcess $identity -VerifiedPythonChild
}
}
$recheck = Get-Identity $actual.Pid
if ($recheck -and $recheck.CreatedUtc -eq $actual.CreatedUtc -and $recheck.CommandLine -eq $actual.CommandLine -and $recheck.ExecutablePath -eq $actual.ExecutablePath) {
Stop-Process -Id $actual.Pid -Force -ErrorAction Stop
Write-Host "Stopped PID $($actual.Pid)"
}
}
foreach ($entry in @($state.Services | Sort-Object { if ($_.Name -match 'worker') { 0 } else { 1 } })) { Stop-MatchedProcess $entry.Process }
Write-Host 'Recorded project services stopped; logs retained in .runtime\dev-logs.'