diff --git a/scripts/Invoke-CIJob.ps1 b/scripts/Invoke-CIJob.ps1 index 7497219..05b6022 100644 --- a/scripts/Invoke-CIJob.ps1 +++ b/scripts/Invoke-CIJob.ps1 @@ -108,7 +108,15 @@ param( [string] $GuestCredentialTarget = 'BuildVMGuest', - [string] $VmrunPath = 'C:\Program Files (x86)\VMware\VMware Workstation\vmrun.exe' + [string] $VmrunPath = 'C:\Program Files (x86)\VMware\VMware Workstation\vmrun.exe', + + # Directory for job execution logs. Each job gets its own sub-directory: + # $LogDir\$JobId\invoke-ci.log + [string] $LogDir = 'F:\CI\Logs', + + # Days to retain job logs. Directories older than this are purged at job end. + # Set to 0 to disable retention/purge. + [int] $LogRetentionDays = 30 ) Set-StrictMode -Version Latest @@ -152,6 +160,19 @@ $hostCloneDir = Join-Path $env:TEMP "ci-src-$JobId" $cloneVmxPath = $null $startTime = Get-Date +# ── Start transcript ────────────────────────────────────────────────────────── +$transcriptStarted = $false +try { + $jobLogDir = Join-Path $LogDir $JobId + if (-not (Test-Path $jobLogDir)) { New-Item -ItemType Directory -Path $jobLogDir -Force | Out-Null } + $transcriptPath = Join-Path $jobLogDir 'invoke-ci.log' + Start-Transcript -Path $transcriptPath -Force -ErrorAction Stop + $transcriptStarted = $true +} +catch { + Write-Warning "[Invoke-CIJob] Could not start transcript: $_" +} + Write-Host "============================================================" Write-Host "[Invoke-CIJob] Job : $JobId" Write-Host "[Invoke-CIJob] Repository : $RepoUrl" @@ -276,6 +297,22 @@ finally { Write-Host "[Cleanup] Removing host source clone ($hostCloneDir)..." Remove-Item $hostCloneDir -Recurse -Force -ErrorAction SilentlyContinue } + + # ── Stop transcript ─────────────────────────────────────────────────── + if ($transcriptStarted) { + try { Stop-Transcript -ErrorAction SilentlyContinue } catch {} + } + + # ── Log retention: purge directories older than $LogRetentionDays days ─ + if ($LogRetentionDays -gt 0 -and (Test-Path $LogDir)) { + $cutoff = (Get-Date).AddDays(-$LogRetentionDays) + Get-ChildItem -Path $LogDir -Directory -ErrorAction SilentlyContinue | + Where-Object { $_.LastWriteTime -lt $cutoff } | + ForEach-Object { + Remove-Item $_.FullName -Recurse -Force -ErrorAction SilentlyContinue + Write-Host "[Cleanup] Purged old log dir: $($_.FullName)" + } + } } exit $exitCode diff --git a/scripts/Invoke-RemoteBuild.ps1 b/scripts/Invoke-RemoteBuild.ps1 index 6a44c29..fc341f5 100644 --- a/scripts/Invoke-RemoteBuild.ps1 +++ b/scripts/Invoke-RemoteBuild.ps1 @@ -132,13 +132,16 @@ try { if ($BuildCommand -ne '') { # ── Custom build command (e.g. python, msbuild, cmake…) ────────── Write-Host "[Invoke-RemoteBuild] Running build command: $BuildCommand" - $buildResult = Invoke-Command -Session $session -ScriptBlock { + $buildExit = Invoke-Command -Session $session -ScriptBlock { param($workDir, $cmd, $artifactZip, $artifactSrc) + # Unbuffered Python output — lines appear in real-time instead of being held in buffer + $env:PYTHONUNBUFFERED = '1' Set-Location $workDir - $output = Invoke-Expression "$cmd 2>&1" | ForEach-Object { "$_" } - $buildExit = $LASTEXITCODE + # Stream each output line via Write-Host — WinRM forwards Write-Host in real-time + & cmd /c "$cmd 2>&1" | ForEach-Object { Write-Host $_ } + $exit = $LASTEXITCODE - if ($buildExit -eq 0) { + if ($exit -eq 0) { $archiveDir = Split-Path $artifactZip -Parent if (-not (Test-Path $archiveDir)) { New-Item -ItemType Directory -Path $archiveDir -Force | Out-Null @@ -147,38 +150,36 @@ try { Compress-Archive -Path $srcPath -DestinationPath $artifactZip -Force } - return @{ ExitCode = $buildExit; Output = ($output -join "`n") } + return $exit } -ArgumentList $GuestWorkDir, $BuildCommand, $GuestArtifactZip, $GuestArtifactSource - Write-Host $buildResult.Output - if ($buildResult.ExitCode -ne 0) { - throw "Build command failed (exit $($buildResult.ExitCode))" + if ($buildExit -ne 0) { + throw "Build command failed (exit $buildExit)" } } else { # ── Default: dotnet restore + dotnet build ──────────────────────── Write-Host "[Invoke-RemoteBuild] Running dotnet restore..." - $restoreResult = Invoke-Command -Session $session -ScriptBlock { + $restoreExit = Invoke-Command -Session $session -ScriptBlock { param($workDir) Set-Location $workDir - $output = dotnet restore 2>&1 - return @{ ExitCode = $LASTEXITCODE; Output = ($output -join "`n") } + dotnet restore 2>&1 | ForEach-Object { Write-Host $_ } + return $LASTEXITCODE } -ArgumentList $GuestWorkDir - Write-Host $restoreResult.Output - if ($restoreResult.ExitCode -ne 0) { - throw "dotnet restore failed (exit $($restoreResult.ExitCode))" + if ($restoreExit -ne 0) { + throw "dotnet restore failed (exit $restoreExit)" } Write-Host "[Invoke-RemoteBuild] Running dotnet build (configuration: $Configuration)..." - $buildResult = Invoke-Command -Session $session -ScriptBlock { + $buildExit = Invoke-Command -Session $session -ScriptBlock { param($workDir, $config, $artifactZip) Set-Location $workDir $outputDir = Join-Path $workDir 'bin\CIOutput' - $output = dotnet build --configuration $config --output $outputDir 2>&1 - $buildExit = $LASTEXITCODE + dotnet build --configuration $config --output $outputDir 2>&1 | ForEach-Object { Write-Host $_ } + $exit = $LASTEXITCODE - if ($buildExit -eq 0) { + if ($exit -eq 0) { $archiveDir = Split-Path $artifactZip -Parent if (-not (Test-Path $archiveDir)) { New-Item -ItemType Directory -Path $archiveDir -Force | Out-Null @@ -186,12 +187,11 @@ try { Compress-Archive -Path "$outputDir\*" -DestinationPath $artifactZip -Force } - return @{ ExitCode = $buildExit; Output = ($output -join "`n") } + return $exit } -ArgumentList $GuestWorkDir, $Configuration, $GuestArtifactZip - Write-Host $buildResult.Output - if ($buildResult.ExitCode -ne 0) { - throw "dotnet build failed (exit $($buildResult.ExitCode))" + if ($buildExit -ne 0) { + throw "dotnet build failed (exit $buildExit)" } }