6cd6bff882
- 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
228 lines
8.5 KiB
PowerShell
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
|
|
}
|