Remove original §1.2, §1.3, §1.5 entries from main security section since they are now consolidated in "Deferred (Home Lab)" section with better organization and context about when to revisit. Also removed original §1.7, §1.8 entries (was done in previous commit). Final §1 structure: 1.1, 1.4, 1.6 (done) — 3 items. All deferred items in separate section with clear criteria for re-enablement. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
44 KiB
TODO — Local CI/CD System
Documento unico di lavoro: roadmap, audit trail dei task completati, e backlog post-v1.0 con priorità e razionale. Le voci aperte sono raggruppate per area e marcate P0..P3:
Doc di setup operativi:
docs/HOST-SETUP.md — bootstrap macchina host (Setup-Host.ps1)
docs/WINDOWS-TEMPLATE-SETUP.md — provisioning template Windows (Deploy + Prepare + Setup-WinBuild2025)
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
Summary
Last updated: 2026-05-10 (post Sprint 7: §1.6/§3.2 done, test plan created)
Stato generale: §2 affidabilità chiuso. §3 perf: 3/6 done (3.1/3.2/3.6). §1.6 threat model + §3.2 7-Zip implementati. Comprehensive test plan (v1.3→HEAD) creato. Security items deferred per home lab. Prossimi: §3.3/3.4 (in-VM clone + pre-warm), §6.6 (toolchain Tier-1).
| Area | Done | Open | Note |
|---|---|---|---|
| Setup & Infrastructure | all | 0 | Gitea + runner + dirs + rete tutti operativi |
| Template VM (Fasi A→D) | all | 0 | Snapshot BaseClean preso, credenziali in Credential Manager |
| Scripts Verification | all | 0 | e2e-008/009 SUCCESS, cleanup orfani + retry deleteVM ok |
| Runner Configuration | all | 0 | config.yaml, capacity 4, BuildVMGuest |
| Gitea Workflow Integration | 4 | 1 | manca solo §P3 generalizzazione workflow |
| §1 Sicurezza & hardening | 3 | 0 | 1.1, 1.4, 1.6 done; 1.2/1.3/1.5/1.7/1.8 deferred (home lab) |
| §2 Affidabilità & resilienza | 7 | 0 | tutto done |
| §3 Performance & throughput | 3 | 3 | 3.1/3.2/3.6 done; 3.3/3.4/3.5 backlog |
| §4 Osservabilità & manutenzione | 3 | 1 | 4.1/4.3/4.4 done; 4.5 backlog (4.2 Prometheus rimosso) |
| §5 Qualità codice & test | 4 | 1 | 5.1/5.2/5.3/5.4 done; 5.5 (type hints) backlog |
| §6 Scalabilità & estensioni | 0 | 6 | Linux VM, composite action, toolchain Tier-1 |
| §7 Refactor Deploy ↔ Setup | 4 | 1 | 7.1/7.2/7.3/7.4 done; 7.5 (rinomina) opzionale |
| In-VM Git Clone (§3.3) | 0 | 4 | dipende da §6.6 (Git+7-Zip nel template) |
| Linux Build VM (§6.1) | 0 | 4 | bozza in docs/LINUX-TEMPLATE-SETUP.md |
Prossimi passi consigliati (vedi anche "Suggerimento di sequencing" in fondo):
- Test Plan — eseguire
docs/TEST-PLAN-v1.3-to-HEAD.md(25+ Pester tests, e2e validation) - §6.6 — Toolchain Tier-1 in template (Git, 7-Zip, PowerShell 7, NSIS, CMake, Node.js, etc.)
- §3.3 — In-VM Git clone (
-UseGitCloneflag, dipende §6.6) - §3.4 — Pre-warm pool di cloni (P3, solo se profilo dimostra bottleneck)
- Security — Rifiori se condizioni cambiano; vedi sezione "Deferred (Home Lab)"
Setup & Infrastructure
-
Gitea Server — già in esecuzione su
http://10.10.20.11:3100(ehttps://gitea.emulab.it)- Runner registration token ottenuto e usato
- Create at least one test repository with
.gitea/workflows/build.yml(Simone/nsis-plugin-nsinnounp— testato e2e, workflow ingitea/workflows/build-nsis.yml) - Gitea Actions abilitato (runner registrato con successo)
-
act_runner — installato e registrato sull'host Windows
- Binario
gitea-runner v1.0.2scaricato inF:\CI\act_runner\act_runner.exe - Registrato con Gitea (
http://10.10.20.11:3100) — runner ID 1, nomelocal-windows-runner config.yamlcopiato inF:\CI\act_runner\config.yamlcon labelwindows-build:host- Installato come servizio Windows via NSSM — stato: Running, StartType: Automatic
- Verificare che il runner appaia "Online" nell'UI Gitea Admin → Actions → Runners
- Confermare riavvio automatico dopo reboot host
- Binario
-
Directory CI create su
F:\CI\F:\CI\BuildVMs\F:\CI\Artifacts\F:\CI\act_runner\F:\CI\Cache\NuGet\F:\CI\RunnerWork\F:\CI\Templates\WinBuild\F:\CI\Logs\— log per job (retention: 30 giorni)F:\CI\ISO\— contiene26100.1742.240906-0331.ge_release_svc_refresh_SERVER_VOL_x64FRE_en-us.iso(Windows Server 2025)
-
CredentialManager PowerShell module v2.0 installato (Scope: CurrentUser)
-
Rete VM: VMnet8 (NAT) — internet access per pip/nuget/npm durante i build
Template VM
-
Provision template VM in VMware Workstation — segui questi passi nell'ordine esatto:
Fase A — Crea e installa la VM (NIC: NAT per internet)
- Crea nuova VM in VMware Workstation con queste impostazioni:
- 4 vCPU, 6144 MB RAM, disco 80 GB thin
- VMX path:
F:\CI\Templates\WinBuild\CI-WinBuild.vmx- NIC: VMnet8 (NAT) ← internet necessario per Windows Update e installer - CD-ROM:F:\CI\ISO\26100.1742.240906-0331.ge_release_svc_refresh_SERVER_VOL_x64FRE_en-us.iso - Installa Windows Server 2025 dall'ISO
- All'interno della VM (come Administrator), abilita WinRM:
cmd winrm quickconfig -qpowershell Enable-PSRemoting -Force -SkipNetworkProfileCheck Set-Item WSMan:\localhost\Service\Auth\Basic $true -Force # AllowUnencrypted left false — Deploy post-install.ps1 configures HTTPS/5986 listener Set-Item WSMan:\localhost\Client\TrustedHosts -Value '*' -Force - Attiva Windows Server 2025 (prima dello snapshot — i linked clone ereditano l'attivazione):
cmd slmgr /dliSe non attivato, scegli il metodo appropriato: - KMS (server KMS in rete):slmgr /skms <IP_KMS>poislmgr /ato- MAK/Retail key:slmgr /ipk XXXXX-XXXXX-XXXXX-XXXXX-XXXXXpoislmgr /ato- KMS pubblico (alternativa lab): vedi https://github.com/robin113x/Windows-Server-Activation/ Verifica:slmgr /xpr— deve mostrare attivazione permanente o scadenza lontana - Nota l'IP assegnato dal DHCP di VMnet8 (es.
192.168.79.x) —ipconfignella VM
Fase B — Provisioning automatico dall'host
- Dall'host, esegui:
powershell cd n:\Code\Workspace\Local-CI-CD-System\template .\Prepare-WinBuild2025.ps1 -VMIPAddress 192.168.79.128 -SkipWindowsUpdateInstalla: WinRM config, utenteci_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/5986 raggiungibile, Setup-WinBuild2025.ps1 presente - Host WinRM: AllowUnencrypted, TrustedHosts - Guest prep:C:\CIesiste, 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. - Setup completato con successo (VS Build Tools 2026 exit code 0).
Setup-WinBuild2025.ps1ora esegue validazioneAssert-Stepdopo 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
- VM rimane su VMnet8 (NAT) — internet access permanente per build che richiedono pip/nuget/npm
- Spegni la VM: Start → Shut down
- Prendi snapshot: VM → Snapshot → Take Snapshot
Nome esatto:
BaseCleanPrerequisito:Prepare-WinBuild2025.ps1deve essere uscito con exit 0 — tutti gliAssert-Steppassati, incluso il Final pre-snapshot gate nel guest. - Lascia la VM spenta — non modificarla mai più dopo questo snapshot
Fase D — Credenziali e config
- Archivia credenziali guest sull'host (PowerShell elevato con CredentialManager):
powershell New-StoredCredential -Target "BuildVMGuest" -UserName "ci_build" ` -Password "<your-build-password>" -Persist LocalMachine - Verifica
GITEA_CI_TEMPLATE_PATHinF:\CI\act_runner\config.yaml— aggiornato aCI-WinBuild.vmx(2026-05-08)
- Crea nuova VM in VMware Workstation con queste impostazioni:
- 4 vCPU, 6144 MB RAM, disco 80 GB thin
- VMX path:
Scripts Verification
- Test
New-BuildVM.ps1standalone — cloneClone_test-002creato correttamente (2026-05-08) - Test
Wait-VMReady.ps1standalone — exit 0 in ~5s, VM pronta (2026-05-08) - Test
Invoke-RemoteBuild.ps1— dotnet restore + build OK, artifacts.zip creato in VM (2026-05-08) - Test
Get-BuildArtifacts.ps1— artifacts.zip (78 KB) copiato in F:\CI\Artifacts\test-003\ (2026-05-08) - Test
Remove-BuildVM.ps1— VM spenta + directory rimossa correttamente (2026-05-08) - Test
Invoke-CIJob.ps1end-to-end su una soluzione reale (nsis-plugin-nsinnounp)- e2e-008: SUCCESS — build OK + artifact raccolto (1632 KB)
- e2e-009: SUCCESS — cleanup automatico VM + clone confermato (finally block)
scripts/Cleanup-OrphanedBuildVMs.ps1creato e verificato —-WhatIf, soglia-MaxAgeHours, hard stop + deleteVM + rimozione dir (2026-05-09)scripts/Remove-BuildVM.ps1fixvmrun deleteVMexit -1 — pollvmrun list+ retry 3× backoff 0/3/6s (2026-05-09)
Runner Configuration
runner/config.yamlaggiornato con percorsiF:\CI\, rete VMnet8 (NAT), label corretticapacity: 4configurato (64 GB RAM / ~8 GB per VM)- Aggiornare
GITEA_CI_TEMPLATE_PATHinconfig.yamldopo creazione template VM - Archiviare credenziali guest in Windows Credential Manager (target:
BuildVMGuest)
Gitea Workflow Integration
- Copiare
gitea/workflow-example.ymlin.gitea/workflows/build.ymlnel repo di test (workflowbuild-nsis.ymlgià presente e funzionante pernsis-plugin-nsinnounp) - Push commit e verificare job in Gitea Actions UI
- Verificare artifact scaricabile da Gitea dopo build riuscita
gitea/workflows/lint.ymlcreato — PSScriptAnalyzer su push/PR che toccano.ps1(2026-05-09)- [P3] Adattare workflow per altri repository — generalizzare
BUILD_COMMANDeARTIFACT_SOURCE. Vedi anche §6.2 (composite action riutilizzabile).
1. Sicurezza & hardening
1.1 [P0] [x] Migrare WinRM da HTTP/Basic a HTTPS/5986 — COMPLETATO 2026-05-10
Azioni:
- Generare cert self-signed nel template prima dello snapshot (già in Deploy post-install.ps1).
- Sostituire
New-PSSession -ComputerName $ipcon-UseSSL -Port 5986 -Authentication BasicinInvoke-RemoteBuild.ps1,Get-BuildArtifacts.ps1,Prepare-WinBuild2025.ps1,Validate-*.ps1. - Rimuovere
AllowUnencrypted=trueda Deploy post-install.ps1 e Setup-WinBuild2025.ps1 (assertion → false). -SkipCACheck/-SkipCNCheck/-SkipRevocationCheckmantenuti neiNew-PSSessionOptioneNew-WSManSessionOption(cert lab self-signed — documentato inline).- Regola firewall
WinRM-HTTPSin Deploy ristretta aRemoteAddress '192.168.79.0/24'. Wait-VMReady.ps1usaNew-WSManSessionOption+Test-WSMan -Port 5986 -UseSSL.Prepare-WinBuild2025.ps1: preflight TCP/5986, host non imposta piùAllowUnencrypted; soloTrustedHostsviene salvato/ripristinato.Validate-DeployState.ps1,Validate-SetupState.ps1: connessione HTTPS/5986, checkAllowUnencrypted=false.
1.4 [P1] [x] Validazione IP per-ottetto — COMPLETATO 2026-05-10
Regex per-ottetto sostituita in tutti e 4 i file:
'^((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)$'
Estrazione in scripts/_Common.psm1 — vedi §5.2 (P2, backlog).
1.6 [P2] [x] Defender + Firewall + UAC tutti disattivati nel template — documentare il modello di minaccia — COMPLETATO 2026-05-10
File: docs/BEST-PRACTICES.md:§2.1 Threat Model.
COMPLETATO: Sezione "2.1. Threat Model — Disabled Security Features" aggiunta a BEST-PRACTICES.md. Documenta:
- Stato corrente: Defender, Firewall, UAC disabilitate (costi vs. benefici tabellati).
- Quando è accettabile: ambiente lab isolato con code trusted.
- Quando rompe: code non fidate, sharing host, esposizione VMnet8 a LAN.
- Mitigazioni: come riabilitare Firewall, Defender con esclusioni, UAC con costi specifici.
Future modifiche di sicurezza vanno documentate nello stesso posto.
2. Affidabilità & resilienza
2.1 [P0] [x] Race su IP allocation — COMPLETATO 2026-05-10
File: runner/config.yaml:17, scripts/Invoke-CIJob.ps1:244-253.
Stato osservato: race non riprodotta in e2e-008/009 (un job alla volta). capacity: 4
resta attivo in produzione — bug teorico finché non si esegue stress test parallelo.
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 inF:\CI\State\ip-leases\<jobid>e iniettarlo comebootArgso viavmrun writeVariableprima di start. Rilascio nelfinally. - 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: 1finché 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] [x] Cleanup orfani schedulato — COMPLETATO 2026-05-10
File: scripts/Cleanup-OrphanedBuildVMs.ps1 — script pronto, scheduling registrato da scripts/Register-CIScheduledTasks.ps1.
Estendere Setup-Host.ps1 per registrare un Task Scheduler:
$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] [x] Retention artifact + log automatica — COMPLETATO 2026-05-10
File: scripts/Invoke-RetentionPolicy.ps1 — script creato; scheduling via scripts/Register-CIScheduledTasks.ps1.
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] [x] Remove-BuildVM.ps1 — supportare -WhatIf — COMPLETATO 2026-05-10
File: scripts/Remove-BuildVM.ps1:28.
[CmdletBinding(SupportsShouldProcess)] aggiunto; op distruttive (stop, deleteVM, Remove-Item) gated su $PSCmdlet.ShouldProcess.
2.5 [P1] [x] Snapshot versionato BaseClean_<yyyyMMdd> — COMPLETATO 2026-05-10
File: docs/BEST-PRACTICES.md:142-145, scripts/Invoke-CIJob.ps1:100.
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.yamlaggiungeGITEA_CI_SNAPSHOT_NAMEcome env var.Invoke-CIJob.ps1legge 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] [x] Backup automatico VMDK template — COMPLETATO 2026-05-10
File: scripts/Backup-CITemplate.ps1.
Copia atomica F:\CI\Templates\WinBuild → F:\CI\Backups\Template_<yyyyMMdd_HHmmss>\.
Ferma act_runner prima, lo riavvia nel finally. Prune automatico (default: mantieni 3).
SupportsShouldProcess → supporta -WhatIf. Esecuzione manuale pre-refresh.
2.7 [P2] [x] Health check del runner + monitoraggio Event Log — COMPLETATO 2026-05-10
File: scripts/Watch-RunnerHealth.ps1; task registrato da scripts/Register-CIScheduledTasks.ps1 come CI-RunnerHealth (ogni 15 min).
Controlla Get-Service act_runner. Se non Running: EventId 1002 (Warning) + Restart-Service.
Cooldown: max 3 restart/h via JSON state file F:\CI\State\runner-restart-log.json.
Oltre il limite: EventId 1004 (Error) + webhook :sos: senza restart. Webhook opzionale
Discord/Gitea identico a Watch-DiskSpace.ps1.
3. Performance & throughput
3.1 [P1] [x] NuGet/pip cache su shared folder — COMPLETATO 2026-05-10
File: scripts/Set-TemplateSharedFolders.ps1, scripts/Invoke-RemoteBuild.ps1.
Set-TemplateSharedFolders.ps1: edita il VMX template aggiungendo due shared folder
(nuget-cache → F:\CI\Cache\NuGet, pip-cache → F:\CI\Cache\pip), idempotente,
.bak del VMX originale mantenuto, SupportsShouldProcess.
Invoke-RemoteBuild.ps1: aggiunto -UseSharedCache switch. Quando attivo, inietta
$env:NUGET_PACKAGES nel scriptblock dotnet restore/build e $env:PIP_CACHE_DIR nel
scriptblock custom build command.
Attivazione:
- Fermare template VM.
.\Set-TemplateSharedFolders.ps1(una tantum, pre-snapshot).- Passare
-UseSharedCacheaInvoke-RemoteBuild.ps1nei job dotnet/pip.
Dopo snapshot refresh: cache si riscalda al primo build — accettabile.
3.2 [P1] [x] Sostituire Compress-Archive con 7-Zip — COMPLETATO 2026-05-10
File: scripts/Invoke-RemoteBuild.ps1 (3 rimpiazzi Compress-Archive → 7-Zip + fallback).
Implementazione:
- Host-side: nuova funzione
Compress-BuildArtifact(check 7-Zip, fallback Compress-Archive) usata per comprimere source tree - Guest-side (custom build): inline 7-Zip check in scriptblock per artifact compression
- Guest-side (dotnet build): inline 7-Zip check in scriptblock per output directory compression
- Usa
-mmt=on -mx1per multi-thread con ratio simile a Compress-Archive
Vantaggio: 10-20 s risparmio per repo >100 MB una volta che 7-Zip installato (§6.6). Attuale: Fallback a Compress-Archive finché 7-Zip non nel template — zero overhead.
3.3 [P2] [ ] In-VM clone (-UseGitClone)
Implementazione disegnata di seguito (sezione "In-VM Git Clone (Ottimizzazione)"). Vincoli sicurezza PAT: vedi §1.5. Tool prerequisiti (Git, 7-Zip): vedi §6.6. 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.
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, 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/-VMMemoryaInvoke-CIJob.ps1con override via workflow.
3.6 [P2] [x] Benchmark baseline — COMPLETATO 2026-05-10
File: scripts/Measure-CIBenchmark.ps1.
Misura 5 fasi per iterazione: clone, start, IP acquire, WinRM ready, destroy.
Output: tabella console + append a F:\CI\Logs\benchmark.jsonl (JSONL, una riga per iterazione).
Supporta -Iterations N per ridurre varianza. Importa _Common.psm1 (Invoke-Vmrun, Resolve-VmrunPath).
Uso: .\Measure-CIBenchmark.ps1 prima e dopo §3.1/§3.2 per misurare impatto.
4. Osservabilità & manutenzione
4.1 [P1] [x] Log strutturati per parsing — COMPLETATO 2026-05-10
File: scripts/Invoke-CIJob.ps1:178-187.
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:
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.3 [P1] [x] Disk space alert — COMPLETATO 2026-05-10
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] [x] Runbook per incident comuni — COMPLETATO 2026-05-10
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] [x] Pester smoke tests sugli script — COMPLETATO 2026-05-10
File: tests/_Common.Tests.ps1, tests/New-BuildVM.Tests.ps1, tests/Remove-BuildVM.Tests.ps1, tests/Wait-VMReady.Tests.ps1.
Test con fake vmrun .cmd (exit code via %FAKE_VMRUN_EXIT% env var — nessuna VM reale):
_Common.psm1:New-CISessionOptionTLS flags,Resolve-VmrunPaththrow/return,Invoke-VmrunExitCode/Output/ThrowOnErrorNew-BuildVM.ps1: throw su template mancante, throw + cleanup su clone fail, formato nome Clone_{JobId}_{timestamp}Remove-BuildVM.ps1: no-op su VMX mancante, cleanup dir parziale, -WhatIf non distrugge, destroy rimuove dirWait-VMReady.ps1: throw su vmrun mancante, throw su timeout, ValidatePattern IP (accetta/rifiuta)
Eseguire: Invoke-Pester tests\ -Output Detailed
5.2 [P2] [x] Modulo PowerShell condiviso scripts/_Common.psm1 — COMPLETATO 2026-05-10
File: scripts/_Common.psm1.
Esporta: New-CISessionOption (WinRM self-signed TLS), Resolve-VmrunPath (valida vmrun.exe),
Invoke-Vmrun (wrapper uniforme -T ws, ritorna {ExitCode, Output}, supporta -ThrowOnError).
Importato da: Invoke-RemoteBuild.ps1, Get-BuildArtifacts.ps1, New-BuildVM.ps1.
5.3 [P2] [x] Esecuzione vmrun con check exit code uniforme — COMPLETATO 2026-05-10
Invoke-Vmrun in _Common.psm1. New-BuildVM.ps1 refactored per usarlo.
Invoke-RemoteBuild.ps1 e Get-BuildArtifacts.ps1 usano New-CISessionOption.
5.4 [P2] [x] PSScriptAnalyzer rules custom — COMPLETATO 2026-05-10
File: PSScriptAnalyzerSettings.psd1.
Regole attive: PSAvoidUsingInvokeExpression, PSUseShouldProcessForStateChangingFunctions,
PSUsePSCredentialType, PSAvoidUsingPlainTextForPassword,
PSAvoidUsingConvertToSecureStringWithPlainText, PSAvoidGlobalVars,
PSUseConsistentIndentation (4 spazi), PSUseConsistentWhitespace.
Soppresso progetto-wide: PSAvoidUsingWriteHost (output strutturato per act_runner).
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 — 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.
Trasformare in composite action Gitea (.gitea/actions/local-ci-build/action.yml)
richiamabile da qualsiasi repo:
- 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.
Attualmente single job. Quando arriverà la VM Linux:
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.
6.6 [P2] [ ] Toolchain Tier-1 in Setup-WinBuild2025
File: template/Setup-WinBuild2025.ps1.
Motivazione: toolchain attuale (.NET SDK, Python, VS Build Tools) copre solo C#/Python/MSBuild.
Aggiungere tools generici-CI riduce il bisogno di installazione ad-hoc nei workflow e abilita
build più ampi senza toccare il template.
Tool da aggiungere come step Setup-WinBuild2025 (ognuno con Assert-Step):
| Tool | Versione | Razionale |
|---|---|---|
| Git for Windows | latest LTS | Self-clone in VM (alt a host-zip-transfer), submodule fetch |
| 7-Zip | latest | Universale unpack/zip; molti installer e workflow ne dipendono |
| PowerShell 7.x | latest LTS | Più veloce di 5.1, parallel pipelines, operatori moderni |
| NSIS | latest | Test build installer (es. nsis-plugin-nsinnounp) |
| CMake | latest | Build system cross-platform per progetti C/C++ nativi |
| Node.js LTS | latest LTS | Frontend/Electron pipelines, JS toolchain (npm) |
| WiX Toolset | v4 stable | MSI building da .NET projects |
| gh CLI | latest | Upload artifact a release Gitea/GitHub, comment PR |
| Sysinternals | sysinternals.com live |
Diagnostica process/handle quando build flaky |
| vcpkg | latest | Package manager C++ per dipendenze native |
Approccio:
- Pinning hash SHA256 per ogni installer (vedi §1.3)
- Convenzione path guest: tool in
C:\BuildTools\<tool>\(separato daC:\CI\runtime —C:\CI\resta per artefatti job e worker temp;C:\BuildTools\per software statico). Documentare indocs/BEST-PRACTICES.mdprima di implementare. - Aggiungere a
PATHmachine-wide - Defender già off → no scan overhead durante install
- Step esegue in seriale dopo
Toolchain cross-checkesistente, prima diCleanup Final pre-snapshot gate: aggiungere checkwhere.exeper ogni tool- Cross-link: Git for Windows e 7-Zip sono richiesti anche da §3.2 e dalla sezione "In-VM Git Clone" — implementare qui per evitare doppio lavoro
Validazione per-tool:
- Eseguibile risolvibile via
where.exe - Versione exact match al param
-<Tool>Version - (per gh)
gh --versionreturns 0 - (per vcpkg)
vcpkg.exe version
Param da aggiungere a Setup + Prepare:
-GitVersion,-7ZipVersion,-Pwsh7Version,-NSISVersion,-CMakeVersion,-NodeJSVersion,-WiXVersion,-GhCliVersion,-VcpkgRev(rev SHA del repo)
Tier-2 (opzionali, separati): Java JDK, Maven/Gradle, Rust, Go, LLVM, Docker, Azure/AWS CLI, WinDbg.
Non includere in Setup base — workflow-level via choco install o download diretto.
7. Refactor confine Deploy ↔ Setup
Obiettivo: separazione netta delle responsabilità — Deploy produce template OS stand-alone, Setup fa build customization (user, dirs, toolchain). Eliminare drift e duplicazioni; ogni concern ha una sola sorgente di verità.
Stato attuale (duplicati): UAC, Explorer LaunchTo, Server Manager off, DisableCAD, OOBE telemetry, WU services disable — entrambi gli script li toccano.
7.1 [P1] [x] Spostare base OS hardening da Setup a Deploy
File: template/Deploy-WinBuild2025.ps1, template/Setup-WinBuild2025.ps1.
- Aggiungere
Set-NetFirewallProfile -Enabled False(tutti i profili) in Deploy post-install.ps1 - Aggiungere
MaxMemoryPerShellMB=2048,IdleTimeOut=7200000,MaxProcessesPerShell=25,StartupType=Automaticin Deploy dopo Enable-PSRemoting - Aggiungere in Deploy items mancanti rispetto a Setup 5c: lock screen (
NoLockScreen), SM GPO policy key (DoNotOpenAtLogon), SM scheduled task disable,DisableCADin Winlogon, OOBE non-policy key - Setup Step 1 (Firewall) → Assert-Step only
- Setup Step 2 (Defender) → già validation-only (refactor 2026-05-09)
- Setup Step 3 (WinRM tuning) → Assert-Step only (rimuovi Enable-PSRemoting, winrm set, Set-Item, Set-Service)
- Setup Step 4b (UAC) → Assert-Step only
- Setup Step 5b (Explorer LaunchTo) → Assert-Step only
- Setup Step 5c (Server Manager / DisableCAD / OOBE) → Assert-Step only
- E2e validation — completata §7.4 (2026-05-10)
7.2 [P1] [x] WU lifecycle — strategia B (Deploy: GPO + Manual; Setup: lifecycle)
File: template/Deploy-WinBuild2025.ps1, template/Setup-WinBuild2025.ps1.
Strategia B: Deploy lascia servizi WU start=demand (Manual, default Windows) e scrive solo
GPO NoAutoUpdate=1 + DisableWindowsUpdateAccess=1. Setup Step 6 clears i GPO locks (Step A),
avvia WU via SchTask SYSTEM, poi Step 6b disabilita servizi permanentemente post-update.
- Deploy: rimuovere
sc.exe config wuauserv start= disabled(e UsoSvc, WaaSMedicSvc) - Deploy: aggiungere GPO
DisableWindowsUpdateAccess=1(era mancante;NoAutoUpdate=1già presente) - Setup Step 6 Step A: già rimuove
DisableWindowsUpdateAccess+NoAutoUpdateprima del COM API — nessuna modifica necessaria - Setup Step 6 Step D: già fa
Set-Service start=Manual+Start-Service— idempotente con servizi già Manual - Setup Step 6b: già disabilita wuauserv/UsoSvc + GPO keys — nessuna modifica necessaria
- E2e validation — completata §7.4 (2026-05-10)
7.3 [P2] [x] Update header docs di entrambi gli script
- template/Deploy-WinBuild2025.ps1
.DESCRIPTION: aggiornato con tutte le hardening voci (firewall, WinRM tuning, WU GPO strategy B, lock screen, SM, OOBE) - template/Setup-WinBuild2025.ps1
.DESCRIPTION: Steps 1/3/4b/5b/5c marcati "validation-only — set by Deploy"; step ordering rationale aggiornato - docs/WINDOWS-TEMPLATE-SETUP.md: architettura "tre script", VMX path
CI-WinBuild.vmx, Fase C step list + razionale + tabella validazioni aggiornati
7.4 [P2] [x] Test e2e refactor — COMPLETATO 2026-05-10
- Deploy stand-alone su VM test →
Get-NetFirewallProfiletuttiEnabled=False,Get-Service wuauserv | Select StartType→Manual,NoAutoUpdate=1GPO presente - Prepare dopo Deploy con
-SkipWindowsUpdate→ tuttiAssert-Steppassano[OK]senza eseguire Set (log mostra solo validation output) - Prepare senza
-SkipWindowsUpdate→ WU (0 update trovati, ResultCode=0), Step 6b disabilita wuauserv/UsoSvc (StartType=Disabled) - Final gate del guest passa su tutti e 7 i check; snapshot
BaseCleanpreso
Fix emersi durante §7.4 (applicati):
- Deploy:
UsoSvcimpostato aManualesplicitamente (default Server 2025 = Automatic) - Setup cleanup:
Set-Service -StartupType Disabledri-applicato su wuauserv/UsoSvc dopo DISMStartComponentCleanup(DISM/WaaSMedicSvc può resettare StartType) - Deploy + Setup: autologin Administrator (
AutoAdminLogon=1) aggiunto in post-install.ps1 e validato in Assert-Step 5c - Nuovi script:
template/Validate-DeployState.ps1,template/Validate-SetupState.ps1
7.5 [P3] [ ] Rinomina Setup → Setup-CITools (opzionale, futuro)
Una volta che Setup fa solo build customization, nome Setup-WinBuild2025 è impreciso —
non setup-a la build VM, estende un template OS già pronto con toolchain CI.
Rinominare in Setup-CITools.ps1 o Install-CIToolchain.ps1 rifletterebbe meglio la
responsabilità ridotta. Tracciare in TODO post-7.1/7.2.
In-VM Git Clone (Ottimizzazione) — riferimento §3.3
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.
Strategia
Flusso attuale:
host: git clone → host: Compress-Archive → WinRM: copia zip → VM: Expand-Archive → VM: build
Flusso proposto:
VM avviata → WinRM: git clone --recurse-submodules https://<token>@gitea/... → VM: build
Punti chiave:
- Il PAT non deve essere nello snapshot — viene iniettato a runtime come env var via WinRM
(
git clone https://Simone:<pat>@gitea.emulab.it/<repo>.git) e non viene mai persistito - Git deve essere installato nel template (
Setup-WinBuild2025.ps1) — ora è assente per design - L'implementazione deve essere non-breaking: nuovo switch opt-in
-UseGitCloneinInvoke-CIJob.ps1 - Si perde il parallelismo parziale (clone host avviene mentre VM boota), ma si elimina tutto l'overhead di zip/transfer (rilevante su repo grandi o con molti submoduli)
- Submoduli funzionano nativamente con
--recurse-submodules(nessun workaround)
Task
-
Tool prerequisiti nel template — implementati in §6.6 (Tier-1 Toolchain):
- Git for Windows + 7-Zip + gh CLI installati con hash pinning e
Assert-Step - Rimuovere il commento "Git is NOT installed" dal docblock di Setup-WinBuild2025.ps1 una volta applicato §6.6
- curl.exe: già presente in Windows Server 2025 — verificare con
where.exe curl
- Git for Windows + 7-Zip + gh CLI installati con hash pinning e
-
Invoke-CIJob.ps1 — aggiungere switch
-UseGitClone- Quando
-UseGitCloneè attivo:- Saltare Phase 1 (host git clone) e la creazione del
HostSourceDirtemporaneo - Dopo Wait-VMReady, leggere PAT da Windows Credential Manager sull'host
(es. target
git:https://gitea.emulab.it/...o un target dedicato CI) - Passare PAT a
Invoke-RemoteBuild.ps1rispettando i vincoli sicurezza di §1.5
- Saltare Phase 1 (host git clone) e la creazione del
- Comportamento default invariato (
-UseGitClone=$false) — nessuna regressione
- Quando
-
Invoke-RemoteBuild.ps1 — modalità in-VM clone
- Aggiungere parametri:
-CloneUrl,-CloneBranch,-CloneCommit,-GitPat(tutti opzionali, usati solo quando-UseGitCloneè attivo) - Quando presenti: anziché copiare zip, eseguire nella VM:
powershell git clone --recurse-submodules ` "https://Simone:$env:CI_GIT_PAT@gitea.emulab.it/Simone/<repo>.git" ` C:\CI\build git -C C:\CI\build checkout $CloneCommit # pin al commit esatto - Implementare iniezione/pulizia PAT secondo §1.5 (env var sessione, no argv, no log,
cleanup
[System.Environment]::SetEnvironmentVariable('CI_GIT_PAT', $null, 'Process'))
- Aggiungere parametri:
-
Test e2e con
-UseGitClone- Aggiornare snapshot
BaseCleancon Git installato - Eseguire
e2e-010con-UseGitClonesunsis-plugin-nsinnounp(ha submoduli) - Confrontare tempo totale con e2e-009 (senza
-UseGitClone) - Verificare che il PAT non appaia nei log di
Invoke-CIJob.ps1
- Aggiornare snapshot
Linux Build VM (Future) — riferimento §6.1
Aggiungere supporto per build su Linux per testare la compilazione cross-platform (es. versione Linux del plugin o build check con GCC/Clang).
-
Template VM Linux — Provisionare template VM con Ubuntu Server 24.04 LTS
- Scegliere distro: Ubuntu 24.04 LTS (raccomandato) o Debian 12
- Installare toolchain: GCC, Clang, CMake, Python 3.x, build-essential
- Configurare accesso SSH (chiave pubblica da host) in alternativa/integrazione a WinRM
- Prendere snapshot
BaseClean-Linux - Archiviare credenziali SSH in Credential Manager (o key file su host)
-
Invoke-CIJob.ps1 — branch Linux
- Aggiungere parametro
-GuestOS(Windows/Linux) o rilevamento automatico da VMX - Sostituire WinRM con SSH + SCP per trasferimento zip e raccolta artifacts
- Aggiungere supporto a comandi Linux (
bash,make,cmake --build) - Astrarre transport in
scripts/_Transport.psm1(vedi §6.1)
- Aggiungere parametro
-
Workflow Gitea — build matrix
- Definire matrix strategy
[windows-build, linux-build]nel workflow (vedi §6.4) - Aggiungere label
linux-buildal runner (o registrare runner separato se necessario) - Verificare che artifacts da entrambe le piattaforme vengano raccolti correttamente
- Definire matrix strategy
-
Test e2e Linux — eseguire almeno un build end-to-end su VM Linux
- Confermare che
build_plugin.py(o analogo script) funzioni su Linux - Confrontare artifact Linux con quelli Windows
- Confermare che
Deferred (Home Lab)
Nel contesto di un home lab isolato, i seguenti security task hanno costi eccessivi relativi ai benefici in questo ambiente. Rifiori se le condizioni cambiano (code non fidata, sharing host, esposizione di rete, etc.).
1.2 [P1] [ ] Audit TrustedHosts setting
File: docs/BEST-PRACTICES.md:§2.
Verificare che host Windows abbia WSMan:\localhost\Client\TrustedHosts configurato
per 192.168.79.* instead of *. Attualmente impostato a * durante setup template.
Stato attuale: Accettabile per lab — WinRM è isolato su VMnet8 NAT. Rifiori se: Host esposto a LAN / internet.
1.3 [P0] [ ] Pinning hash SHA256 degli installer
File: template/Setup-WinBuild2025.ps1.
SHA256 hash per dotnet-install.ps1, Python installer, vs_buildtools.exe.
Stato attuale: Struttura aggiunta, valori placeholder — funziona con warning. Rifiori quando: Build di codice di terze parti non fidate che scaricano installer.
1.5 [P1] [ ] PAT mai persistito per -UseGitClone
File: sezione "In-VM Git Clone (Ottimizzazione)" in TODO.
Vincoli: PAT da Credential Manager subito prima sessione, non in argv/log, cancellato in finally, grep post-job se compare in transcript.
Stato attuale: Struttura documentata, non implementata.
Rifiori quando: -UseGitClone implementato (§3.3).
1.7 [P2] [ ] Rotazione password guest VM
Aggiornare BuildVMGuest in Credential Manager + nel template ogni trimestre.
Stato attuale: Password manuale. Rifiori quando: Change management/compliance lo richiede.
1.8 [P2] [ ] Verifica Get-StoredCredential
Confermare che Get-StoredCredential (CredentialManager module) funzioni
in tutti gli script che lo usano post-migrazione HTTPS.
Stato attuale: Usato in template ma non testato in unit test. Rifiori quando: Script fallisce su recovery da Credential Manager.
Suggerimento di sequencing
Sprint progressivi per ridurre rischio / ottenere valore prima:
- Sprint 1 (sicurezza) — §1.1, §1.2, §1.3, §1.4 — superficie di attacco e bug noti.
- Sprint 2 (operatività) — §2.1, §2.2, §2.3, §2.5 — rendere il sistema "fire and forget".
- Sprint 3 (osservabilità) — §4.1, §4.3, §4.4 — visibilità prima di estendere.
- Sprint 4 (perf) — §3.1, §3.2 misurati su un repo reale (baseline §3.6).
- Sprint 5 (qualità) — §5.1, §5.2 — rifattorizzazione con safety net di test.
- 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.
Reference Paths
| Item | Path / Value |
|---|---|
| vmrun.exe | C:\Program Files (x86)\VMware\VMware Workstation\vmrun.exe |
| act_runner | F:\CI\act_runner\act_runner.exe (servizio: act_runner) |
| act_runner config | F:\CI\act_runner\config.yaml |
| act_runner logs | F:\CI\act_runner\logs\ |
| Template VMX | F:\CI\Templates\WinBuild\CI-WinBuild.vmx |
| Snapshot name | BaseClean |
| Clone base dir | F:\CI\BuildVMs\ |
| Artifact dir | F:\CI\Artifacts\ |
| Log dir | F:\CI\Logs\ (retention: 30 giorni) |
| Gitea URL (LAN) | http://10.10.20.11:3100 |
| Gitea URL (ext) | https://gitea.emulab.it |
| Runner name | local-windows-runner (ID: 1) |
| Build VM subnet | 192.168.79.0/24 (VMnet8 — NAT, internet access per build) |
| Credential Manager target | BuildVMGuest |
| Guest username | ci_build — configurato e verificato |