feat(sprint4-6): quality, reliability, perf baseline
Sprint 4 — qualità codice: - scripts/_Common.psm1: New-CISessionOption, Resolve-VmrunPath, Invoke-Vmrun - PSScriptAnalyzerSettings.psd1: project-wide PSSA rules (security + formatting) - Remove-BuildVM.ps1: SupportsShouldProcess (-WhatIf support) - Invoke-RemoteBuild.ps1, Get-BuildArtifacts.ps1: use New-CISessionOption from module - New-BuildVM.ps1: use Resolve-VmrunPath + Invoke-Vmrun from module Sprint 5 — affidabilità: - Backup-CITemplate.ps1: stop runner, timestamped VMDK backup, prune old, restart - Watch-RunnerHealth.ps1: auto-restart act_runner, cooldown 3/h, EventLog + webhook - Register-CIScheduledTasks.ps1: add CI-RunnerHealth task (every 15 min) Sprint 6 — perf baseline: - Set-TemplateSharedFolders.ps1: idempotent VMX editor for NuGet/pip shared folders - Invoke-RemoteBuild.ps1: -UseSharedCache injects NUGET_PACKAGES + PIP_CACHE_DIR in guest - Measure-CIBenchmark.ps1: times clone/start/IP/WinRM/destroy, appends to benchmark.jsonl Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
#Requires -Version 5.1
|
||||
#Requires -RunAsAdministrator
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Creates a timestamped backup of the CI template VM directory.
|
||||
|
||||
.DESCRIPTION
|
||||
Run before any template refresh (toolchain update, KMS re-activation, snapshot
|
||||
rebuild). The act_runner service is stopped for the duration so no vmrun clone
|
||||
races the file copy, then restarted automatically.
|
||||
|
||||
Restore procedure (after a bad refresh):
|
||||
Stop-Service act_runner
|
||||
Remove-Item 'F:\CI\Templates\WinBuild' -Recurse -Force
|
||||
Copy-Item 'F:\CI\Backups\Template_<stamp>' 'F:\CI\Templates\WinBuild' -Recurse
|
||||
Start-Service act_runner
|
||||
|
||||
.PARAMETER TemplatePath
|
||||
Directory containing the template VM files (.vmx, .vmdk, snapshots).
|
||||
Default: F:\CI\Templates\WinBuild
|
||||
|
||||
.PARAMETER BackupBaseDir
|
||||
Directory under which timestamped backup folders are created.
|
||||
Default: F:\CI\Backups
|
||||
|
||||
.PARAMETER KeepCount
|
||||
Number of most-recent backups to retain. Older ones are deleted.
|
||||
Default: 3
|
||||
|
||||
.PARAMETER SkipRunnerStop
|
||||
Skip stopping/restarting act_runner. Use when the service is already stopped
|
||||
or when called from a maintenance window where the runner is offline.
|
||||
|
||||
.EXAMPLE
|
||||
# Standard pre-refresh backup (run elevated)
|
||||
.\Backup-CITemplate.ps1
|
||||
|
||||
# Dry run to preview what would happen
|
||||
.\Backup-CITemplate.ps1 -WhatIf
|
||||
|
||||
# Keep 5 backups
|
||||
.\Backup-CITemplate.ps1 -KeepCount 5
|
||||
#>
|
||||
[CmdletBinding(SupportsShouldProcess)]
|
||||
param(
|
||||
[string] $TemplatePath = 'F:\CI\Templates\WinBuild',
|
||||
|
||||
[string] $BackupBaseDir = 'F:\CI\Backups',
|
||||
|
||||
[ValidateRange(1, 20)]
|
||||
[int] $KeepCount = 3,
|
||||
|
||||
[switch] $SkipRunnerStop
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
if (-not (Test-Path $TemplatePath -PathType Container)) {
|
||||
throw "Template directory not found: $TemplatePath"
|
||||
}
|
||||
|
||||
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
|
||||
$backupDest = Join-Path $BackupBaseDir "Template_$timestamp"
|
||||
|
||||
if (-not (Test-Path $BackupBaseDir)) {
|
||||
New-Item -ItemType Directory -Path $BackupBaseDir -Force | Out-Null
|
||||
}
|
||||
|
||||
# ── Step 1: Stop runner ──────────────────────────────────────────────────────
|
||||
$runnerWasRunning = $false
|
||||
if (-not $SkipRunnerStop) {
|
||||
$svc = Get-Service -Name act_runner -ErrorAction SilentlyContinue
|
||||
if ($svc -and $svc.Status -eq 'Running') {
|
||||
if ($PSCmdlet.ShouldProcess('act_runner', 'Stop service before backup')) {
|
||||
Write-Host "[Backup-CITemplate] Stopping act_runner..."
|
||||
Stop-Service -Name act_runner -Force
|
||||
$runnerWasRunning = $true
|
||||
Write-Host "[Backup-CITemplate] act_runner stopped."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
# ── Step 2: Copy template directory ───────────────────────────────────────
|
||||
if ($PSCmdlet.ShouldProcess($TemplatePath, "Copy to $backupDest")) {
|
||||
Write-Host "[Backup-CITemplate] Copying $TemplatePath -> $backupDest ..."
|
||||
$sw = [System.Diagnostics.Stopwatch]::StartNew()
|
||||
Copy-Item -Path $TemplatePath -Destination $backupDest -Recurse -Force
|
||||
$sw.Stop()
|
||||
|
||||
# Size sum without Measure-Object.Sum (Set-StrictMode safe)
|
||||
$backupFiles = Get-ChildItem $backupDest -Recurse -File -ErrorAction SilentlyContinue
|
||||
$totalBytes = [long]0
|
||||
if ($backupFiles) { foreach ($f in $backupFiles) { $totalBytes += $f.Length } }
|
||||
$sizeMB = [math]::Round($totalBytes / 1MB, 0)
|
||||
|
||||
Write-Host "[Backup-CITemplate] Backup complete: $backupDest ($sizeMB MB, $($sw.Elapsed.TotalSeconds.ToString('F0'))s)"
|
||||
}
|
||||
|
||||
# ── Step 3: Prune old backups ──────────────────────────────────────────────
|
||||
$existing = Get-ChildItem $BackupBaseDir -Directory -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.Name -like 'Template_????????_??????' } |
|
||||
Sort-Object LastWriteTime -Descending
|
||||
|
||||
if ($existing.Count -gt $KeepCount) {
|
||||
$toRemove = $existing | Select-Object -Skip $KeepCount
|
||||
foreach ($old in $toRemove) {
|
||||
if ($PSCmdlet.ShouldProcess($old.FullName, 'Remove old backup')) {
|
||||
Write-Host "[Backup-CITemplate] Pruning old backup: $($old.Name)"
|
||||
Remove-Item $old.FullName -Recurse -Force
|
||||
}
|
||||
}
|
||||
Write-Host "[Backup-CITemplate] Retained $KeepCount backup(s)."
|
||||
}
|
||||
}
|
||||
finally {
|
||||
# ── Step 4: Restart runner ────────────────────────────────────────────────
|
||||
if ($runnerWasRunning) {
|
||||
if ($PSCmdlet.ShouldProcess('act_runner', 'Start service after backup')) {
|
||||
Write-Host "[Backup-CITemplate] Restarting act_runner..."
|
||||
Start-Service -Name act_runner
|
||||
$newStatus = (Get-Service -Name act_runner).Status
|
||||
Write-Host "[Backup-CITemplate] act_runner status: $newStatus"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user