Files
Simone 0d6486b19b chore(plans): archive Phase A and B completed documents
Moves all A/B closeouts, checklists, idea docs, and implementation plans
to plans/archived/2026-05-23/. Both phases are production-stable.

Active plans remaining in plans/:
  - idea-3-powershell-removal.md  (Phase C — in progress)
  - idea-3-esxi-support.md        (Phase D — future)
  - ideas-overview.md             (roadmap reference)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 19:52:09 +02:00

11 KiB

Fase A — Rewrite in Python

Stato: piano esecutivo (parte committed della roadmap). Prerequisito di Fase B. Vedi overview. Baseline: ~13.000 righe PowerShell 5.1 (41 file .ps1 + 2 .psm1), 2 script bash, 5 workflow Gitea Actions.


1. Obiettivo

Riscrivere host orchestrator + script di gestione VM in Python 3.11+, con design cross-platform fin dall'inizio (la Fase B trasferirà l'host su Linux Mint senza riscrivere il core).

Vincoli mantenuti:

  • runtime act_runner + Gitea (consuma comandi via shell, agnostico al linguaggio)
  • template VM (Windows con WinRM, Linux con SSH)
  • VMware Workstation Pro come hypervisor (vmrun invariato)
  • formato artifact e layout di storage
  • workflow YAML esistenti (gitea/workflows/*.yml) — cambia solo la shell: e il comando invocato

Il punto di ingresso Invoke-CIJob.ps1 viene sostituito da python -m ci_orchestrator job ..., invocato da gitea/actions/local-ci-build/action.yml.


2. Decisioni di design forzate dal target Linux futuro

Per evitare un doppio porting in Fase B, il codice Python deve rispettare da subito queste regole:

Regola Implementazione
Nessun path hardcoded Tutti i path via pathlib.Path + variabili ambiente (CI_ROOT, CI_TEMPLATES, CI_BUILD_VMS, CI_ARTIFACTS, CI_KEYS) con default in un file config.toml.
Path-binari astratti vmrun, ssh, scp cercati via shutil.which() con fallback a env var (VMRUN_PATH); default vmrun.exe su Windows, vmrun su Linux.
Nessuna API Windows-only nel core keyring (cross-platform), paramiko (no openssh client esterno), pypsrp (no New-PSSession).
Astrazione hypervisor Interfaccia VmBackend (Protocol) con metodi clone_linked, start, stop, delete, get_ip. Implementazione iniziale WorkstationVmrunBackend. Hook per EsxiBackend futuro (Fase C).
Astrazione credential store Interfaccia CredentialStore con backend KeyringCredentialStore (default) — funziona uguale su Win Credential Manager e Linux Secret Service.
Logging logging con formatter strutturato; nessun colore ANSI obbligatorio (alcuni runner cattura non li gestiscono).
Path separator nei workflow YAML Lasciare i workflow esistenti per ora; in Fase B si aggiorneranno i path (variabili env già pronte ad assorbire la modifica).

3. Mappa di traduzione

Componente PowerShell Equivalente Python
Invoke-Vmrun (wrapper vmrun.exe su Win / vmrun su Linux) subprocess.run([vmrun, '-T', 'ws', op, *args], check=False, capture_output=True)
New-PSSession + Invoke-Command (WinRM) pypsrp.client.Client(host, username, password, ssl=True, cert_validation=False)
Copy-Item -ToSession / -FromSession pypsrp.client.Client.copy() / fetch()
_Transport.psm1 (ssh.exe / scp.exe) paramiko.SSHClient / paramiko.SFTPClient
Credential Manager (BuildVMGuest, GiteaPAT) keyring.get_credential('BuildVMGuest', None)
Pester pytest + pytest-mock
PSScriptAnalyzer ruff + mypy --strict
Write-Host logging.getLogger(__name__).info(...)
ConvertTo-Json per stato job json.dumps() su dataclass
Workflow YAML invariati in Fase A
Script .sh in gitea/burnin-dummy/ invariati (girano nel guest Linux)

4. Cosa NON viene portato in Python

Gli script template/ (Prepare-*.ps1, Deploy-*.ps1, Install-CIToolchain-*.ps1) restano in PowerShell:

  • girano una tantum (ricostruzione template)
  • sono fortemente legati a Windows (autounattend.xml, registry, sysprep, WinRM enable)
  • gli script Install-CIToolchain-WinBuild*.ps1 girano dentro il guest Windows e PS è il linguaggio nativo lì

In Fase B questi script restano richiamabili da host Linux con pwsh 7 quando serve ricostruire un template Windows (operazione rara, anche manuale).


5. Strategia di migrazione: strangler fig in 4 step

Sequenza obbligata. Ad ogni step, gli script PS non ancora portati continuano a funzionare; la coesistenza è garantita dalla stessa CLI python -m ci_orchestrator chiamata internamente dagli script .ps1 man mano che vengono "scavati".

Step A1 — Bootstrap progetto + moduli core (_Common, _Transport)

Output:

  • pyproject.toml, package src/ci_orchestrator/
  • venv in F:\CI\python\venv\
  • moduli: config.py (env + TOML), vmrun.py (wrapper), winrm.py (pypsrp), ssh.py (paramiko), credentials.py (keyring), backends/workstation.py
  • protocollo VmBackend definito
  • test pytest per ognuno (mock subprocess, paramiko, pypsrp)

Validazione: lint.yml e self-test.yml continuano a passare; nuovi job pytest aggiunti al workflow lint.yml.

Step A2 — Script "foglia" (no state condiviso con orchestratore)

Portare in Python (CLI subcommands python -m ci_orchestrator <cmd>):

  • Wait-VMReadywait-ready
  • Remove-BuildVMvm remove
  • Cleanup-OrphanedBuildVMsvm cleanup
  • Watch-DiskSpacemonitor disk
  • Watch-RunnerHealthmonitor runner
  • Get-CIJobSummaryreport job

Per ognuno: il file .ps1 viene sostituito da uno shim di 3 righe che chiama python -m ci_orchestrator <cmd> ..., così i call site esterni (scheduled tasks, ecc.) non si rompono.

Step A3 — Pipeline di build

Portare in Python:

  • New-BuildVMvm new
  • Invoke-RemoteBuildbuild run
  • Get-BuildArtifactsartifacts collect

Stessa strategia shim per i .ps1 esistenti.

Step A4 — Orchestratore + switch workflow

Portare Invoke-CIJob.ps1python -m ci_orchestrator job. Aggiornare gitea/actions/local-ci-build/action.yml per chiamare Python direttamente (non più via shim, per ridurre overhead startup).

A questo punto i .ps1 shim restano come fallback; eliminarli al primo ciclo di pulizia successivo.

Step trasversale A5 — Test e documentazione

Parallelo agli step A1-A4:

  • ogni feature portata ha test pytest che coprono almeno gli scenari documentati negli "errori frequenti" di AGENTS.md (#9, #10, #11, #12)
  • AGENTS.md aggiornato con sezione "Python development" (venv, stile, pytest, ruff)
  • docs/ARCHITECTURE.md aggiornato con il nuovo layout package
  • README.md aggiornato con setup Python

6. Layout repository post-Fase A

src/ci_orchestrator/
    __init__.py
    __main__.py            # entry point CLI (click)
    config.py              # env vars + config.toml loader
    credentials.py         # CredentialStore protocol + KeyringStore
    backends/
        __init__.py
        protocol.py        # VmBackend Protocol
        workstation.py     # WorkstationVmrunBackend
    transport/
        __init__.py
        winrm.py           # pypsrp client wrapper
        ssh.py             # paramiko client wrapper
    commands/
        job.py             # ex Invoke-CIJob
        vm.py              # new / remove / cleanup
        build.py           # build run
        artifacts.py       # collect
        wait.py            # wait-ready
        monitor.py         # disk / runner
        report.py          # job summary
tests/
    test_backends_workstation.py
    test_transport_winrm.py
    test_transport_ssh.py
    test_commands_job.py
    ...
pyproject.toml
config.example.toml
scripts/                   # PS legacy (shim sottili durante migrazione)
template/                  # PS invariati (provisioning template)
gitea/                     # workflow + actions invariati

7. Rischi e mitigazioni

Rischio Severità Mitigazione
pypsrp ha edge case con WinRM HTTPS self-signed Media PoC su wait-ready come primo deliverable di Step A1 prima di committare al resto.
keyring sotto SYSTEM account (act_runner service) può non vedere credenziali utente Alta Già un problema oggi con Credential Manager; documentare l'uso di credenziali a livello machine (DPAPI machine scope su Win, file vault con age su Linux).
Perdita di know-how degli "errori frequenti" AGENTS.md durante refactor Media Convertire ognuno (#9, #10, #11, #12) in un test pytest prima di rimuovere il .ps1 corrispondente.
Doppio mantenimento durante la migrazione Alta Strategia shim minimizza la finestra; lint.yml deve passare per entrambi i mondi (PSSA + ruff).
Astrazione VmBackend over-engineered se Fase C non parte mai Bassa Una sola implementazione concreta + Protocol = ~50 righe extra; costo trascurabile.
act_runner cattura male l'output Python (encoding UTF-8 vs cp1252 su Win) Media Forzare PYTHONIOENCODING=utf-8 in runner/config.yaml env.

8. Definizione di "fatto" (Fase A)

  • Tutti gli script in scripts/ portati a Python o ridotti a shim
  • Invoke-CIJob.ps1 non più presente nei workflow YAML
  • pytest verde con coverage minima 70% su src/ci_orchestrator/
  • ruff check + mypy --strict puliti su tutto src/
  • lint.yml aggiornato: PSSA per file PS legacy + ruff/mypy per Python
  • Workflow build-ns7zip.yml PASS su matrice Windows + Linux
  • Smoke test (Test-Smoke.ps1 o equivalente Python) PASS
  • Capacity burn-in 4 job concorrenti PASS
  • README.md, AGENTS.md, docs/ARCHITECTURE.md, docs/HOST-SETUP.md aggiornati