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:
@@ -1,6 +1,22 @@
|
||||
# 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
|
||||
|
||||
@@ -71,13 +87,24 @@
|
||||
.\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.
|
||||
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-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
|
||||
- [x] VM rimane su **VMnet8 (NAT)** — internet access permanente per build che richiedono pip/nuget/npm
|
||||
- [x] Spegni la VM: Start → Shut down
|
||||
- [x] Prendi snapshot: VM → Snapshot → Take Snapshot
|
||||
**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
|
||||
|
||||
### Fase D — Credenziali e config
|
||||
@@ -115,29 +142,412 @@
|
||||
- [x] Push commit e verificare job in Gitea Actions UI
|
||||
- [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)
|
||||
- [ ] 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
|
||||
- [ ] 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`.
|
||||
## 1. Sicurezza & hardening
|
||||
|
||||
## 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`
|
||||
`ValidatePattern` attuale accetta `999.x.x.x`. Sostituire con validazione per-ottetto 0-255.
|
||||
**Motivazione**: la combo `AllowUnencrypted=true` + `Auth/Basic=true` invia credenziali del
|
||||
`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`
|
||||
Python, VS BuildTools, `dotnet-install.ps1` scaricati senza verifica hash. Aggiungere
|
||||
`-ExpectedHash` o hash hardcoded per le versioni pinnate.
|
||||
**Azioni**:
|
||||
- [ ] Generare cert self-signed nel template *prima* dello snapshot.
|
||||
- [ ] 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
|
||||
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**
|
||||
- [ ] **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à
|
||||
- [ ] 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
|
||||
(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
|
||||
- [ ] 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)
|
||||
|
||||
- [ ] **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)
|
||||
- [ ] 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
|
||||
- [ ] Rimuovere `AllowUnencrypted=true` dalla config WinRM dopo migrazione HTTPS
|
||||
- [ ] Verificare che Get-StoredCredential funzioni correttamente negli script
|
||||
- [ ] Regole firewall — limitare WinRM alla subnet build VM (192.168.79.0/24 — VMnet8 NAT)
|
||||
- [ ] Rotazione password guest VM trimestralmente
|
||||
Sprint progressivi per ridurre rischio / ottenere valore prima:
|
||||
|
||||
1. **Sprint 1 (sicurezza)** — §1.1, §1.2, §1.3, §1.4 — superficie di attacco e bug noti.
|
||||
2. **Sprint 2 (operatività)** — §2.1, §2.2, §2.3, §2.5 — rendere il sistema "fire and forget".
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user