14 KiB
AGENTS.md — Environment Reference
Questo file descrive l'ambiente su cui e' costruito il progetto. Leggerlo prima di generare o modificare qualsiasi script.
Istruzione per gli agenti: questo file va tenuto aggiornato. Se durante il lavoro si scopre un errore nuovo, un vincolo non documentato, un pattern che ha causato problemi o una best-practice utile, aggiungerla nella sezione "Errori frequenti da evitare" o in una sezione apposita. Non aggiungere dettagli ovvi o gia' coperti; solo informazioni che impedirebbero errori concreti in sessioni future.
Host
| Parametro | Valore |
|---|---|
| OS | Windows 11 |
| Hardware | i9-10900X, 64 GB RAM, NVMe SSD |
| PowerShell | 5.1 (Windows PowerShell — NON Core/7) |
| VMware Workstation | Pro, vmrun.exe in C:\Program Files (x86)\VMware\VMware Workstation\ |
| SSH client | ssh.exe / scp.exe built-in Windows 11 (PATH) |
| act_runner | v1.0.2 — F:\CI\act_runner\act_runner.exe |
| Gitea | http://10.10.20.11:3100 / https://gitea.emulab.it |
PowerShell 5.1 — Vincoli critici
Tutti gli script devono girare su Windows PowerShell 5.1. Le seguenti sintassi sono disponibili solo in PS 7+ e NON vanno usate:
| Costrutto PS 7+ | Alternativa PS 5.1 corretta |
|---|---|
$x ??= 'default' |
if ($null -eq $x) { $x = 'default' } |
$a ?? $b |
if ($null -ne $a) { $a } else { $b } |
cmd1 && cmd2 |
cmd1; if ($LASTEXITCODE -eq 0) { cmd2 } |
cmd1 || cmd2 |
cmd1; if ($LASTEXITCODE -ne 0) { cmd2 } |
$x is [int] |
$x -is [int] |
Ternary $a ? $b : $c |
if ($a) { $b } else { $c } |
foreach-object con && |
pipeline classico con -ErrorAction Stop |
Ogni script deve avere in testa:
#Requires -Version 5.1
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
Python development
L'orchestratore CI vive in src/ci_orchestrator/ (package ci-orchestrator,
entry point python -m ci_orchestrator). Tutta la logica applicativa
nuova va scritta in Python, non in PowerShell. Gli script PS in scripts/
sono ridotti a shim a 3 righe verso la CLI Python e sono mantenuti solo
per compatibilita' con Task Scheduler e caller esterni.
| Parametro | Valore |
|---|---|
| Python | 3.11+ richiesto (requires-python in pyproject.toml) |
| Venv produzione | F:\CI\python\venv\ (interprete F:\CI\python\venv\Scripts\python.exe) |
| Venv dev locale | .venv\ (NON committare; gitignored) |
| Install editable | python -m pip install -e .[dev] |
| Test | python -m pytest tests\python -q --cov=ci_orchestrator --cov-fail-under=90 |
| Lint | python -m ruff check src tests\python |
| Type-check | python -m mypy --strict src |
Convenzioni Python (enforced da ruff + mypy --strict):
- Type hints obbligatori su funzioni pubbliche e helper esportati. Evitare
Anyse esiste un tipo concreto/Protocol;Anysolo per fixture pytest e mock. - Niente variabili globali. Lo state vive in oggetti passati esplicitamente
(factory
backends.load_backend(config),Configdataclass). - Niente
print(): usareclick.echo(rispetta il flag-qdi click e il redirecterr=Trueper stderr). - Niente
subprocess.run(..., shell=True). Niente shell-out per cose che esistono come libreria Python (paramiko per SSH, pypsrp per WinRM, keyring per credenziali). - Encoding stdio sempre UTF-8: i moduli Python lo assumono e
runner/config.yamlesportaPYTHONIOENCODING=utf-8ad act_runner.
Mappatura script PowerShell → sotto-comando Python (Fase A2/A3/A4):
| PowerShell legacy (shim) | Python sub-command |
|---|---|
Wait-VMReady.ps1 |
python -m ci_orchestrator wait-ready |
New-BuildVM.ps1 |
python -m ci_orchestrator vm new |
Remove-BuildVM.ps1 |
python -m ci_orchestrator vm remove |
Cleanup-OrphanedBuildVMs.ps1 |
python -m ci_orchestrator vm cleanup |
Invoke-RemoteBuild.ps1 |
python -m ci_orchestrator build run |
Get-BuildArtifacts.ps1 |
python -m ci_orchestrator artifacts collect |
Watch-DiskSpace.ps1 |
python -m ci_orchestrator monitor disk |
Watch-RunnerHealth.ps1 |
python -m ci_orchestrator monitor runner |
Get-CIJobSummary.ps1 |
python -m ci_orchestrator report job |
Invoke-CIJob.ps1 |
python -m ci_orchestrator job |
Restano in PowerShell (girano sull'host fuori dal flusso CI o nel guest):
Setup-Host.ps1, Register-CIScheduledTasks.ps1, Backup-CITemplate.ps1,
Invoke-RetentionPolicy.ps1, Measure-CIBenchmark.ps1,
Test-CapacityBurnIn.ps1, tutti i template/Deploy-*.ps1,
template/Prepare-*.ps1, template/Install-CIToolchain-*.ps1 e i
tests/Test-*.ps1 di smoke locale.
Test gate: coverage totale ≥90% (alzato in A5, poi a 90%). Nuovi moduli devono
includere unit test con mock; gli errori frequenti di questa stessa
sezione sono coperti da tests/python/test_agents_errors.py come
regression test — non rimuoverli senza aggiornare la voce in
"Errori frequenti da evitare" e motivare la rimozione.
Closeout fasi A: vedi plans/A1-closeout.md, plans/A2-closeout.md,
plans/A3-closeout.md, plans/A4-closeout.md, plans/A5-closeout.md
per stato per-fase, decisioni prese e voci pendenti a carico utente.
Struttura directory host (fissa)
F:\CI\
act_runner\ — binario e stato act_runner
Templates\
WinBuild2025\ — template Windows Server 2025
WinBuild2022\ — template Windows Server 2022
LinuxBuild2404\ — template Ubuntu 24.04
BuildVMs\ — clone efimeri (creati/distrutti per ogni job)
Artifacts\ — artifact raccolti (per job-id)
ISO\ — ISO/VMDK di bootstrap (cache download)
keys\
ci_linux — chiave SSH privata per guest Linux (no passphrase)
ci_linux.pub — chiave pubblica corrispondente
VM Template
Windows (WinBuild2025 — primario)
| Parametro | Valore |
|---|---|
| OS | Windows Server 2025 |
| VMX | F:\CI\Templates\WinBuild2025\WinBuild2025.vmx |
| Snapshot clone | BaseClean |
| Transport | WinRM HTTPS porta 5986, cert self-signed (lab-only) |
| Credenziali | Windows Credential Manager target BuildVMGuest |
| Rete | VMnet8 NAT — 192.168.79.0/24 |
| Toolchain | VS Build Tools 2026 (MSBuild 18.5 / v145), .NET SDK 10, Python 3.13, Git, 7-Zip |
Variante WinBuild2022: path F:\CI\Templates\WinBuild2022\WinBuild2022.vmx, stesso snapshot BaseClean.
Linux (LinuxBuild2404)
| Parametro | Valore |
|---|---|
| OS | Ubuntu 24.04 LTS (cloud image) |
| VMX | F:\CI\Templates\LinuxBuild2404\LinuxBuild2404.vmx |
| Snapshot clone | BaseClean-Linux |
| Transport | SSH porta 22, utente ci_build, chiave F:\CI\keys\ci_linux |
| Rete | VMnet8 NAT — 192.168.79.0/24 |
| Toolchain | GCC, G++, Clang, CMake, Ninja, Python 3, Git, 7-Zip |
| CI dirs | /opt/ci/{build,output,scripts,cache} — owner ci_build |
Transport layer
- Windows guest: WinRM (
New-PSSessionconNew-CISessionOptionda_Common.psm1). UsareInvoke-Command -Session,Copy-Item -ToSession / -FromSession. - Linux guest: SSH/SCP via
ssh.exe/scp.exe. Usare le funzioni discripts/_Transport.psm1:Invoke-SshCommand,Copy-SshItem,Test-SshReady. - vmrun.exe: sempre invocato tramite
Invoke-Vmrunda_Common.psm1(wrapper uniforme che gestisce$LASTEXITCODEe output).
NON modificare il branch WinRM esistente quando si lavora sul branch Linux e viceversa.
act_runner / Gitea Actions
- Labels runner:
windows-build:host,linux-build:host - Env vars iniettate in ogni job (da
runner/config.yaml):GITEA_CI_TEMPLATE_PATH— VMX Windows templateGITEA_CI_LINUX_TEMPLATE_PATH— VMX Linux templateGITEA_CI_CLONE_BASE_DIR—F:\CI\BuildVMsGITEA_CI_ARTIFACT_DIR—F:\CI\ArtifactsGITEA_CI_GUEST_CRED_TARGET—BuildVMGuestGITEA_CI_SSH_KEY_PATH—F:\CI\keys\ci_linuxCI_PYTHON_LAUNCHER— absolute path al Python di sistema usato per bootstrap del venv produzione (F:\CI\python\venv). act_runner gira come SYSTEM con PATH sanitizzato (nopython/py.exerisolvibili), quindi i workflow Python leggono questo env come single source of truth. Aggiornare solo quando si fa upgrade dell'interprete.
Regole di stile (PSScriptAnalyzer)
Configurazione in PSScriptAnalyzerSettings.psd1. Regole principali:
- Indentazione: 4 spazi (no tab)
Write-Hoste' permesso (output catturato da act_runner;Write-Outputinterferisce con i return value)Invoke-Expressionvietato- Funzioni state-changing devono supportare
-WhatIf/ShouldProcess - Credenziali solo come
[PSCredential], mai plain-text string - Nessuna variabile globale (
$global:)
Errori frequenti da evitare
- Sintassi PS 7+ in script PS 5.1 — vedi tabella sopra.
$LASTEXITCODEnon controllato dopovmrun,ssh,scp— controllare sempre e farethrowse non zero.- Percorsi hardcoded senza parametro — ogni path critico deve avere un parametro con default documentato.
- Modificare entrambi i branch (WinRM e SSH) nello stesso script — mantenerli separati con
if ($GuestOS -eq 'Linux'). - Usare
New-PSSession/ WinRM verso host Linux — Linux usa solo SSH tramite_Transport.psm1. - Non rimuovere il seed ISO dal VMX dopo il primo boot Linux —
ide1:0.present = "FALSE"deve essere settato dopo cloud-init. [ordered]@{}non disponibile in PS 2 — non e' un problema qui (PS 5.1), ma ricordare che[ordered]e' PS 3+.ForEach-Object -Parallel— disponibile solo in PS 7+; usareforeachclassico oStart-Job.- Snapshot template catturato con VM accesa — produce file
*.vmem/*.vmsncon memoria;vmrun clone ... linked -snapshot=<name>rifiuta conError: The virtual machine should not be powered on. It is already running.anche sevmrun listmostra 0 VM. Catturare snapshot SOLO da stato fully powered-off (verificare assenza di.vmemnella dir template). vmrun getGuestIPAddressper check "VM running" — richiede VMware Tools attivi nel guest; in headless Tools puo' impiegare 30-60s a rispondere e il check appare bloccato. Usarevmrun liste cercare il path del VMX nell'output peris_running. Per recuperare l'IP usaregetGuestIPAddress -wait(blocca internamente finché Tools registrano l'IP) e filtrare le righe che iniziano con"Error:"perché vmrun in modalità headless può tornare exit code 0 con"Error: The VMware Tools are not running..."su stdout invece di usare un exit code non-zero.- Machine-id identico tra clone Linux — le cloud image Ubuntu hanno un
/etc/machine-idfisso. VMware DHCP assegna l'IP tramite DHCP client-id derivato da machine-id: se tutti i clone hanno lo stesso machine-id ottengono lo stesso IP e si bloccano per "IP collision". Fix nel template (prima di prendere lo snapshot):sudo truncate -s 0 /etc/machine-id && sudo rm -f /var/lib/dbus/machine-id && sudo shutdown -h now, poi ricreare lo snapshotBaseClean-Linuxda stato powered-off. & nativecmd 2>$nullNON sopprime stderr in PS 5.1 con$ErrorActionPreference='Stop'— la stderr del processo nativo viene convertita in ErrorRecord nel pipeline di PowerShell; sotto'Stop'questo diventa un errore terminante PRIMA che2>$nullpossa scartarlo. Esempio:ssh-keygen -R <ip> -f <file>stampa "Host X not found in file" su stderr → eccezione terminante. Fix: usare$ErrorActionPreference = 'SilentlyContinue'attorno alla chiamata oppure2>&1 | Out-Null. Per i job CI su VM ephemere locali, usare direttamenteStrictHostKeyChecking=no UserKnownHostsFile=NUL(default in_Transport.psm1conKnownHostsFile='') — nessunssh-keygennecessario.