diff --git a/scripts/Cleanup-OrphanedBuildVMs.ps1 b/scripts/Cleanup-OrphanedBuildVMs.ps1 index ee1f3f9..cdece3c 100644 --- a/scripts/Cleanup-OrphanedBuildVMs.ps1 +++ b/scripts/Cleanup-OrphanedBuildVMs.ps1 @@ -1,23 +1,37 @@ -#Requires -Version 5.1 -Set-StrictMode -Version Latest -$ErrorActionPreference = 'Stop' - -# Shim: delegates to the Python ci_orchestrator CLI. -# Original PS implementation moved to git history; see plans/A2-closeout.md. -$pyArgs = @() -foreach ($a in $args) { - if ($a -is [string] -and $a.Length -gt 1 -and $a.StartsWith('-') -and -not $a.StartsWith('--')) { - $name = $a.Substring(1) - $kebab = [Regex]::Replace($name, '(?<=[a-z0-9])([A-Z])', '-$1') - $kebab = [Regex]::Replace($kebab, '([A-Z]+)([A-Z][a-z])', '$1-$2') - $pyArgs += '--' + $kebab.ToLower() - } - else { - $pyArgs += $a - } -} - -$venvPython = $env:CI_VENV_PYTHON -if (-not $venvPython) { $venvPython = 'F:\CI\python\venv\Scripts\python.exe' } -& $venvPython -m ci_orchestrator vm cleanup @pyArgs -exit $LASTEXITCODE +#Requires -Version 5.1 +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +# Shim: delegates to the Python ci_orchestrator CLI. +# Original PS implementation moved to git history; see plans/A2-closeout.md. +# PowerShell common params (-ErrorAction etc.) must not be forwarded +# to the Python CLI. Value-taking ones also consume the next token. +$commonWithValue = @{ + erroraction = $true; warningaction = $true; informationaction = $true + progressaction = $true; errorvariable = $true; warningvariable = $true + informationvariable = $true; outvariable = $true; outbuffer = $true + pipelinevariable = $true +} +$commonSwitch = @{ verbose = $true; debug = $true; whatif = $true; confirm = $true } + +$pyArgs = @() +for ($i = 0; $i -lt $args.Count; $i++) { + $a = $args[$i] + if ($a -is [string] -and $a.Length -gt 1 -and $a.StartsWith('-') -and -not $a.StartsWith('--')) { + $name = $a.Substring(1) + $lname = $name.ToLower() + if ($commonSwitch.ContainsKey($lname)) { continue } + if ($commonWithValue.ContainsKey($lname)) { $i++; continue } + $kebab = [Regex]::Replace($name, '(?<=[a-z0-9])([A-Z])', '-$1') + $kebab = [Regex]::Replace($kebab, '([A-Z]+)([A-Z][a-z])', '$1-$2') + $pyArgs += '--' + $kebab.ToLower() + } + else { + $pyArgs += $a + } +} + +$venvPython = $env:CI_VENV_PYTHON +if (-not $venvPython) { $venvPython = 'F:\CI\python\venv\Scripts\python.exe' } +& $venvPython -m ci_orchestrator vm cleanup @pyArgs +exit $LASTEXITCODE diff --git a/scripts/Get-CIJobSummary.ps1 b/scripts/Get-CIJobSummary.ps1 index d5df4c2..f8041a4 100644 --- a/scripts/Get-CIJobSummary.ps1 +++ b/scripts/Get-CIJobSummary.ps1 @@ -1,201 +1,215 @@ -#Requires -Version 5.1 -Set-StrictMode -Version Latest -$ErrorActionPreference = 'Stop' - -# Shim: delegates to the Python ci_orchestrator CLI. -# Original PS implementation moved to git history; see plans/A2-closeout.md. -$pyArgs = @() -foreach ($a in $args) { - if ($a -is [string] -and $a.Length -gt 1 -and $a.StartsWith('-') -and -not $a.StartsWith('--')) { - $name = $a.Substring(1) - $kebab = [Regex]::Replace($name, '(?<=[a-z0-9])([A-Z])', '-$1') - $kebab = [Regex]::Replace($kebab, '([A-Z]+)([A-Z][a-z])', '$1-$2') - $pyArgs += '--' + $kebab.ToLower() - } - else { - $pyArgs += $a - } -} - -$venvPython = $env:CI_VENV_PYTHON -if (-not $venvPython) { $venvPython = 'F:\CI\python\venv\Scripts\python.exe' } -& $venvPython -m ci_orchestrator report job @pyArgs -exit $LASTEXITCODE - - -.DESCRIPTION - Scans F:\CI\Logs\ (or a custom path) for invoke-ci.jsonl files and - prints a compact table with job ID, status, total elapsed time, and the - last error message (if any). - - Each row corresponds to one completed (or failed) CI job. - Use -JobId to inspect a single job in detail. - -.PARAMETER LogDir - Base directory containing per-job log subdirectories. - Default: F:\CI\Logs - -.PARAMETER Last - Show only the N most recent jobs (sorted by job-start timestamp). - Default: 20 - -.PARAMETER JobId - When specified, shows a detailed phase-by-phase breakdown for that job. - -.PARAMETER Failed - When specified, filters to failed jobs only. - -.EXAMPLE - # Show last 20 jobs - .\Get-CIJobSummary.ps1 - -.EXAMPLE - # Show last 5 failed jobs - .\Get-CIJobSummary.ps1 -Last 5 -Failed - -.EXAMPLE - # Detailed breakdown for a specific job - .\Get-CIJobSummary.ps1 -JobId 'run-12345' -#> -[CmdletBinding()] -param( - [string] $LogDir = 'F:\CI\Logs', - - [ValidateRange(1, 1000)] - [int] $Last = 20, - - [string] $JobId = '', - - [switch] $Failed -) - -Set-StrictMode -Version Latest -$ErrorActionPreference = 'Stop' - -# ── Helpers ──────────────────────────────────────────────────────────────────── -function Format-SecToHMS { - param([int] $Seconds) - if ($Seconds -lt 0) { return '?' } - $h = [math]::Floor($Seconds / 3600) - $m = [math]::Floor(($Seconds % 3600) / 60) - $s = $Seconds % 60 - if ($h -gt 0) { return '{0}h{1:00}m{2:00}s' -f $h, $m, $s } - return '{0}m{1:00}s' -f $m, $s -} - -function Read-JobEvents { - param([string] $JsonlPath) - $events = [System.Collections.Generic.List[PSCustomObject]]::new() - foreach ($line in (Get-Content $JsonlPath -Encoding UTF8 -ErrorAction SilentlyContinue)) { - $line = $line.Trim() - if (-not $line) { continue } - try { - $events.Add(($line | ConvertFrom-Json)) - } catch { - Write-Debug "[Get-CIJobSummary] Skipping malformed JSONL line in $JsonlPath" - } - } - return $events -} - -# ── Validate log dir ──────────────────────────────────────────────────────── -if (-not (Test-Path $LogDir -PathType Container)) { - Write-Warning "Log directory not found: $LogDir" - exit 1 -} - -# ── Single-job detail mode ────────────────────────────────────────────────── -if ($JobId -ne '') { - $jsonlPath = Join-Path $LogDir (Join-Path $JobId 'invoke-ci.jsonl') - if (-not (Test-Path $jsonlPath)) { - Write-Warning "No JSONL log found for job '$JobId' at: $jsonlPath" - exit 1 - } - $events = Read-JobEvents -JsonlPath $jsonlPath - if ($events.Count -eq 0) { - Write-Warning "JSONL file is empty or unreadable: $jsonlPath" - exit 1 - } - Write-Host "`nJob: $JobId" - Write-Host ('=' * 60) - Write-Host ('{0,-35} {1,-10} {2}' -f 'Phase', 'Status', 'Timestamp') - Write-Host ('{0,-35} {1,-10} {2}' -f ('-'*35), ('-'*10), ('-'*24)) - foreach ($ev in $events) { - $ts = if ($ev.ts) { $ev.ts } else { '' } - Write-Host ('{0,-35} {1,-10} {2}' -f $ev.phase, $ev.status, $ts) - } - # Print error if present - $failEvent = $events | Where-Object { $_.status -eq 'failure' } | Select-Object -Last 1 - if ($failEvent -and $failEvent.data -and $failEvent.data.error) { - Write-Host "`nError: $($failEvent.data.error)" - } - exit 0 -} - -# ── Scan all job JSONL files ───────────────────────────────────────────────── -$jsonlFiles = Get-ChildItem -Path $LogDir -Recurse -Filter 'invoke-ci.jsonl' -ErrorAction SilentlyContinue | - Sort-Object { $_.LastWriteTime } -Descending - -if ($jsonlFiles.Count -eq 0) { - Write-Host "No invoke-ci.jsonl files found under $LogDir" - exit 0 -} - -$rows = [System.Collections.Generic.List[PSCustomObject]]::new() - -foreach ($file in $jsonlFiles) { - $events = Read-JobEvents -JsonlPath $file.FullName - if ($events.Count -eq 0) { continue } - - $jobEvent = $events | Where-Object { $_.phase -eq 'job' -and $_.status -eq 'start' } | Select-Object -First 1 - $successEvent= $events | Where-Object { $_.phase -eq 'job' -and $_.status -eq 'success' } | Select-Object -First 1 - $failEvent = $events | Where-Object { $_.phase -eq 'job' -and $_.status -eq 'failure' } | Select-Object -First 1 - - $jId = if ($events[0].jobId) { $events[0].jobId } else { (Split-Path (Split-Path $file.FullName -Parent) -Leaf) } - $started = if ($jobEvent -and $jobEvent.ts) { $jobEvent.ts } else { '' } - - if ($successEvent) { - $status = 'success' - $elSec = if ($successEvent.data -and $null -ne $successEvent.data.elapsedSec) { [int]$successEvent.data.elapsedSec } else { -1 } - $errMsg = '' - } elseif ($failEvent) { - $status = 'FAILED' - $elSec = if ($failEvent.data -and $null -ne $failEvent.data.elapsedSec) { [int]$failEvent.data.elapsedSec } else { -1 } - $errMsg = if ($failEvent.data -and $failEvent.data.error) { "$($failEvent.data.error)" } else { '' } - # Truncate error for table display - if ($errMsg.Length -gt 60) { $errMsg = $errMsg.Substring(0, 57) + '...' } - } else { - $status = 'in-progress' - $elSec = -1 - $errMsg = '' - } - - $rows.Add([PSCustomObject]@{ - JobId = $jId - Status = $status - Elapsed = Format-SecToHMS -Seconds $elSec - Started = $started - Error = $errMsg - }) -} - -# ── Apply filters + limit ──────────────────────────────────────────────────── -if ($Failed) { - $rows = @($rows | Where-Object { $_.Status -eq 'FAILED' }) -} - -$rows = @($rows | Select-Object -First $Last) - -if ($rows.Count -eq 0) { - Write-Host "No matching jobs found." - exit 0 -} - -# ── Print table ────────────────────────────────────────────────────────────── -Write-Host "`nCI Job Summary (last $($rows.Count) jobs):" -Write-Host ('{0,-40} {1,-12} {2,-10} {3,-26} {4}' -f 'JobId', 'Status', 'Elapsed', 'Started', 'Error') -Write-Host ('{0,-40} {1,-12} {2,-10} {3,-26} {4}' -f ('-'*40), ('-'*12), ('-'*10), ('-'*26), ('-'*30)) -foreach ($r in $rows) { - Write-Host ('{0,-40} {1,-12} {2,-10} {3,-26} {4}' -f $r.JobId, $r.Status, $r.Elapsed, $r.Started, $r.Error) -} -Write-Host '' +#Requires -Version 5.1 +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +# Shim: delegates to the Python ci_orchestrator CLI. +# Original PS implementation moved to git history; see plans/A2-closeout.md. +# PowerShell common params (-ErrorAction etc.) must not be forwarded +# to the Python CLI. Value-taking ones also consume the next token. +$commonWithValue = @{ + erroraction = $true; warningaction = $true; informationaction = $true + progressaction = $true; errorvariable = $true; warningvariable = $true + informationvariable = $true; outvariable = $true; outbuffer = $true + pipelinevariable = $true +} +$commonSwitch = @{ verbose = $true; debug = $true; whatif = $true; confirm = $true } + +$pyArgs = @() +for ($i = 0; $i -lt $args.Count; $i++) { + $a = $args[$i] + if ($a -is [string] -and $a.Length -gt 1 -and $a.StartsWith('-') -and -not $a.StartsWith('--')) { + $name = $a.Substring(1) + $lname = $name.ToLower() + if ($commonSwitch.ContainsKey($lname)) { continue } + if ($commonWithValue.ContainsKey($lname)) { $i++; continue } + $kebab = [Regex]::Replace($name, '(?<=[a-z0-9])([A-Z])', '-$1') + $kebab = [Regex]::Replace($kebab, '([A-Z]+)([A-Z][a-z])', '$1-$2') + $pyArgs += '--' + $kebab.ToLower() + } + else { + $pyArgs += $a + } +} + +$venvPython = $env:CI_VENV_PYTHON +if (-not $venvPython) { $venvPython = 'F:\CI\python\venv\Scripts\python.exe' } +& $venvPython -m ci_orchestrator report job @pyArgs +exit $LASTEXITCODE + + +.DESCRIPTION + Scans F:\CI\Logs\ (or a custom path) for invoke-ci.jsonl files and + prints a compact table with job ID, status, total elapsed time, and the + last error message (if any). + + Each row corresponds to one completed (or failed) CI job. + Use -JobId to inspect a single job in detail. + +.PARAMETER LogDir + Base directory containing per-job log subdirectories. + Default: F:\CI\Logs + +.PARAMETER Last + Show only the N most recent jobs (sorted by job-start timestamp). + Default: 20 + +.PARAMETER JobId + When specified, shows a detailed phase-by-phase breakdown for that job. + +.PARAMETER Failed + When specified, filters to failed jobs only. + +.EXAMPLE + # Show last 20 jobs + .\Get-CIJobSummary.ps1 + +.EXAMPLE + # Show last 5 failed jobs + .\Get-CIJobSummary.ps1 -Last 5 -Failed + +.EXAMPLE + # Detailed breakdown for a specific job + .\Get-CIJobSummary.ps1 -JobId 'run-12345' +#> +[CmdletBinding()] +param( + [string] $LogDir = 'F:\CI\Logs', + + [ValidateRange(1, 1000)] + [int] $Last = 20, + + [string] $JobId = '', + + [switch] $Failed +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +# ── Helpers ──────────────────────────────────────────────────────────────────── +function Format-SecToHMS { + param([int] $Seconds) + if ($Seconds -lt 0) { return '?' } + $h = [math]::Floor($Seconds / 3600) + $m = [math]::Floor(($Seconds % 3600) / 60) + $s = $Seconds % 60 + if ($h -gt 0) { return '{0}h{1:00}m{2:00}s' -f $h, $m, $s } + return '{0}m{1:00}s' -f $m, $s +} + +function Read-JobEvents { + param([string] $JsonlPath) + $events = [System.Collections.Generic.List[PSCustomObject]]::new() + foreach ($line in (Get-Content $JsonlPath -Encoding UTF8 -ErrorAction SilentlyContinue)) { + $line = $line.Trim() + if (-not $line) { continue } + try { + $events.Add(($line | ConvertFrom-Json)) + } catch { + Write-Debug "[Get-CIJobSummary] Skipping malformed JSONL line in $JsonlPath" + } + } + return $events +} + +# ── Validate log dir ──────────────────────────────────────────────────────── +if (-not (Test-Path $LogDir -PathType Container)) { + Write-Warning "Log directory not found: $LogDir" + exit 1 +} + +# ── Single-job detail mode ────────────────────────────────────────────────── +if ($JobId -ne '') { + $jsonlPath = Join-Path $LogDir (Join-Path $JobId 'invoke-ci.jsonl') + if (-not (Test-Path $jsonlPath)) { + Write-Warning "No JSONL log found for job '$JobId' at: $jsonlPath" + exit 1 + } + $events = Read-JobEvents -JsonlPath $jsonlPath + if ($events.Count -eq 0) { + Write-Warning "JSONL file is empty or unreadable: $jsonlPath" + exit 1 + } + Write-Host "`nJob: $JobId" + Write-Host ('=' * 60) + Write-Host ('{0,-35} {1,-10} {2}' -f 'Phase', 'Status', 'Timestamp') + Write-Host ('{0,-35} {1,-10} {2}' -f ('-'*35), ('-'*10), ('-'*24)) + foreach ($ev in $events) { + $ts = if ($ev.ts) { $ev.ts } else { '' } + Write-Host ('{0,-35} {1,-10} {2}' -f $ev.phase, $ev.status, $ts) + } + # Print error if present + $failEvent = $events | Where-Object { $_.status -eq 'failure' } | Select-Object -Last 1 + if ($failEvent -and $failEvent.data -and $failEvent.data.error) { + Write-Host "`nError: $($failEvent.data.error)" + } + exit 0 +} + +# ── Scan all job JSONL files ───────────────────────────────────────────────── +$jsonlFiles = Get-ChildItem -Path $LogDir -Recurse -Filter 'invoke-ci.jsonl' -ErrorAction SilentlyContinue | + Sort-Object { $_.LastWriteTime } -Descending + +if ($jsonlFiles.Count -eq 0) { + Write-Host "No invoke-ci.jsonl files found under $LogDir" + exit 0 +} + +$rows = [System.Collections.Generic.List[PSCustomObject]]::new() + +foreach ($file in $jsonlFiles) { + $events = Read-JobEvents -JsonlPath $file.FullName + if ($events.Count -eq 0) { continue } + + $jobEvent = $events | Where-Object { $_.phase -eq 'job' -and $_.status -eq 'start' } | Select-Object -First 1 + $successEvent= $events | Where-Object { $_.phase -eq 'job' -and $_.status -eq 'success' } | Select-Object -First 1 + $failEvent = $events | Where-Object { $_.phase -eq 'job' -and $_.status -eq 'failure' } | Select-Object -First 1 + + $jId = if ($events[0].jobId) { $events[0].jobId } else { (Split-Path (Split-Path $file.FullName -Parent) -Leaf) } + $started = if ($jobEvent -and $jobEvent.ts) { $jobEvent.ts } else { '' } + + if ($successEvent) { + $status = 'success' + $elSec = if ($successEvent.data -and $null -ne $successEvent.data.elapsedSec) { [int]$successEvent.data.elapsedSec } else { -1 } + $errMsg = '' + } elseif ($failEvent) { + $status = 'FAILED' + $elSec = if ($failEvent.data -and $null -ne $failEvent.data.elapsedSec) { [int]$failEvent.data.elapsedSec } else { -1 } + $errMsg = if ($failEvent.data -and $failEvent.data.error) { "$($failEvent.data.error)" } else { '' } + # Truncate error for table display + if ($errMsg.Length -gt 60) { $errMsg = $errMsg.Substring(0, 57) + '...' } + } else { + $status = 'in-progress' + $elSec = -1 + $errMsg = '' + } + + $rows.Add([PSCustomObject]@{ + JobId = $jId + Status = $status + Elapsed = Format-SecToHMS -Seconds $elSec + Started = $started + Error = $errMsg + }) +} + +# ── Apply filters + limit ──────────────────────────────────────────────────── +if ($Failed) { + $rows = @($rows | Where-Object { $_.Status -eq 'FAILED' }) +} + +$rows = @($rows | Select-Object -First $Last) + +if ($rows.Count -eq 0) { + Write-Host "No matching jobs found." + exit 0 +} + +# ── Print table ────────────────────────────────────────────────────────────── +Write-Host "`nCI Job Summary (last $($rows.Count) jobs):" +Write-Host ('{0,-40} {1,-12} {2,-10} {3,-26} {4}' -f 'JobId', 'Status', 'Elapsed', 'Started', 'Error') +Write-Host ('{0,-40} {1,-12} {2,-10} {3,-26} {4}' -f ('-'*40), ('-'*12), ('-'*10), ('-'*26), ('-'*30)) +foreach ($r in $rows) { + Write-Host ('{0,-40} {1,-12} {2,-10} {3,-26} {4}' -f $r.JobId, $r.Status, $r.Elapsed, $r.Started, $r.Error) +} +Write-Host '' diff --git a/scripts/Invoke-CIJob.ps1 b/scripts/Invoke-CIJob.ps1 index fb53043..aa62674 100644 --- a/scripts/Invoke-CIJob.ps1 +++ b/scripts/Invoke-CIJob.ps1 @@ -1,23 +1,37 @@ -#Requires -Version 5.1 -Set-StrictMode -Version Latest -$ErrorActionPreference = 'Stop' - -# Shim: delegates to the Python ci_orchestrator CLI. -# Original PS implementation moved to git history; see plans/A4-closeout.md. -$pyArgs = @() -foreach ($a in $args) { - if ($a -is [string] -and $a.Length -gt 1 -and $a.StartsWith('-') -and -not $a.StartsWith('--')) { - $name = $a.Substring(1) - $kebab = [Regex]::Replace($name, '(?<=[a-z0-9])([A-Z])', '-$1') - $kebab = [Regex]::Replace($kebab, '([A-Z]+)([A-Z][a-z])', '$1-$2') - $pyArgs += '--' + $kebab.ToLower() - } - else { - $pyArgs += $a - } -} - -$venvPython = $env:CI_VENV_PYTHON -if (-not $venvPython) { $venvPython = 'F:\CI\python\venv\Scripts\python.exe' } -& $venvPython -m ci_orchestrator job @pyArgs -exit $LASTEXITCODE +#Requires -Version 5.1 +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +# Shim: delegates to the Python ci_orchestrator CLI. +# Original PS implementation moved to git history; see plans/A4-closeout.md. +# PowerShell common params (-ErrorAction etc.) must not be forwarded +# to the Python CLI. Value-taking ones also consume the next token. +$commonWithValue = @{ + erroraction = $true; warningaction = $true; informationaction = $true + progressaction = $true; errorvariable = $true; warningvariable = $true + informationvariable = $true; outvariable = $true; outbuffer = $true + pipelinevariable = $true +} +$commonSwitch = @{ verbose = $true; debug = $true; whatif = $true; confirm = $true } + +$pyArgs = @() +for ($i = 0; $i -lt $args.Count; $i++) { + $a = $args[$i] + if ($a -is [string] -and $a.Length -gt 1 -and $a.StartsWith('-') -and -not $a.StartsWith('--')) { + $name = $a.Substring(1) + $lname = $name.ToLower() + if ($commonSwitch.ContainsKey($lname)) { continue } + if ($commonWithValue.ContainsKey($lname)) { $i++; continue } + $kebab = [Regex]::Replace($name, '(?<=[a-z0-9])([A-Z])', '-$1') + $kebab = [Regex]::Replace($kebab, '([A-Z]+)([A-Z][a-z])', '$1-$2') + $pyArgs += '--' + $kebab.ToLower() + } + else { + $pyArgs += $a + } +} + +$venvPython = $env:CI_VENV_PYTHON +if (-not $venvPython) { $venvPython = 'F:\CI\python\venv\Scripts\python.exe' } +& $venvPython -m ci_orchestrator job @pyArgs +exit $LASTEXITCODE diff --git a/scripts/Remove-BuildVM.ps1 b/scripts/Remove-BuildVM.ps1 index 1553dfb..0107b30 100644 --- a/scripts/Remove-BuildVM.ps1 +++ b/scripts/Remove-BuildVM.ps1 @@ -4,10 +4,27 @@ $ErrorActionPreference = 'Stop' # Shim: delegates to the Python ci_orchestrator CLI. # Original PS implementation moved to git history; see plans/A2-closeout.md. +# +# Callers (e.g. Measure-CIBenchmark.ps1) may pass PowerShell common +# parameters like -ErrorAction SilentlyContinue. Those must NOT be +# forwarded as --error-action etc. (the Python CLI rejects them). +# Value-taking common params consume the following token too. +$commonWithValue = @{ + erroraction = $true; warningaction = $true; informationaction = $true + progressaction = $true; errorvariable = $true; warningvariable = $true + informationvariable = $true; outvariable = $true; outbuffer = $true + pipelinevariable = $true +} +$commonSwitch = @{ verbose = $true; debug = $true; whatif = $true; confirm = $true } + $pyArgs = @() -foreach ($a in $args) { +for ($i = 0; $i -lt $args.Count; $i++) { + $a = $args[$i] if ($a -is [string] -and $a.Length -gt 1 -and $a.StartsWith('-') -and -not $a.StartsWith('--')) { $name = $a.Substring(1) + $lname = $name.ToLower() + if ($commonSwitch.ContainsKey($lname)) { continue } + if ($commonWithValue.ContainsKey($lname)) { $i++; continue } $kebab = [Regex]::Replace($name, '(?<=[a-z0-9])([A-Z])', '-$1') $kebab = [Regex]::Replace($kebab, '([A-Z]+)([A-Z][a-z])', '$1-$2') $pyArgs += '--' + $kebab.ToLower() diff --git a/scripts/Wait-VMReady.ps1 b/scripts/Wait-VMReady.ps1 index 44c09e8..605eff4 100644 --- a/scripts/Wait-VMReady.ps1 +++ b/scripts/Wait-VMReady.ps1 @@ -1,23 +1,37 @@ -#Requires -Version 5.1 -Set-StrictMode -Version Latest -$ErrorActionPreference = 'Stop' - -# Shim: delegates to the Python ci_orchestrator CLI. -# Original PS implementation moved to git history; see plans/A2-closeout.md. -$pyArgs = @() -foreach ($a in $args) { - if ($a -is [string] -and $a.Length -gt 1 -and $a.StartsWith('-') -and -not $a.StartsWith('--')) { - $name = $a.Substring(1) - $kebab = [Regex]::Replace($name, '(?<=[a-z0-9])([A-Z])', '-$1') - $kebab = [Regex]::Replace($kebab, '([A-Z]+)([A-Z][a-z])', '$1-$2') - $pyArgs += '--' + $kebab.ToLower() - } - else { - $pyArgs += $a - } -} - -$venvPython = $env:CI_VENV_PYTHON -if (-not $venvPython) { $venvPython = 'F:\CI\python\venv\Scripts\python.exe' } -& $venvPython -m ci_orchestrator wait-ready @pyArgs -exit $LASTEXITCODE +#Requires -Version 5.1 +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +# Shim: delegates to the Python ci_orchestrator CLI. +# Original PS implementation moved to git history; see plans/A2-closeout.md. +# PowerShell common params (-ErrorAction etc.) must not be forwarded +# to the Python CLI. Value-taking ones also consume the next token. +$commonWithValue = @{ + erroraction = $true; warningaction = $true; informationaction = $true + progressaction = $true; errorvariable = $true; warningvariable = $true + informationvariable = $true; outvariable = $true; outbuffer = $true + pipelinevariable = $true +} +$commonSwitch = @{ verbose = $true; debug = $true; whatif = $true; confirm = $true } + +$pyArgs = @() +for ($i = 0; $i -lt $args.Count; $i++) { + $a = $args[$i] + if ($a -is [string] -and $a.Length -gt 1 -and $a.StartsWith('-') -and -not $a.StartsWith('--')) { + $name = $a.Substring(1) + $lname = $name.ToLower() + if ($commonSwitch.ContainsKey($lname)) { continue } + if ($commonWithValue.ContainsKey($lname)) { $i++; continue } + $kebab = [Regex]::Replace($name, '(?<=[a-z0-9])([A-Z])', '-$1') + $kebab = [Regex]::Replace($kebab, '([A-Z]+)([A-Z][a-z])', '$1-$2') + $pyArgs += '--' + $kebab.ToLower() + } + else { + $pyArgs += $a + } +} + +$venvPython = $env:CI_VENV_PYTHON +if (-not $venvPython) { $venvPython = 'F:\CI\python\venv\Scripts\python.exe' } +& $venvPython -m ci_orchestrator wait-ready @pyArgs +exit $LASTEXITCODE diff --git a/scripts/Watch-DiskSpace.ps1 b/scripts/Watch-DiskSpace.ps1 index 9328def..aa4c7f9 100644 --- a/scripts/Watch-DiskSpace.ps1 +++ b/scripts/Watch-DiskSpace.ps1 @@ -1,23 +1,37 @@ -#Requires -Version 5.1 -Set-StrictMode -Version Latest -$ErrorActionPreference = 'Stop' - -# Shim: delegates to the Python ci_orchestrator CLI. -# Original PS implementation moved to git history; see plans/A2-closeout.md. -$pyArgs = @() -foreach ($a in $args) { - if ($a -is [string] -and $a.Length -gt 1 -and $a.StartsWith('-') -and -not $a.StartsWith('--')) { - $name = $a.Substring(1) - $kebab = [Regex]::Replace($name, '(?<=[a-z0-9])([A-Z])', '-$1') - $kebab = [Regex]::Replace($kebab, '([A-Z]+)([A-Z][a-z])', '$1-$2') - $pyArgs += '--' + $kebab.ToLower() - } - else { - $pyArgs += $a - } -} - -$venvPython = $env:CI_VENV_PYTHON -if (-not $venvPython) { $venvPython = 'F:\CI\python\venv\Scripts\python.exe' } -& $venvPython -m ci_orchestrator monitor disk @pyArgs -exit $LASTEXITCODE +#Requires -Version 5.1 +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +# Shim: delegates to the Python ci_orchestrator CLI. +# Original PS implementation moved to git history; see plans/A2-closeout.md. +# PowerShell common params (-ErrorAction etc.) must not be forwarded +# to the Python CLI. Value-taking ones also consume the next token. +$commonWithValue = @{ + erroraction = $true; warningaction = $true; informationaction = $true + progressaction = $true; errorvariable = $true; warningvariable = $true + informationvariable = $true; outvariable = $true; outbuffer = $true + pipelinevariable = $true +} +$commonSwitch = @{ verbose = $true; debug = $true; whatif = $true; confirm = $true } + +$pyArgs = @() +for ($i = 0; $i -lt $args.Count; $i++) { + $a = $args[$i] + if ($a -is [string] -and $a.Length -gt 1 -and $a.StartsWith('-') -and -not $a.StartsWith('--')) { + $name = $a.Substring(1) + $lname = $name.ToLower() + if ($commonSwitch.ContainsKey($lname)) { continue } + if ($commonWithValue.ContainsKey($lname)) { $i++; continue } + $kebab = [Regex]::Replace($name, '(?<=[a-z0-9])([A-Z])', '-$1') + $kebab = [Regex]::Replace($kebab, '([A-Z]+)([A-Z][a-z])', '$1-$2') + $pyArgs += '--' + $kebab.ToLower() + } + else { + $pyArgs += $a + } +} + +$venvPython = $env:CI_VENV_PYTHON +if (-not $venvPython) { $venvPython = 'F:\CI\python\venv\Scripts\python.exe' } +& $venvPython -m ci_orchestrator monitor disk @pyArgs +exit $LASTEXITCODE diff --git a/scripts/Watch-RunnerHealth.ps1 b/scripts/Watch-RunnerHealth.ps1 index a7562d8..b2515e9 100644 --- a/scripts/Watch-RunnerHealth.ps1 +++ b/scripts/Watch-RunnerHealth.ps1 @@ -1,23 +1,37 @@ -#Requires -Version 5.1 -Set-StrictMode -Version Latest -$ErrorActionPreference = 'Stop' - -# Shim: delegates to the Python ci_orchestrator CLI. -# Original PS implementation moved to git history; see plans/A2-closeout.md. -$pyArgs = @() -foreach ($a in $args) { - if ($a -is [string] -and $a.Length -gt 1 -and $a.StartsWith('-') -and -not $a.StartsWith('--')) { - $name = $a.Substring(1) - $kebab = [Regex]::Replace($name, '(?<=[a-z0-9])([A-Z])', '-$1') - $kebab = [Regex]::Replace($kebab, '([A-Z]+)([A-Z][a-z])', '$1-$2') - $pyArgs += '--' + $kebab.ToLower() - } - else { - $pyArgs += $a - } -} - -$venvPython = $env:CI_VENV_PYTHON -if (-not $venvPython) { $venvPython = 'F:\CI\python\venv\Scripts\python.exe' } -& $venvPython -m ci_orchestrator monitor runner @pyArgs -exit $LASTEXITCODE +#Requires -Version 5.1 +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +# Shim: delegates to the Python ci_orchestrator CLI. +# Original PS implementation moved to git history; see plans/A2-closeout.md. +# PowerShell common params (-ErrorAction etc.) must not be forwarded +# to the Python CLI. Value-taking ones also consume the next token. +$commonWithValue = @{ + erroraction = $true; warningaction = $true; informationaction = $true + progressaction = $true; errorvariable = $true; warningvariable = $true + informationvariable = $true; outvariable = $true; outbuffer = $true + pipelinevariable = $true +} +$commonSwitch = @{ verbose = $true; debug = $true; whatif = $true; confirm = $true } + +$pyArgs = @() +for ($i = 0; $i -lt $args.Count; $i++) { + $a = $args[$i] + if ($a -is [string] -and $a.Length -gt 1 -and $a.StartsWith('-') -and -not $a.StartsWith('--')) { + $name = $a.Substring(1) + $lname = $name.ToLower() + if ($commonSwitch.ContainsKey($lname)) { continue } + if ($commonWithValue.ContainsKey($lname)) { $i++; continue } + $kebab = [Regex]::Replace($name, '(?<=[a-z0-9])([A-Z])', '-$1') + $kebab = [Regex]::Replace($kebab, '([A-Z]+)([A-Z][a-z])', '$1-$2') + $pyArgs += '--' + $kebab.ToLower() + } + else { + $pyArgs += $a + } +} + +$venvPython = $env:CI_VENV_PYTHON +if (-not $venvPython) { $venvPython = 'F:\CI\python\venv\Scripts\python.exe' } +& $venvPython -m ci_orchestrator monitor runner @pyArgs +exit $LASTEXITCODE