34b33c5011
Test-WSMan in Windows PowerShell 5.1 does not support -SessionOption (WSManSessionOption),
causing "Cannot find a parameter matching the name 'SessionOption'" at runtime.
Prepare-WinBuild2025.ps1:
- Remove Test-WSMan connectivity check step entirely
- Open New-PSSession directly (handles -SkipCACheck via PSSessionOption)
- Wrap New-PSSession in try/catch with the same actionable error message
- Remove $wsmOpt (WSManSessionOption) — no longer needed
Wait-GuestWinRMReady: replace Test-WSMan probe with New-PSSession probe + Remove-PSSession
Wait-VMReady.ps1:
- Remove $wsmOpt entirely
- Replace Test-WSMan -UseSSL -SessionOption with Test-NetConnection -Port 5986
(TCP open on 5986 = HTTPS listener up = VM ready; credentials not available here)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
826 lines
45 KiB
Markdown
826 lines
45 KiB
Markdown
# TODO — Local CI/CD System
|
||
|
||
<!-- Last updated: 2026-05-10 — line refs e wording allineati al refactor 2026-05-09 -->
|
||
|
||
> 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](docs/HOST-SETUP.md) — bootstrap macchina host (Setup-Host.ps1)
|
||
> - [docs/WINDOWS-TEMPLATE-SETUP.md](docs/WINDOWS-TEMPLATE-SETUP.md) — provisioning template Windows (Deploy + Prepare + Setup-WinBuild2025)
|
||
> - [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
|
||
|
||
---
|
||
## Summary
|
||
|
||
_Last updated: 2026-05-10 (post §7.4 e2e + tag v1.2)_
|
||
|
||
**Stato generale**: infrastruttura base e refactor Deploy↔Setup (§7) completi. Sicurezza sprint 1 in corso (HTTPS/5986 done, IP regex done, hash pinning struttura done — valori da riempire). Backlog operatività/perf/qualità intatto.
|
||
|
||
| 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 | 2 | 6 | 1.1, 1.4 done; 1.2/1.3 parzialmente; 1.5/1.6/1.7/1.8 da fare |
|
||
| §2 Affidabilità & resilienza | 0 | 7 | tutto backlog (race IP, cleanup schedulato, snapshot versionato…) |
|
||
| §3 Performance & throughput | 0 | 6 | nessuna ottimizzazione applicata; baseline §3.6 prerequisito |
|
||
| §4 Osservabilità & manutenzione | 0 | 5 | log JSONL, metriche, runbook tutti aperti |
|
||
| §5 Qualità codice & test | 0 | 5 | Pester, modulo `_Common.psm1`, regole PSScriptAnalyzer custom |
|
||
| §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):
|
||
1. Chiudere §1.3 — riempire hash SHA256 in `Setup-WinBuild2025.ps1` prima del prossimo refresh snapshot.
|
||
2. Audit §1.2 (TrustedHosts `*` su host), poi §1.7 (rotazione password guest).
|
||
3. Sprint 2 operatività — §2.2 (cleanup schedulato), §2.3 (retention), §2.5 (snapshot versionato).
|
||
|
||
---
|
||
## Setup & Infrastructure
|
||
|
||
- [x] **Gitea Server** — già in esecuzione su `http://10.10.20.11:3100` (e `https://gitea.emulab.it`)
|
||
- [x] Runner registration token ottenuto e usato
|
||
- [x] Create at least one test repository with `.gitea/workflows/build.yml`
|
||
(`Simone/nsis-plugin-nsinnounp` — testato e2e, workflow in `gitea/workflows/build-nsis.yml`)
|
||
- [x] Gitea Actions abilitato (runner registrato con successo)
|
||
|
||
- [x] **act_runner** — installato e registrato sull'host Windows
|
||
- [x] Binario `gitea-runner v1.0.2` scaricato in `F:\CI\act_runner\act_runner.exe`
|
||
- [x] Registrato con Gitea (`http://10.10.20.11:3100`) — runner ID 1, nome `local-windows-runner`
|
||
- [x] `config.yaml` copiato in `F:\CI\act_runner\config.yaml` con label `windows-build:host`
|
||
- [x] Installato come servizio Windows via NSSM — stato: **Running**, StartType: Automatic
|
||
- [x] Verificare che il runner appaia "Online" nell'UI Gitea Admin → Actions → Runners
|
||
- [x] Confermare riavvio automatico dopo reboot host
|
||
|
||
- [x] **Directory CI** create su `F:\CI\`
|
||
- [x] `F:\CI\BuildVMs\`
|
||
- [x] `F:\CI\Artifacts\`
|
||
- [x] `F:\CI\act_runner\`
|
||
- [x] `F:\CI\Cache\NuGet\`
|
||
- [x] `F:\CI\RunnerWork\`
|
||
- [x] `F:\CI\Templates\WinBuild\`
|
||
- [x] `F:\CI\Logs\` — log per job (retention: 30 giorni)
|
||
- [x] `F:\CI\ISO\` — contiene `26100.1742.240906-0331.ge_release_svc_refresh_SERVER_VOL_x64FRE_en-us.iso` (Windows Server 2025)
|
||
|
||
- [x] **CredentialManager** PowerShell module v2.0 installato (Scope: CurrentUser)
|
||
|
||
- [x] **Rete VM**: VMnet8 (NAT) — internet access per pip/nuget/npm durante i build
|
||
|
||
## Template VM
|
||
|
||
- [x] **Provision template VM** in VMware Workstation — segui questi passi nell'ordine esatto:
|
||
|
||
### Fase A — Crea e installa la VM (NIC: NAT per internet)
|
||
- [x] 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`
|
||
- [x] Installa Windows Server 2025 dall'ISO
|
||
- [x] All'interno della VM (come Administrator), abilita WinRM:
|
||
```cmd
|
||
winrm quickconfig -q
|
||
```
|
||
```powershell
|
||
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
|
||
```
|
||
- [x] **Attiva Windows Server 2025** (prima dello snapshot — i linked clone ereditano l'attivazione):
|
||
```cmd
|
||
slmgr /dli
|
||
```
|
||
Se non attivato, scegli il metodo appropriato:
|
||
- **KMS** (server KMS in rete): `slmgr /skms <IP_KMS>` poi `slmgr /ato`
|
||
- **MAK/Retail key**: `slmgr /ipk XXXXX-XXXXX-XXXXX-XXXXX-XXXXX` poi `slmgr /ato`
|
||
- **KMS pubblico** (alternativa lab): vedi https://github.com/robin113x/Windows-Server-Activation/
|
||
Verifica: `slmgr /xpr` — deve mostrare attivazione permanente o scadenza lontana
|
||
- [x] Nota l'IP assegnato dal DHCP di VMnet8 (es. `192.168.79.x`) — `ipconfig` nella VM
|
||
|
||
### Fase B — Provisioning automatico dall'host
|
||
- [x] Dall'host, esegui:
|
||
```powershell
|
||
cd n:\Code\Workspace\Local-CI-CD-System\template
|
||
.\Prepare-WinBuild2025.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/5986 raggiungibile, Setup-WinBuild2025.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-WinBuild2025.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-WinBuild2025.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
|
||
- [x] Archivia credenziali guest sull'host (PowerShell elevato con CredentialManager):
|
||
```powershell
|
||
New-StoredCredential -Target "BuildVMGuest" -UserName "ci_build" `
|
||
-Password "<your-build-password>" -Persist LocalMachine
|
||
```
|
||
- [x] Verifica `GITEA_CI_TEMPLATE_PATH` in `F:\CI\act_runner\config.yaml` — aggiornato a `CI-WinBuild.vmx` (2026-05-08)
|
||
|
||
## Scripts Verification
|
||
|
||
- [x] Test `New-BuildVM.ps1` standalone — clone `Clone_test-002` creato correttamente (2026-05-08)
|
||
- [x] Test `Wait-VMReady.ps1` standalone — exit 0 in ~5s, VM pronta (2026-05-08)
|
||
- [x] Test `Invoke-RemoteBuild.ps1` — dotnet restore + build OK, artifacts.zip creato in VM (2026-05-08)
|
||
- [x] Test `Get-BuildArtifacts.ps1` — artifacts.zip (78 KB) copiato in F:\CI\Artifacts\test-003\ (2026-05-08)
|
||
- [x] Test `Remove-BuildVM.ps1` — VM spenta + directory rimossa correttamente (2026-05-08)
|
||
- [x] Test `Invoke-CIJob.ps1` end-to-end su una soluzione reale (nsis-plugin-nsinnounp)
|
||
- [x] e2e-008: SUCCESS — build OK + artifact raccolto (1632 KB)
|
||
- [x] e2e-009: SUCCESS — cleanup automatico VM + clone confermato (finally block)
|
||
- [x] `scripts/Cleanup-OrphanedBuildVMs.ps1` creato e verificato — `-WhatIf`, soglia `-MaxAgeHours`, hard stop + deleteVM + rimozione dir (2026-05-09)
|
||
- [x] `scripts/Remove-BuildVM.ps1` fix `vmrun deleteVM` exit -1 — poll `vmrun list` + retry 3× backoff 0/3/6s (2026-05-09)
|
||
|
||
## Runner Configuration
|
||
|
||
- [x] `runner/config.yaml` aggiornato con percorsi `F:\CI\`, rete VMnet8 (NAT), label corretti
|
||
- [x] `capacity: 4` configurato (64 GB RAM / ~8 GB per VM)
|
||
- [x] Aggiornare `GITEA_CI_TEMPLATE_PATH` in `config.yaml` dopo creazione template VM
|
||
- [x] Archiviare credenziali guest in Windows Credential Manager (target: `BuildVMGuest`)
|
||
|
||
## Gitea Workflow Integration
|
||
|
||
- [x] Copiare `gitea/workflow-example.yml` in `.gitea/workflows/build.yml` nel repo di test
|
||
(workflow `build-nsis.yml` già presente e funzionante per `nsis-plugin-nsinnounp`)
|
||
- [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)
|
||
- [ ] **[P3] Adattare workflow per altri repository** — generalizzare `BUILD_COMMAND` e `ARTIFACT_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**:
|
||
- [x] Generare cert self-signed nel template *prima* dello snapshot (già in Deploy post-install.ps1).
|
||
- [x] Sostituire `New-PSSession -ComputerName $ip` con `-UseSSL -Port 5986 -Authentication Basic` in `Invoke-RemoteBuild.ps1`, `Get-BuildArtifacts.ps1`, `Prepare-WinBuild2025.ps1`, `Validate-*.ps1`.
|
||
- [x] Rimuovere `AllowUnencrypted=true` da Deploy post-install.ps1 e Setup-WinBuild2025.ps1 (assertion → false).
|
||
- [x] `-SkipCACheck`/`-SkipCNCheck`/`-SkipRevocationCheck` mantenuti nei `New-PSSessionOption` e `New-WSManSessionOption` (cert lab self-signed — documentato inline).
|
||
- [x] Regola firewall `WinRM-HTTPS` in Deploy ristretta a `RemoteAddress '192.168.79.0/24'`.
|
||
- [x] `Wait-VMReady.ps1` usa `New-WSManSessionOption` + `Test-WSMan -Port 5986 -UseSSL`.
|
||
- [x] `Prepare-WinBuild2025.ps1`: preflight TCP/5986, host non imposta più `AllowUnencrypted`; solo `TrustedHosts` viene salvato/ripristinato.
|
||
- [x] `Validate-DeployState.ps1`, `Validate-SetupState.ps1`: connessione HTTPS/5986, check `AllowUnencrypted=false`.
|
||
|
||
### 1.2 [P0] [ ] Restringere `TrustedHosts` lato host (audit-only — residuo: audit host pre-esistenti)
|
||
File: nessuno script imposta `*` sull'host. Stato corrente:
|
||
- `Setup-Host.ps1` non tocca TrustedHosts (verificato 2026-05-10)
|
||
- [template/Prepare-WinBuild2025.ps1](template/Prepare-WinBuild2025.ps1) appende `$VMIPAddress` ai `TrustedHosts` e ripristina nel `finally` (post-§1.1: solo TrustedHosts, no AllowUnencrypted)
|
||
- [docs/HOST-SETUP.md:121-126](docs/HOST-SETUP.md:121) raccomanda `'192.168.79.*'` in setup manuale
|
||
|
||
**Azione residua**: audit eventuali host già configurati con `*` (eredità manuale) e
|
||
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 — STRUTTURA AGGIUNTA 2026-05-10 (valori da riempire)
|
||
|
||
`$script:Hashes` + `Assert-Hash` aggiunti a [template/Setup-WinBuild2025.ps1](template/Setup-WinBuild2025.ps1).
|
||
`Assert-Hash` viene chiamato dopo ogni download (dotnet-install.ps1, python installer, vs_buildtools.exe).
|
||
Se l'hash è `''` viene emesso un warning ma l'esecuzione continua (non-breaking).
|
||
|
||
**Azione residua — OBBLIGATORIA prima del prossimo refresh snapshot**:
|
||
- [ ] Scaricare i tre file, calcolare hash, riempire `$script:Hashes` in Setup-WinBuild2025.ps1:
|
||
```powershell
|
||
# Inside or outside VM — qualsiasi PowerShell:
|
||
(Get-FileHash 'python-3.13.3-amd64.exe' -Algorithm SHA256).Hash # → $script:Hashes['Python']
|
||
(Get-FileHash 'dotnet-install.ps1' -Algorithm SHA256).Hash # → $script:Hashes['DotNetInstallScript']
|
||
(Get-FileHash 'vs_buildtools.exe' -Algorithm SHA256).Hash # → $script:Hashes['VSBuildToolsBootstrapper']
|
||
```
|
||
- [ ] Verificare che `Assert-Hash` throwi correttamente su hash errato (test con hash fake).
|
||
|
||
### 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.5 [P1] [ ] PAT mai persistito quando si abilita `-UseGitClone`
|
||
Requisiti di sicurezza per l'implementazione del clone in-VM. La sequenza operativa
|
||
(quando, dove, come) è dettagliata nella sezione "In-VM Git Clone (Ottimizzazione) — riferimento §3.3".
|
||
|
||
Vincoli da rispettare in qualsiasi implementazione di `-UseGitClone`:
|
||
- [ ] PAT recuperato da Credential Manager **subito prima** della WinRM session, mai prima.
|
||
- [ ] Iniettato via `$using:cred` o env var di sessione, mai in argv né in log/transcript.
|
||
- [ ] Cancellato in `finally` interno alla scriptblock guest.
|
||
- [ ] **Grep automatico post-job sui log**: se il PAT compare grezzo in `$jobLog` o
|
||
`transcript.txt`, marcare la build come fallita e ruotare il PAT (regola di safety net,
|
||
non sostituisce le precedenti).
|
||
|
||
### 1.6 [P2] [ ] Defender + Firewall + UAC tutti disattivati nel template — documentare il modello di minaccia
|
||
File: [template/Deploy-WinBuild2025.ps1:485-551](template/Deploy-WinBuild2025.ps1) (UAC, ServerManager, DisableCAD, OOBE — set da Deploy), [template/Setup-WinBuild2025.ps1:176-336](template/Setup-WinBuild2025.ps1) (Firewall Step 1, Defender Step 2 validation-only post-refactor 2026-05-09, WinRM Step 3, User Step 4, UAC Step 4b, Dirs Step 5).
|
||
|
||
Stato attuale (post-refactor §7):
|
||
- **Defender**: disabled da Deploy (`DisableAntiSpyware=1` GPO + RTP off) — Setup Step 2 è validation-only
|
||
- **Firewall**: disabled da Setup Step 1 (`Set-NetFirewallProfile ... -Enabled False`); §7.1 prevede spostamento a Deploy
|
||
- **UAC**: `EnableLUA=0`, `LocalAccountTokenFilterPolicy=1` — duplicato Deploy + Setup Step 4b (§7.1 lo riduce a Assert-Step in Setup)
|
||
|
||
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).
|
||
|
||
**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 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:112,148,185](scripts/Invoke-RemoteBuild.ps1) (3 chiamate `Compress-Archive`).
|
||
Prerequisito: 7-Zip nel template — vedi §6.6 e sezione "In-VM Git Clone".
|
||
|
||
**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)").
|
||
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](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-WinBuild2025.ps1:123](template/Setup-WinBuild2025.ps1:123) (`[string] $BuildPassword` → `ConvertTo-SecureString -AsPlainText` riga 253).
|
||
- `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.
|
||
|
||
### 6.6 [P2] [ ] Toolchain Tier-1 in Setup-WinBuild2025
|
||
File: [template/Setup-WinBuild2025.ps1](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 da `C:\CI\` runtime
|
||
— `C:\CI\` resta per artefatti job e worker temp; `C:\BuildTools\` per software statico).
|
||
Documentare in `docs/BEST-PRACTICES.md` prima di implementare.
|
||
- Aggiungere a `PATH` machine-wide
|
||
- Defender già off → no scan overhead durante install
|
||
- Step esegue in seriale dopo `Toolchain cross-check` esistente, prima di `Cleanup`
|
||
- `Final pre-snapshot gate`: aggiungere check `where.exe` per 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 --version` returns 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/Deploy-WinBuild2025.ps1),
|
||
[template/Setup-WinBuild2025.ps1](template/Setup-WinBuild2025.ps1).
|
||
|
||
- [x] Aggiungere `Set-NetFirewallProfile -Enabled False` (tutti i profili) in Deploy post-install.ps1
|
||
- [x] Aggiungere `MaxMemoryPerShellMB=2048`, `IdleTimeOut=7200000`, `MaxProcessesPerShell=25`, `StartupType=Automatic` in Deploy dopo Enable-PSRemoting
|
||
- [x] Aggiungere in Deploy items mancanti rispetto a Setup 5c: lock screen (`NoLockScreen`), SM GPO policy key (`DoNotOpenAtLogon`), SM scheduled task disable, `DisableCAD` in Winlogon, OOBE non-policy key
|
||
- [x] Setup Step 1 (Firewall) → Assert-Step only
|
||
- [x] Setup Step 2 (Defender) → già validation-only (refactor 2026-05-09)
|
||
- [x] Setup Step 3 (WinRM tuning) → Assert-Step only (rimuovi Enable-PSRemoting, winrm set, Set-Item, Set-Service)
|
||
- [x] Setup Step 4b (UAC) → Assert-Step only
|
||
- [x] Setup Step 5b (Explorer LaunchTo) → Assert-Step only
|
||
- [x] Setup Step 5c (Server Manager / DisableCAD / OOBE) → Assert-Step only
|
||
- [x] 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/Deploy-WinBuild2025.ps1),
|
||
[template/Setup-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.
|
||
|
||
- [x] Deploy: rimuovere `sc.exe config wuauserv start= disabled` (e UsoSvc, WaaSMedicSvc)
|
||
- [x] Deploy: aggiungere GPO `DisableWindowsUpdateAccess=1` (era mancante; `NoAutoUpdate=1` già presente)
|
||
- [x] Setup Step 6 Step A: già rimuove `DisableWindowsUpdateAccess` + `NoAutoUpdate` prima del COM API — nessuna modifica necessaria
|
||
- [x] Setup Step 6 Step D: già fa `Set-Service start=Manual` + `Start-Service` — idempotente con servizi già Manual
|
||
- [x] Setup Step 6b: già disabilita wuauserv/UsoSvc + GPO keys — nessuna modifica necessaria
|
||
- [x] E2e validation — completata §7.4 (2026-05-10)
|
||
|
||
### 7.3 [P2] [x] Update header docs di entrambi gli script
|
||
|
||
- [x] [template/Deploy-WinBuild2025.ps1](template/Deploy-WinBuild2025.ps1) `.DESCRIPTION`: aggiornato con tutte le hardening voci (firewall, WinRM tuning, WU GPO strategy B, lock screen, SM, OOBE)
|
||
- [x] [template/Setup-WinBuild2025.ps1](template/Setup-WinBuild2025.ps1) `.DESCRIPTION`: Steps 1/3/4b/5b/5c marcati "validation-only — set by Deploy"; step ordering rationale aggiornato
|
||
- [x] [docs/WINDOWS-TEMPLATE-SETUP.md](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
|
||
|
||
- [x] Deploy stand-alone su VM test → `Get-NetFirewallProfile` tutti `Enabled=False`, `Get-Service wuauserv | Select StartType` → `Manual`, `NoAutoUpdate=1` GPO presente
|
||
- [x] Prepare dopo Deploy con `-SkipWindowsUpdate` → tutti `Assert-Step` passano `[OK]` senza eseguire Set (log mostra solo validation output)
|
||
- [x] Prepare senza `-SkipWindowsUpdate` → WU (0 update trovati, ResultCode=0), Step 6b disabilita wuauserv/UsoSvc (`StartType=Disabled`)
|
||
- [x] Final gate del guest passa su tutti e 7 i check; snapshot `BaseClean` preso
|
||
|
||
**Fix emersi durante §7.4** (applicati):
|
||
- Deploy: `UsoSvc` impostato a `Manual` esplicitamente (default Server 2025 = Automatic)
|
||
- Setup cleanup: `Set-Service -StartupType Disabled` ri-applicato su wuauserv/UsoSvc dopo DISM `StartComponentCleanup` (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 `-UseGitClone` in `Invoke-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`
|
||
|
||
- [ ] **Invoke-CIJob.ps1 — aggiungere switch `-UseGitClone`**
|
||
- [ ] Quando `-UseGitClone` è attivo:
|
||
- Saltare Phase 1 (host git clone) e la creazione del `HostSourceDir` temporaneo
|
||
- 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.ps1` rispettando i vincoli sicurezza di §1.5
|
||
- [ ] Comportamento default invariato (`-UseGitClone` = `$false`) — nessuna regressione
|
||
|
||
- [ ] **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')`)
|
||
|
||
- [ ] **Test e2e con `-UseGitClone`**
|
||
- [ ] Aggiornare snapshot `BaseClean` con Git installato
|
||
- [ ] Eseguire `e2e-010` con `-UseGitClone` su `nsis-plugin-nsinnounp` (ha submoduli)
|
||
- [ ] Confrontare tempo totale con e2e-009 (senza `-UseGitClone`)
|
||
- [ ] Verificare che il PAT non appaia nei log di `Invoke-CIJob.ps1`
|
||
|
||
---
|
||
|
||
## 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)
|
||
|
||
- [ ] **Workflow Gitea — build matrix**
|
||
- [ ] 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
|
||
|
||
- [ ] **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
|
||
|
||
---
|
||
|
||
## Suggerimento di sequencing
|
||
|
||
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 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 |
|