fix(shims): add Linux fallback for venv Python path in all PS shims

All shims defaulted to F:\CI\python\venv\Scripts\python.exe when
CI_VENV_PYTHON was not set. On Linux host this caused instant failure.

Now uses $IsWindows (PS 6+ automatic var; absent on PS 5.1 = Windows)
to select /opt/ci/venv/bin/python on Linux. Windows paths unchanged.

Affects: Invoke-CIJob, Get-BuildArtifacts, Cleanup-OrphanedBuildVMs,
New-BuildVM, Remove-BuildVM, Wait-VMReady, Get-CIJobSummary,
Invoke-RemoteBuild, Watch-DiskSpace, Watch-RunnerHealth,
Set-CIGuestCredential, Test-CIGuestWinRM.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-23 22:43:48 +02:00
parent 568740ed17
commit 10998247a0
12 changed files with 440 additions and 368 deletions
+44 -37
View File
@@ -1,37 +1,44 @@
#Requires -Version 5.1 #Requires -Version 5.1
Set-StrictMode -Version Latest Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
# Shim: delegates to the Python ci_orchestrator CLI. # Shim: delegates to the Python ci_orchestrator CLI.
# Original PS implementation moved to git history; see plans/A2-closeout.md. # Original PS implementation moved to git history; see plans/A2-closeout.md.
# PowerShell common params (-ErrorAction etc.) must not be forwarded # PowerShell common params (-ErrorAction etc.) must not be forwarded
# to the Python CLI. Value-taking ones also consume the next token. # to the Python CLI. Value-taking ones also consume the next token.
$commonWithValue = @{ $commonWithValue = @{
erroraction = $true; warningaction = $true; informationaction = $true erroraction = $true; warningaction = $true; informationaction = $true
progressaction = $true; errorvariable = $true; warningvariable = $true progressaction = $true; errorvariable = $true; warningvariable = $true
informationvariable = $true; outvariable = $true; outbuffer = $true informationvariable = $true; outvariable = $true; outbuffer = $true
pipelinevariable = $true pipelinevariable = $true
} }
$commonSwitch = @{ verbose = $true; debug = $true; whatif = $true; confirm = $true } $commonSwitch = @{ verbose = $true; debug = $true; whatif = $true; confirm = $true }
$pyArgs = @() $pyArgs = @()
for ($i = 0; $i -lt $args.Count; $i++) { for ($i = 0; $i -lt $args.Count; $i++) {
$a = $args[$i] $a = $args[$i]
if ($a -is [string] -and $a.Length -gt 1 -and $a.StartsWith('-') -and -not $a.StartsWith('--')) { if ($a -is [string] -and $a.Length -gt 1 -and $a.StartsWith('-') -and -not $a.StartsWith('--')) {
$name = $a.Substring(1) $name = $a.Substring(1)
$lname = $name.ToLower() $lname = $name.ToLower()
if ($commonSwitch.ContainsKey($lname)) { continue } if ($commonSwitch.ContainsKey($lname)) { continue }
if ($commonWithValue.ContainsKey($lname)) { $i++; continue } if ($commonWithValue.ContainsKey($lname)) { $i++; continue }
$kebab = [Regex]::Replace($name, '(?<=[a-z0-9])([A-Z])', '-$1') $kebab = [Regex]::Replace($name, '(?<=[a-z0-9])([A-Z])', '-$1')
$kebab = [Regex]::Replace($kebab, '([A-Z]+)([A-Z][a-z])', '$1-$2') $kebab = [Regex]::Replace($kebab, '([A-Z]+)([A-Z][a-z])', '$1-$2')
$pyArgs += '--' + $kebab.ToLower() $pyArgs += '--' + $kebab.ToLower()
} }
else { else {
$pyArgs += $a $pyArgs += $a
} }
} }
$venvPython = $env:CI_VENV_PYTHON $venvPython = $env:CI_VENV_PYTHON
if (-not $venvPython) { $venvPython = 'F:\CI\python\venv\Scripts\python.exe' } if (-not $venvPython) {
& $venvPython -m ci_orchestrator vm cleanup @pyArgs # $IsWindows exists only on PS 6+; absent on PS 5.1 (always Windows)
exit $LASTEXITCODE if ($null -ne $IsWindows -and $IsWindows -eq $false) {
$venvPython = '/opt/ci/venv/bin/python'
} else {
$venvPython = 'F:\CI\python\venv\Scripts\python.exe'
}
}
& $venvPython -m ci_orchestrator vm cleanup @pyArgs
exit $LASTEXITCODE
+8 -1
View File
@@ -57,6 +57,13 @@ if ($Credential) {
} }
$venvPython = $env:CI_VENV_PYTHON $venvPython = $env:CI_VENV_PYTHON
if (-not $venvPython) { $venvPython = 'F:\CI\python\venv\Scripts\python.exe' } if (-not $venvPython) {
# $IsWindows exists only on PS 6+; absent on PS 5.1 (always Windows)
if ($null -ne $IsWindows -and $IsWindows -eq $false) {
$venvPython = '/opt/ci/venv/bin/python'
} else {
$venvPython = 'F:\CI\python\venv\Scripts\python.exe'
}
}
& $venvPython -m ci_orchestrator artifacts collect @pyArgs & $venvPython -m ci_orchestrator artifacts collect @pyArgs
exit $LASTEXITCODE exit $LASTEXITCODE
+222 -215
View File
@@ -1,215 +1,222 @@
#Requires -Version 5.1 #Requires -Version 5.1
Set-StrictMode -Version Latest Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
# Shim: delegates to the Python ci_orchestrator CLI. # Shim: delegates to the Python ci_orchestrator CLI.
# Original PS implementation moved to git history; see plans/A2-closeout.md. # Original PS implementation moved to git history; see plans/A2-closeout.md.
# PowerShell common params (-ErrorAction etc.) must not be forwarded # PowerShell common params (-ErrorAction etc.) must not be forwarded
# to the Python CLI. Value-taking ones also consume the next token. # to the Python CLI. Value-taking ones also consume the next token.
$commonWithValue = @{ $commonWithValue = @{
erroraction = $true; warningaction = $true; informationaction = $true erroraction = $true; warningaction = $true; informationaction = $true
progressaction = $true; errorvariable = $true; warningvariable = $true progressaction = $true; errorvariable = $true; warningvariable = $true
informationvariable = $true; outvariable = $true; outbuffer = $true informationvariable = $true; outvariable = $true; outbuffer = $true
pipelinevariable = $true pipelinevariable = $true
} }
$commonSwitch = @{ verbose = $true; debug = $true; whatif = $true; confirm = $true } $commonSwitch = @{ verbose = $true; debug = $true; whatif = $true; confirm = $true }
$pyArgs = @() $pyArgs = @()
for ($i = 0; $i -lt $args.Count; $i++) { for ($i = 0; $i -lt $args.Count; $i++) {
$a = $args[$i] $a = $args[$i]
if ($a -is [string] -and $a.Length -gt 1 -and $a.StartsWith('-') -and -not $a.StartsWith('--')) { if ($a -is [string] -and $a.Length -gt 1 -and $a.StartsWith('-') -and -not $a.StartsWith('--')) {
$name = $a.Substring(1) $name = $a.Substring(1)
$lname = $name.ToLower() $lname = $name.ToLower()
if ($commonSwitch.ContainsKey($lname)) { continue } if ($commonSwitch.ContainsKey($lname)) { continue }
if ($commonWithValue.ContainsKey($lname)) { $i++; continue } if ($commonWithValue.ContainsKey($lname)) { $i++; continue }
$kebab = [Regex]::Replace($name, '(?<=[a-z0-9])([A-Z])', '-$1') $kebab = [Regex]::Replace($name, '(?<=[a-z0-9])([A-Z])', '-$1')
$kebab = [Regex]::Replace($kebab, '([A-Z]+)([A-Z][a-z])', '$1-$2') $kebab = [Regex]::Replace($kebab, '([A-Z]+)([A-Z][a-z])', '$1-$2')
$pyArgs += '--' + $kebab.ToLower() $pyArgs += '--' + $kebab.ToLower()
} }
else { else {
$pyArgs += $a $pyArgs += $a
} }
} }
$venvPython = $env:CI_VENV_PYTHON $venvPython = $env:CI_VENV_PYTHON
if (-not $venvPython) { $venvPython = 'F:\CI\python\venv\Scripts\python.exe' } if (-not $venvPython) {
& $venvPython -m ci_orchestrator report job @pyArgs # $IsWindows exists only on PS 6+; absent on PS 5.1 (always Windows)
exit $LASTEXITCODE if ($null -ne $IsWindows -and $IsWindows -eq $false) {
$venvPython = '/opt/ci/venv/bin/python'
} else {
.DESCRIPTION $venvPython = 'F:\CI\python\venv\Scripts\python.exe'
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). & $venvPython -m ci_orchestrator report job @pyArgs
exit $LASTEXITCODE
Each row corresponds to one completed (or failed) CI job.
Use -JobId to inspect a single job in detail.
.DESCRIPTION
.PARAMETER LogDir Scans F:\CI\Logs\ (or a custom path) for invoke-ci.jsonl files and
Base directory containing per-job log subdirectories. prints a compact table with job ID, status, total elapsed time, and the
Default: F:\CI\Logs last error message (if any).
.PARAMETER Last Each row corresponds to one completed (or failed) CI job.
Show only the N most recent jobs (sorted by job-start timestamp). Use -JobId to inspect a single job in detail.
Default: 20
.PARAMETER LogDir
.PARAMETER JobId Base directory containing per-job log subdirectories.
When specified, shows a detailed phase-by-phase breakdown for that job. Default: F:\CI\Logs
.PARAMETER Failed .PARAMETER Last
When specified, filters to failed jobs only. Show only the N most recent jobs (sorted by job-start timestamp).
Default: 20
.EXAMPLE
# Show last 20 jobs .PARAMETER JobId
.\Get-CIJobSummary.ps1 When specified, shows a detailed phase-by-phase breakdown for that job.
.EXAMPLE .PARAMETER Failed
# Show last 5 failed jobs When specified, filters to failed jobs only.
.\Get-CIJobSummary.ps1 -Last 5 -Failed
.EXAMPLE
.EXAMPLE # Show last 20 jobs
# Detailed breakdown for a specific job .\Get-CIJobSummary.ps1
.\Get-CIJobSummary.ps1 -JobId 'run-12345'
#> .EXAMPLE
[CmdletBinding()] # Show last 5 failed jobs
param( .\Get-CIJobSummary.ps1 -Last 5 -Failed
[string] $LogDir = 'F:\CI\Logs',
.EXAMPLE
[ValidateRange(1, 1000)] # Detailed breakdown for a specific job
[int] $Last = 20, .\Get-CIJobSummary.ps1 -JobId 'run-12345'
#>
[string] $JobId = '', [CmdletBinding()]
param(
[switch] $Failed [string] $LogDir = 'F:\CI\Logs',
)
[ValidateRange(1, 1000)]
Set-StrictMode -Version Latest [int] $Last = 20,
$ErrorActionPreference = 'Stop'
[string] $JobId = '',
# ── Helpers ────────────────────────────────────────────────────────────────────
function Format-SecToHMS { [switch] $Failed
param([int] $Seconds) )
if ($Seconds -lt 0) { return '?' }
$h = [math]::Floor($Seconds / 3600) Set-StrictMode -Version Latest
$m = [math]::Floor(($Seconds % 3600) / 60) $ErrorActionPreference = 'Stop'
$s = $Seconds % 60
if ($h -gt 0) { return '{0}h{1:00}m{2:00}s' -f $h, $m, $s } # ── Helpers ────────────────────────────────────────────────────────────────────
return '{0}m{1:00}s' -f $m, $s function Format-SecToHMS {
} param([int] $Seconds)
if ($Seconds -lt 0) { return '?' }
function Read-JobEvents { $h = [math]::Floor($Seconds / 3600)
param([string] $JsonlPath) $m = [math]::Floor(($Seconds % 3600) / 60)
$events = [System.Collections.Generic.List[PSCustomObject]]::new() $s = $Seconds % 60
foreach ($line in (Get-Content $JsonlPath -Encoding UTF8 -ErrorAction SilentlyContinue)) { if ($h -gt 0) { return '{0}h{1:00}m{2:00}s' -f $h, $m, $s }
$line = $line.Trim() return '{0}m{1:00}s' -f $m, $s
if (-not $line) { continue } }
try {
$events.Add(($line | ConvertFrom-Json)) function Read-JobEvents {
} catch { param([string] $JsonlPath)
Write-Debug "[Get-CIJobSummary] Skipping malformed JSONL line in $JsonlPath" $events = [System.Collections.Generic.List[PSCustomObject]]::new()
} foreach ($line in (Get-Content $JsonlPath -Encoding UTF8 -ErrorAction SilentlyContinue)) {
} $line = $line.Trim()
return $events if (-not $line) { continue }
} try {
$events.Add(($line | ConvertFrom-Json))
# ── Validate log dir ──────────────────────────────────────────────────────── } catch {
if (-not (Test-Path $LogDir -PathType Container)) { Write-Debug "[Get-CIJobSummary] Skipping malformed JSONL line in $JsonlPath"
Write-Warning "Log directory not found: $LogDir" }
exit 1 }
} return $events
}
# ── Single-job detail mode ──────────────────────────────────────────────────
if ($JobId -ne '') { # ── Validate log dir ────────────────────────────────────────────────────────
$jsonlPath = Join-Path $LogDir (Join-Path $JobId 'invoke-ci.jsonl') if (-not (Test-Path $LogDir -PathType Container)) {
if (-not (Test-Path $jsonlPath)) { Write-Warning "Log directory not found: $LogDir"
Write-Warning "No JSONL log found for job '$JobId' at: $jsonlPath" exit 1
exit 1 }
}
$events = Read-JobEvents -JsonlPath $jsonlPath # ── Single-job detail mode ──────────────────────────────────────────────────
if ($events.Count -eq 0) { if ($JobId -ne '') {
Write-Warning "JSONL file is empty or unreadable: $jsonlPath" $jsonlPath = Join-Path $LogDir (Join-Path $JobId 'invoke-ci.jsonl')
exit 1 if (-not (Test-Path $jsonlPath)) {
} Write-Warning "No JSONL log found for job '$JobId' at: $jsonlPath"
Write-Host "`nJob: $JobId" exit 1
Write-Host ('=' * 60) }
Write-Host ('{0,-35} {1,-10} {2}' -f 'Phase', 'Status', 'Timestamp') $events = Read-JobEvents -JsonlPath $jsonlPath
Write-Host ('{0,-35} {1,-10} {2}' -f ('-'*35), ('-'*10), ('-'*24)) if ($events.Count -eq 0) {
foreach ($ev in $events) { Write-Warning "JSONL file is empty or unreadable: $jsonlPath"
$ts = if ($ev.ts) { $ev.ts } else { '' } exit 1
Write-Host ('{0,-35} {1,-10} {2}' -f $ev.phase, $ev.status, $ts) }
} Write-Host "`nJob: $JobId"
# Print error if present Write-Host ('=' * 60)
$failEvent = $events | Where-Object { $_.status -eq 'failure' } | Select-Object -Last 1 Write-Host ('{0,-35} {1,-10} {2}' -f 'Phase', 'Status', 'Timestamp')
if ($failEvent -and $failEvent.data -and $failEvent.data.error) { Write-Host ('{0,-35} {1,-10} {2}' -f ('-'*35), ('-'*10), ('-'*24))
Write-Host "`nError: $($failEvent.data.error)" foreach ($ev in $events) {
} $ts = if ($ev.ts) { $ev.ts } else { '' }
exit 0 Write-Host ('{0,-35} {1,-10} {2}' -f $ev.phase, $ev.status, $ts)
} }
# Print error if present
# ── Scan all job JSONL files ───────────────────────────────────────────────── $failEvent = $events | Where-Object { $_.status -eq 'failure' } | Select-Object -Last 1
$jsonlFiles = Get-ChildItem -Path $LogDir -Recurse -Filter 'invoke-ci.jsonl' -ErrorAction SilentlyContinue | if ($failEvent -and $failEvent.data -and $failEvent.data.error) {
Sort-Object { $_.LastWriteTime } -Descending Write-Host "`nError: $($failEvent.data.error)"
}
if ($jsonlFiles.Count -eq 0) { exit 0
Write-Host "No invoke-ci.jsonl files found under $LogDir" }
exit 0
} # ── Scan all job JSONL files ─────────────────────────────────────────────────
$jsonlFiles = Get-ChildItem -Path $LogDir -Recurse -Filter 'invoke-ci.jsonl' -ErrorAction SilentlyContinue |
$rows = [System.Collections.Generic.List[PSCustomObject]]::new() Sort-Object { $_.LastWriteTime } -Descending
foreach ($file in $jsonlFiles) { if ($jsonlFiles.Count -eq 0) {
$events = Read-JobEvents -JsonlPath $file.FullName Write-Host "No invoke-ci.jsonl files found under $LogDir"
if ($events.Count -eq 0) { continue } exit 0
}
$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 $rows = [System.Collections.Generic.List[PSCustomObject]]::new()
$failEvent = $events | Where-Object { $_.phase -eq 'job' -and $_.status -eq 'failure' } | Select-Object -First 1
foreach ($file in $jsonlFiles) {
$jId = if ($events[0].jobId) { $events[0].jobId } else { (Split-Path (Split-Path $file.FullName -Parent) -Leaf) } $events = Read-JobEvents -JsonlPath $file.FullName
$started = if ($jobEvent -and $jobEvent.ts) { $jobEvent.ts } else { '' } if ($events.Count -eq 0) { continue }
if ($successEvent) { $jobEvent = $events | Where-Object { $_.phase -eq 'job' -and $_.status -eq 'start' } | Select-Object -First 1
$status = 'success' $successEvent= $events | Where-Object { $_.phase -eq 'job' -and $_.status -eq 'success' } | Select-Object -First 1
$elSec = if ($successEvent.data -and $null -ne $successEvent.data.elapsedSec) { [int]$successEvent.data.elapsedSec } else { -1 } $failEvent = $events | Where-Object { $_.phase -eq 'job' -and $_.status -eq 'failure' } | Select-Object -First 1
$errMsg = ''
} elseif ($failEvent) { $jId = if ($events[0].jobId) { $events[0].jobId } else { (Split-Path (Split-Path $file.FullName -Parent) -Leaf) }
$status = 'FAILED' $started = if ($jobEvent -and $jobEvent.ts) { $jobEvent.ts } else { '' }
$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 { '' } if ($successEvent) {
# Truncate error for table display $status = 'success'
if ($errMsg.Length -gt 60) { $errMsg = $errMsg.Substring(0, 57) + '...' } $elSec = if ($successEvent.data -and $null -ne $successEvent.data.elapsedSec) { [int]$successEvent.data.elapsedSec } else { -1 }
} else { $errMsg = ''
$status = 'in-progress' } elseif ($failEvent) {
$elSec = -1 $status = 'FAILED'
$errMsg = '' $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
$rows.Add([PSCustomObject]@{ if ($errMsg.Length -gt 60) { $errMsg = $errMsg.Substring(0, 57) + '...' }
JobId = $jId } else {
Status = $status $status = 'in-progress'
Elapsed = Format-SecToHMS -Seconds $elSec $elSec = -1
Started = $started $errMsg = ''
Error = $errMsg }
})
} $rows.Add([PSCustomObject]@{
JobId = $jId
# ── Apply filters + limit ──────────────────────────────────────────────────── Status = $status
if ($Failed) { Elapsed = Format-SecToHMS -Seconds $elSec
$rows = @($rows | Where-Object { $_.Status -eq 'FAILED' }) Started = $started
} Error = $errMsg
})
$rows = @($rows | Select-Object -First $Last) }
if ($rows.Count -eq 0) { # ── Apply filters + limit ────────────────────────────────────────────────────
Write-Host "No matching jobs found." if ($Failed) {
exit 0 $rows = @($rows | Where-Object { $_.Status -eq 'FAILED' })
} }
# ── Print table ────────────────────────────────────────────────────────────── $rows = @($rows | Select-Object -First $Last)
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') if ($rows.Count -eq 0) {
Write-Host ('{0,-40} {1,-12} {2,-10} {3,-26} {4}' -f ('-'*40), ('-'*12), ('-'*10), ('-'*26), ('-'*30)) Write-Host "No matching jobs found."
foreach ($r in $rows) { exit 0
Write-Host ('{0,-40} {1,-12} {2,-10} {3,-26} {4}' -f $r.JobId, $r.Status, $r.Elapsed, $r.Started, $r.Error) }
}
Write-Host '' # ── 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 ''
+8 -1
View File
@@ -32,6 +32,13 @@ for ($i = 0; $i -lt $args.Count; $i++) {
} }
$venvPython = $env:CI_VENV_PYTHON $venvPython = $env:CI_VENV_PYTHON
if (-not $venvPython) { $venvPython = 'F:\CI\python\venv\Scripts\python.exe' } if (-not $venvPython) {
# $IsWindows exists only on PS 6+; absent on PS 5.1 (always Windows)
if ($null -ne $IsWindows -and $IsWindows -eq $false) {
$venvPython = '/opt/ci/venv/bin/python'
} else {
$venvPython = 'F:\CI\python\venv\Scripts\python.exe'
}
}
& $venvPython -m ci_orchestrator job @pyArgs & $venvPython -m ci_orchestrator job @pyArgs
exit $LASTEXITCODE exit $LASTEXITCODE
+8 -1
View File
@@ -86,6 +86,13 @@ if ($Credential) {
} }
$venvPython = $env:CI_VENV_PYTHON $venvPython = $env:CI_VENV_PYTHON
if (-not $venvPython) { $venvPython = 'F:\CI\python\venv\Scripts\python.exe' } if (-not $venvPython) {
# $IsWindows exists only on PS 6+; absent on PS 5.1 (always Windows)
if ($null -ne $IsWindows -and $IsWindows -eq $false) {
$venvPython = '/opt/ci/venv/bin/python'
} else {
$venvPython = 'F:\CI\python\venv\Scripts\python.exe'
}
}
& $venvPython -m ci_orchestrator build run @pyArgs & $venvPython -m ci_orchestrator build run @pyArgs
exit $LASTEXITCODE exit $LASTEXITCODE
+8 -1
View File
@@ -31,6 +31,13 @@ $pyArgs = @(
) )
$venvPython = $env:CI_VENV_PYTHON $venvPython = $env:CI_VENV_PYTHON
if (-not $venvPython) { $venvPython = 'F:\CI\python\venv\Scripts\python.exe' } if (-not $venvPython) {
# $IsWindows exists only on PS 6+; absent on PS 5.1 (always Windows)
if ($null -ne $IsWindows -and $IsWindows -eq $false) {
$venvPython = '/opt/ci/venv/bin/python'
} else {
$venvPython = 'F:\CI\python\venv\Scripts\python.exe'
}
}
& $venvPython -m ci_orchestrator vm new @pyArgs & $venvPython -m ci_orchestrator vm new @pyArgs
exit $LASTEXITCODE exit $LASTEXITCODE
+8 -1
View File
@@ -35,6 +35,13 @@ for ($i = 0; $i -lt $args.Count; $i++) {
} }
$venvPython = $env:CI_VENV_PYTHON $venvPython = $env:CI_VENV_PYTHON
if (-not $venvPython) { $venvPython = 'F:\CI\python\venv\Scripts\python.exe' } if (-not $venvPython) {
# $IsWindows exists only on PS 6+; absent on PS 5.1 (always Windows)
if ($null -ne $IsWindows -and $IsWindows -eq $false) {
$venvPython = '/opt/ci/venv/bin/python'
} else {
$venvPython = 'F:\CI\python\venv\Scripts\python.exe'
}
}
& $venvPython -m ci_orchestrator vm remove @pyArgs & $venvPython -m ci_orchestrator vm remove @pyArgs
exit $LASTEXITCODE exit $LASTEXITCODE
+1
View File
@@ -68,6 +68,7 @@ param(
[System.Security.SecureString] $Password, [System.Security.SecureString] $Password,
[string] $VenvPython = $(if ($env:CI_VENV_PYTHON) { $env:CI_VENV_PYTHON } [string] $VenvPython = $(if ($env:CI_VENV_PYTHON) { $env:CI_VENV_PYTHON }
elseif ($null -ne $IsWindows -and $IsWindows -eq $false) { '/opt/ci/venv/bin/python' }
else { 'F:\CI\python\venv\Scripts\python.exe' }), else { 'F:\CI\python\venv\Scripts\python.exe' }),
[switch] $SkipVerify [switch] $SkipVerify
+1
View File
@@ -74,6 +74,7 @@ param(
[string[]] $AuthOrder = @('ntlm', 'negotiate'), [string[]] $AuthOrder = @('ntlm', 'negotiate'),
[string] $VenvPython = $(if ($env:CI_VENV_PYTHON) { $env:CI_VENV_PYTHON } [string] $VenvPython = $(if ($env:CI_VENV_PYTHON) { $env:CI_VENV_PYTHON }
elseif ($null -ne $IsWindows -and $IsWindows -eq $false) { '/opt/ci/venv/bin/python' }
else { 'F:\CI\python\venv\Scripts\python.exe' }), else { 'F:\CI\python\venv\Scripts\python.exe' }),
[ValidateRange(5, 300)] [ValidateRange(5, 300)]
+44 -37
View File
@@ -1,37 +1,44 @@
#Requires -Version 5.1 #Requires -Version 5.1
Set-StrictMode -Version Latest Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
# Shim: delegates to the Python ci_orchestrator CLI. # Shim: delegates to the Python ci_orchestrator CLI.
# Original PS implementation moved to git history; see plans/A2-closeout.md. # Original PS implementation moved to git history; see plans/A2-closeout.md.
# PowerShell common params (-ErrorAction etc.) must not be forwarded # PowerShell common params (-ErrorAction etc.) must not be forwarded
# to the Python CLI. Value-taking ones also consume the next token. # to the Python CLI. Value-taking ones also consume the next token.
$commonWithValue = @{ $commonWithValue = @{
erroraction = $true; warningaction = $true; informationaction = $true erroraction = $true; warningaction = $true; informationaction = $true
progressaction = $true; errorvariable = $true; warningvariable = $true progressaction = $true; errorvariable = $true; warningvariable = $true
informationvariable = $true; outvariable = $true; outbuffer = $true informationvariable = $true; outvariable = $true; outbuffer = $true
pipelinevariable = $true pipelinevariable = $true
} }
$commonSwitch = @{ verbose = $true; debug = $true; whatif = $true; confirm = $true } $commonSwitch = @{ verbose = $true; debug = $true; whatif = $true; confirm = $true }
$pyArgs = @() $pyArgs = @()
for ($i = 0; $i -lt $args.Count; $i++) { for ($i = 0; $i -lt $args.Count; $i++) {
$a = $args[$i] $a = $args[$i]
if ($a -is [string] -and $a.Length -gt 1 -and $a.StartsWith('-') -and -not $a.StartsWith('--')) { if ($a -is [string] -and $a.Length -gt 1 -and $a.StartsWith('-') -and -not $a.StartsWith('--')) {
$name = $a.Substring(1) $name = $a.Substring(1)
$lname = $name.ToLower() $lname = $name.ToLower()
if ($commonSwitch.ContainsKey($lname)) { continue } if ($commonSwitch.ContainsKey($lname)) { continue }
if ($commonWithValue.ContainsKey($lname)) { $i++; continue } if ($commonWithValue.ContainsKey($lname)) { $i++; continue }
$kebab = [Regex]::Replace($name, '(?<=[a-z0-9])([A-Z])', '-$1') $kebab = [Regex]::Replace($name, '(?<=[a-z0-9])([A-Z])', '-$1')
$kebab = [Regex]::Replace($kebab, '([A-Z]+)([A-Z][a-z])', '$1-$2') $kebab = [Regex]::Replace($kebab, '([A-Z]+)([A-Z][a-z])', '$1-$2')
$pyArgs += '--' + $kebab.ToLower() $pyArgs += '--' + $kebab.ToLower()
} }
else { else {
$pyArgs += $a $pyArgs += $a
} }
} }
$venvPython = $env:CI_VENV_PYTHON $venvPython = $env:CI_VENV_PYTHON
if (-not $venvPython) { $venvPython = 'F:\CI\python\venv\Scripts\python.exe' } if (-not $venvPython) {
& $venvPython -m ci_orchestrator wait-ready @pyArgs # $IsWindows exists only on PS 6+; absent on PS 5.1 (always Windows)
exit $LASTEXITCODE if ($null -ne $IsWindows -and $IsWindows -eq $false) {
$venvPython = '/opt/ci/venv/bin/python'
} else {
$venvPython = 'F:\CI\python\venv\Scripts\python.exe'
}
}
& $venvPython -m ci_orchestrator wait-ready @pyArgs
exit $LASTEXITCODE
+44 -37
View File
@@ -1,37 +1,44 @@
#Requires -Version 5.1 #Requires -Version 5.1
Set-StrictMode -Version Latest Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
# Shim: delegates to the Python ci_orchestrator CLI. # Shim: delegates to the Python ci_orchestrator CLI.
# Original PS implementation moved to git history; see plans/A2-closeout.md. # Original PS implementation moved to git history; see plans/A2-closeout.md.
# PowerShell common params (-ErrorAction etc.) must not be forwarded # PowerShell common params (-ErrorAction etc.) must not be forwarded
# to the Python CLI. Value-taking ones also consume the next token. # to the Python CLI. Value-taking ones also consume the next token.
$commonWithValue = @{ $commonWithValue = @{
erroraction = $true; warningaction = $true; informationaction = $true erroraction = $true; warningaction = $true; informationaction = $true
progressaction = $true; errorvariable = $true; warningvariable = $true progressaction = $true; errorvariable = $true; warningvariable = $true
informationvariable = $true; outvariable = $true; outbuffer = $true informationvariable = $true; outvariable = $true; outbuffer = $true
pipelinevariable = $true pipelinevariable = $true
} }
$commonSwitch = @{ verbose = $true; debug = $true; whatif = $true; confirm = $true } $commonSwitch = @{ verbose = $true; debug = $true; whatif = $true; confirm = $true }
$pyArgs = @() $pyArgs = @()
for ($i = 0; $i -lt $args.Count; $i++) { for ($i = 0; $i -lt $args.Count; $i++) {
$a = $args[$i] $a = $args[$i]
if ($a -is [string] -and $a.Length -gt 1 -and $a.StartsWith('-') -and -not $a.StartsWith('--')) { if ($a -is [string] -and $a.Length -gt 1 -and $a.StartsWith('-') -and -not $a.StartsWith('--')) {
$name = $a.Substring(1) $name = $a.Substring(1)
$lname = $name.ToLower() $lname = $name.ToLower()
if ($commonSwitch.ContainsKey($lname)) { continue } if ($commonSwitch.ContainsKey($lname)) { continue }
if ($commonWithValue.ContainsKey($lname)) { $i++; continue } if ($commonWithValue.ContainsKey($lname)) { $i++; continue }
$kebab = [Regex]::Replace($name, '(?<=[a-z0-9])([A-Z])', '-$1') $kebab = [Regex]::Replace($name, '(?<=[a-z0-9])([A-Z])', '-$1')
$kebab = [Regex]::Replace($kebab, '([A-Z]+)([A-Z][a-z])', '$1-$2') $kebab = [Regex]::Replace($kebab, '([A-Z]+)([A-Z][a-z])', '$1-$2')
$pyArgs += '--' + $kebab.ToLower() $pyArgs += '--' + $kebab.ToLower()
} }
else { else {
$pyArgs += $a $pyArgs += $a
} }
} }
$venvPython = $env:CI_VENV_PYTHON $venvPython = $env:CI_VENV_PYTHON
if (-not $venvPython) { $venvPython = 'F:\CI\python\venv\Scripts\python.exe' } if (-not $venvPython) {
& $venvPython -m ci_orchestrator monitor disk @pyArgs # $IsWindows exists only on PS 6+; absent on PS 5.1 (always Windows)
exit $LASTEXITCODE if ($null -ne $IsWindows -and $IsWindows -eq $false) {
$venvPython = '/opt/ci/venv/bin/python'
} else {
$venvPython = 'F:\CI\python\venv\Scripts\python.exe'
}
}
& $venvPython -m ci_orchestrator monitor disk @pyArgs
exit $LASTEXITCODE
+44 -37
View File
@@ -1,37 +1,44 @@
#Requires -Version 5.1 #Requires -Version 5.1
Set-StrictMode -Version Latest Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
# Shim: delegates to the Python ci_orchestrator CLI. # Shim: delegates to the Python ci_orchestrator CLI.
# Original PS implementation moved to git history; see plans/A2-closeout.md. # Original PS implementation moved to git history; see plans/A2-closeout.md.
# PowerShell common params (-ErrorAction etc.) must not be forwarded # PowerShell common params (-ErrorAction etc.) must not be forwarded
# to the Python CLI. Value-taking ones also consume the next token. # to the Python CLI. Value-taking ones also consume the next token.
$commonWithValue = @{ $commonWithValue = @{
erroraction = $true; warningaction = $true; informationaction = $true erroraction = $true; warningaction = $true; informationaction = $true
progressaction = $true; errorvariable = $true; warningvariable = $true progressaction = $true; errorvariable = $true; warningvariable = $true
informationvariable = $true; outvariable = $true; outbuffer = $true informationvariable = $true; outvariable = $true; outbuffer = $true
pipelinevariable = $true pipelinevariable = $true
} }
$commonSwitch = @{ verbose = $true; debug = $true; whatif = $true; confirm = $true } $commonSwitch = @{ verbose = $true; debug = $true; whatif = $true; confirm = $true }
$pyArgs = @() $pyArgs = @()
for ($i = 0; $i -lt $args.Count; $i++) { for ($i = 0; $i -lt $args.Count; $i++) {
$a = $args[$i] $a = $args[$i]
if ($a -is [string] -and $a.Length -gt 1 -and $a.StartsWith('-') -and -not $a.StartsWith('--')) { if ($a -is [string] -and $a.Length -gt 1 -and $a.StartsWith('-') -and -not $a.StartsWith('--')) {
$name = $a.Substring(1) $name = $a.Substring(1)
$lname = $name.ToLower() $lname = $name.ToLower()
if ($commonSwitch.ContainsKey($lname)) { continue } if ($commonSwitch.ContainsKey($lname)) { continue }
if ($commonWithValue.ContainsKey($lname)) { $i++; continue } if ($commonWithValue.ContainsKey($lname)) { $i++; continue }
$kebab = [Regex]::Replace($name, '(?<=[a-z0-9])([A-Z])', '-$1') $kebab = [Regex]::Replace($name, '(?<=[a-z0-9])([A-Z])', '-$1')
$kebab = [Regex]::Replace($kebab, '([A-Z]+)([A-Z][a-z])', '$1-$2') $kebab = [Regex]::Replace($kebab, '([A-Z]+)([A-Z][a-z])', '$1-$2')
$pyArgs += '--' + $kebab.ToLower() $pyArgs += '--' + $kebab.ToLower()
} }
else { else {
$pyArgs += $a $pyArgs += $a
} }
} }
$venvPython = $env:CI_VENV_PYTHON $venvPython = $env:CI_VENV_PYTHON
if (-not $venvPython) { $venvPython = 'F:\CI\python\venv\Scripts\python.exe' } if (-not $venvPython) {
& $venvPython -m ci_orchestrator monitor runner @pyArgs # $IsWindows exists only on PS 6+; absent on PS 5.1 (always Windows)
exit $LASTEXITCODE if ($null -ne $IsWindows -and $IsWindows -eq $false) {
$venvPython = '/opt/ci/venv/bin/python'
} else {
$venvPython = 'F:\CI\python\venv\Scripts\python.exe'
}
}
& $venvPython -m ci_orchestrator monitor runner @pyArgs
exit $LASTEXITCODE