docs(todo): merge CONSIGLI backlog, add P0..P3 priorities and rationale

- Unified TODO.md + CONSIGLI.md into single working document
- Added priority levels P0 (security/reliability) through P3 (nice-to-have)
- Added references to new docs/HOST-SETUP.md, WINDOWS-TEMPLATE-SETUP.md,
  LINUX-TEMPLATE-SETUP.md
- Documented Assert-Step validation flow in Fase B
- Updated open items with rationale and priority tagging
This commit is contained in:
Simone
2026-05-09 21:15:43 +02:00
parent d4099b94fd
commit 0f21e61668
+444 -27
View File
@@ -1,6 +1,22 @@
# TODO — Local CI/CD System # TODO — Local CI/CD System
<!-- Last updated: 2026-05-09 — v1.0 taggato, fix post-review applicati --> <!-- Last updated: 2026-05-09 — TODO+CONSIGLI uniti, priorità P0..P3 + rationale per ogni voce aperta -->
> Documento unico di lavoro: roadmap, audit trail dei task completati, e backlog post-v1.0
> con priorità e razionale (assorbito da `CONSIGLI.md`). Le voci aperte sono raggruppate
> per area e marcate **P0..P3**:
>
> **Doc di setup operativi**:
> - [docs/HOST-SETUP.md](docs/HOST-SETUP.md) — bootstrap macchina host (Setup-Host.ps1)
> - [docs/WINDOWS-TEMPLATE-SETUP.md](docs/WINDOWS-TEMPLATE-SETUP.md) — provisioning template Windows (Prepare + Setup-TemplateVM)
> - [docs/LINUX-TEMPLATE-SETUP.md](docs/LINUX-TEMPLATE-SETUP.md) — bozza/TODO template Linux (non implementato)
>
> - **P0** — sicurezza o affidabilità: affrontare prima di estendere il sistema
> - **P1** — operatività quotidiana: automatizzare manutenzione e debug
> - **P2** — performance e qualità del codice
> - **P3** — estensioni nice-to-have
---
## Setup & Infrastructure ## Setup & Infrastructure
@@ -71,13 +87,24 @@
.\Prepare-TemplateSetup.ps1 -VMIPAddress 192.168.79.128 -SkipWindowsUpdate .\Prepare-TemplateSetup.ps1 -VMIPAddress 192.168.79.128 -SkipWindowsUpdate
``` ```
Installa: WinRM config, utente `ci_build`, .NET SDK 10.0, VS Build Tools 2026, Python 3.13.3. Installa: WinRM config, utente `ci_build`, .NET SDK 10.0, VS Build Tools 2026, Python 3.13.3.
Lo script esegue validazione automatica ad ogni passaggio (`Assert-Step`):
- Pre-flight: IP ottetti 0-255, TCP/5985 raggiungibile, Setup-TemplateVM.ps1 presente
- Host WinRM: AllowUnencrypted, TrustedHosts
- Guest prep: `C:\CI` esiste, file copiato, size match
- Post-setup remoto (9 check): WinRM, user+admin, UAC, firewall, .NET, Python, MSBuild, CI dirs
Se qualsiasi check fallisce → script termina con errore → **non prendere lo snapshot**.
- [x] Setup completato con successo (VS Build Tools 2026 exit code 0). - [x] Setup completato con successo (VS Build Tools 2026 exit code 0).
- [x] `Setup-TemplateVM.ps1` ora esegue validazione `Assert-Step` dopo ogni step interno
(WinRM, Firewall, User, UAC, Dirs, Defender, .NET, Python, VS Build Tools, Toolchain,
Cleanup, Final pre-snapshot gate) — throws se qualsiasi check fallisce (2026-05-09)
### Fase C — Spegni e prendi lo snapshot ### Fase C — Spegni e prendi lo snapshot
- [x] VM rimane su **VMnet8 (NAT)** — internet access permanente per build che richiedono pip/nuget/npm - [x] VM rimane su **VMnet8 (NAT)** — internet access permanente per build che richiedono pip/nuget/npm
- [x] Spegni la VM: Start → Shut down - [x] Spegni la VM: Start → Shut down
- [x] Prendi snapshot: VM → Snapshot → Take Snapshot - [x] Prendi snapshot: VM → Snapshot → Take Snapshot
**Nome esatto: `BaseClean`** **Nome esatto: `BaseClean`**
**Prerequisito: `Prepare-TemplateSetup.ps1` deve essere uscito con exit 0** — tutti
gli `Assert-Step` passati, incluso il Final pre-snapshot gate nel guest.
- [x] Lascia la VM spenta — non modificarla mai più dopo questo snapshot - [x] Lascia la VM spenta — non modificarla mai più dopo questo snapshot
### Fase D — Credenziali e config ### Fase D — Credenziali e config
@@ -115,29 +142,412 @@
- [x] Push commit e verificare job in Gitea Actions UI - [x] Push commit e verificare job in Gitea Actions UI
- [x] Verificare artifact scaricabile da Gitea dopo build riuscita - [x] Verificare artifact scaricabile da Gitea dopo build riuscita
- [x] `gitea/workflows/lint.yml` creato — PSScriptAnalyzer su push/PR che toccano `.ps1` (2026-05-09) - [x] `gitea/workflows/lint.yml` creato — PSScriptAnalyzer su push/PR che toccano `.ps1` (2026-05-09)
- [ ] Adattare workflow per altri repository (generalizzare `BUILD_COMMAND` e `ARTIFACT_SOURCE`) - [ ] **[P3] Adattare workflow per altri repository** — generalizzare `BUILD_COMMAND` e `ARTIFACT_SOURCE`.
Vedi anche §6.2 (composite action riutilizzabile).
## Performance & Maintenance ---
- [ ] Benchmark: tempo creazione clone + tempo WinRM readiness ## 1. Sicurezza & hardening
- [ ] Configurare NuGet package cache su shared folder host (vedi OPTIMIZATION.md)
- [ ] Pianificare refresh semestrale snapshot template VM (KMS lease = 180 giorni):
Boot template su VMnet8 (NAT) → `slmgr /ato` → nuovo snapshot BaseClean
- [ ] Monitoraggio Windows Event Log per fallimenti servizio act_runner
- [ ] Configurare retention artifact (pulizia `F:\CI\Artifacts` più vecchi di N giorni)
- [ ] **Scheduled task per `Cleanup-OrphanedBuildVMs.ps1`**
Creare via Task Scheduler o estendere `Setup-Host.ps1`. Suggerito: ogni 6 ore, `-MaxAgeHours 4`.
## Open Bug Fixes ### 1.1 [P0] Migrare WinRM da HTTP/Basic a HTTPS/5986
File: [template/Setup-TemplateVM.ps1:179-198](template/Setup-TemplateVM.ps1), [scripts/Invoke-RemoteBuild.ps1:82-91](scripts/Invoke-RemoteBuild.ps1).
- [ ] **Validazione IP ottetti**`scripts/Invoke-CIJob.ps1` **Motivazione**: la combo `AllowUnencrypted=true` + `Auth/Basic=true` invia credenziali del
`ValidatePattern` attuale accetta `999.x.x.x`. Sostituire con validazione per-ottetto 0-255. `ci_build` (admin) in chiaro su VMnet8. Anche in lab isolato, l'host può essere compromesso
via altri vettori e scrapare la rete VMware. Il piano è già in `docs/BEST-PRACTICES.md §2`;
manca solo l'esecuzione.
- [ ] **Verifica SHA256 installer**`template/Setup-TemplateVM.ps1` **Azioni**:
Python, VS BuildTools, `dotnet-install.ps1` scaricati senza verifica hash. Aggiungere - [ ] Generare cert self-signed nel template *prima* dello snapshot.
`-ExpectedHash` o hash hardcoded per le versioni pinnate. - [ ] Sostituire `New-PSSession -ComputerName $ip` con `-UseSSL -Port 5986` in `Invoke-RemoteBuild.ps1` e `Get-BuildArtifacts.ps1`.
- [ ] Rimuovere `AllowUnencrypted=true` dal `Setup-TemplateVM.ps1`.
- [ ] Tenere `-SkipCACheck` lato host: per cert lab self-signed è accettabile, ma documentare la motivazione inline.
- [ ] Regole firewall — limitare WinRM alla subnet build VM (`192.168.79.0/24` — VMnet8 NAT).
## In-VM Git Clone (Ottimizzazione) ### 1.2 [P0] Restringere `TrustedHosts` lato host
File: probabile `Setup-Host.ps1` (e in vari run setup) — pattern `*` è incompatibile con altri carichi sull'host Windows 11.
Sostituire con la subnet build:
```powershell
Set-Item WSMan:\localhost\Client\TrustedHosts -Value '192.168.79.*' -Force -Concatenate
```
### 1.3 [P0] Pinning hash SHA256 degli installer
File: [template/Setup-TemplateVM.ps1:332-399](template/Setup-TemplateVM.ps1), [template/Setup-TemplateVM.ps1:401-502](template/Setup-TemplateVM.ps1).
**Motivazione**: Python, `dotnet-install.ps1`, `vs_buildtools.exe` vengono scaricati senza
verifica integrità. Un MITM nella rete dell'host (anche temporaneo) può iniettare binari
compromessi nel template, che poi viene snapshottato e usato da *ogni* build.
Pattern minimale:
```powershell
$expected = '6F25A7DF...' # SHA256 pinnato per la versione esatta
$actual = (Get-FileHash $pyInstallerPath -Algorithm SHA256).Hash.ToLower()
if ($actual -ne $expected.ToLower()) { throw "Hash mismatch: $actual" }
```
Aggiungere un blocco hash per ogni download e bloccarli in costanti `$script:Hashes`.
Aggiornare al cambio versione (è raro).
### 1.4 [P1] Validazione IP per-ottetto
File: [scripts/Invoke-CIJob.ps1:106](scripts/Invoke-CIJob.ps1), [scripts/Invoke-RemoteBuild.ps1:52](scripts/Invoke-RemoteBuild.ps1), [scripts/Get-BuildArtifacts.ps1:42](scripts/Get-BuildArtifacts.ps1), [scripts/Wait-VMReady.ps1:43](scripts/Wait-VMReady.ps1).
**Motivazione**: `'^(\d{1,3}\.){3}\d{1,3}$'` accetta `999.999.999.999`. Sostituire con regex
per-ottetto:
```powershell
$ipv4 = '^((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$'
[ValidatePattern($ipv4)]
```
Estrarre la costante in un modulo condiviso `scripts/_Common.psm1` per evitare drift fra i 4 file (vedi §5.2).
### 1.5 [P1] PAT mai persistito quando si abilita `-UseGitClone`
Quando arriva il momento di implementare il clone in-VM (vedi §3.3 / sezione "In-VM Git Clone"):
- [ ] Recuperare il PAT da Credential Manager dell'host **subito prima** della WinRM session, mai prima.
- [ ] Iniettare via `$using:cred` in `Invoke-Command` e cancellare la variabile in un `finally` interno alla scriptblock.
- [ ] Aggiungere un grep automatico nei log job: se il PAT compare grezzo, fallire la build.
### 1.6 [P2] Defender + Firewall + UAC tutti disattivati nel template — documentare il modello di minaccia
File: [template/Setup-TemplateVM.ps1:200-329](template/Setup-TemplateVM.ps1).
Attualmente: `Set-NetFirewallProfile ... -Enabled False`, `EnableLUA=0`, `DisableAntiSpyware=1`,
`DisableRealtimeMonitoring=1`, `LocalAccountTokenFilterPolicy=1`.
Le scelte sono ragionevoli per un template lab efimero, ma vanno raccolte in un'unica sezione
`BEST-PRACTICES.md §X — Threat Model & Hardening Trade-offs` che indichi:
- Cosa è disattivato e perché (costo build vs. superficie d'attacco).
- Quali condizioni rendono il modello inaccettabile (es. condivisione dell'host, esposizione
VMnet8 a LAN aziendale, build di codice di terze parti non fidate).
- Mitigazioni se uno di quei vincoli salta (Firewall On con regola WinRM esplicita,
Defender con esclusione `C:\CI`, UAC On con `LocalAccountTokenFilterPolicy=1`).
Senza questa nota, future modifiche possono accumulare ulteriori riduzioni di sicurezza
senza tracciamento.
### 1.7 [P2] Rotazione password guest VM
- [ ] Trimestrale. Aggiornare credenziale `BuildVMGuest` in Credential Manager + password
effettiva nel template (richiede refresh snapshot — coordinare con §2.5).
### 1.8 [P2] Verifica Get-StoredCredential
- [ ] Confermare che `Get-StoredCredential` funzioni correttamente in tutti gli script che
lo usano (post-migrazione HTTPS).
---
## 2. Affidabilità & resilienza
### 2.1 [P0] Race su IP allocation con `capacity: 4`
File: [runner/config.yaml:17](runner/config.yaml), [scripts/Invoke-CIJob.ps1:244-253](scripts/Invoke-CIJob.ps1).
**Motivazione**: con 4 job paralleli e DHCP VMnet8, due VM possono ottenere lo stesso IP se
la lease pool è stretta o se due `vmrun start` rispondono troppo vicini.
`getGuestIPAddress` ritorna l'IP visto dalla VM stessa, quindi la collisione non viene
rilevata in fase di Wait-VMReady — apparirà come "WinRM risponde ma è la VM sbagliata".
**Opzioni**:
- **A** — IP allocator file-based con lock: prendere un IP libero da `192.168.79.101..104`,
scriverlo in `F:\CI\State\ip-leases\<jobid>` e iniettarlo come `bootArgs` o via
`vmrun writeVariable` prima di start. Rilascio nel `finally`.
- **B** — DHCP reservation per MAC nel VMware NAT DHCP server (`vmnetdhcp.conf`): assegnare
4 MAC noti, hard-coded nei VMX dei cloni. Più semplice ma richiede gestire il VMX clonato.
- **C** — pool fissato a `capacity: 1` finché non si implementa A o B.
L'opzione A è la più portabile e si integra con il pre-warm pool (§3.4).
### 2.2 [P1] Cleanup orfani schedulato
File: [scripts/Cleanup-OrphanedBuildVMs.ps1](scripts/Cleanup-OrphanedBuildVMs.ps1) — script pronto, manca lo scheduling.
Estendere `Setup-Host.ps1` per registrare un Task Scheduler:
```powershell
$action = New-ScheduledTaskAction -Execute 'powershell.exe' `
-Argument '-NoProfile -NonInteractive -ExecutionPolicy Bypass -File "N:\Code\Workspace\Local-CI-CD-System\scripts\Cleanup-OrphanedBuildVMs.ps1"'
$trigger = New-ScheduledTaskTrigger -Daily -At 4am -RandomDelay (New-TimeSpan -Minutes 30)
$principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -RunLevel Highest
Register-ScheduledTask -TaskName 'CI-CleanupOrphans' -Action $action -Trigger $trigger -Principal $principal -Force
```
Aggiungere anche un trigger `AtStartup` per gestire crash dell'host.
Suggerito: ogni 6 ore, `-MaxAgeHours 4`.
### 2.3 [P1] Retention artifact + log automatica
File: [docs/OPTIMIZATION.md:160-181](docs/OPTIMIZATION.md) — la logica c'è solo come snippet, non eseguita.
Stessa pattern di §2.2 ma su `F:\CI\Artifacts` con `-AddDays(-30)` e su `F:\CI\Logs` con la
finestra già configurata in `Invoke-CIJob.ps1`. Aggiungere
`Get-PSDrive F | Where-Object { $_.Free -lt 50GB }` come *guard*: se libero < 50 GB,
retention più aggressiva (7 giorni) e log warning.
### 2.4 [P1] `Remove-BuildVM.ps1` — supportare `-WhatIf`
File: [scripts/Remove-BuildVM.ps1:28](scripts/Remove-BuildVM.ps1).
Aggiungere `[CmdletBinding(SupportsShouldProcess)]` come fatto in `Cleanup-OrphanedBuildVMs.ps1`.
Permette debug e dry-run senza forkare un secondo script.
### 2.5 [P1] Snapshot versionato `BaseClean_<yyyyMMdd>`
File: [docs/BEST-PRACTICES.md:142-145](docs/BEST-PRACTICES.md), [scripts/Invoke-CIJob.ps1:100](scripts/Invoke-CIJob.ps1).
**Motivazione**: quando si refresha il template (KMS lease semestrale, update toolchain), il
flusso attuale richiede di sovrascrivere `BaseClean` — i cloni esistenti diventano invalidi
e non c'è rollback.
**Strategia**:
- Naming: `BaseClean_20260509`.
- `runner/config.yaml` aggiunge `GITEA_CI_SNAPSHOT_NAME` come env var.
- `Invoke-CIJob.ps1` legge da env, default `'BaseClean'` per compat.
- Pratica: tieni gli ultimi 2 snapshot, cancella il più vecchio dopo 1 settimana di uso pulito del nuovo.
Si lega al task: refresh semestrale snapshot template VM (KMS lease = 180 giorni).
Boot template su VMnet8 (NAT) → `slmgr /ato` → nuovo snapshot.
### 2.6 [P2] Backup automatico VMDK template
File: [docs/BEST-PRACTICES.md:130-138](docs/BEST-PRACTICES.md) — solo come snippet manuale.
Pre-snapshot: copia atomica del `parent` VMDK in `F:\CI\Backups\Template_<date>\`. Su errore
di refresh (es. installer rotto), rollback in 1 minuto.
### 2.7 [P2] Health check del runner + monitoraggio Event Log
File: [docs/BEST-PRACTICES.md:101-115](docs/BEST-PRACTICES.md) — script presente in doc, non operativo.
Schedulare ogni 15 min: query `gitea/api/v1/admin/runners`, se `local-windows-runner` non
`online``Restart-Service act_runner` + log evento. Limitare a 3 restart/h con cooldown.
Inoltre: monitoraggio Windows Event Log per fallimenti servizio `act_runner` (alert via
EventLog query schedulata o webhook).
---
## 3. Performance & throughput
### 3.1 [P1] NuGet/pip cache su shared folder
File: [docs/OPTIMIZATION.md:93-128](docs/OPTIMIZATION.md), [scripts/Invoke-RemoteBuild.ps1:158-194](scripts/Invoke-RemoteBuild.ps1).
**Motivazione**: `F:\CI\Cache\NuGet` esiste come dir, non viene mai usata.
Aggiungere a `CI-WinBuild.vmx`:
```ini
sharedFolder0.present = "TRUE"
sharedFolder0.hostPath = "F:\\CI\\Cache\\NuGet"
sharedFolder0.guestName = "nuget-cache"
```
Poi nello scriptblock di build:
```powershell
$env:NUGET_PACKAGES = '\\vmware-host\Shared Folders\nuget-cache'
```
Equivalente per pip:
```powershell
$env:PIP_CACHE_DIR = '\\vmware-host\Shared Folders\pip-cache'
```
Dopo lo snapshot refresh la cache va riscaldata una volta — accettabile.
### 3.2 [P1] Sostituire `Compress-Archive` con 7-Zip o robocopy
File: [scripts/Invoke-RemoteBuild.ps1:109-122](scripts/Invoke-RemoteBuild.ps1).
**Motivazione**: `Compress-Archive` è single-threaded e su repo medio-grandi (>100 MB sorgente)
può prendere 20-40 s.
**Alternative**:
- **7-Zip** (richiede installazione nel template — vedi sezione "In-VM Git Clone" §B):
```powershell
& 'C:\Program Files\7-Zip\7z.exe' a -mmt=on -mx1 $hostZip "$HostSourceDir\*"
```
Vantaggio: multi-thread, ratio simile con `-mx1`.
- **robocopy via admin share UNC** (`\\<vmIP>\C$\CI\build`): nessun zip/unzip, supporto delta,
ma richiede SMB aperto sulla VM — confliggente con §1.2.
Misurare con un repo reale: probabile risparmio 10-20 s/build su `nsis-plugin-nsinnounp`.
### 3.3 [P2] In-VM clone (`-UseGitClone`)
Implementazione disegnata di seguito (sezione "In-VM Git Clone — Ottimizzazione").
Beneficio reale solo per repo > 200 MB con submoduli grandi; misurare prima.
### 3.4 [P3] Pre-warm pool di cloni
File: [docs/OPTIMIZATION.md:133-156](docs/OPTIMIZATION.md).
Solo se profilo dimostra che `New-BuildVM` + `Wait-VMReady` (~30-60 s) è il collo di bottiglia.
Con build di 2 minuti, l'overhead è ~40% — vale lo sforzo. Con build di 10+ minuti, no.
Implementazione minima: scheduled task ogni 5 min mantiene 2 cloni avviati in
`F:\CI\WarmPool\`; `Invoke-CIJob` fa `Move-Item` di un clone caldo nel suo job dir e parte
da Phase 4 saltando Phase 2-3.
### 3.5 [P2] vCPU/RAM tuning per workload
File: [docs/OPTIMIZATION.md:82-89](docs/OPTIMIZATION.md), VMX template.
`numvcpus=4`, `memsize=6144` è ragionevole ma non ottimo per ogni build. Per
`nsis-plugin-nsinnounp` (4 build paralleli interni con thread divisi) 4 vCPU sono già saturati.
Considerare:
- VMX pinning: 6 vCPU per build C++ pesanti, 2 vCPU per build .NET piccoli.
- Esporre `-VMCpu` / `-VMMemory` a `Invoke-CIJob.ps1` con override via workflow.
### 3.6 [P2] Benchmark baseline
- [ ] Tempo creazione clone + tempo WinRM readiness (per stabilire baseline prima di §3.1/3.2/3.4).
---
## 4. Osservabilità & manutenzione
### 4.1 [P1] Log strutturati per parsing
File: [scripts/Invoke-CIJob.ps1:178-187](scripts/Invoke-CIJob.ps1).
**Motivazione**: i log attuali sono `Write-Host` testuale. Per dashboard / alerting servono
log machine-readable.
Aggiungere a fianco del transcript un secondo file `invoke-ci.jsonl`:
```powershell
function Write-JobEvent {
param([string]$Phase, [string]$Status, [hashtable]$Data = @{})
$event = @{
ts = (Get-Date).ToString('o')
jobId = $JobId
phase = $Phase
status = $Status
data = $Data
}
$event | ConvertTo-Json -Compress | Add-Content $jsonLog
}
```
Emettere in ogni transizione di fase. Permette grafici (durata media per fase, tasso di
fallimento) con `jq` o Loki/Grafana se in futuro.
### 4.2 [P2] Metriche su Prometheus textfile
Generare `F:\CI\Metrics\runner.prom` da uno scheduled task ogni 60s:
```
ci_runner_orphan_vms 0
ci_runner_disk_free_gb 423
ci_runner_artifacts_total 247
ci_runner_active_jobs 1
```
Se l'homelab ha già Prometheus + node_exporter, basta
`--collector.textfile.directory=F:\CI\Metrics`.
### 4.3 [P1] Disk space alert
File: estendere `Setup-Host.ps1` o creare `scripts/Watch-DiskSpace.ps1`.
`F:` riempito = build silenziosamente fallite (linked clone fallirà su `vmrun clone` con
messaggi criptici). Alert via `eventcreate` o webhook Gitea/Discord quando libero < 50 GB.
### 4.4 [P2] Runbook per incident comuni
File: nuovo `docs/RUNBOOK.md`.
Documentare con copy-pasta:
- "Runner offline in Gitea UI" → check service, log, restart.
- "Tutte le build falliscono in Phase 2" → `vmrun -T ws list`, parent VMDK lock, snapshot mancante.
- "Build lente" → check disk free, vmware-vmx.exe CPU, rete VMnet8.
- "VMX corrotto post-crash host" → restore from `F:\CI\Backups\Template_<latest>`.
Ogni voce: sintomo, comando di triage, fix, escalation.
### 4.5 [P3] Dashboard read-only
Una pagina HTML statica generata da `Invoke-CIJob.ps1` (append a `F:\CI\dashboard.html`)
con ultime 50 build, durata, esito, link agli artifact. Servita da IIS Express o solo
file:// — utile per debug a colpo d'occhio senza aprire Gitea UI.
---
## 5. Qualità codice & test
### 5.1 [P1] Pester smoke tests sugli script
File: nuovo `tests/` directory.
**Motivazione**: manca un livello di test sotto l'e2e.
Pester può coprire:
- `New-BuildVM` con vmrun mockato — verifica che il VMX path costruito sia ben formato
per JobId con caratteri speciali.
- `Wait-VMReady` con `Test-WSMan` mockato — verifica timeout, fasi, ritorni.
- `Remove-BuildVM` con `vmrun list` mockato — verifica retry deleteVM.
- Validazione IP regex (§1.4) — table tests.
Eseguire in CI tramite il runner stesso (workflow `lint.yml` esiste già — espandere).
### 5.2 [P2] Modulo PowerShell condiviso `scripts/_Common.psm1`
**Duplicazioni da estrarre**:
- `New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck` (3 file).
- `ValidatePattern` IP (4 file — vedi §1.4).
- Risoluzione `vmrun.exe` (3 file).
- `Test-Path` + `New-Item` per dir lazy creation.
- Function `Invoke-VmrunCommand` con gestione `$LASTEXITCODE`.
Risultato: meno LoC, fix in un punto solo, più facile da testare.
### 5.3 [P2] Esecuzione `vmrun` con check exit code uniforme
Pattern attuale ripetuto in tutti gli script che invocano `vmrun`:
```powershell
$out = & $VmrunPath ... 2>&1
if ($LASTEXITCODE -ne 0) { throw "..." }
```
Wrapping in `Invoke-VmrunCommand -Operation 'clone' -Args @(...)` riduce errori di copy-paste
e centralizza retry/log.
### 5.4 [P2] PSScriptAnalyzer rules custom
File: [gitea/workflows/lint.yml](gitea/workflows/lint.yml) (già esiste).
Aggiungere regole specifiche progetto (`PSScriptAnalyzerSettings.psd1` in root):
- `PSAvoidUsingPlainTextForPassword` — rilevante per `Setup-TemplateVM.ps1:55`.
- `PSAvoidUsingInvokeExpression`.
- `PSUseShouldProcessForStateChangingFunctions`.
- Regole custom: vietare hardcoded `F:\CI\` fuori da config (forzare param/env).
### 5.5 [P3] Type hints nei param block
Già parzialmente fatto. Estendere a tutti gli script per migliorare IntelliSense e
validazione runtime.
---
## 6. Scalabilità & estensioni
### 6.1 [P2] Linux Build VM
**Documento di pianificazione dedicato**: [docs/LINUX-TEMPLATE-SETUP.md](docs/LINUX-TEMPLATE-SETUP.md)
— bozza completa con fasi A→H, decisioni di design, differenze rispetto al template Windows.
Vedi anche sezione "Linux Build VM" sotto per il riassunto inline.
**Considerazione architetturale**: SSH su WSL2 non basta — serve VM vera per parità con il
flusso Windows. Ubuntu 24.04 minimal con script bash diretto (parità con flusso Windows).
Sostituire WinRM con SSH key-based; `Invoke-Command` non funziona — usare `ssh.exe` +
`scp.exe` o il modulo `Posh-SSH`.
Aggiungere `scripts/_Transport.psm1` con due implementazioni (`WinRM`, `SSH`) dietro la
stessa interfaccia. `Invoke-CIJob.ps1` sceglie dal parametro `-GuestOS`.
### 6.2 [P3] Workflow generico riutilizzabile (composite action)
File: [gitea/workflow-example.yml](gitea/workflow-example.yml).
Trasformare in *composite action* Gitea (`.gitea/actions/local-ci-build/action.yml`)
richiamabile da qualsiasi repo:
```yaml
- uses: ./Simone/local-ci-build@v1
with:
build-command: 'python build_plugin.py --final'
artifact-source: 'dist'
submodules: true
```
Riduce la duplicazione su nuovi repo e centralizza l'evoluzione del wrapper.
### 6.3 [P3] Multi-host runner federation
Quando 4 VM in parallelo non bastano, l'opzione naturale è un secondo host Windows con
stesso `Setup-Host.ps1` registrato come secondo runner Gitea (label `windows-build:host-2`).
Già supportato lato Gitea, basta replicare il setup.
**Premessa**: prima di scalare orizzontale, profilare se il collo di bottiglia è CPU
(i9-10900X 20T è abbondante) o I/O (NVMe). Se è I/O, una seconda NVMe sullo stesso host basta.
### 6.4 [P3] Build matrix nel workflow
File: [gitea/workflows/build-nsis.yml](gitea/workflows/build-nsis.yml).
Attualmente single job. Quando arriverà la VM Linux:
```yaml
strategy:
matrix:
target: [windows, linux]
runs-on: ${{ matrix.target }}-build
```
### 6.5 [P3] Secret injection workflow-level
Per chiavi di firma (Authenticode, GPG), usare i Gitea secrets nel workflow e passarli a
`Invoke-CIJob.ps1` come param + env, mai loggati. Stesso modello di §1.5 per il PAT.
---
## In-VM Git Clone (Ottimizzazione) — riferimento §3.3
Sfruttare l'accesso internet della VM (VMnet8 NAT) per fare il `git clone` direttamente Sfruttare l'accesso internet della VM (VMnet8 NAT) per fare il `git clone` direttamente
nella VM, eliminando il ciclo host-clone → zip → WinRM-transfer → unzip. nella VM, eliminando il ciclo host-clone → zip → WinRM-transfer → unzip.
@@ -176,7 +586,7 @@ VM avviata → WinRM: git clone --recurse-submodules https://<token>@gitea/...
- [ ] **Setup-TemplateVM.ps1 — altri tool utili** - [ ] **Setup-TemplateVM.ps1 — altri tool utili**
- [ ] **7-Zip**: installer silenzioso — molto più veloce di `Compress-Archive`/`Expand-Archive` - [ ] **7-Zip**: installer silenzioso — molto più veloce di `Compress-Archive`/`Expand-Archive`
per archivi grandi (es. npm/NuGet cache warm) per archivi grandi (es. npm/NuGet cache warm). Necessario anche per §3.2.
- [ ] **curl.exe**: già presente in Windows 11/Server 2025 (v7.x) — verificare disponibilità - [ ] **curl.exe**: già presente in Windows 11/Server 2025 (v7.x) — verificare disponibilità
- [ ] Valutare: `jq` (parsing JSON in script PowerShell o bash) — opzionale - [ ] Valutare: `jq` (parsing JSON in script PowerShell o bash) — opzionale
@@ -209,7 +619,7 @@ VM avviata → WinRM: git clone --recurse-submodules https://<token>@gitea/...
--- ---
## Linux Build VM (Future) ## Linux Build VM (Future) — riferimento §6.1
Aggiungere supporto per build su Linux per testare la compilazione cross-platform Aggiungere supporto per build su Linux per testare la compilazione cross-platform
(es. versione Linux del plugin o build check con GCC/Clang). (es. versione Linux del plugin o build check con GCC/Clang).
@@ -225,9 +635,10 @@ Aggiungere supporto per build su Linux per testare la compilazione cross-platfor
- [ ] Aggiungere parametro `-GuestOS` (`Windows` / `Linux`) o rilevamento automatico da VMX - [ ] Aggiungere parametro `-GuestOS` (`Windows` / `Linux`) o rilevamento automatico da VMX
- [ ] Sostituire WinRM con SSH + SCP per trasferimento zip e raccolta artifacts - [ ] Sostituire WinRM con SSH + SCP per trasferimento zip e raccolta artifacts
- [ ] Aggiungere supporto a comandi Linux (`bash`, `make`, `cmake --build`) - [ ] Aggiungere supporto a comandi Linux (`bash`, `make`, `cmake --build`)
- [ ] Astrarre transport in `scripts/_Transport.psm1` (vedi §6.1)
- [ ] **Workflow Gitea — build matrix** - [ ] **Workflow Gitea — build matrix**
- [ ] Definire matrix strategy `[windows-build, linux-build]` nel workflow - [ ] Definire matrix strategy `[windows-build, linux-build]` nel workflow (vedi §6.4)
- [ ] Aggiungere label `linux-build` al runner (o registrare runner separato se necessario) - [ ] Aggiungere label `linux-build` al runner (o registrare runner separato se necessario)
- [ ] Verificare che artifacts da entrambe le piattaforme vengano raccolti correttamente - [ ] Verificare che artifacts da entrambe le piattaforme vengano raccolti correttamente
@@ -237,13 +648,19 @@ Aggiungere supporto per build su Linux per testare la compilazione cross-platfor
--- ---
## Security Hardening (Post-MVP) ## Suggerimento di sequencing
- [ ] Passare WinRM da HTTP (5985) a HTTPS (5986) con certificato self-signed Sprint progressivi per ridurre rischio / ottenere valore prima:
- [ ] Rimuovere `AllowUnencrypted=true` dalla config WinRM dopo migrazione HTTPS
- [ ] Verificare che Get-StoredCredential funzioni correttamente negli script 1. **Sprint 1 (sicurezza)** — §1.1, §1.2, §1.3, §1.4 — superficie di attacco e bug noti.
- [ ] Regole firewall — limitare WinRM alla subnet build VM (192.168.79.0/24 — VMnet8 NAT) 2. **Sprint 2 (operatività)** — §2.1, §2.2, §2.3, §2.5 — rendere il sistema "fire and forget".
- [ ] Rotazione password guest VM trimestralmente 3. **Sprint 3 (osservabilità)** — §4.1, §4.3, §4.4 — visibilità prima di estendere.
4. **Sprint 4 (perf)** — §3.1, §3.2 misurati su un repo reale (baseline §3.6).
5. **Sprint 5 (qualità)** — §5.1, §5.2 — rifattorizzazione con safety net di test.
6. **Sprint 6+ (estensioni)** — Linux VM, workflow generico/composite action, federation.
Tenere ogni sprint piccolo: 1-2 voci alla volta, validare e2e dopo ognuna prima di passare
alla successiva.
--- ---