Implements the repo-side artifacts of Phase B5 (plans/implementation-plan-A-B.md). Creates 5 .service + .timer pairs under deploy/systemd/ that replicate the periodic tasks registered on Windows by scripts/Register-CIScheduledTasks.ps1: - ci-cleanup-orphans -> python -m ci_orchestrator vm cleanup --max-age-hours 6 (every 6h + boot) - ci-retention-policy -> pwsh Invoke-RetentionPolicy.ps1 (daily 03:00) - ci-watch-disk-space -> python -m ci_orchestrator monitor disk (every 15min) - ci-watch-runner-health -> python -m ci_orchestrator monitor runner (every 15min) - ci-backup-template -> pwsh Backup-CITemplate.ps1 (weekly Sun 02:00, NEW) Includes deploy/systemd/README.md with mapping table, install/test/rollback instructions, and PowerShell Core install note for the two PS-only scripts. Plan checklist updated; B5-closeout.md added. Hardware install steps (systemctl enable + smoke) remain user responsibility on the Linux host.
53 KiB
Piano implementativo unificato — Fase A (Python) + Fase B (host Linux)
Summary esecutivo
Obiettivo combinato: riscrivere l'orchestratore CI/CD in Python 3.11+ cross-platform (Fase A) e poi spostare l'host da Windows 11 a Linux Mint mantenendo VMware Workstation Pro come hypervisor (Fase B). Il risultato è un unico stack Python in produzione su Linux, con i template VM, i workflow Gitea Actions e act_runner invariati nella sostanza.
Sintesi fasi:
- Fase A — Rewrite Python dell'orchestratore (host Windows attuale)
- Output verificabile:
python -m ci_orchestrator job ...sostituisceInvoke-CIJob.ps1;pytest≥70% coverage; workflowbuild-nsInnoUnp.ymlPASS. - Stato: da iniziare.
- Output verificabile:
- Fase B — Migrazione host a Linux Mint + Workstation Pro Linux
- Output verificabile: act_runner come
act-runner.servicesystemd; storage/var/lib/ci/; burn-in 4 job concorrenti PASS. - Stato: da iniziare (gating: A done).
- Output verificabile: act_runner come
- Fase C — (Hook only, non implementata) backend ESXi via
pyVmomi- Output verificabile: Protocol
VmBackendrispettato;[backend]selector inconfig.toml. - Stato: non in scope.
- Output verificabile: Protocol
Decisioni architetturali già prese e non rinegoziabili:
- Linguaggio: Python 3.11+, packaging
pyproject.toml, CLI viaclick - Transport WinRM:
pypsrp(no dipendenza daNew-PSSession) - Transport SSH:
paramiko(no dipendenza dassh.exe/scp.exeesterni) - Credential store:
keyring(Credential Manager su Win, Secret Service su Linux) - Hypervisor host Linux: VMware Workstation Pro Linux (binario
/usr/bin/vmrun) - Astrazione hypervisor: Protocol
VmBackendcon prima implementazioneWorkstationVmrunBackend - Lint dual-stack:
ruff+mypy --strictper Python, PSScriptAnalyzer per i PS legacy residui
Vincoli che NON cambiano: act_runner v1.0.2+ come consumer agnostico di
shell, Gitea http://10.10.20.11:3100 / https://gitea.emulab.it,
template VM (WinBuild2025, WinBuild2022, LinuxBuild2404) e relativi
snapshot BaseClean / BaseClean-Linux, workflow YAML in
gitea/workflows/ (cambia solo la shell: e il comando invocato in Fase
A4; cambiano solo path/env vars in Fase B).
Checklist riassuntiva master
- [A1] Creare
pyproject.toml+ packagesrc/ci_orchestrator/+ venvF:\CI\python\venv\ - [A1] Implementare
config.py(env vars +config.toml) con default Windows e hook path Linux - [A1] Implementare
backends/protocol.pycon ProtocolVmBackend(firma neutra, novmrun_*nei nomi pubblici) - [A1] Implementare
backends/workstation.py(WorkstationVmrunBackend) usandosubprocess+shutil.which('vmrun') - [A1] Implementare
transport/winrm.pycon wrapperpypsrp.client.Client - [A1] Implementare
transport/ssh.pycon wrapperparamiko.SSHClient/SFTPClient - [A1] Implementare
credentials.pycon ProtocolCredentialStore+KeyringCredentialStore - [A1] PoC
wait-readyend-to-end viapypsrpcontro un guest Windows reale - [A1] Test pytest unitari per
vmrun.py,winrm.py,ssh.py,credentials.pycon mock - [A1] Aggiungere job pytest a
gitea/workflows/lint.yml - [A2] Portare
Wait-VMReady.ps1→python -m ci_orchestrator wait-ready - [A2] Portare
Remove-BuildVM.ps1→vm remove - [A2] Portare
Cleanup-OrphanedBuildVMs.ps1→vm cleanup - [A2] Portare
Watch-DiskSpace.ps1→monitor disk - [A2] Portare
Watch-RunnerHealth.ps1→monitor runner - [A2] Portare
Get-CIJobSummary.ps1→report job - [A2] Sostituire ognuno dei
.ps1portati con shim a 3 righe verso la CLI Python - [A3] Portare
New-BuildVM.ps1→vm new - [A3] Portare
Invoke-RemoteBuild.ps1→build run - [A3] Portare
Get-BuildArtifacts.ps1→artifacts collect - [A3] Convertire test Pester
New-BuildVM.Tests.ps1,Wait-VMReady.Tests.ps1,Remove-BuildVM.Tests.ps1in pytest - [A4] Portare
Invoke-CIJob.ps1→python -m ci_orchestrator job - [A4] Aggiornare
gitea/actions/local-ci-build/action.ymlper invocare la CLI Python direttamente - [A4] Forzare
PYTHONIOENCODING=utf-8inrunner/config.yaml - [A4] Eseguire workflow
build-nsInnoUnp.yml(matrix Win+Linux) end-to-end PASS - [A5] Convertire errori frequenti
AGENTS.md#9, #10, #11, #12 in test pytest dedicati - [A5] Aggiornare
AGENTS.mdcon sezione "Python development" (venv, ruff, mypy, pytest) - [A5] Aggiornare
docs/ARCHITECTURE.mdcon nuovo layout package - [A5] Aggiornare
README.mdcon setup Python - [A5] Eseguire
Test-CapacityBurnIn(versione Python) 4 job concorrenti PASS - [B1] Installare Linux Mint LTS + aggiornamenti su hardware target
- [B1] Installare VMware Workstation Pro Linux (bundle ufficiale Broadcom) e validare
vmrunsu VM di test - [B1] Configurare
vmnet8NAT range192.168.79.0/24(allineato all'host Windows) - [B1] Creare utente
ci-runner(uid dedicato) e storage/var/lib/ci/{build-vms,artifacts,templates,keys}con ownershipci-runner:ci-runnermode 750 - [B1] Installare Python 3.11+ e creare venv in
/opt/ci/venv/conpip install -e .del package portato in Fase A - [B1] Validare
python -m ci_orchestrator --helpda utenteci-runner - [B2] Spegnere fully powered-off i template VM sull'host Windows
- [B2] Trasferire
F:\CI\Templates\→/var/lib/ci/templates/conrsync(preservare snapshotBaseClean/BaseClean-Linux) - [B2] Registrare i
.vmxsu Workstation Linux e validare snapshot viavmrun listSnapshots - [B2] Smoke test manuale:
vm new+wait-ready+vm removeda host Linux - [B3] Copiare
F:\CI\keys\ci_linux*→/etc/ci/keys/con perms 600 ownerci-runner - [B3] Re-store credenziali
BuildVMGuesteGiteaPATconsecret-toolnel keyring Linux - [B3] PoC accesso headless al keyring sotto systemd (file vault
age/sopscome fallback) - [B4] Scaricare act_runner Linux ≥ v1.0.2 e registrarlo verso Gitea con label
windows-build:hostelinux-build:host - [B4] Creare unit
/etc/systemd/system/act-runner.serviceconUser=ci-runner, envCI_*,PYTHONIOENCODING=utf-8 - [B4]
systemctl enable --now act-runner.servicee validare viajournalctl -u act-runner -f - [B5] Convertire
Register-CIScheduledTasks.ps1in coppie*.service+*.timer(cleanup-orphaned-vms,retention-policy,watch-disk-space,watch-runner-health,backup-template) - [B5] Abilitare tutti i timer con
systemctl enable --nowe validaresystemctl list-timers - [B6] Stop act_runner sull'host Windows e verifica coda Gitea vuota
- [B6] Rsync incrementale finale
F:\CI\Artifacts\→/var/lib/ci/artifacts/ - [B6] Trigger smoke workflow +
build-nsInnoUnp.ymlmatrix da host Linux PASS - [B7] Eseguire burn-in 4 job concorrenti × 10 round su template Windows e Linux con tempi entro ±20% baseline
- [B8] Backup finale di
F:\CI\su archivio - [B8] Lasciare host Windows spento ma reinstallato per ≥1 mese come rollback
- [X1] Aggiornare
lint.ymlper girare ruff + mypy + pytest oltre a PSSA - [X2] Aggiungere observability: log strutturato
logging+ journald handler in Fase B - [X3] Documentare gestione credenziali headless (DPAPI machine scope su Win, vault
agesu Linux) - [X4] Aggiornare
docs/RUNBOOK.md,docs/HOST-SETUP.md,docs/CI-FLOW.mdend-of-Fase-B
0. Prerequisiti e gating
Da soddisfare prima di iniziare A1:
- Repo
local-ci-cd-systemclonato e workflowlint.yml+self-test.ymlverdi sull'host Windows attuale - Backup integro di
F:\CI\Templates\(snapshotBaseCleanvalidato convmrun listSnapshots) - Python 3.11+ installato sull'host Windows e disponibile come
pythonnel PATH dell'utente che gira act_runner - Accesso amministrativo a Gitea per registrare/de-registrare runner durante A4 e B4
- Credenziali
BuildVMGuesteGiteaPATesistenti nel Credential Manager Windows e leggibili dall'utente di servizio AGENTS.mdletto integralmente da chi esegue il piano (errori #1–#12 sono test case obbligatori)- Hardware target Linux Mint disponibile fisicamente (può restare spento fino a B1)
1. Fase A — Rewrite Python (host Windows attuale)
A1 — Bootstrap progetto + moduli core
Obiettivo: creare scheletro package Python con VmBackend, transport
WinRM/SSH e credential store funzionanti contro l'ambiente reale via PoC
wait-ready.
Input / dipendenze: prerequisiti §0; scripts/_Common.psm1 e
scripts/_Transport.psm1 come riferimento di comportamento atteso;
AGENTS.md errori #9–#12 da preservare semanticamente.
Attività:
- Creare
pyproject.toml(build backendsetuptoolsohatchling) con dipendenzepypsrp,paramiko,keyring,click,tomli(se Python <3.11),richopzionale per logging - Creare layout
src/ci_orchestrator/con__init__.py,__main__.py(entry pointclick) - Creare venv in
F:\CI\python\venv\e installare il package in editable (pip install -e .[dev]) - Implementare
config.py: caricamento env vars (CI_ROOT,CI_TEMPLATES,CI_BUILD_VMS,CI_ARTIFACTS,CI_KEYS) con merge daconfig.tomlopzionale; default OS-aware (Windows →F:\CI\..., Linux →/var/lib/ci/...) - Implementare
backends/protocol.pycon ProtocolVmBackend(metodiclone_linked,start,stop,delete,get_ip,list_snapshots) e dataclassVmHandle(path/identificatore opaco) - Implementare
backends/workstation.py:WorkstationVmrunBackendche usasubprocess.run([vmrun, '-T', 'ws', op, *args], check=False, capture_output=True, text=True, encoding='utf-8')con check esplicito sureturncode - Implementare
transport/winrm.py: wrapper supypsrp.client.Client(host, username, password, ssl=True, cert_validation=False)con metodirun,copy,fetch - Implementare
transport/ssh.py: wrapper suparamiko.SSHClientconset_missing_host_key_policy(AutoAddPolicy),known_hostsconfigurabile (defaultNoneper non interferire con clone ephemeri — vediAGENTS.mderrore #12) - Implementare
credentials.py: ProtocolCredentialStore+KeyringCredentialStoreche usakeyring.get_credential(target, None)e ritorna oggettoCredential(username, password) - PoC end-to-end: comando
python -m ci_orchestrator wait-ready --vmx <path> --timeout 120su un clone reale del templateWinBuild2025(validare WinRM HTTPS self-signed) - Setup
pytest,pytest-mock,ruff,mypycome dev dependencies; configurarepyproject.tomlcon[tool.ruff]e[tool.mypy]strict - Test pytest unitari:
test_vmrun.py(mock subprocess, casivmrun listcon e senza VMX target — copre errore #10),test_winrm.py(mockpypsrp),test_ssh.py(mockparamiko, copre errore #12),test_credentials.py(mockkeyring) - Aggiungere job
pythonagitea/workflows/lint.yml: setup venv →ruff check src/ tests/→mypy --strict src/→pytest --cov=ci_orchestrator --cov-fail-under=70
Hook futuri Fase C: il Protocol VmBackend deve usare nomi neutri
(clone_linked, non vmrun_clone), accettare template/snapshot/name
come stringhe opache (non path Windows), separare clone_linked da
start (ESXi richiede PowerOn come task distinto). Niente assunzioni
"backend == stesso host del control plane" — WorkstationVmrunBackend
deve esporre la stessa firma anche se di fatto è locale.
Test / validazione:
pytest -q tests/PASSpytest --cov=ci_orchestrator --cov-fail-under=70PASSruff check src/ tests/0 errorimypy --strict src/0 errori- PoC
wait-readyritorna exit 0 entro 120s contro clone reale diWinBuild2025 gitea/workflows/lint.ymlPASS in CI
Rollback: rimuovere cartella src/, tests/, pyproject.toml,
venv F:\CI\python\venv\; ripristinare lint.yml precedente da git
(git checkout HEAD~1 -- gitea/workflows/lint.yml). Nessun PS legacy
modificato in A1.
Definizione di fatto step A1:
- PoC
wait-readyPASS contro VM reale - Coverage pytest ≥70% sui moduli core
lint.ymlaggiornato e verde- Protocol
VmBackendreviewato e congelato - Documentato setup venv in
README.md(sezione minima)
A2 — Script "foglia" (no state condiviso)
Obiettivo: portare in Python tutti gli script PS che non condividono stato con l'orchestratore, sostituendoli con shim minimi.
Input / dipendenze: A1 done. File: Wait-VMReady.ps1,
Remove-BuildVM.ps1, Cleanup-OrphanedBuildVMs.ps1,
Watch-DiskSpace.ps1, Watch-RunnerHealth.ps1, Get-CIJobSummary.ps1.
Attività:
- Implementare
commands/wait.pycon sottocomandowait-ready(parametri--vmx,--timeout,--guest-os) - Implementare
commands/vm.pycon sottocomandovm remove(parametri--vmx,--force) - Implementare
vm cleanup(scanCI_BUILD_VMS, riconoscimento clone orfani per pattern naming + età) - Implementare
commands/monitor.pyconmonitor disk(soglie configurabili, output JSON o human) - Implementare
monitor runner(controllo processo act_runner attivo + ultimo heartbeat) - Implementare
commands/report.pyconreport job(read-only su artifact dir + log job) - Sostituire
scripts/Wait-VMReady.ps1con shim PS 5.1 di 3 righe:& 'F:\CI\python\venv\Scripts\python.exe' -m ci_orchestrator wait-ready @args; exit $LASTEXITCODE - Idem per
Remove-BuildVM.ps1,Cleanup-OrphanedBuildVMs.ps1,Watch-DiskSpace.ps1,Watch-RunnerHealth.ps1,Get-CIJobSummary.ps1 - Aggiungere test pytest per ogni comando (mock backend + mock filesystem via
tmp_path) - Convertire
tests/Wait-VMReady.Tests.ps1intests/test_commands_wait.py(preservando i casi negativi) - Convertire
tests/Remove-BuildVM.Tests.ps1intests/test_commands_vm_remove.py - Verificare che gli scheduled task esistenti (
Register-CIScheduledTasks.ps1) continuino a funzionare invocando gli shim PS
Hook futuri Fase C: vm cleanup non deve assumere scan locale del
filesystem — la firma deve accettare un VmBackend come dipendenza,
così che in Fase C la stessa logica interroghi l'API ESXi (folder-scoped
list).
Test / validazione:
pytest tests/test_commands_*.pyPASS- Coverage globale rimane ≥70%
- Trigger manuale di
Watch-DiskSpacevia shim PS produce stesso output semantico della versione PS originale (json comparabile) - Smoke: clone manuale di una VM →
wait-ready→vm removeend-to-end
Rollback: git checkout HEAD~1 -- scripts/Wait-VMReady.ps1 ...
(ripristino .ps1 originali); rimozione commands/*.py portati;
scheduled tasks restano collegati ai .ps1 ripristinati.
Definizione di fatto step A2:
- Tutti gli script "foglia" hanno shim PS che chiama Python
- Test pytest sostituiscono i Pester corrispondenti
- Scheduled task continuano a funzionare via shim
- Nessuna regressione su
self-test.yml
A3 — Pipeline di build
Obiettivo: portare la pipeline core (clone → build → collect) in
Python preservando il comportamento dei .ps1 esistenti.
Input / dipendenze: A2 done. File: New-BuildVM.ps1,
Invoke-RemoteBuild.ps1, Get-BuildArtifacts.ps1.
Attività:
- Implementare
vm newincommands/vm.py(parametri--template,--snapshot,--name,--guest-os) - Integrare
WorkstationVmrunBackend.clone_linked+start+ attesa IP viaget_ip - Implementare
commands/build.pyconbuild run(parametri--vmx,--script,--workdir,--guest-os); usatransport.winrmper Windows,transport.sshper Linux - Gestione streaming output build verso stdout (act_runner cattura stdout — preservare comportamento
Write-Host) - Implementare
commands/artifacts.pyconartifacts collect(parametri--vmx,--remote-path,--local-dir) - Sostituire
scripts/New-BuildVM.ps1,Invoke-RemoteBuild.ps1,Get-BuildArtifacts.ps1con shim - Convertire
tests/New-BuildVM.Tests.ps1intests/test_commands_vm_new.py - Aggiungere test pytest per
build run(mock transport + capture stdout) - Aggiungere test pytest per
artifacts collect(mock SFTP/WinRM file copy + tmp_path) - Validare end-to-end: clone WinBuild2025 → build script PowerShell trivial → collect artifact ZIP
Hook futuri Fase C: build run deve ricevere un VmHandle opaco,
non un path VMX (ESXi non ha path locale al control plane). Adattare le
CLI a accettare entrambe (--vmx per workstation, --vm-id futuro per
ESXi) con dispatcher nel layer commands/.
Test / validazione:
pytest tests/test_commands_vm_new.py tests/test_commands_build.py tests/test_commands_artifacts.pyPASS- Coverage globale ≥70%
- Smoke: workflow CI manuale end-to-end usando esclusivamente gli shim
Test-NsinnounpBuild.ps1continua a passare (usa shim sotto)
Rollback: ripristino .ps1 originali da git; rimozione comandi
Python; nessuna modifica ai workflow YAML in A3 (solo shim).
Definizione di fatto step A3:
- Pipeline build completa accessibile via Python CLI
- Shim PS preservano l'API per i caller esistenti
- Smoke build PASS contro VM reale Windows e Linux
- Pester
New-BuildVM.Tests.ps1rimosso e sostituito
A4 — Orchestratore + switch workflow
Obiettivo: portare Invoke-CIJob.ps1 e fare switch dei workflow
Gitea per chiamare la CLI Python direttamente.
Input / dipendenze: A3 done. File: Invoke-CIJob.ps1,
gitea/actions/local-ci-build/action.yml, runner/config.yaml.
Attività:
- Implementare
commands/job.pycon sottocomandojob(entry point completo: parsing parametri job, clone, wait, build, collect, cleanup) - Gestione errori e cleanup garantito (try/finally con
vm removeanche su failure) - Aggiornare
gitea/actions/local-ci-build/action.yml: cambiareshell: powershell→shell: cmdo invocazione direttapython -m ci_orchestrator job ... - Aggiungere
PYTHONIOENCODING=utf-8inrunner/config.yamlenv - Lasciare
Invoke-CIJob.ps1come shim per scheduled task / chiamate manuali esterne (rimosso in A5) - Test pytest end-to-end con mock backend + transport:
test_commands_job.py - Eseguire workflow
build-nsInnoUnp.ymlmatrix (Win + Linux) PASS - Eseguire workflow
self-test.ymlPASS - Eseguire workflow
lint.ymlPASS
Hook futuri Fase C: la selezione del backend deve avvenire in
job.py leggendo config.toml [backend].type (default workstation).
Niente import diretto di WorkstationVmrunBackend in job.py — usare
factory backends.load_backend(config).
Test / validazione:
pytest tests/test_commands_job.pyPASS con coverage ≥80% sul comandojob- Workflow
build-nsInnoUnp.ymlend-to-end PASS dal nuovo entry point - Output catturato da act_runner leggibile (no encoding broken, no ANSI rotti)
- Tempi job entro ±10% rispetto al baseline PS (misurati con
Measure-CIBenchmarkancora in PS, lecito)
Rollback: git checkout HEAD~1 -- gitea/actions/local-ci-build/action.yml runner/config.yaml;
Invoke-CIJob.ps1 originale è ancora in git history e referenziabile.
Definizione di fatto step A4:
action.ymlchiama Python direttamente- Workflow matrix Win+Linux PASS
runner/config.yamlhaPYTHONIOENCODING=utf-8- Coverage
job.py≥80% - Misurazione tempo entro ±10% baseline
A5 — Test, documentazione, cleanup
Obiettivo: chiudere debito tecnico, rimuovere shim ridondanti, aggiornare documentazione, eseguire burn-in finale.
Input / dipendenze: A4 done.
Attività:
- Implementare test pytest dedicati per ognuno degli errori frequenti
AGENTS.md#9 (snapshot powered-off), #10 (vmrun listvsgetGuestIPAddress), #11 (machine-id reset → unique clone-name guard), #12 (stderr handling → AutoAddPolicy + no shell-out) - Verificare con
grep_searchche gli shim PS abbiano ancora call site esterni (Register-CIScheduledTasks.ps1, doc,Test-*.ps1) → nessuno candidato a rimozione in A5; rimozione differita a B5 (systemd timers) - Lasciare
Install-CIToolchain-*.ps1,Prepare-*.ps1,Deploy-*.ps1in PowerShell (girano dentro guest o per ricostruzione template — vedi §4 di idea-1) - Aggiornare
AGENTS.md: aggiungere sezione "Python development" (venv path, ruff, mypy strict, pytest, encoding utf-8, mappatura PS → Python) - Aggiornare
docs/ARCHITECTURE.mdcon nuovo layoutsrc/ci_orchestrator/+ Phase C extension point - Aggiornare
README.mdcon setup Python e quick-start CLI per tutti i 10 sub-comandi - Portare
Test-CapacityBurnIn.ps1incommands/burnin.py(o lasciare in PS se ancora utile come driver esterno) — non implementato in A5; lo shim attuale già delega viajobPython - Eseguire burn-in 4 job concorrenti × 10 round PASS sull'host Windows — richiede VM reali, a carico utente
- Aggiornare
docs/RUNBOOK.mdcon sezione "Operare da Python CLI" — deferito a chiusura Fase B (RUNBOOK riscritto end-to-end in B-finale)
Hook futuri Fase C: documentare in docs/ARCHITECTURE.md il
contratto VmBackend come "extension point" e il selettore [backend]
in config.toml come unica via per aggiungere ESXi.
Test / validazione:
pytest -qPASS, coverage globale ≥70%ruff check+mypy --strictpulitilint.ymlPASS (PSSA su PS residui + ruff/mypy/pytest su Python)- Burn-in 4×10 PASS senza VM orfane
grep_search Invoke-CIJobnei workflow YAML ritorna 0 hit
Rollback: ripristino doc da git; ri-aggiungere shim rimossi se qualche caller esterno non ancora migrato emerge.
Definizione di fatto step A5:
- Errori #9–#12 coperti da test pytest (
tests/python/test_agents_errors.py, 12 test) - Documentazione aggiornata (
AGENTS.md,docs/ARCHITECTURE.md,README.md) - Shim non referenziati rimossi → nessuno candidato (tutti referenziati da
Register-CIScheduledTasks.ps1/doc/Test-*.ps1); rimozione differita a B5 con sostituzione systemd - Burn-in PASS — pendenza utente (richiede VM reali, vedi
plans/A5-closeout.md) - Fase A done: code-complete; pending solo validazioni hardware (burn-in + workflow matrix end-to-end)
2. Fase B — Migrazione host Linux Mint
Parallelizzazione: B1 può iniziare durante A3/A4 (setup hardware indipendente dal codice). B2, B3 possono iniziare appena B1 è done e A5 è done. B4–B6 sono strettamente sequenziali dopo "A done".
B1 — Setup host Linux Mint in parallelo
Obiettivo: preparare hardware Linux Mint con Workstation Pro Linux, storage layout e venv Python pronti, senza impatto produzione.
Input / dipendenze: hardware target disponibile. Può girare in
parallelo a A3/A4. Riferimento idea-2-linux-host.md §4.
Attività:
- Installare Linux Mint LTS sull'hardware target + aggiornamenti (
apt full-upgrade) - Scaricare e installare VMware Workstation Pro Linux (bundle
.bundleda Broadcom) - Validare
vmrun -T ws --versionritorna versione coerente - Smoke test Workstation: creare VM trivia, clone, start, stop, delete via UI e via
vmrun - Configurare
vmnet8NAT range192.168.79.0/24(allineato all'host Windows) editando/etc/vmware/vmnet8/dhcpd/dhcpd.confo viavmware-netcfg - Creare utente di servizio
ci-runner(useradd -r -m -s /bin/bash ci-runner) con uid/gid dedicati - Creare layout
/var/lib/ci/{build-vms,artifacts,templates,keys}con ownershipci-runner:ci-runnermode 750 - Applicare ACL POSIX:
setfacl -d -m u:ci-runner:rwX /var/lib/ci/build-vms - Installare Python 3.11+ (
apt install python3.11 python3.11-venv) - Creare venv
/opt/ci/venv/conpython3.11 -m venve installare il package:/opt/ci/venv/bin/pip install -e /path/to/local-ci-cd-system - Validare
sudo -u ci-runner /opt/ci/venv/bin/python -m ci_orchestrator --help - Aggiungere
[paths.linux]aconfig.example.tomlcon i path POSIX
Hook futuri Fase C: lo stesso host Linux farà da control plane se
si attiverà ESXi. Non installare nulla che assuma "VM girano in locale"
(es. condivisioni vmware-shared-folders host-only).
Rischi specifici (da idea-2-linux-host.md §6):
vmrunsu Linux ha differenze sottili nel parsing output → mitigazione: test del modulovmrun.pysu Linux subito in B1, confronto output con Windows- Permessi
/var/lib/ci/build-vms/:vmruncomeci-runnerdeve scrivere VMDK CoW → mitigazione: ACL POSIX + test scrittura vmnet8NAT range diverso → mitigazione: allineamento range a192.168.79.0/24
Test / validazione:
vmrun -T ws listritorna lista vuota o coerentepython -m ci_orchestrator --helpPASS daci-runner- ACL POSIX verificata con
getfacl /var/lib/ci/build-vms pytest(subset cross-platform) PASS sul venv Linux
Rollback: il host Linux è fisicamente separato. Spegnerlo. Nessun impatto su produzione Windows.
Definizione di fatto step B1:
- Workstation Pro Linux operativa
- Storage layout creato con ACL
- Python venv pronto e package installato
vmnet8configurato
B2 — Trasferimento template VM
Obiettivo: copiare i template VMware dall'host Windows a Linux mantenendo snapshot integri.
Input / dipendenze: B1 done. Può girare durante A4/A5. Riferimento
AGENTS.md errore #9 (snapshot solo da powered-off).
Attività:
- Verificare assenza di
*.vmem/*.vmsndi runtime inF:\CI\Templates\<name>\(template fully powered-off) - Eseguire
rsync -av --progress /cygdrive/f/CI/Templates/ ci-runner@<linux-host>:/var/lib/ci/templates/(oscp -rse rsync non disponibile) - Verificare integrità:
find /var/lib/ci/templates -name '*.vmx' -exec vmrun -T ws listSnapshots {} \; - Aprire i
.vmxsu Workstation Linux per registrarli (eventuale prompt "I moved it / I copied it" → "I copied it") - Validare snapshot
BaseCleansuWinBuild2025.vmx,WinBuild2022.vmx - Validare snapshot
BaseClean-LinuxsuLinuxBuild2404.vmx - Smoke test:
python -m ci_orchestrator vm new --template /var/lib/ci/templates/WinBuild2025/WinBuild2025.vmx --snapshot BaseClean --name smoke1+wait-ready+vm remove
Hook futuri Fase C: i template restano in formato VMX. Per ESXi
serviranno OVF (ovftool) — non fare nulla in B2, ma documentare che
il dataset attuale è la base per export futuro.
Rischi specifici:
- Performance I/O linked clone su ext4 vs NTFS → mitigazione: misurare in B7, eventualmente XFS/BTRFS
- Snapshot corrotto in transit → mitigazione: checksum (
sha256sum) prima e dopo rsync
Test / validazione:
vmrun listSnapshotsritorna gli snapshot attesi su tutti i template- Smoke
vm new+wait-readyPASS con WinRM da Linux verso guest Windows - Smoke
vm new+wait-readyPASS con SSH da Linux verso guest Linux
Rollback: i template originali su F:\CI\Templates\ sono intatti.
Spegnere host Linux. Eventualmente eliminare /var/lib/ci/templates/.
Definizione di fatto step B2:
- Tutti i template registrati su Workstation Linux
- Snapshot integri verificati
- Smoke
vm newPASS per Win e Linux
B3 — Trasferimento credenziali e chiavi
Obiettivo: rendere disponibili sull'host Linux le credenziali
guest e i token Gitea, accessibili da ci-runner headless.
Input / dipendenze: B1 done. Può girare durante A5. Riferimento
idea-2-linux-host.md §6 (rischio Alta su keyring headless).
Attività:
- Copiare
F:\CI\keys\ci_linuxeci_linux.pub→/etc/ci/keys/conchmod 600echown ci-runner:ci-runner - Re-store credenziale guest Windows:
sudo -u ci-runner secret-tool store --label='BuildVMGuest' service ci target BuildVMGuest - Re-store Gitea PAT:
sudo -u ci-runner secret-tool store --label='GiteaPAT' service ci target GiteaPAT - Validare lettura:
sudo -u ci-runner /opt/ci/venv/bin/python -c "import keyring; print(keyring.get_credential('BuildVMGuest', None))" - PoC headless: testare lettura keyring da contesto systemd (no sessione D-Bus utente). Se fallisce, implementare backend
keyringfile-based con vaultageosopscome fallback documentato - Documentare la scelta finale in
docs/HOST-SETUP.md
Hook futuri Fase C: anche ESXi userà credenziali da keyring (user
vSphere). Lo stesso KeyringCredentialStore deve poter caricare EsxiHost
target — niente hard-coding di nomi credenziali.
Rischi specifici:
secret-toolheadless: act_runner systemd non ha D-Bus user session → mitigazione: backend file-based conage(decisione PoC in B3)
Test / validazione:
- Lettura keyring PASS da utente interattivo
- Lettura keyring PASS da contesto systemd (
systemd-run --uid=ci-runner ...come simulazione) - Pytest
test_credentials.pycon backend Linux PASS
Rollback: rimuovere /etc/ci/keys/, secret-tool clear per ogni
target. Credenziali su host Windows intatte.
Definizione di fatto step B3:
- Chiavi SSH copiate con perms corretti
- Credenziali nel keyring leggibili headless
- Strategia documentata in
docs/HOST-SETUP.md
B4 — Setup act_runner come systemd service
Obiettivo: act_runner Linux registrato verso Gitea e gestito da systemd, equivalente al servizio Windows attuale.
Input / dipendenze: B1, B2, B3 done. Sequenziale dopo "A done" (serve la CLI Python stabile per i comandi invocati dai workflow).
Attività:
- Scaricare binario act_runner Linux ≥ v1.0.2 in
/opt/ci/act_runner/ - Generare token registrazione su Gitea per il nuovo runner
- Registrare runner:
act_runner register --no-interactive --instance <gitea-url> --token <t> --name ci-linux --labels windows-build:host,linux-build:host - Creare
/etc/systemd/system/act-runner.service:[Service]User=ci-runner,WorkingDirectory=/var/lib/ci/runner,ExecStart=/opt/ci/act_runner/act_runner daemonEnvironment="PYTHONIOENCODING=utf-8",Environment="CI_ROOT=/var/lib/ci",Environment="CI_TEMPLATES=/var/lib/ci/templates",Environment="CI_BUILD_VMS=/var/lib/ci/build-vms",Environment="CI_ARTIFACTS=/var/lib/ci/artifacts",Environment="CI_KEYS=/etc/ci/keys"Restart=on-failure,RestartSec=10
systemctl daemon-reload && systemctl enable --now act-runner.service- Validare
journalctl -u act-runner -fmostra connessione a Gitea OK - NON avviare ancora workflow di produzione — il runner Windows è ancora primario
Hook futuri Fase C: env vars CI_* restano valide; per ESXi si
aggiungeranno solo CI_BACKEND=esxi + sezione [backend.esxi] in
config.toml. act-runner.service non cambia.
Rischi specifici:
- Runner Linux in idle che intercetta job destinati a Windows → mitigazione: in B4 il runner viene registrato ma in stato
pausedlato Gitea, oppure label distinte temporanee fino a B6
Test / validazione:
systemctl status act-runner→active (running)- Runner visibile in Gitea admin UI come "online"
- Job di test manuale (workflow
self-test.yml) PASS sul runner Linux journalctl -u act-runner --since "5min ago"senza errori critici
Rollback: systemctl disable --now act-runner, de-registrare
runner da Gitea. Runner Windows resta primario.
Definizione di fatto step B4:
- act-runner.service attivo e abilitato
- Runner online in Gitea
self-test.ymlPASS dal nuovo runner- Logging via journald validato
B5 — Conversione scheduled tasks → systemd timers
Obiettivo: tutti i task periodici del runner Windows sono
replicati come coppie *.service + *.timer su Linux.
Input / dipendenze: B4 done. Riferimento Register-CIScheduledTasks.ps1
per l'inventario dei task.
Attività:
- Inventariare i task in
Register-CIScheduledTasks.ps1(identificare cadenza e comando per ognuno) - Per
cleanup-orphaned-vms: creareci-cleanup-orphaned-vms.service(oneshot,ExecStart=/opt/ci/venv/bin/python -m ci_orchestrator vm cleanup) +.timer(OnCalendar=hourly) - Per
retention-policy: creareci-retention-policy.service+.timer(OnCalendar=daily) - Per
watch-disk-space:ci-watch-disk-space.service+.timer(OnCalendar=*:0/15) - Per
watch-runner-health:ci-watch-runner-health.service+.timer(OnCalendar=*:0/5) - Per
backup-template:ci-backup-template.service+.timer(OnCalendar=weekly) systemctl daemon-reloadesystemctl enable --now <ognuno>.timer- Validare
systemctl list-timersmostra tutti i timer schedulati - Documentare il mapping in
docs/HOST-SETUP.md
Hook futuri Fase C: vm cleanup con backend ESXi userà la stessa
unit — il selettore backend è in config.toml, non nel comando.
Rischi specifici:
- Cadenza non perfettamente equivalente tra Task Scheduler e systemd timers → mitigazione: usare
OnCalendaresplicito +Persistent=trueper non perdere esecuzioni durante reboot
Test / validazione:
systemctl list-timers --allmostra tutti i timer attesi- Trigger manuale
systemctl start ci-cleanup-orphaned-vms.serviceesegue senza errori journalctl -u ci-*mostra esecuzioni riuscite
Rollback: systemctl disable --now <name>.timer per ognuno;
rimuovere file unit. Task Scheduler Windows ancora attivo.
Definizione di fatto step B5:
- Tutti i task periodici come timer systemd attivi
- Trigger manuale di ognuno PASS
- Mapping documentato
B6 — Cutover
Obiettivo: transizione produzione dall'host Windows al Linux in una finestra di manutenzione concordata.
Input / dipendenze: A done, B1–B5 done. Strettamente sequenziale.
Attività:
- Annunciare finestra di manutenzione
- Verificare nessun job in coda lato Gitea
- Stop act_runner sull'host Windows:
Stop-Service actions-runner(PS 5.1,$LASTEXITCODEcontrollato) - Disabilitare scheduled task Windows:
Unregister-ScheduledTask -TaskName 'CI-*' -Confirm:$false - Rsync incrementale finale
F:\CI\Artifacts\→/var/lib/ci/artifacts/(preserva permessi) - Verificare che il runner Linux sia "online" e non "paused" su Gitea
- Trigger manuale di workflow smoke (
self-test.yml) PASS - Trigger manuale
build-nsInnoUnp.ymlmatrix Win + Linux PASS - Monitorare
journalctl -u act-runner -fper ≥30 min sotto carico reale
Hook futuri Fase C: nessuno specifico in B6. Cutover non tocca backend.
Rischi specifici:
- Gap di tempo tra stop runner Windows e start runner Linux durante il quale i job vanno in errore → mitigazione: cutover in finestra a zero traffic + Gitea UI "paused" preventivo
- WinRM da Linux verso guest Windows fallisce su edge case TLS → mitigazione: test in B2 + B4, fallback documentato in
idea-2-linux-host.md§7 (dual-host)
Test / validazione:
self-test.ymlPASS dal runner Linuxbuild-nsInnoUnp.ymlmatrix Win+Linux PASS dal runner Linux- Nessun errore in
journalctl -u act-runner --since "1h ago"di severità critica - Artifact correttamente in
/var/lib/ci/artifacts/
Rollback (entro 30 min se PASS critico fallisce):
systemctl stop act-runnersu LinuxStart-Service actions-runnersu WindowsRegister-ScheduledTaskper riabilitare task Windows- Documentare incident e tornare a B1–B5 per fix
Definizione di fatto step B6:
- Runner Linux primario, runner Windows fermo
- Workflow matrix PASS dal nuovo host
- Artifact migrati
- 30 min di esercizio senza incidenti
B7 — Capacity burn-in sul nuovo host
Obiettivo: validare che il nuovo host regga il carico target con tempi paragonabili.
Input / dipendenze: B6 done.
Attività:
- Eseguire burn-in 4 job concorrenti × 10 round su template
WinBuild2025 - Eseguire burn-in 4 job concorrenti × 10 round su template
LinuxBuild2404 - Confrontare tempi medi vs baseline pre-migrazione (registrato in A5)
- Verificare success rate 100% (no VM orfane, no failure transienti)
- Misurare uso disco
/var/lib/ci/build-vms/durante e dopo burn-in (cleanup automatico) - Documentare risultati in
docs/RUNBOOK.md
Hook futuri Fase C: i risultati di B7 sono il baseline contro cui
confrontare un eventuale backend ESXi (criterio di "C done" in
idea-3-esxi-support.md §8).
Rischi specifici:
- Performance I/O ext4 inferiore → mitigazione: se delta >20%, valutare XFS/BTRFS
- VM orfane non pulite → mitigazione: timer
ci-cleanup-orphaned-vmsdeve essere già attivo (B5)
Test / validazione:
- Burn-in PASS con success rate 100%
- Tempo medio entro ±20% baseline Windows
- Nessuna VM orfana al termine
- Uso disco torna al baseline post-cleanup
Rollback: non applicabile (B7 è validazione, non modifica). Se fallisce, rollback a Windows (vedi B6 rollback).
Definizione di fatto step B7:
- Burn-in PASS
- Tempi documentati
- Nessuna VM orfana
B8 — Decommissioning host Windows (opzionale)
Obiettivo: dopo ≥1 settimana di stabilità, dismettere host Windows.
Input / dipendenze: B7 done + ≥1 settimana di esercizio Linux senza incidenti.
Attività:
- Backup finale di
F:\CI\su archivio esterno (tar+ checksum) - Spegnimento runner Windows (
Stop-Service actions-runner, runner già fermo da B6) - Lasciare host Windows installato ma spento per ≥1 mese come rollback
- Documentare procedura di riaccensione in
docs/RUNBOOK.md - Dopo 1 mese: riallocare hardware se non ci sono stati rollback
Hook futuri Fase C: nessuno.
Rischi specifici:
- Decommissioning prematuro elimina rollback → mitigazione: gating ≥1 settimana + ≥1 mese spento prima di riallocare
Test / validazione:
- Backup verificato (estrazione di prova in tmp)
- Host Windows spento, hardware integro
- Runbook aggiornato
Rollback: riaccendere host Windows, ripristinare scheduled task, re-registrare runner Windows verso Gitea.
Definizione di fatto step B8:
- Backup
F:\CI\archiviato e validato - Host Windows spento ma intatto
- Runbook rollback documentato
- Fase B done: tutti i criteri di "fatto" §7 soddisfatti
3. Step trasversali (X)
X1 — Lint dual-stack (PSSA + ruff/mypy)
Obiettivo: pipeline di lint unica che copre Python e PowerShell residuo.
Input / dipendenze: A1 (per la parte Python). Aggiornata in A5 e B8.
Attività:
- Aggiornare
gitea/workflows/lint.ymlcon jobpython-lint(ruff + mypy + pytest) e jobpwsh-lint(PSSA suscripts/,template/,tests/) - In B8: rimuovere o relegare a job opzionale la parte PSSA (resta utile solo per
template/Windows) - Configurare
PSScriptAnalyzerSettings.psd1per coprirescripts/,template/,tests/(già presente in repo) - Configurare
pyproject.tomlcon[tool.ruff],[tool.mypy],[tool.pytest.ini_options]
Hook futuri Fase C: nessuno specifico.
Test / validazione:
lint.ymlPASS con entrambi i job- 0 errori PSSA, 0 errori ruff, 0 errori mypy strict
Rollback: ripristino lint.yml da git.
Definizione di fatto step X1:
lint.ymlcon job dual-stack- 0 warning bloccanti
- Documentato in
AGENTS.md
X2 — Observability
Obiettivo: log strutturati + integrazione journald in Fase B.
Input / dipendenze: A1 (logger setup); B4 (systemd journald).
Attività:
- Configurare
loggingPython con formatter strutturato (JSON o key=value), nessun colore ANSI obbligatorio (act_runner non li gestisce sempre) - In B4: configurare service unit per inviare stdout/stderr a journald (default systemd) e validare
journalctl -u act-runner -o json - Documentare query journald utili in
docs/RUNBOOK.md
Hook futuri Fase C: log strutturati facilitano correlazione job ↔ backend.
Test / validazione:
journalctl -u act-runner -o jsonritorna eventi parsabili- Log Python catturato correttamente da act_runner stdout
Rollback: revert formatter logging.
Definizione di fatto step X2:
- Logging strutturato
- journald query documentate
X3 — Gestione credenziali headless
Obiettivo: credenziali accessibili da contesto SYSTEM (Win) e systemd (Linux) senza sessione utente interattiva.
Input / dipendenze: A1 (credential store); B3 (PoC headless Linux).
Attività:
- Documentare in
docs/HOST-SETUP.mdil problema noto: keyring Win sotto SYSTEM e keyring Linux sotto systemd richiedono soluzioni dedicate - In Fase A: documentare l'uso corrente (Credential Manager con utente di servizio + DPAPI machine scope)
- In B3: PoC + decisione finale (Secret Service via D-Bus user@.service, oppure file vault
age/sops) - Implementare backend
keyringfile-based come opzioneKeyringFileBackend(vault_path, age_key_path)se serve - Test pytest che simulano contesto headless (mock
keyring.get_credentialritornaNone→ fallback a file vault)
Hook futuri Fase C: stesso CredentialStore userà credenziali
vSphere — mantenere API generica.
Test / validazione:
- Lettura credenziale da contesto headless PASS in entrambi gli OS
- Test pytest fallback PASS
Rollback: usare credenziali in file plaintext non accettabile — nessun rollback se PoC fallisce, va risolto come blocker.
Definizione di fatto step X3:
- Strategia documentata
- Test pytest fallback verde
- PoC Linux PASS
X4 — Documentazione consolidata
Obiettivo: documentazione docs/ allineata allo stato finale (host
Linux + Python).
Input / dipendenze: A5 e B8.
Attività:
- Aggiornare
docs/ARCHITECTURE.md: layoutsrc/ci_orchestrator/, ProtocolVmBackend, hook ESXi - Aggiornare
docs/HOST-SETUP.md: setup Linux Mint, Workstation Pro Linux, ACL, keyring headless - Aggiornare
docs/CI-FLOW.md: nuovo entry point Python, env vars - Aggiornare
docs/RUNBOOK.md: comandi systemd, journalctl, troubleshooting - Aggiornare
docs/BEST-PRACTICES.md: convenzioni Python (no path hardcoded, ruff, mypy strict) - Aggiornare
AGENTS.md: sezione "Python development" + nota errore #12 N/A in Python - Aggiornare
README.md: quick-start Python + Linux
Hook futuri Fase C: lasciare placeholder "Backend ESXi (futuro)" in
docs/ARCHITECTURE.md e docs/HOST-SETUP.md.
Test / validazione:
- Lettura manuale: un nuovo operatore deve poter setuppare host Linux + lanciare un job seguendo solo
docs/ - Link interni risolvono (
grep_searchsu\[.*\]\(.*\.md\))
Rollback: revert doc da git.
Definizione di fatto step X4:
- Docs aggiornate
- Link interni validi
- Quick-start testato da operatore esterno
4. Hook architetturali per Fase C
Punti di design da rispettare durante A e B per non bloccare un futuro backend ESXi senza implementarlo:
- Protocol
VmBackendinsrc/ci_orchestrator/backends/protocol.py: metodiclone_linked(template, snapshot, name) -> VmHandle,start(vm),stop(vm, hard=True),delete(vm),get_ip(vm, timeout) -> str,list_snapshots(template) -> list[str]. Firma neutra, nessun riferimento avmrun,vmx,esxinei nomi pubblici. VmHandledataclass: campoidopaco (string). Per Workstation contiene il path VMX, per ESXi conterrà il MoRef. Chi consumaVmHandlenon può fare assunzioni sul contenuto.- Selezione backend via config:
config.tomlha sezione[backend]contype = "workstation"(default) o"esxi"(futuro). Factorybackends.load_backend(config)istanzia il backend corretto. Niente import diretto diWorkstationVmrunBackendnei comandi. - Separazione control plane / compute: il control plane (host con
act_runner + Python) non deve assumere che le VM girino in locale.
Le funzioni
vm new/vm cleanupricevono il backend come parametro, non leggono filesystem locale (eccetto perWorkstationVmrunBackendinternamente). - Naming neutro nelle API pubbliche: i sottocomandi CLI sono
vm new,vm remove,vm cleanup(nonvmrun-clone). Le env vars sonoCI_BUILD_VMS,CI_TEMPLATES(nonVMRUN_*). - Credenziali:
CredentialStoreaccetta target name arbitrario. Per ESXi si aggiungerà targetEsxiHostsenza modifiche al codice. - Logging strutturato: include sempre
backend=<type>evm_id=<handle.id>nei log per tracciabilità multi-backend.
Riferimento esplicito: idea-3-esxi-support.md
§3 (interfaccia EsxiBackend) e §4 (mappa operazioni).
5. Cronoprogramma e dipendenze
flowchart TD
P0[0. Prerequisiti] --> A1
A1[A1 Bootstrap + core] --> A2[A2 Script foglia]
A2 --> A3[A3 Pipeline build]
A3 --> A4[A4 Orchestratore + workflow]
A4 --> A5[A5 Test + docs + cleanup]
A1 --> X1[X1 Lint dual-stack]
A1 --> X2[X2 Observability]
A1 --> X3[X3 Credenziali headless]
A3 -.parallelo.-> B1[B1 Setup host Linux]
A4 -.parallelo.-> B1
B1 --> B2[B2 Trasferimento template]
B1 --> B3[B3 Credenziali Linux]
A5 --> B4
B2 --> B4[B4 act_runner systemd]
B3 --> B4
B4 --> B5[B5 Timer systemd]
B5 --> B6[B6 Cutover]
B6 --> B7[B7 Burn-in Linux]
B7 --> B8[B8 Decommissioning Win]
A5 --> X4[X4 Docs consolidate]
B8 --> X4
X3 --> B3
X1 --> A5
X2 --> B4
6. Matrice rischi consolidata
Ogni voce: Rischio — Fase / Severità / Mitigazione / Owner action item.
pypsrpedge case con WinRM HTTPS self-signed- Fase: A1 — Severità: Media
- Mitigazione: PoC
wait-readycome primo deliverable A1 prima di committare al resto. - Owner: A1 attività #10.
keyringsotto SYSTEM (act_runner service) non vede credenziali utente- Fase: A1, B3 — Severità: Alta
- Mitigazione: DPAPI machine scope su Win; PoC vault file
age/sopssu Linux in B3. - Owner: X3 attività complete.
- Perdita di know-how errori
AGENTS.md#9–#12 durante refactor- Fase: A5 — Severità: Media
- Mitigazione: ognuno convertito in test pytest prima di rimuovere
il
.ps1corrispondente. - Owner: A5 attività #1.
- Doppio mantenimento PS+Python durante migrazione
- Fase: A2–A4 — Severità: Alta
- Mitigazione: strategia shim minimizza la finestra;
lint.ymldual-stack. - Owner: X1 attività complete.
- Astrazione
VmBackendover-engineered se Fase C non parte mai- Fase: A1 — Severità: Bassa
- Mitigazione: 1 implementazione concreta + Protocol = ~50 LOC extra; costo trascurabile.
- Owner: accettato.
- act_runner cattura male output Python (encoding cp1252)
- Fase: A4 — Severità: Media
- Mitigazione:
PYTHONIOENCODING=utf-8inrunner/config.yaml. - Owner: A4 attività #4.
vmrunsu Linux ha differenze parsing outputlist/getGuestIPAddress- Fase: B1 — Severità: Media
- Mitigazione: test modulo
vmrun.pysu Linux subito in B1. - Owner: B1 attività #3.
- Performance I/O linked clone su ext4 vs NTFS
- Fase: B7 — Severità: Bassa
- Mitigazione: misurare con burn-in B7; eventualmente XFS/BTRFS.
- Owner: B7 attività #5.
vmnet8NAT range diverso → IP collision LAN- Fase: B1 — Severità: Media
- Mitigazione: allineare
192.168.79.0/24in B1. - Owner: B1 attività #5.
- WinRM HTTPS da Linux: TLS 1.2 cipher non supportati dal Windows guest
- Fase: B2 — Severità: Media
- Mitigazione: smoke test prima del cutover; stesso codice già testato in A1 da Win host.
- Owner: B2 attività #7.
- Permessi
/var/lib/ci/build-vms/perci-runner- Fase: B1 — Severità: Media
- Mitigazione: ACL POSIX con
setfacl -d -m. - Owner: B1 attività #8.
secret-toolheadless senza D-Bus session- Fase: B3 — Severità: Alta
- Mitigazione: PoC + fallback file vault
age. - Owner: B3 attività #5.
- Errore
AGENTS.md#11 (machine-id Linux) si ripropone- Fase: B2 — Severità: Bassa
- Mitigazione: già documentato; fix nel template prima dello snapshot.
- Owner: preservato.
- Errore
AGENTS.md#12 (stderr nativa +'Stop')- Fase: A — Severità: N/A
- Mitigazione: sparisce in Python.
- Owner: dichiarato in A5.
- Cutover gap window (job in errore tra stop Win e start Linux)
- Fase: B6 — Severità: Media
- Mitigazione: finestra a zero traffic + Gitea "paused" preventivo.
- Owner: B6 attività #2.
- Decommissioning prematuro elimina rollback
- Fase: B8 — Severità: Media
- Mitigazione: gating ≥1 settimana + ≥1 mese spento prima di riallocare.
- Owner: B8 attività #3.
- (integrazione A↔B) Shim PS che assume path Windows si rompe in Fase B
- Fase: A2, B6 — Severità: Media
- Mitigazione: shim deve invocare Python via path da env
(
%CI_PYTHON_VENV%), non hardcodedF:\. - Owner: A2 attività #7.
- (integrazione A↔B)
config.tomlcon default Win-only blocca B1- Fase: A1, B1 — Severità: Bassa
- Mitigazione: A1 deve già includere
[paths.linux]con default POSIX. - Owner: A1 attività #4.
- (integrazione A↔B) Test pytest assumono
Path('F:\\CI')e falliscono su Linux- Fase: A1, B1 — Severità: Media
- Mitigazione: fixture parametrizzata
tmp_pathin tutti i test, no path hardcoded. - Owner: A1 attività #11.
- Licenza Workstation Pro Linux cambia post-Broadcom
- Fase: B1 — Severità: Bassa
- Mitigazione: free per uso personale e commerciale dal 2024; monitorare; piano B = KVM (rimandato).
- Owner: B1 monitoring continuo.
7. Definizione di "fatto" globale (A+B)
- Tutti gli script
scripts/*.ps1portati a Python o ridotti a shim (eccettotemplate/*.ps1eInstall-CIToolchain-*.ps1che restano PS by design) Invoke-CIJob.ps1non più referenziato nei workflow YAMLpytestverde con coverage ≥70% susrc/ci_orchestrator/ruff check+mypy --strictpuliti su tuttosrc/lint.ymldual-stack (PSSA per PS legacy + ruff/mypy/pytest per Python) PASS- act_runner gira come
act-runner.servicesystemd su Linux Mint - Tutti i template VM (
WinBuild2025,WinBuild2022,LinuxBuild2404) operativi dal nuovo host con snapshot integri - Workflow
build-nsInnoUnp.ymlmatrix Win+Linux PASS dal nuovo host - Burn-in capacity 4 job concorrenti × 10 round PASS, tempi entro ±20% baseline Windows
- Storage migrato a
/var/lib/ci/, host Windows spento come rollback - Tutti i timer systemd attivi e schedulati equivalenti ai Task Scheduler Windows
- Credenziali guest accessibili da
ci-runnerheadless (strategia documentata) README.md,AGENTS.md,docs/ARCHITECTURE.md,docs/HOST-SETUP.md,docs/CI-FLOW.md,docs/RUNBOOK.mdaggiornati allo stato finale- ≥1 settimana di esercizio in produzione su host Linux senza incidenti
- Backup
F:\CI\archiviato e validato - Hook §4 (Protocol
VmBackend, factory backend, naming neutro) presenti nel codice e referenziati indocs/ARCHITECTURE.md
7.1 Auto-check
Domande di auto-verifica a cui il documento risponde "sì":
- Ogni voce della checklist master compare almeno una volta come
- [ ]dentro lo step corrispondente? Sì: voci[A1]…[A5]mappate alle attività in §1,[B1]…[B8]in §2,[X1]…[X4]in §3. - Ogni step
A<n>/B<n>/X<n>ha tutte le sottosezioni richieste? Sì: Obiettivo, Input/dipendenze, Attività, Hook futuri Fase C, Test/validazione, Rollback, Definizione di fatto presenti per ogni step (B*: in più rischi specifici). - I 12 errori frequenti di
AGENTS.mdsono stati indirizzati o dichiarati non applicabili?- #1 (sintassi PS 7+): si applica solo agli shim PS residui in A2/A3 — vincolo PS 5.1 mantenuto
- #2 (
$LASTEXITCODEnon controllato): N/A in Python (subprocess.returncodeesplicito) - #3 (path hardcoded): risolto da A1 attività #4 (env vars +
config.toml) - #4 (mix branch WinRM/SSH): risolto dalla separazione
transport/winrm.pyvstransport/ssh.py - #5 (WinRM verso Linux): impossibile per design — backend selezionato per
guest_os - #6 (seed ISO Linux): preservato nei template, non toccato in A/B
- #7 (
[ordered]@{}PS 2): N/A - #8 (
ForEach-Object -Parallel): N/A in Python (concorrenza viaconcurrent.futuresoasynciose serve) - #9 (snapshot powered-off): coperto da test pytest in A5 + procedura B2 attività #1
- #10 (
vmrun getGuestIPAddress): coperto da test pytest in A1 attività #11 - #11 (machine-id Linux): preservato nel template, citato in B2 e in §6 rischi
- #12 (stderr nativa +
'Stop'): N/A in Python, dichiarato in A5 e §6
- Gli hook §4 sono coerenti con
idea-3-esxi-support.md§3? Sì: ProtocolVmBackendha esattamente i metodiclone_linked,start,stop,delete,get_ipprevisti dalla firmaEsxiBackend; selettore[backend]inconfig.tomlcome da §3 di idea-3.
8. Riferimenti
- AGENTS.md — vincoli host, PowerShell 5.1, errori frequenti #1–#12
- plans/ideas-overview.md — razionale ordine fasi, criteri "fase completata", rollback
- plans/idea-1-python-rewrite.md — Fase A: design, mappa traduzione, step A1–A5, layout repo, rischi
- plans/idea-2-linux-host.md — Fase B: hypervisor, mappa adattamento, step B1–B8, rischi
- plans/idea-3-esxi-support.md — Fase C: §2 architettura, §3 backend pyVmomi (solo per hook)
- docs/ARCHITECTURE.md — layout attuale del sistema
- docs/CI-FLOW.md — flusso job CI corrente
- docs/HOST-SETUP.md — setup host Windows attuale
- docs/RUNBOOK.md — operatività corrente
- scripts/ — inventario PowerShell da portare/preservare
- template/ — script provisioning template (preservati in PS)