From 962a69d287a4c6807a32ad459596a91980558d83 Mon Sep 17 00:00:00 2001 From: Simone Date: Sun, 7 Jun 2026 20:15:10 +0200 Subject: [PATCH] docs: record Phase C cutover; close $IsWindows shim bug (Phase C9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CLAUDE.md: Linux host no longer runs pwsh; PS 5.1 constraints scoped to Windows guests + Windows runner. - RUNBOOK: add "Phase C — Python equivalents" table; §6-§13 benchmark records kept as historical provenance. - TODO §3.7: close $IsWindows shim bug (resolved-by-replacement — bench run bypasses the shim). - plans: mark C1-C8 done + docs C9; flag operational cutover/uninstall pending. Co-Authored-By: Claude Opus 4.8 --- CLAUDE.md | 4 + TODO.md | 15 +- docs/RUNBOOK.md | 24 +++- plans/idea-3-implementation-plan.md | 185 ++++++++++++++++++++++++ plans/idea-3-powershell-removal.md | 213 ++++++++++++++++++++-------- 5 files changed, 373 insertions(+), 68 deletions(-) create mode 100644 plans/idea-3-implementation-plan.md diff --git a/CLAUDE.md b/CLAUDE.md index 06674e4..740494b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,6 +10,8 @@ Self-hosted CI/CD system running on a Windows 11 host with VMware Workstation. E **Current state**: The Python orchestrator (`src/ci_orchestrator/`) is the production entry point. The PowerShell scripts in `scripts/` are 3-line shims that forward to the Python CLI. All new logic goes in Python. +**Phase C (PowerShell-free Linux host)**: the manual burn-in / benchmark / smoke / host-validation / credential-setup tooling that used to run on the Linux host via `pwsh` is now native Python: `bench run`, `bench measure`, `smoke run`, `validate host`, `validate guest`, `creds set`. The Linux host no longer needs `pwsh` for any operation — it may be uninstalled (`setup-host-linux.sh` no longer installs it by default; opt in with `--with-pwsh`). PSScriptAnalyzer lint runs on a Windows runner on-demand, not on the Linux runner. + --- ## Development Commands @@ -115,6 +117,8 @@ The config file is auto-discovered at `$CI_ROOT/config.toml` (Linux default: `/v ## PowerShell 5.1 Constraints +These constraints apply to the `.ps1` / `.psm1` scripts that still run **inside Windows guests** and on the optional Windows runner. The **Linux host itself no longer runs any `.ps1`** (Phase C) — do not add PowerShell to the Linux host orchestration path. + All `.ps1` / `.psm1` scripts run on **Windows PowerShell 5.1** (not Core/7). Every script must start with: ```powershell diff --git a/TODO.md b/TODO.md index 8911adb..c8dcc82 100644 --- a/TODO.md +++ b/TODO.md @@ -849,17 +849,18 @@ liberi su 63.7). **Mitigazioni**: ridurre vCPU/VM (lega a §3.5), `-Parallelism alzare connect-timeout pypsrp + retry a livello job in `_windows_build`. **Rifiori quando**: si vuole concorrenza 4× Windows-guest affidabile su host Windows. -### 3.7 [P3] [ ] Shim Invoke-CIJob.ps1 — `$IsWindows` non guardato (PS 5.1 + StrictMode) +### 3.7 [P3] [x] Shim Invoke-CIJob.ps1 — `$IsWindows` non guardato (PS 5.1 + StrictMode) — RISOLTO 2026-06-07 (Fase C) File: [scripts/Invoke-CIJob.ps1:37](scripts/Invoke-CIJob.ps1). `if ($null -ne $IsWindows -and $IsWindows -eq $false)` dereferenzia `$IsWindows`, inesistente su Windows PowerShell 5.1 → con `Set-StrictMode -Version Latest` -lancia `VariableIsUndefined` e il job fallisce subito. Si manifesta solo quando lo -shim gira sotto PS 5.1 **senza** `CI_VENV_PYTHON` settato (il ramo auto-detect). -Produzione setta `CI_VENV_PYTHON` via runner, quindi non si vede; colpisce invocazioni -manuali/burn-in. **Fix**: guardare con `if ((Test-Path variable:IsWindows) -and -not $IsWindows)` -o `Get-Variable IsWindows -EA SilentlyContinue`. -**Workaround attuale**: esportare `CI_VENV_PYTHON` prima di lanciare lo shim. +lancia `VariableIsUndefined` e il job fallisce subito. Si manifestava solo quando lo +shim girava sotto PS 5.1 **senza** `CI_VENV_PYTHON` settato (il ramo auto-detect), +cioè nelle invocazioni manuali/burn-in. +**Risoluzione**: il percorso burn-in manuale non passa più dallo shim — `bench run` +(Fase C) lancia i job concorrenti in-process via `concurrent.futures`, senza +`Start-Job`/`pwsh`/`$IsWindows`. Lo shim resta solo per il runner Windows, dove +`CI_VENV_PYTHON` è sempre settato. Il bug non è più raggiungibile sull'host Linux. ### 8.1 [P3] [ ] Mirror GitHub Actions su Gitea locale (rimuovi dipendenza github.com) File: [.gitea/workflows/lint.yml](.gitea/workflows/lint.yml), Gitea (org `actions`). diff --git a/docs/RUNBOOK.md b/docs/RUNBOOK.md index 76c1215..5bed577 100644 --- a/docs/RUNBOOK.md +++ b/docs/RUNBOOK.md @@ -738,8 +738,28 @@ running §11–§13; required for any Windows-host orchestration: `Invoke-CIJob.ps1` interactively. The shim's auto-detect branch dereferences `$IsWindows`, which is undefined under Windows PowerShell 5.1 + `StrictMode` and throws `VariableIsUndefined`; setting `CI_VENV_PYTHON` skips that branch. - Production sets it via the runner (`CI_PYTHON_LAUNCHER`). A code fix to guard - the `$IsWindows` reference is tracked in `TODO.md`. + Production sets it via the runner (`CI_PYTHON_LAUNCHER`). *Note (Phase C): on + the Linux host this is moot — burn-in now runs via `bench run` (below), which + never touches the shim.* + +### Phase C — Python equivalents (run these on the Linux host; no `pwsh`) + +The manual `.ps1` procedures above are superseded by native CLI commands. Invoke +as the service user (`sudo -u ci-runner /opt/ci/venv/bin/python -m ci_orchestrator ...`): + +| Old PowerShell (Linux host) | Python command | +| --------------------------------------------------- | ------------------------------------------------ | +| `Test-CapacityBurnIn.ps1 -Parallelism 4 -Rounds 10` | `bench run --concurrency 4 --rounds 10 --guest-os linux` | +| `Measure-CIBenchmark.ps1 -Iterations N` | `bench measure --iterations N --guest-os linux` | +| `Test-Smoke.ps1 -GuestOS Linux` | `smoke run --guest-os linux` | +| `Validate-HostState.ps1` (host checks) | `validate host` | +| `Test-CIGuestWinRM.ps1` (transport diag) | `validate guest --host [--winrm\|--ssh]` | +| `Set-CIGuestCredential.ps1` | `creds set --target BuildVMGuest --user --password-stdin` | + +`bench run` writes a JSON report to `CI_ARTIFACTS/bench/`; `bench measure` appends +to `benchmark.jsonl` (same fields as before, so the §6–§13 trend lines stay +comparable). The benchmark records in §6–§13 above are kept as historical +provenance — they were captured with the `.ps1` tools before the cutover. --- diff --git a/plans/idea-3-implementation-plan.md b/plans/idea-3-implementation-plan.md new file mode 100644 index 0000000..d5250ed --- /dev/null +++ b/plans/idea-3-implementation-plan.md @@ -0,0 +1,185 @@ +# Fase C — Piano di applicazione (eliminazione `pwsh` dall'host Linux) + +> Piano operativo per [idea-3-powershell-removal.md](idea-3-powershell-removal.md). +> Generato il 2026-06-07. Eseguire i passi **in ordine**: ogni passo è +> autonomo, testabile e con rollback banale (i `.ps1` non vengono rimossi +> finché C9 non chiude il burn-in di validazione). + +--- + +## Summary — checklist master + +Stato passi (spunta man mano): + +- [x] **C1** — Scaffolding gruppi CLI `bench` / `smoke` / `validate` / `creds` + test skeleton +- [x] **C2** — `bench run`: porting burn-in concorrenza (`Test-CapacityBurnIn` + `Start-BurnInTest*`) +- [x] **C3** — `bench measure`: porting timing fasi (`Measure-CIBenchmark`) + retire `_Common.psm1`/`_Transport.psm1`/`_Common.Tests.ps1` +- [x] **C4** — `smoke run`: porting smoke + build E2E (`Test-Smoke`, `Test-*Build-Linux`) +- [x] **C5** — `validate host`: equivalente Linux di `Validate-HostState` +- [x] **C6** — `creds set` (rimpiazza `Set-CIGuestCredential`) + `validate guest` (diag transport) +- [x] **C7** — Lint CI: risolvere dipendenza `pwsh` di `lint.yml` (decisione §4 dell'analisi) +- [x] **C8** — `setup-host-linux.sh`: `pwsh` non più installato di default (`--with-pwsh` opt-in) +- [~] **C9** — Docs ✅ + cutover/burn-in ≥1 settimana ⏳ + disinstallazione `pwsh` ⏳ (operativi, vedi sotto) + +**Definition of done globale**: tutti i criteri del §7 dell'analisi +verificati; `pwsh` rimosso dall'host Linux; pytest verde ≥90%. + +--- + +## Convenzioni trasversali (valgono per ogni passo) + +- Codice nuovo solo in `src/ci_orchestrator/`. `click.echo`, no `print()`, + no `subprocess(..., shell=True)`, type hints completi, `mypy --strict`. +- Importare **solo** `backends.load_backend` + transport esistenti + (`transport/ssh.py`, `transport/winrm.py`). Mai `WorkstationVmrunBackend`. +- Ogni nuovo comando ha test in `tests/python/test_commands_.py` + con backend e transport mockati (no VM reali in unit test). +- Gate coverage 90% per ogni PR. `ruff` + `mypy --strict` verdi. +- Un passo = una PR. Non toccare il branch WinRM e SSH nello stesso commit + (gotcha CLAUDE.md): se un comando ha entrambi i rami, separarli o + isolare dietro `if guest_os == "linux"`. + +--- + +## C1 — Scaffolding + +**Obiettivo**: registrare i gruppi vuoti e i file, senza logica reale, +per fissare l'interfaccia CLI e i test. + +- [ ] Creare `commands/bench.py` con `@click.group("bench")` e i comandi + stub `run` / `measure` (firma completa, corpo `raise NotImplementedError` + dietro `--dry-run` per i test). +- [ ] Creare `commands/smoke.py` (`@click.group("smoke")` → `run`). +- [ ] Creare `commands/validate.py` (`@click.group("validate")` → `host`, `guest`). +- [ ] Creare `commands/creds.py` (`@click.group("creds")` → `set`). +- [ ] Registrarli in `__main__.py` (`cli.add_command(...)`). +- [ ] `tests/python/test_commands_bench.py` ecc.: verificare `--help` e + registrazione comandi (smoke test CLI). +- **Done**: `python -m ci_orchestrator bench --help` ecc. funzionano; + pytest verde. + +## C2 — `bench run` (burn-in concorrenza) + +**Sostituisce**: `Test-CapacityBurnIn.ps1`, `Start-BurnInTest.ps1`, +`Start-BurnInTest-Linux.ps1`. + +- [ ] Implementare il lancio di N job concorrenti **in-process** + (`concurrent.futures` su `commands/job.py`) o come N subprocess del + CLI Python — **niente `Start-Job`/`pwsh`/`$IsWindows`** (chiude il bug + shim f22b142). +- [ ] Per ogni round, asserire: tutti i job exit 0; nessuna clone-dir + orfana in `CI_BUILD_VMS`; nessun `vm-start.lock` stale (>5 min). +- [ ] Report JSON in `CI_ARTIFACTS/bench/` + tabella riepilogo a video. +- [ ] Default lab: `--concurrency 4 --rounds 10`, build-command no-op + (`ping`/`sleep ~30s`) come negli wrapper. +- [ ] Test: round con job mockati (PASS/FAIL), rilevamento orfani e lock. +- **Done**: output equivalente a `Test-CapacityBurnIn.ps1` su Win e Linux. + +## C3 — `bench measure` (timing fasi) + retire moduli PS + +**Sostituisce**: `Measure-CIBenchmark.ps1` (+ `_Common.psm1`, +`_Transport.psm1`, `tests/_Common.Tests.ps1`). + +- [ ] Misurare fasi clone / start / IP acquire / transport ready / destroy + via `load_backend` + transport, su 1+ VM effimere. +- [ ] Append a `benchmark.jsonl` (stesso formato, per continuità trend). +- [ ] Test con backend/transport mockati che simulano i tempi di fase. +- [ ] **Rimuovere** `scripts/_Common.psm1`, `scripts/_Transport.psm1`, + `tests/_Common.Tests.ps1` (nessun altro consumatore — verificato). +- [ ] Aggiornare `lint.yml` se elencava esplicitamente quei path. +- **Done**: `bench measure` riproduce le metriche; moduli PS rimossi; + pytest verde. + +## C4 — `smoke run` + +**Sostituisce**: `Test-Smoke.ps1`, `Test-Ns7zipBuild-Linux.ps1`, +`Test-NsinnounpBuild-Linux.ps1` (e i corrispettivi Win restano per fallback). + +- [ ] `smoke run` esegue un job no-op (marker file) end-to-end e verifica: + exit 0, dir artifact creata, evento `job-success` nel JSONL. +- [ ] `--guest-os windows|linux`; preset build-command per le build reali + ns7zip / nsinnounp (opzionali, dietro flag o `--build-command`). +- [ ] Test: pipeline mockata, asserzioni su artifact dir + evento. +- **Done**: `smoke run --guest-os linux` e `--guest-os windows` passano. + +## C5 — `validate host` + +**Nuovo** equivalente Linux (non porting 1:1 di `Validate-HostState.ps1`). + +- [ ] Verificare: `vmrun` presente/eseguibile; snapshot template presenti + (`BaseClean` / `BaseClean-Linux`); keyring file-based leggibile + (`keyring.get_credential`); permessi `/var/lib/ci/...`; unit + `act-runner` / `ci-*` attive (`systemctl is-active`). +- [ ] Exit 0 = tutto ok; non-zero + report per check fallito. +- [ ] Test: ogni check mockato (pass/fail) → exit code corretto. +- **Done**: `validate host` esce 0 su host configurato. + +## C6 — `creds set` + `validate guest` + +**Sostituisce**: `Set-CIGuestCredential.ps1` (host Linux) e +`Test-CIGuestWinRM.ps1` (parte diagnostica host-side). + +- [ ] `creds set --target ... --user ... --password-stdin`: scrive nel + backend `keyring` letto da `ci_orchestrator` + (`keyrings.alt.file.PlaintextKeyring`). Password mai su command line. +- [ ] `validate guest --host IP [--winrm|--ssh]`: tenta connessione + + comando banale, riporta causa esplicita su fallimento (la sonda + `is_ready` ingoia gli errori — vedi gotcha). +- [ ] Test: scrittura/lettura credenziale (keyring mockato); diag con + transport mockato (connect ok / auth fail / connect fail). +- **Done**: credenziali settabili e transport diagnosticabile senza `pwsh`. + +## C7 — Lint CI (decisione `pwsh`) + +**Risolve dipendenza #5**. Applicare l'opzione scelta nel §4 dell'analisi. + +- [ ] Decidere: (1) spostare job `pssa` su runner Windows / on-demand + [consigliata]; (2) isolare `pwsh` nel solo runner di lint; (3) + rimuovere PSSA da CI. +- [ ] Aggiornare `.gitea/workflows/lint.yml` di conseguenza. +- [ ] Verificare che nessun job che gira su `runs-on: linux-build` invochi + più `shell: pwsh`. +- **Done**: pipeline lint verde senza `pwsh` su runner Linux. + +## C8 — `setup-host-linux.sh` + +- [ ] Invertire lo step 6: non installare `pwsh` di default; aggiungere + flag `--with-pwsh` per opt-in (mantenere `--skip-pwsh` come alias + no-op deprecato o rimuoverlo). +- [ ] Aggiornare l'help e gli esempi d'uso nello script. +- **Done**: `setup-host-linux.sh` su host pulito non installa `pwsh`. + +## C9 — Docs, cutover, burn-in, disinstallazione + +Parte **docs** completata in codice (2026-06-07). Parte **operativa** (cutover ++ burn-in di validazione + disinstallazione) resta da eseguire sull'host reale. + +- [x] `docs/RUNBOOK.md`: aggiunta tabella «Phase C — Python equivalents» + nota + che i record §6–§13 restano provenance storica; op-note §13 punto 3 aggiornato. +- [x] `CLAUDE.md`: nota "host Linux non usa più `pwsh`" (constraints PS 5.1 + restano per script guest Windows + runner Windows). +- [x] `TODO.md`: chiusa voce bug shim `$IsWindows` (§3.7, risolto-per-rimpiazzo). +- [ ] **Cutover** (operativo): usare esclusivamente i comandi Python per ≥1 + settimana (burn-in `bench run` periodico + smoke), `pwsh` ancora installato. +- [ ] **Burn-in verde ≥1 settimana** → `sudo apt-get remove powershell` sull'host + Linux. +- [ ] Riconfermare i criteri "C done" del §7 dell'analisi sull'host reale. +- **Done (codice/docs)**: porting C1–C8 mergeabile, suite verde ≥90%. +- **Done (operativo, pendente)**: `pwsh` assente dall'host dopo burn-in verde. + +--- + +## Ordine consigliato e parallelizzabilità + +`C1` prima di tutto. `C2`–`C6` indipendenti tra loro (parallelizzabili in +PR separate, ognuna sopra C1). `C3` deve precedere la rimozione dei moduli +`.psm1`. `C7` e `C8` indipendenti dai porting. `C9` ultimo, dopo che +C2–C8 sono merge-ati e il burn-in di validazione è verde. + +## Rischi + +- **WinRM 4× concorrenza instabile** (TODO noto): può far fallire `bench run` + su Windows con concorrenza alta — non è regressione del porting. Tarare + `--concurrency` e annotare. +- **Equivalenza output**: validare A/B (`.ps1` vs Python) finché `pwsh` è + ancora installato (prerequisito §6) prima della disinstallazione in C9. diff --git a/plans/idea-3-powershell-removal.md b/plans/idea-3-powershell-removal.md index d142b8d..bbc8e5b 100644 --- a/plans/idea-3-powershell-removal.md +++ b/plans/idea-3-powershell-removal.md @@ -1,103 +1,198 @@ # Fase C — Eliminazione dipendenza PowerShell dall'host Linux -> **Stato**: **pianificata**, da avviare dopo che Fase B è stabile in -> produzione (≥1 settimana di burn-in, Passo 8 completato). -> Vedi [overview](ideas-overview.md). +> **Stato**: **codice completato** (C1–C8 implementati + docs C9, 2026-06-07); +> resta solo la parte **operativa** di C9 (cutover ≥1 settimana + `apt-get +> remove powershell` sull'host reale). +> Vedi [overview](ideas-overview.md) e il +> [piano di applicazione](idea-3-implementation-plan.md) (checklist operativa). +> +> **Analisi rifatta il 2026-06-07** sullo stato reale del repo (la versione +> precedente proponeva gruppi CLI inesistenti e ometteva metà della +> superficie `pwsh`). --- ## 1. Obiettivo Rendere l'host Linux completamente indipendente da PowerShell Core (`pwsh`). -Dopo la Fase B l'orchestrazione ordinaria (job, timer, monitoring, backup) -è già Python puro. Rimangono script PS usati per test manuali, burn-in e -validazione host che richiedono `pwsh` installato. +Dopo la Fase B l'orchestrazione ordinaria (job, timer, monitoring, backup, +retention, deploy template) è già Python puro. Restano script `.ps1` +eseguiti **manualmente** sull'host Linux con `pwsh` per: burn-in di +capacità, benchmark di timing, smoke/E2E test, validazione stato host e +setup credenziali. Inoltre il job di lint CI usa `shell: pwsh`. **C done**: nessuno script in `scripts/*.ps1` viene più eseguito -direttamente sull'host Linux; `pwsh` può essere disinstallato senza -impatti operativi. I `.ps1` rimangono nel repo come riferimento storico -e per il runner Windows (fallback dual-boot). +direttamente sull'host Linux, e nessun job CI che gira su un runner Linux +richiede `pwsh`; `pwsh` può essere disinstallato dall'host Linux senza +impatti operativi. I `.ps1` restano nel repo come riferimento storico e +per l'eventuale runner Windows (fallback dual-boot Passo 9). --- -## 2. Script da portare +## 2. Superficie `pwsh` reale sull'host Linux -| Script PS | Uso attuale (Linux host) | Target Python | -| ---------------------------- | -------------------------------- | -------------------------------------------------------- | -| `Test-CapacityBurnIn.ps1` | burn-in B7: 4 job × 10 round | `python -m ci_orchestrator bench run` | -| `Start-BurnInTest.ps1` | burn-in Windows single-shot | `python -m ci_orchestrator bench run --guest-os windows` | -| `Start-BurnInTest-Linux.ps1` | burn-in Linux single-shot | `python -m ci_orchestrator bench run --guest-os linux` | -| `Validate-HostState.ps1` | verifica stato host pre-cutover | `python -m ci_orchestrator validate host` | -| `Measure-CIBenchmark.ps1` | raccolta metriche timing | integrato in `bench run --collect-metrics` | -| `Test-Smoke.ps1` | smoke end-to-end manuale | `python -m ci_orchestrator smoke run` | -| `Test-CapacityBurnIn.ps1` | già referenziato in B7 checklist | vedi sopra | +Lo stato CLI attuale (`src/ci_orchestrator/__main__.py`) espone i gruppi: +`wait-ready`, `vm`, `build`, `artifacts`, `monitor`, `report`, +`retention`, `template`, `job`. **Non esistono** gruppi `bench`, +`smoke`, `validate`, `creds` — vanno creati. -Script **non da portare** (girano solo su Windows o sono già shim): -- `Register-CIScheduledTasks.ps1` — Windows Scheduler, non serve su Linux -- `Invoke-RetentionPolicy.ps1` / `Backup-CITemplate.ps1` — già portati in B5 -- Tutti gli script shim in `scripts/` — 3 righe che chiamano Python, restano -- Script guest (`Install-CIToolchain-*.ps1`, `Prepare-*.ps1`, ecc.) — girano - dentro le VM Windows, non sull'host +### 2.1 Cosa blocca davvero la disinstallazione di `pwsh` + +| # | Dipendenza | Dove | Tipo | +| - | ---------- | ---- | ---- | +| 1 | Burn-in di capacità (4 job × N round) | `Test-CapacityBurnIn.ps1` + wrapper `Start-BurnInTest-Linux.ps1` (lanciato via `sudo -u ci-runner -H pwsh …`, vedi PhaseB-user-checklist §B7) | host Linux, manuale | +| 2 | Benchmark timing fasi (clone/start/IP/transport/destroy) | `Measure-CIBenchmark.ps1` — **unico consumatore** di `scripts/_Common.psm1` e `scripts/_Transport.psm1` | host Linux, manuale | +| 3 | Smoke end-to-end | `Test-Smoke.ps1 -GuestOS Linux` | host Linux, manuale | +| 4 | Build E2E reali (validazione pipeline) | `Test-Ns7zipBuild-Linux.ps1`, `Test-NsinnounpBuild-Linux.ps1` | host Linux, manuale | +| 5 | Lint CI (PSScriptAnalyzer) | `.gitea/workflows/lint.yml` → `shell: pwsh`, `runs-on: linux-build` | runner Linux, automatico | +| 6 | Installazione `pwsh` di default | `setup-host-linux.sh` step 6/6 (opt-out `--skip-pwsh`) | provisioning host | +| 7 | Pester (test dei moduli PS) | `tests/_Common.Tests.ps1` | host/CI, manuale | +| 8 | Setup credenziali guest | `Set-CIGuestCredential.ps1` (equivalente Linux mancante in CLI) | host Linux, manuale | + +### 2.2 Script Windows-only (NON bloccano `pwsh` su Linux) + +Hardcodano `F:\`, richiedono `#Requires -RunAsAdministrator` o usano il +Windows Credential Manager: **non girano sull'host Linux**, quindi non +vanno portati per l'obiettivo di Fase C. Restano per il runner Windows. + +- `Validate-HostState.ps1` — logica Credential Manager/ACL NTFS, path `F:\`. + Serve però un **equivalente Linux** (`validate host`), non un porting 1:1. +- `Set-CIGuestCredential.ps1` — scrive nel vault SYSTEM via scheduled task. + Su Linux le credenziali stanno in `keyrings.alt.file.PlaintextKeyring` + (B3, `secret-tool` scartato). Serve un `creds set` Linux-nativo. +- `Test-CIGuestWinRM.ps1` — diagnostica WinRM, `RunAsAdministrator`. +- `Start-BurnInTest.ps1`, `Test-NsinnounpBuild.ps1`, `Test-E2E-Section3.3.ps1` + — wrapper Windows (path `F:\`, template WinBuild). + +### 2.3 Già fuori scope (confermato) + +- `Register-CIScheduledTasks.ps1` — Windows Scheduler. +- `Invoke-RetentionPolicy.ps1` / `Backup-CITemplate.ps1` — portati in B5 + (`retention run` / `template backup`). +- Shim in `scripts/` (3 righe → Python) — restano. +- Script guest (`Install-CIToolchain-*`, `Prepare-*`, `Deploy-*`, + `template/guest-setup/windows/*`) — girano dentro le VM, non sull'host. --- -## 3. Architettura prevista - -Nuovi sub-comandi CLI sotto due nuovi gruppi: +## 3. Architettura prevista (nuovi gruppi CLI) ``` -python -m ci_orchestrator bench run --guest-os windows|linux - --template - --concurrency N - --rounds N - --collect-metrics +python -m ci_orchestrator bench run --guest-os windows|linux + --concurrency N # default 4 + --rounds N # default 10 + --build-command "..." + --json-out PATH +python -m ci_orchestrator bench measure --guest-os windows|linux + --iterations N + --json-out PATH # append a benchmark.jsonl -python -m ci_orchestrator validate host # verifica stato host -python -m ci_orchestrator smoke run # end-to-end smoke test +python -m ci_orchestrator smoke run --guest-os windows|linux + [--build-command "..."] # default: marker no-op + +python -m ci_orchestrator validate host # vmrun, snapshot template, keyring, + # permessi dir, act-runner systemd attivo +python -m ci_orchestrator validate guest --host IP [--winrm|--ssh] # diag transport + +python -m ci_orchestrator creds set --target BuildVMGuest --user ... [--password-stdin] ``` -Il gruppo `bench` sostituisce i tre script `*BurnIn*.ps1` con logica -unificata: avvia N clone in parallelo, misura tempo per job, confronta -con baseline, emette un report JSON in `CI_ARTIFACTS/bench/`. +- **`bench run`** unifica `Test-CapacityBurnIn.ps1` + `Start-BurnInTest*.ps1`: + lancia N job concorrenti (riusando `commands/job.py` in-process o come + subprocess del CLI Python — niente `Start-Job`/`pwsh`), per R round, + e asserisce dopo ogni round: tutti exit 0, nessuna clone-dir orfana in + `CI_BUILD_VMS`, nessun `vm-start.lock` stale. Emette report JSON. +- **`bench measure`** porta `Measure-CIBenchmark.ps1` (fasi + clone/start/IP/transport/destroy) → consente di **retirare** + `_Common.psm1` e `_Transport.psm1` (nessun altro consumatore). +- **`smoke run`** porta `Test-Smoke.ps1` + i `Test-*Build-Linux.ps1` + (build E2E reali come preset opzionali del build-command). +- **`validate host`** è un nuovo equivalente Linux (non un porting di + `Validate-HostState.ps1`): verifica `vmrun`, presenza snapshot template, + leggibilità keyring file-based, permessi `/var/lib/ci/...`, unit + `act-runner`/`ci-*` attive. +- **`creds set`** sostituisce `Set-CIGuestCredential.ps1` su Linux usando + direttamente il backend `keyring` letto da `ci_orchestrator`. -Il gruppo `validate` porta `Validate-HostState.ps1`: verifica vmrun, -template snapshot, credenziali, permessi directory, act-runner attivo. +**Vincolo di design**: i nuovi comandi devono importare +`backends.load_backend` e i transport esistenti, **mai** +`WorkstationVmrunBackend` diretto (rotta ESXi Fase D aperta). --- -## 4. Aggiornamenti collaterali +## 4. Nodo aperto — lint PSScriptAnalyzer (dipendenza #5) +Gli script `.ps1` restano nel repo (guest + fallback Windows), quindi il +lint PSSA ha ancora valore. Ma `lint.yml` lo esegue con `shell: pwsh` su +`runs-on: linux-build`, il che reintroduce `pwsh` sul runner Linux. +**Decisione applicata (C7, 2026-06-07): opzione 1.** Il job `pssa` gira ora su +`runs-on: windows-build`, `shell: powershell` (PS 5.1), con guardia +`if: github.event_name == 'workflow_dispatch'` così non si accoda su push/PR se +il runner Windows non è registrato. Il job `python` resta su `linux-build`. +Nessun job su runner Linux usa più `pwsh`. + +Tre opzioni considerate: + +1. **(Consigliata)** Spostare il job `pssa` su un runner Windows + (`runs-on: windows-build`, `shell: powershell` PS 5.1) — coerente col + fatto che i `.ps1` target sono Windows-native. Richiede un runner + Windows registrato (dual-boot Passo 9) o eseguirlo solo on-demand. +2. Mantenere `pwsh` **solo nel container/runner di lint**, non sull'host + orchestratore: l'obiettivo "host Linux senza pwsh" resta soddisfatto se + il job gira in un runner isolato. Pragmatica ma ambigua sul "C done". +3. Rimuovere il job PSSA da CI e affidare il lint PS a un hook locale + manuale. Riduce copertura. + +--- + +## 5. Aggiornamenti collaterali + +- `setup-host-linux.sh`: invertire il default dello step 6 (no install + `pwsh`; flag `--with-pwsh` per opt-in) o rimuovere lo step. +- `.gitea/workflows/lint.yml`: applicare la decisione del §4. +- `tests/_Common.Tests.ps1`: rimuovere insieme ai moduli `.psm1` quando + `bench measure` li retira (Passo C3); la copertura passa a pytest. +- `CLAUDE.md`: la sezione "PowerShell 5.1 Constraints" resta valida per gli + script guest Windows; aggiungere nota che l'host Linux non usa più `pwsh`. +- `docs/RUNBOOK.md`: aggiornare procedure burn-in / smoke / validazione ai + nuovi comandi Python. - `plans/archived/2026-06-07/PhaseB-user-checklist.md` §B7 (archiviato): - sostituire il comando `pwsh Test-CapacityBurnIn.ps1` con - `python -m ci_orchestrator bench run`. -- `deploy/systemd/README.md`: nessun riferimento a `pwsh`. -- `CLAUDE.md`: rimuovere nota "PowerShell 5.1 Constraints" se non più - rilevante per l'host (rimane valida per gli script guest Windows). -- `docs/RUNBOOK.md`: aggiornare procedure di burn-in e smoke test. + nota che il comando `pwsh Test-CapacityBurnIn.ps1` è superato da + `bench run`. +- `TODO.md`: chiudere la voce shim `$IsWindows` bug (f22b142) — sparisce + con la rimozione di `Start-Job`/`pwsh` nel burn-in. --- -## 5. Prerequisiti +## 6. Prerequisiti - Fase B completata e stabile (≥1 settimana, Passo 8 ✅). +- `pwsh` ancora installato durante il porting (per confronto output A/B). - Nessuna dipendenza da `pwsh` nei timer systemd (già soddisfatta da B5). --- -## 6. Criteri di "C done" +## 7. Criteri di "C done" -- `python -m ci_orchestrator bench run --concurrency 4 --rounds 10` esegue - burn-in completo (Win + Linux) con output equivalente a `Test-CapacityBurnIn.ps1`. -- `python -m ci_orchestrator validate host` esce con 0 su host configurato - correttamente. -- `pwsh --version` può essere assente sull'host Linux senza che nessun - test, timer o script CI fallisca. +- `bench run --concurrency 4 --rounds 10` (Win + Linux) produce esito + equivalente a `Test-CapacityBurnIn.ps1` (stesse asserzioni di cleanup). +- `bench measure` riproduce le fasi di `Measure-CIBenchmark.ps1`; i moduli + `_Common.psm1`/`_Transport.psm1` e `tests/_Common.Tests.ps1` rimossi. +- `smoke run --guest-os linux` e `--guest-os windows` passano. +- `validate host` esce 0 su host configurato correttamente. +- `creds set` sostituisce `Set-CIGuestCredential.ps1` sull'host Linux. +- Decisione §4 applicata: nessun job CI su runner Linux richiede `pwsh`. +- `setup-host-linux.sh` non installa più `pwsh` di default. +- `pwsh --version` può essere assente sull'host Linux senza che alcun + test, timer, job CI o procedura di RUNBOOK fallisca. - Suite pytest ≥90% coverage, verde. --- -## 7. Rollback +## 8. Rollback -I `.ps1` originali rimangono nel repo e funzionano su host Windows o su -Linux con `pwsh` installato. Il rollback è semplicemente reinstallare -`pwsh` e usare gli script esistenti. +I `.ps1` originali restano nel repo e funzionano su host Windows o su +Linux con `pwsh` reinstallato (`setup-host-linux.sh --with-pwsh`). Il +rollback è reinstallare `pwsh` e usare gli script esistenti. Nessuna +distruzione di dati: il porting aggiunge comandi, non rimuove i `.ps1` +finché il burn-in di validazione non è verde.