dd238121b3
Sistema CI locale basato su VMware Workstation + Gitea Actions + act_runner. Testato e2e con nsis-plugin-nsinnounp (MSBuild + Python, 4 configurazioni parallele). - scripts/: Invoke-CIJob, Invoke-RemoteBuild, New/Remove-BuildVM, Wait-VMReady, Get-BuildArtifacts - runner/: act_runner config (windows-build label, capacity 4) - gitea/workflows/: build-nsis.yml (template per progetti MSBuild/Python) - template/: script di provisioning template VM (VS BuildTools 2026, .NET SDK 10, Python 3.13) - docs/: ARCHITECTURE, CI-FLOW, OPTIMIZATION, BEST-PRACTICES, Setup-GiteaSSH Verificato: e2e-009 SUCCESS in 02:09, cleanup automatico VM confermato.
203 lines
8.3 KiB
PowerShell
203 lines
8.3 KiB
PowerShell
#Requires -Version 5.1
|
|
<#
|
|
.SYNOPSIS
|
|
Copies pre-cloned source from host into a build VM and runs dotnet build.
|
|
|
|
.DESCRIPTION
|
|
The caller (Invoke-CIJob.ps1) is responsible for cloning the repository
|
|
on the HOST before calling this script. This script then:
|
|
1. Compresses the local source tree on the host, copies the archive into
|
|
the VM via WinRM (single file = much faster than recursive Copy-Item),
|
|
then expands it inside the VM.
|
|
2. Runs dotnet restore and dotnet build inside the VM
|
|
3. Packages the output as C:\CI\output\artifacts.zip inside the VM
|
|
|
|
The build VM does NOT need internet access or git. All network I/O happens
|
|
on the host before this script is called.
|
|
|
|
.PARAMETER IPAddress
|
|
IP of the build VM (e.g. 192.168.11.101).
|
|
|
|
.PARAMETER Credential
|
|
PSCredential for the build user inside the VM.
|
|
Retrieve from Windows Credential Manager in production:
|
|
$cred = Get-StoredCredential -Target 'BuildVMGuest'
|
|
|
|
.PARAMETER HostSourceDir
|
|
Full path on the HOST to the already-cloned repository directory.
|
|
This directory is copied into the VM as the build working directory.
|
|
Example: C:\Users\user\AppData\Local\Temp\ci-src-run-42
|
|
|
|
.PARAMETER Configuration
|
|
MSBuild/dotnet build configuration. Default: Release
|
|
|
|
.PARAMETER GuestWorkDir
|
|
Working directory inside the VM where source will be placed.
|
|
Default: C:\CI\build
|
|
|
|
.PARAMETER GuestArtifactZip
|
|
Full path inside the VM where the artifact archive will be written.
|
|
Default: C:\CI\output\artifacts.zip
|
|
|
|
.EXAMPLE
|
|
$cred = Get-Credential
|
|
.\Invoke-RemoteBuild.ps1 `
|
|
-IPAddress "192.168.11.101" `
|
|
-Credential $cred `
|
|
-HostSourceDir "C:\Temp\ci-src-run-42"
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[ValidatePattern('^(\d{1,3}\.){3}\d{1,3}$')]
|
|
[string] $IPAddress,
|
|
|
|
[Parameter(Mandatory)]
|
|
[System.Management.Automation.PSCredential] $Credential,
|
|
|
|
[Parameter(Mandatory)]
|
|
[ValidateScript({ Test-Path $_ -PathType Container })]
|
|
[string] $HostSourceDir,
|
|
|
|
[string] $Configuration = 'Release',
|
|
|
|
# Custom build command to run inside the VM (working dir = GuestWorkDir).
|
|
# When empty, defaults to: dotnet restore + dotnet build --configuration $Configuration
|
|
# Example: 'python build_plugin.py --final'
|
|
[string] $BuildCommand = '',
|
|
|
|
# Glob pattern (relative to GuestWorkDir) of files to include in the artifact zip.
|
|
# Used only when BuildCommand is set. Default: collect everything under 'dist'.
|
|
# Example: 'dist\**' or 'Contrib\nsInnoUnp\Build\**'
|
|
[string] $GuestArtifactSource = 'dist',
|
|
|
|
[string] $GuestWorkDir = 'C:\CI\build',
|
|
|
|
[string] $GuestArtifactZip = 'C:\CI\output\artifacts.zip'
|
|
)
|
|
|
|
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
# ── Session options: skip SSL certificate checks for self-signed (lab use) ──
|
|
$sessionOptions = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck
|
|
|
|
Write-Host "[Invoke-RemoteBuild] Connecting to VM at $IPAddress..."
|
|
|
|
$session = New-PSSession `
|
|
-ComputerName $IPAddress `
|
|
-Credential $Credential `
|
|
-SessionOption $sessionOptions `
|
|
-ErrorAction Stop
|
|
|
|
try {
|
|
# ── Ensure working directories exist on guest ────────────────────────
|
|
Invoke-Command -Session $session -ScriptBlock {
|
|
param($workDir, $outputDir)
|
|
foreach ($dir in @($workDir, (Split-Path $outputDir -Parent))) {
|
|
if (-not (Test-Path $dir)) {
|
|
New-Item -ItemType Directory -Path $dir -Force | Out-Null
|
|
}
|
|
}
|
|
# Clean previous build if present
|
|
if (Test-Path $workDir) {
|
|
Remove-Item $workDir -Recurse -Force
|
|
}
|
|
New-Item -ItemType Directory -Path $workDir -Force | Out-Null
|
|
} -ArgumentList $GuestWorkDir, $GuestArtifactZip
|
|
|
|
# ── Copy source tree: compress on host, transfer zip, expand in guest ──
|
|
# Single-file transfer over WinRM is far faster than recursive Copy-Item.
|
|
$hostZip = [System.IO.Path]::GetTempFileName() -replace '\.tmp$', '.zip'
|
|
try {
|
|
Write-Host "[Invoke-RemoteBuild] Compressing source on host..."
|
|
Compress-Archive -Path "$HostSourceDir\*" -DestinationPath $hostZip -CompressionLevel Fastest -Force
|
|
$zipSize = [math]::Round((Get-Item $hostZip).Length / 1MB, 1)
|
|
Write-Host "[Invoke-RemoteBuild] Transferring archive to guest ($zipSize MB)..."
|
|
$guestZip = 'C:\CI\src-transfer.zip'
|
|
Copy-Item -Path $hostZip -Destination $guestZip -ToSession $session -Force
|
|
Write-Host "[Invoke-RemoteBuild] Expanding archive in guest..."
|
|
Invoke-Command -Session $session -ScriptBlock {
|
|
param($zip, $dest)
|
|
Expand-Archive -Path $zip -DestinationPath $dest -Force
|
|
Remove-Item $zip -Force
|
|
} -ArgumentList $guestZip, $GuestWorkDir
|
|
Write-Host "[Invoke-RemoteBuild] Source transfer complete."
|
|
}
|
|
finally {
|
|
Remove-Item $hostZip -Force -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
# ── Build ──────────────────────────────────────────────────────────
|
|
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 {
|
|
param($workDir, $cmd, $artifactZip, $artifactSrc)
|
|
Set-Location $workDir
|
|
$output = Invoke-Expression "$cmd 2>&1" | ForEach-Object { "$_" }
|
|
$buildExit = $LASTEXITCODE
|
|
|
|
if ($buildExit -eq 0) {
|
|
$archiveDir = Split-Path $artifactZip -Parent
|
|
if (-not (Test-Path $archiveDir)) {
|
|
New-Item -ItemType Directory -Path $archiveDir -Force | Out-Null
|
|
}
|
|
$srcPath = Join-Path $workDir $artifactSrc
|
|
Compress-Archive -Path $srcPath -DestinationPath $artifactZip -Force
|
|
}
|
|
|
|
return @{ ExitCode = $buildExit; Output = ($output -join "`n") }
|
|
} -ArgumentList $GuestWorkDir, $BuildCommand, $GuestArtifactZip, $GuestArtifactSource
|
|
|
|
Write-Host $buildResult.Output
|
|
if ($buildResult.ExitCode -ne 0) {
|
|
throw "Build command failed (exit $($buildResult.ExitCode))"
|
|
}
|
|
}
|
|
else {
|
|
# ── Default: dotnet restore + dotnet build ────────────────────────
|
|
Write-Host "[Invoke-RemoteBuild] Running dotnet restore..."
|
|
$restoreResult = Invoke-Command -Session $session -ScriptBlock {
|
|
param($workDir)
|
|
Set-Location $workDir
|
|
$output = dotnet restore 2>&1
|
|
return @{ ExitCode = $LASTEXITCODE; Output = ($output -join "`n") }
|
|
} -ArgumentList $GuestWorkDir
|
|
|
|
Write-Host $restoreResult.Output
|
|
if ($restoreResult.ExitCode -ne 0) {
|
|
throw "dotnet restore failed (exit $($restoreResult.ExitCode))"
|
|
}
|
|
|
|
Write-Host "[Invoke-RemoteBuild] Running dotnet build (configuration: $Configuration)..."
|
|
$buildResult = 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
|
|
|
|
if ($buildExit -eq 0) {
|
|
$archiveDir = Split-Path $artifactZip -Parent
|
|
if (-not (Test-Path $archiveDir)) {
|
|
New-Item -ItemType Directory -Path $archiveDir -Force | Out-Null
|
|
}
|
|
Compress-Archive -Path "$outputDir\*" -DestinationPath $artifactZip -Force
|
|
}
|
|
|
|
return @{ ExitCode = $buildExit; Output = ($output -join "`n") }
|
|
} -ArgumentList $GuestWorkDir, $Configuration, $GuestArtifactZip
|
|
|
|
Write-Host $buildResult.Output
|
|
if ($buildResult.ExitCode -ne 0) {
|
|
throw "dotnet build failed (exit $($buildResult.ExitCode))"
|
|
}
|
|
}
|
|
|
|
Write-Host "[Invoke-RemoteBuild] Build succeeded. Artifact at $GuestArtifactZip"
|
|
}
|
|
finally {
|
|
Remove-PSSession $session -ErrorAction SilentlyContinue
|
|
}
|