Files
local-ci-cd-system/scripts/Get-BuildArtifacts.ps1
T
Simone 6cd6bff882 nice-to-have: implement all 6 tier items
- Get-CIJobSummary.ps1: already present with full implementation (marked done)
- Get-BuildArtifacts.ps1: add -JobId/-Commit params; Write-ArtifactManifest writes
  manifest.json (name, size, sha256, commit, jobId) after both Linux and Windows
  artifact collection branches
- Invoke-CIJob.ps1: add -GuestCPU/-GuestMemoryMB params; insert post-clone VMX
  override block (numvcpus/memsize rewrite) before vmrun start; pass -JobId/-Commit
  to Get-BuildArtifacts calls
- Watch-RunnerHealth.ps1: add -GiteaUrl/-GiteaCredentialTarget params; optional
  GET /api/v1/admin/runners check in Running branch warns if 0 online runners
- Install-CIToolchain-WinBuild2025.ps1: fill SHA256 hashes for Python 3.13.3,
  7-Zip 26.01, Node.js 22.14.0, dotnet-install.ps1
- Measure-CIBenchmark.ps1: add deltaKB field (linked-clone disk footprint in KB)
  measured post-clone; added to result object and summary Format-Table
2026-05-13 10:38:49 +02:00

228 lines
8.5 KiB
PowerShell

#Requires -Version 5.1
<#
.SYNOPSIS
Copies build artifacts from an ephemeral VM to the host artifact directory.
.DESCRIPTION
Opens a WinRM session to the build VM and uses Copy-Item -FromSession
to transfer the artifact archive and optional log files.
Validates that at least one file was successfully transferred.
Throws on failure so the caller's try/finally can clean up the VM.
.PARAMETER IPAddress
IP of the build VM.
.PARAMETER Credential
PSCredential for the guest build user.
.PARAMETER GuestArtifactPath
Full path to the artifact file or directory inside the VM.
Example: C:\CI\output\artifacts.zip
.PARAMETER HostArtifactDir
Directory on the host where artifacts will be stored.
The directory is created if it does not exist.
Example: F:\CI\Artifacts\run-42
.PARAMETER IncludeLogs
If set, also copies *.log files from the guest work directory
(C:\CI\build\*.log) alongside the artifact archive.
.EXAMPLE
$cred = Get-Credential
.\Get-BuildArtifacts.ps1 `
-IPAddress "192.168.11.101" `
-Credential $cred `
-GuestArtifactPath "C:\CI\output\artifacts.zip" `
-HostArtifactDir "F:\CI\Artifacts\run-42"
#>
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[ValidatePattern('^((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$')]
[string] $IPAddress,
[Parameter()]
[System.Management.Automation.PSCredential] $Credential,
[Parameter(Mandatory)]
[string] $GuestArtifactPath,
[Parameter(Mandatory)]
[string] $HostArtifactDir,
[switch] $IncludeLogs,
# Guest OS type — Linux uses SCP instead of WinRM.
[ValidateSet('Windows', 'Linux')]
[string] $GuestOS = 'Windows',
# SSH private key path (used when GuestOS=Linux).
[string] $SshKeyPath = 'F:\CI\keys\ci_linux',
# SSH username (used when GuestOS=Linux).
[string] $SshUser = 'ci_build',
# Persistent known_hosts file for SSH calls (CI jobs).
# Empty string (default) = permissive mode (StrictHostKeyChecking=no, UserKnownHostsFile=NUL).
[string] $SshKnownHostsFile = '',
# Optional job ID and commit SHA written into manifest.json.
# When either is non-empty a manifest.json is written to $HostArtifactDir
# listing all collected files with name, size, and SHA256.
[string] $JobId = '',
[string] $Commit = ''
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
Import-Module (Join-Path $PSScriptRoot '_Common.psm1') -Force
# ── Helper: write artifact manifest ─────────────────────────────────────────
function Write-ArtifactManifest {
param([string] $Dir, [string] $JobId, [string] $Commit)
$files = @(Get-ChildItem -Path $Dir -Recurse -File -ErrorAction SilentlyContinue |
Where-Object { $_.Name -ne 'manifest.json' })
$fileEntries = foreach ($f in $files) {
[pscustomobject]@{
name = $f.FullName.Substring($Dir.TrimEnd('\', '/').Length).TrimStart('\', '/')
size = $f.Length
sha256 = (Get-FileHash $f.FullName -Algorithm SHA256).Hash.ToLower()
}
}
$manifest = [ordered]@{
ts = (Get-Date -Format 'o')
jobId = $JobId
commit = $Commit
files = @($fileEntries)
}
$manifestPath = Join-Path $Dir 'manifest.json'
try {
$manifest | ConvertTo-Json -Depth 4 |
Set-Content -Path $manifestPath -Encoding UTF8 -Force -ErrorAction Stop
Write-Host "[Get-BuildArtifacts] Manifest written: $manifestPath ($($files.Count) file(s))"
} catch {
Write-Warning "[Get-BuildArtifacts] Could not write manifest.json: $_"
}
}
if ($GuestOS -eq 'Windows' -and $null -eq $Credential) {
throw "Credential is required when GuestOS is Windows."
}
# ── Linux branch: use scp to transfer artifacts ─────────────────────────────────────────────
if ($GuestOS -eq 'Linux') {
Import-Module (Join-Path $PSScriptRoot '_Transport.psm1') -Force
if (-not (Test-Path $HostArtifactDir)) {
New-Item -ItemType Directory -Path $HostArtifactDir -Force | Out-Null
Write-Host "[Get-BuildArtifacts] Created host artifact directory: $HostArtifactDir"
}
Write-Host "[Get-BuildArtifacts] Transferring artifacts via SCP from $GuestArtifactPath ..."
Copy-SshItem `
-Source "$GuestArtifactPath/." `
-Destination $HostArtifactDir `
-IP $IPAddress `
-User $SshUser `
-KeyPath $SshKeyPath `
-KnownHostsFile $SshKnownHostsFile `
-Direction 'FromGuest' `
-Recurse
$transferred = @(Get-ChildItem -Path $HostArtifactDir -Recurse -File -ErrorAction SilentlyContinue)
if ($transferred.Count -eq 0) {
throw "[Get-BuildArtifacts] No files found in $HostArtifactDir after SCP transfer."
}
Write-Host "[Get-BuildArtifacts] $($transferred.Count) file(s) collected from Linux guest."
if ($JobId -ne '' -or $Commit -ne '') {
Write-ArtifactManifest -Dir $HostArtifactDir -JobId $JobId -Commit $Commit
}
return
}
# Ensure destination exists on host
if (-not (Test-Path $HostArtifactDir)) {
New-Item -ItemType Directory -Path $HostArtifactDir -Force | Out-Null
Write-Host "[Get-BuildArtifacts] Created host artifact directory: $HostArtifactDir"
}
$sessionOptions = New-CISessionOption
Write-Host "[Get-BuildArtifacts] Connecting to VM at $IPAddress..."
$session = $null
$attempts = 3
$delay = 10 # seconds between attempts
for ($i = 1; $i -le $attempts; $i++) {
try {
$session = New-PSSession `
-ComputerName $IPAddress `
-Credential $Credential `
-SessionOption $sessionOptions `
-UseSSL `
-Port 5986 `
-Authentication Basic `
-ErrorAction Stop
break
} catch {
if ($i -eq $attempts) { throw }
Write-Warning "[Get-BuildArtifacts] WinRM connect attempt $i/$attempts failed — retrying in ${delay}s... ($_)"
Start-Sleep -Seconds $delay
}
}
try {
# ── Verify artifact exists in guest ──────────────────────────────────
$guestExists = Invoke-Command -Session $session -ScriptBlock {
param($path)
Test-Path $path
} -ArgumentList $GuestArtifactPath
if (-not $guestExists) {
throw "Artifact not found in guest at: $GuestArtifactPath"
}
# ── Copy main artifact ────────────────────────────────────────────────
$artifactFileName = Split-Path $GuestArtifactPath -Leaf
$hostDestPath = Join-Path $HostArtifactDir $artifactFileName
Write-Host "[Get-BuildArtifacts] Copying $artifactFileName from guest..."
Copy-Item -Path $GuestArtifactPath -Destination $hostDestPath -FromSession $session -Force
# ── Copy logs if requested ────────────────────────────────────────────
if ($IncludeLogs) {
$logFiles = Invoke-Command -Session $session -ScriptBlock {
Get-ChildItem -Path 'C:\CI\build' -Filter '*.log' -Recurse -ErrorAction SilentlyContinue |
Select-Object -ExpandProperty FullName
}
foreach ($log in $logFiles) {
$logDest = Join-Path $HostArtifactDir (Split-Path $log -Leaf)
Write-Host "[Get-BuildArtifacts] Copying log: $(Split-Path $log -Leaf)"
Copy-Item -Path $log -Destination $logDest -FromSession $session -Force
}
}
# ── Validate at least the main artifact landed ────────────────────────
if (-not (Test-Path $hostDestPath)) {
throw "Artifact was not found at expected host path after copy: $hostDestPath"
}
$fileItem = Get-Item $hostDestPath
if ($fileItem.Length -eq 0) {
throw "Artifact file exists but is empty: $hostDestPath"
}
$sizeKB = [math]::Round($fileItem.Length / 1KB, 1)
Write-Host "[Get-BuildArtifacts] Artifact collected: $hostDestPath ($sizeKB KB)"
if ($JobId -ne '' -or $Commit -ne '') {
Write-ArtifactManifest -Dir $HostArtifactDir -JobId $JobId -Commit $Commit
}
return $hostDestPath
}
finally {
Remove-PSSession $session -ErrorAction SilentlyContinue
}