AGENTS.md (new): - Environment reference for AI agents: host specs, PS 5.1 constraints, dir structure, VM templates, transport layer, style rules - Lessons #9 (snapshot cold state) and #10 (vmrun list vs getGuestIPAddress) ARCHITECTURE.md: - Add SSH/SCP transport section (Linux VMs) - Update system diagram to include _Transport.psm1, SSH transport path - Network layout: add SSH port 22 for Linux VMs - Timeline: Linux readiness check phases documented CI-FLOW.md: - Prerequisites table: add Template VM (Linux), SSH key row - Step 6 (readiness): document Windows vs Linux phases - Step 7 (build) and Step 8 (artifacts): document Linux/SSH paths - ci-report-ip guestinfo mechanism referenced HOST-SETUP.md: - Directory tree: add LinuxBuild2404 template dir and keys/ - Post-setup checklist: add step 5 (SSH key generation + Linux template) - Fix duplicate step numbering (56, 67) - Reference Paths table: add Linux template VMX, snapshot, SSH key LINUX-TEMPLATE-SETUP.md: - Rewritten as implementation record (no longer a plan) - Fase A user-data: minimal (user+SSH key only, no packages/runcmd) - All phases marked complete with actual paths and snapshot names WINDOWS-TEMPLATE-SETUP.md: minor updates for consistency TODO.md: - Summary: update last-updated to 2026-05-11, Sprint 9 description - Linux Build VM row: all 5 items complete - Prossimi passi: Test Plan + §6.6 Tier-2 as next priorities
11 KiB
CI Pipeline Flow — Step by Step
Trigger to Artifact: Complete Pipeline Walk-Through
Step 0 — Prerequisites (one-time setup)
Before any CI job runs, the following must be in place:
| Component | State |
|---|---|
| Gitea server | Running, accessible on LAN |
| act_runner | Registered, running as Windows service on host |
| Template VM (Win) | Provisioned, snapshot "BaseClean" taken, VM powered off |
| Template VM (Lin) | Ubuntu 24.04 LTS, snapshot "BaseClean-Linux" taken, VM powered off (optional) |
| WinRM | Enabled on Windows template VM (inherits to all clones) |
| SSH key | F:\CI\keys\ci_linux (ed25519) present; public key in Linux template |
| vmrun.exe | Present at C:\Program Files (x86)\VMware\VMware Workstation\vmrun.exe |
F:\CI\BuildVMs\ |
Directory exists and writable |
F:\CI\Artifacts\ |
Directory exists and writable |
Step 1 — Developer Pushes Code
Developer → git push origin main
- Git push lands on Gitea server
- Gitea evaluates
.gitea/workflows/build.ymlin the repository - If the workflow trigger matches (e.g.
on: pushtomain), Gitea enqueues a new Actions job - Job is tagged with the label defined in the workflow (
runs-on: windows-build)
Step 2 — act_runner Picks Up the Job
act_runner (host) ─polling─► Gitea Actions API
◄─job data─
- act_runner polls the Gitea API approximately every 5–10 seconds
- Detects a pending job matching its labels (
windows-build) - Downloads the workflow YAML and job parameters
- If
capacityslots are available, starts job execution immediately - If all slots are busy, job waits in Gitea queue until a slot frees
Step 3 — act_runner Executes Workflow Steps
The workflow YAML defines steps that run as PowerShell commands on the host.
The first substantive step calls Invoke-CIJob.ps1:
- name: Run CI Job
shell: pwsh
run: |
& 'N:\Code\Workspace\Local-CI-CD-System\scripts\Invoke-CIJob.ps1' `
-JobId '${{ github.run_id }}' `
-RepoUrl 'ssh://gitea-ci/Simone/<repo>.git' `
-Branch '${{ github.ref_name }}' `
-Commit '${{ github.sha }}' `
-TemplatePath $env:GITEA_CI_TEMPLATE_PATH `
-Submodules `
-BuildCommand 'python build_plugin.py --final --dist-dir dist' `
-GuestArtifactSource 'dist'
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
Il parametro
-VMIPAddressnon è più necessario — l'IP viene rilevato automaticamente tramitevmrun getGuestIPAddressdopo l'avvio della VM.
Step 4 — Invoke-CIJob.ps1: Clone VM
New-BuildVM.ps1
vmrun.exe -T ws clone <template.vmx> <clone.vmx> linked -snapshot "BaseClean"
- Generates unique clone name:
Clone_{JobId}_{yyyyMMdd_HHmmss} - Creates clone directory under
F:\CI\BuildVMs\ - Linked clone creation takes ~5–15 seconds
- The template snapshot "BaseClean" is not modified
- Returns the full path to the new
.vmxfile
Step 5 — Start VM
vmrun.exe -T ws start <clone.vmx> nogui
- Starts the cloned VM in headless (no GUI) mode
- VM boots from the "BaseClean" state: clean OS, build tools installed, WinRM enabled
- Boot takes ~20–40 seconds
Step 6 — Wait for VM Readiness
Wait-VMReady.ps1 -VMPath <clone.vmx> -IPAddress <dynamic-NAT-ip> -TimeoutSeconds 300
IP is discovered dynamically via vmrun getGuestIPAddress (VMnet8 NAT, subnet 192.168.79.0/24).
No hardcoded IPs — each clone gets a DHCP-assigned address from VMware NAT.
Three-phase readiness check (retried every 5 seconds, up to 300s timeout).
Transport is selected automatically from the VMX guestOS field (-Transport WinRM for Windows, -Transport SSH for Linux):
Phase 1: vmrun getState → must return "running"
Windows (-Transport WinRM, default):
Phase 2: Test-Connection (ICMP ping) → must succeed
Phase 3: Test-WSMan → WinRM listener must respond
Linux (-Transport SSH):
Phase 2: Test-NetConnection -Port 22 → must succeed
Phase 3: ssh -o ConnectTimeout=5 ... "echo ready" → must return "ready"
- Total wait is typically 30–90 seconds after
vmrun start - If timeout exceeded, VM is destroyed and job fails with a clear error message
- On success, IP and credentials are ready for PSSession
Step 7 — Remote Build Execution
Invoke-RemoteBuild.ps1
-IPAddress <dynamic-NAT-ip> # from vmrun getGuestIPAddress
-GuestOS Windows|Linux # auto-detected from VMX guestOS field
# Windows path (default):
-Credential (from Windows Credential Manager)
-BuildCommand 'python build_plugin.py --final --dist-dir dist'
-GuestArtifactSource 'dist'
# Linux path (-GuestOS Linux):
-SshKeyPath F:\CI\keys\ci_linux
-SshUser ci_build
-CloneUrl / -CloneBranch # always in-VM git clone for Linux
-BuildCommand 'make all'
Sequence inside the PSSession:
1. New-PSSession → open WinRM session to VM
2a. (default) Compress-Archive (host) → copy zip to C:\CI\build.zip (guest) → Expand-Archive to C:\CI\build\
(source code è già clonato sull'host in Phase 1 — nessun git clone nella VM)
2b. (opt-in, `-UseGitClone`, §3.3 DONE 2026-05-10)
git clone direttamente in VM tramite `ssh://gitea-ci/...` (richiede Git+7-Zip in template, §6.6 Tier-1)
Vantaggio: skip host-zip-transfer, -25.7% durata pipeline (e2e PASS). Verifica SHA commit post-clone.
3. Invoke-Expression "$BuildCommand" in C:\CI\build\
Esempio: python build_plugin.py --final --dist-dir dist
a. Build 4 configurazioni MSBuild in parallelo (x86-unicode, x64-unicode, cli-x86, cli-x64)
b. Raccoglie artifact in C:\CI\build\dist\
c. Cleanup build dir (--final)
4. Compress-Archive C:\CI\build\$GuestArtifactSource C:\CI\output\artifacts.zip
5. Cattura exit code; se non-zero il job fallisce con errore chiaro
6. Remove-PSSession
- Output standard e stderr dalla build sono streamati all'host e catturati da act_runner per i log Gitea Actions
- Se la build fallisce (exit code ≠ 0), il controllo passa al blocco
finally
Step 8 — Collect Artifacts
Get-BuildArtifacts.ps1
-IPAddress <dynamic-NAT-ip>
-GuestOS Windows|Linux # auto-detected from Invoke-CIJob
# Windows path (default):
-Credential (from Windows Credential Manager)
-GuestArtifactPath C:\CI\output\artifacts.zip
# Linux path (-GuestOS Linux):
-SshKeyPath F:\CI\keys\ci_linux
-GuestArtifactPath /opt/ci/output
-HostArtifactDir F:\CI\Artifacts\{JobId}\
- Windows: apre PSSession e copia
C:\CI\output\artifacts.ziptramite WinRM - Linux:
scp -r ci_build@<ip>:/opt/ci/output/. <HostArtifactDir>tramite_Transport.psm1 - In entrambi i casi valida che almeno un file sia stato trasferito
Step 9 — Destroy VM
Remove-BuildVM.ps1 -VMPath <clone.vmx>
Sequence:
1. vmrun stop <clone.vmx> soft (graceful shutdown attempt)
2. Wait 5 seconds
3. vmrun getState → if still "running":
vmrun stop <clone.vmx> hard (force off)
4. vmrun deleteVM <clone.vmx> (removes VMX + VMDK delta files)
5. Remove-Item <clone dir> -Recurse (removes any leftover files)
- VM is gone from disk within ~5–10 seconds
- No state, no temp files, no credentials remain in the clone
- Template VM and "BaseClean" snapshot are unaffected
Note: This step runs in a
finallyblock insideInvoke-CIJob.ps1, so it executes even if Steps 6–8 failed. A build VM is never left running.
Step 10 — Report Status & Upload Artifacts
act_runner ─► Gitea Actions API (job status: success / failure)
act_runner ─► Gitea artifact store (uploads F:\CI\Artifacts\{JobId}\*)
- act_runner reads the exit code from
Invoke-CIJob.ps1 - Reports
successorfailureto Gitea - Uploads artifacts using
actions/upload-artifactstep in the workflow YAML - Build log (stdout from all steps) is stored in Gitea Actions UI
- Commit/PR status badge is updated
Parallel Build Flow
When multiple pushes happen concurrently (or multiple PRs are open):
act_runner (capacity: 4)
├── Job #1 ──► Clone_Job_001 (192.168.79.x) → build → destroy
├── Job #2 ──► Clone_Job_002 (192.168.79.x) → build → destroy
├── Job #3 ──► Clone_Job_003 (192.168.79.x) → build → destroy
└── Job #4 ──► Clone_Job_004 (192.168.79.x) → build → destroy
(Job #5 waits in Gitea queue)
Note: each clone gets a dynamic IP from VMware NAT DHCP (192.168.79.0/24). IP is discovered
automatically via vmrun getGuestIPAddress — no static assignment required.
- Each job creates its own independently named clone
- Clones share the parent "BaseClean" snapshot (read-only CoW)
- No filesystem or process overlap between concurrent builds
- act_runner enforces the
capacitylimit
Failure Scenarios
| Failure Point | Behavior |
|---|---|
| VM clone fails | Invoke-CIJob.ps1 throws; no VM to destroy; job fails immediately |
| VM never reaches ready state (timeout) | Wait-VMReady.ps1 throws; finally destroys the (running) VM |
| Build fails inside VM | Invoke-RemoteBuild.ps1 throws; finally collects partial artifacts (if any), destroys VM |
| Artifact collection fails | Job marked failed; VM is still destroyed in finally |
| Host machine loses power mid-job | act_runner resumes on reboot, marks in-flight job as failed; orphan VMs must be cleaned up manually (see TODO.md) |
Timing Summary (typical build)
| Phase | Typical Duration |
|---|---|
| Gitea → act_runner poll | 5–10 s |
| Linked clone creation | 5–15 s |
| VM boot | 20–40 s |
| WinRM readiness | 10–30 s (after boot) |
| git clone (from local Gitea) | 2–10 s |
| dotnet restore + build | project-dependent |
| Artifact collection | 2–10 s |
| VM destruction | 5–10 s |
| Overhead total | ~60–120 s |