Files
local-ci-cd-system/TODO.md
T
Simone 97ec820ebb ci: rename build-nsInnoUnp.yml -> build-ns7zip.yml; update doc refs
Workflow now targets Simone/nsis-plugin-ns7zip. Renamed the file and
the workflow `name:`, and updated all references in active docs
(plans/archived and docs/archived left as frozen history).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 20:12:25 +02:00

879 lines
51 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TODO — Local CI/CD System
<!-- Last updated: 2026-05-11 — Sprint 15: §7.5 DONE (Rinomina Setup-* → Install-CIToolchain-*: WinBuild2025/2022/Linux2404; tutti i riferimenti aggiornati in Prepare/Deploy/docs/README/TODO/action.yml) -->
<!-- Sprint 14: §6.4+§6.5 DONE (build-ns7zip.yml matrix windows+linux via composite action; ExtraGuestEnv chain Invoke-CIJob→Invoke-RemoteBuild, Windows session inject + Linux export prefix) -->
<!-- Sprint 13: §6.2 Composite Action DONE (gitea/actions/local-ci-build/action.yml — inputs: build-command/artifact-source/submodules/guest-os/use-git-clone) -->
<!-- Sprint 12: §6.1 Linux Build VM DONE (Deploy/Prepare/Setup/Transport/e2e 4xPASS — nsis7z.dll 2272 KB parity Windows) -->
<!-- Sprint 11: §6.6 Tier-2 Toolchain DONE (Pwsh7/NSIS/CMake/Node.js/WiX/gh CLI/Sysinternals/vcpkg — Step 12 in Install-CIToolchain-WinBuild2025) -->
> 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 + Install-CIToolchain-WinBuild2025)
> - [docs/LINUX-TEMPLATE-SETUP.md](docs/LINUX-TEMPLATE-SETUP.md) — provisioning template Linux Ubuntu 24.04 (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-11 (post Sprint 15: §7.5 DONE — Rinomina Setup-* → Install-CIToolchain-* (WinBuild2025/2022/Linux2404); post Sprint 14: §6.4+§6.5 DONE)_
**Stato generale**: §1, §2, §4, §5, §7 chiusi. §3 completo. §6 chiuso: 6.1-6.2 DONE, 6.3 deferred, 6.4-6.6 DONE. Prossimi: Test Plan validation con VM reali.
| Stato | Area | Done | Open | Note |
| ----- | ------------------------------- | ---: | ---: | ------------------------------------------------------------------ |
| [x] | Setup & Infrastructure | 8 | 0 | Gitea + runner + dirs + rete tutti operativi |
| [x] | Template VM (Fasi A→D) | 4 | 0 | Snapshot `BaseClean` preso, credenziali in Credential Manager |
| [x] | Scripts Verification | 3 | 0 | e2e-008/009 SUCCESS, cleanup orfani + retry deleteVM ok |
| [x] | Runner Configuration | 2 | 0 | `config.yaml`, capacity 4, `BuildVMGuest` |
| [x] | Gitea Workflow Integration | 5 | 0 | tutti completati — example.yml usa composite action |
| [x] | §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) |
| [x] | §2 Affidabilità & resilienza | 7 | 0 | tutto done (2.1-2.7) |
| [x] | §3 Performance & throughput | 4 | 0 | 3.1/3.2/3.3/3.6 done; 3.4/3.5 deferred — **§3 CHIUSO** |
| [x] | §4 Osservabilità & manutenzione | 3 | 0 | 4.1/4.3/4.4 done (4.2 Prometheus, 4.5 Dashboard rimossi) |
| [x] | §5 Qualità codice & test | 5 | 0 | 5.1/5.2/5.3/5.4/5.5 done |
| [x] | §6 Scalabilità & estensioni | 5 | 0 | 6.1-6.2 done, 6.3 deferred (home lab), 6.4-6.5 done, 6.6 done |
| [x] | §7 Refactor Deploy ↔ Setup | 5 | 0 | 7.1-7.5 done; Setup-* → Install-CIToolchain-* (2026-05-11) |
| [x] | In-VM Git Clone (§3.3) | 4 | 0 | COMPLETATO 2026-05-10 — e2e PASS: 34s/25.7% vs baseline |
| [x] | Linux Build VM (§6.1) | 5 | 0 | Deploy/Prepare/Setup/Transport + doc + e2e PASS nsis7z.dll 2272 KB |
**Prossimi passi consigliati** (vedi anche "Suggerimento di sequencing" in fondo):
1. **Test Plan** — eseguire `Invoke-Pester tests\ -Output Detailed` + validare §2.1/§3.1/§3.6 con VM reali
2. **Test Plan** — validazione con VM reali (§2.1 IP race, §3.1 shared cache, §3.6 benchmark, §4.1 JSONL) — prossima priorità
3. **Security** — Rifiori se condizioni cambiano; vedi sezione "Deferred (Home Lab)"
4. **§3.5** — vCPU/RAM tuning per workload (P2, deferred — home lab optimization)
5. **Validate-SetupState.ps1** — update riferimento a `Install-CIToolchain-*` se compare il vecchio nome
---
## 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-ns7zip` — testato e2e, workflow in `gitea/workflows/build-ns7zip.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\WinBuild2025\`
- [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\WinBuild2025\WinBuild2025.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, Install-CIToolchain-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] `Install-CIToolchain-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 `WinBuild2025.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-ns7zip)
- [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-ns7zip.yml` già presente e funzionante per `nsis-plugin-ns7zip`)
- [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)
- [x] **[P3] Adattare workflow per altri repository** — generalizzare `BUILD_COMMAND` e `ARTIFACT_SOURCE`.
COMPLETATO 2026-05-11: `gitea/workflow-example.yml` riscritto per usare la composite action
`Simone/local-ci-cd-system/.gitea/actions/local-ci-build@main`. Commenti inline guidano
la personalizzazione di `build-command`, `artifact-source`, submodules, SSH URL,
`extra-guest-env-json` e `artifact-name`. Nessuna logica di orchestrazione nei repo chiamanti.
---
## 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 Install-CIToolchain-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.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](docs/BEST-PRACTICES.md).
**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](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] [x] Cleanup orfani schedulato — COMPLETATO 2026-05-10
File: [scripts/Cleanup-OrphanedBuildVMs.ps1](scripts/Cleanup-OrphanedBuildVMs.ps1) — script pronto, scheduling registrato da [scripts/Register-CIScheduledTasks.ps1](scripts/Register-CIScheduledTasks.ps1).
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] [x] Retention artifact + log automatica — COMPLETATO 2026-05-10
File: [scripts/Invoke-RetentionPolicy.ps1](scripts/Invoke-RetentionPolicy.ps1) — script creato; scheduling via [scripts/Register-CIScheduledTasks.ps1](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](scripts/Remove-BuildVM.ps1).
`[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](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] [x] Backup automatico VMDK template — COMPLETATO 2026-05-10
File: [scripts/Backup-CITemplate.ps1](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](scripts/Watch-RunnerHealth.ps1); task registrato da [scripts/Register-CIScheduledTasks.ps1](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/Set-TemplateSharedFolders.ps1), [scripts/Invoke-RemoteBuild.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**:
1. Fermare template VM.
2. `.\Set-TemplateSharedFolders.ps1` (una tantum, pre-snapshot).
3. Passare `-UseSharedCache` a `Invoke-RemoteBuild.ps1` nei 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](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 -mx1` per 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] [x] In-VM clone (`-UseGitClone`) — COMPLETATO 2026-05-10
File: [scripts/Invoke-CIJob.ps1](scripts/Invoke-CIJob.ps1), [scripts/Invoke-RemoteBuild.ps1](scripts/Invoke-RemoteBuild.ps1).
**Implementazione**:
- Invoke-CIJob.ps1: `-UseGitClone` switch + conditional Phase 1 (skip host clone)
- Invoke-RemoteBuild.ps1: Mode 1 (host) vs Mode 2 (guest) + params (CloneUrl, CloneBranch, CloneCommit, CloneSubmodules)
- Guest clone: git clone direttamente nel VM, PAT da Credential Manager (§1.5 no-log cleanup)
- Prerequisiti satisfatti: Git + 7-Zip in template (§6.6, completato)
**Beneficio**: Elimina host-zip-transfer overhead (rilevante per repo > 200 MB con submoduli).
**Prossimo**: E2E test con nsis-plugin-ns7zip (-UseGitClone flag) per misurare impatto.
### 3.5 [P2] [ ] vCPU/RAM tuning per workload — DEFERRED (home lab)
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-ns7zip` (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.
**Deferred**: Non necessario per home lab; priorità bassa fino a che performance non è bottleneck.
### 3.6 [P2] [x] Benchmark baseline — COMPLETATO 2026-05-10
File: [scripts/Measure-CIBenchmark.ps1](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](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.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.
---
## 5. Qualità codice & test
### 5.1 [P1] [x] Pester smoke tests sugli script — COMPLETATO 2026-05-10
File: [tests/_Common.Tests.ps1](tests/_Common.Tests.ps1), [tests/New-BuildVM.Tests.ps1](tests/New-BuildVM.Tests.ps1), [tests/Remove-BuildVM.Tests.ps1](tests/Remove-BuildVM.Tests.ps1), [tests/Wait-VMReady.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-CISessionOption` TLS flags, `Resolve-VmrunPath` throw/return, `Invoke-Vmrun` ExitCode/Output/ThrowOnError
- `New-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 dir
- `Wait-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](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](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] [x] Type hints nei param block — COMPLETATO 2026-05-10
File: tutti script in `scripts/` — 15 file, tutti con type hints.
**Coverage**: Tutti param hanno type hints (`[string]`, `[int]`, `[switch]`) +
validation (`[ValidateRange]`, `[ValidatePattern]`, `[ValidateScript]`) +
`[Parameter(Mandatory)]` dove necessario.
---
## 6. Scalabilità & estensioni
### 6.1 [P2] [x] 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.
Implementato — vedi 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] [x] Workflow generico riutilizzabile (composite action)
File: [gitea/actions/local-ci-build/action.yml](gitea/actions/local-ci-build/action.yml).
**Status**: COMPLETATO 2026-05-11 — Composite action implementata.
Inputs: `build-command` (required), `artifact-source`, `submodules`, `guest-os`, `use-git-clone`,
`configuration`, `template-path`, `snapshot-name`, `artifact-name`, `artifact-retention-days`.
Outputs: `artifact-path`, `artifact-name`.
Inputs passati via variabili d'ambiente (no injection). Upload artifact incluso nell'action.
Usage da qualsiasi repo:
```yaml
jobs:
build:
runs-on: windows-build # or linux-build
steps:
- uses: actions/checkout@v4
- uses: Simone/local-ci-cd-system/.gitea/actions/local-ci-build@main
with:
build-command: 'python build_plugin.py --final --dist-dir dist'
artifact-source: 'dist'
submodules: 'true'
```
### 6.3 [DEFERRED — home lab, bassissima priorità] 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.
**Deferred**: in un home lab single-host questa funzionalità non serve. Non pianificare.
### 6.4 [P3] [x] Build matrix nel workflow
File: [gitea/workflows/build-ns7zip.yml](gitea/workflows/build-ns7zip.yml).
**Status**: COMPLETATO 2026-05-11 — `build-ns7zip.yml` riscritto con matrix strategy.
Matrix `target: [windows, linux]`, `runs-on: ${{ matrix.target }}-build`,
`fail-fast: false`. Usa la composite action con `job-id-suffix: ${{ matrix.target }}`
per artifact dir separate e `repo-url: ssh://gitea-ci/Simone/nsis-plugin-ns7zip.git`.
Artifact separati: `nsis-plugin-ns7zip-windows-<tag>` e `nsis-plugin-ns7zip-linux-<tag>`.
### 6.5 [P3] [x] Secret injection workflow-level
**Status**: COMPLETATO 2026-05-11 — `ExtraGuestEnv [hashtable]` aggiunto a:
- `Invoke-CIJob.ps1` (param `-ExtraGuestEnv`, forwarded a Invoke-RemoteBuild)
- `Invoke-RemoteBuild.ps1` (param `-ExtraGuestEnv`, iniettato nei guest):
- **Windows**: `[System.Environment]::SetEnvironmentVariable` nel session ScriptBlock prima del build command
- **Linux**: `export KEY='val'; ` prefisso prima della `cd && buildcmd`
- `action.yml`: input `extra-guest-env-json` (JSON object), parsato via `ConvertFrom-Json`, mai loggato
Usage dal workflow:
```yaml
- uses: Simone/local-ci-cd-system/.gitea/actions/local-ci-build@main
with:
build-command: 'python sign_and_build.py'
extra-guest-env-json: '{"SIGN_PASS":"${{ secrets.SIGN_PASS }}","CERT_THUMBPRINT":"${{ secrets.CERT_THUMBPRINT }}"}'
```
### 6.6 [P2] [x] Toolchain Tier-1 + Tier-2 in Install-CIToolchain-WinBuild2025
File: [template/Install-CIToolchain-WinBuild2025.ps1](template/Install-CIToolchain-WinBuild2025.ps1),
[template/Prepare-WinBuild2025.ps1](template/Prepare-WinBuild2025.ps1).
**Status**: COMPLETATO 2026-05-11 — Step 11 (Tier-1: Git + 7-Zip) + Step 12 (Tier-2: 8 tools) implementati.
**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.
### Tier-1 — Step 11 (implementato)
| Tool | Versione | Razionale |
| ------------------- | ---------------- | ------------------------------------------------------ |
| **Git for Windows** | 2.54.0.windows.1 | Self-clone in VM, submodule fetch, fallback §3.2 |
| **7-Zip** | 26.01 | Unpack/zip universale; usato da Step 12 stesso (vcpkg) |
| **PowerShell 7.x** | 7.6.1 | Parallel pipelines, operatori moderni, pwsh in CI |
| **NSIS** | 3.12 | Build installer Windows (es. `nsis-plugin-ns7zip`) |
| **gh CLI** | 2.92.0 | Upload artifact a release Gitea/GitHub, comment PR |
### Tier-2 — Step 12 (implementato)
| Tool | Versione | Razionale |
| ---------------- | ---------------------- | --------------------------------------------- |
| **CMake** | 3.31.6 | Build system C/C++ cross-platform |
| **Node.js LTS** | 22.14.0 | Frontend/Electron pipelines, npm toolchain |
| **WiX Toolset** | 4.0.5 (dotnet tool) | MSI building da .NET projects |
| **Sysinternals** | latest (CDN Microsoft) | Diagnostica process/handle quando build flaky |
| **vcpkg** | HEAD (shallow clone) | 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/Install-CIToolchain-WinBuild2025.ps1](template/Install-CIToolchain-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/Install-CIToolchain-WinBuild2025.ps1](template/Install-CIToolchain-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/Install-CIToolchain-WinBuild2025.ps1](template/Install-CIToolchain-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 `WinBuild2025.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] [x] Rinomina Setup-* → Install-CIToolchain-* (2026-05-11)
Rinominati i tre script guest-side:
- `template/Setup-WinBuild2025.ps1``template/Install-CIToolchain-WinBuild2025.ps1`
- `template/Setup-WinBuild2022.ps1``template/Install-CIToolchain-WinBuild2022.ps1`
- `template/Setup-LinuxBuild2404.sh``template/Install-CIToolchain-Linux2404.sh`
Tutti i riferimenti aggiornati in: Prepare-WinBuild2025/2022.ps1, Prepare-LinuxBuild2404.ps1,
Deploy-WinBuild2025/2022.ps1, Deploy-LinuxBuild2404.ps1, README.md, TODO.md,
docs/WINDOWS-TEMPLATE-SETUP.md, docs/LINUX-TEMPLATE-SETUP.md, docs/TEST-PLAN-v1.3-to-HEAD.md,
gitea/actions/local-ci-build/action.yml.
---
## 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 (`Install-CIToolchain-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
- [x] **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 Install-CIToolchain-WinBuild2025.ps1
una volta applicato §6.6
- curl.exe: già presente in Windows Server 2025 — verificare con `where.exe curl`
- [x] **Invoke-CIJob.ps1 — aggiungere switch `-UseGitClone`**
- [x] 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
- [x] Comportamento default invariato (`-UseGitClone` = `$false`) — nessuna regressione
- [x] **Invoke-RemoteBuild.ps1 — modalità in-VM clone**
- [x] Aggiungere parametri: `-CloneUrl`, `-CloneBranch`, `-CloneCommit`, `-GitPat`
(tutti opzionali, usati solo quando `-UseGitClone` è attivo)
- [x] 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
```
- [x] Implementare iniezione/pulizia PAT secondo §1.5 (env var sessione, no argv, no log,
cleanup `[System.Environment]::SetEnvironmentVariable('CI_GIT_PAT', $null, 'Process')`)
- [x] **Test e2e con `-UseGitClone`** — COMPLETATO 2026-05-10
- [x] Aggiornare snapshot `BaseClean` con Git installato
- [x] Eseguire `e2e-010` con `-UseGitClone` su `nsis-plugin-ns7zip` (ha submoduli)
- [x] Confrontare tempo totale con e2e-009 (senza `-UseGitClone`) — 34s / 25.7% più veloce
- [x] Verificare che il PAT non appaia nei log di `Invoke-CIJob.ps1`
---
## Linux Build VM — riferimento §6.1 [IMPLEMENTATO]
Supporto build su Linux aggiunto con le seguenti fasi completate:
- [x] **Template VM Linux** — script di provisioning per Ubuntu Server 24.04 LTS
- [x] `template/Deploy-LinuxBuild2404.ps1` — crea VM da cloud VMDK, cloud-init, SSH wait
- [x] `template/Install-CIToolchain-Linux2404.sh` — installa toolchain CI (GCC, Clang, CMake, Python 3, git, 7z), hardening SSH, swap off, timer apt off
- [x] `template/Prepare-LinuxBuild2404.ps1` — SCP script, esegue via SSH, valida, snapshot `BaseClean-Linux`
- [x] **Invoke-CIJob.ps1 — branch Linux**
- [x] Parametro `-GuestOS` (Windows/Linux/Auto) con auto-detect dal VMX `guestOS` field
- [x] Transport SSH via `scripts/_Transport.psm1` (Invoke-SshCommand, Copy-SshItem, Test-SshReady)
- [x] Phase 4/5/6 branch su GuestOS: SSH per Linux, WinRM per Windows (nessuna regressione)
- [x] **Workflow Gitea — build matrix**
- [x] Label `linux-build:host` aggiunto a `runner/config.yaml`
- [x] `GITEA_CI_LINUX_TEMPLATE_PATH` e `GITEA_CI_SSH_KEY_PATH` aggiunti alle env vars del runner
- [x] Matrix strategy cross-platform (windows + linux) documentata in `gitea/workflow-example.yml`
- [x] **Doc updates**
- [x] `docs/ARCHITECTURE.md` — SSH transport, _Transport.psm1, Linux VM boxes, topology SSH :22
- [x] `docs/CI-FLOW.md` — Step 0: Linux template + SSH key; Step 6 SSH transport note; Steps 7-8 Linux branch
- [x] `docs/HOST-SETUP.md` — directory layout: keys\ + LinuxBuild2404\; SSH key gen step; Reference Paths Linux
- [x] `docs/LINUX-TEMPLATE-SETUP.md` — Status: implementato; tutte le checkbox Fasi A-E marcate [x]
- [x] **Test e2e Linux** — COMPLETATO 2026-05-11 — Test-Ns7zipBuild-Linux.ps1 4× PASS
- [x] Confermare che la build di esempio funzioni su Linux — nsis7z.dll 2272 KB prodotto
- [x] Confrontare artifact Linux con quelli Windows — 2272 KB su entrambe le piattaforme
---
## 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](docs/BEST-PRACTICES.md).
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/Install-CIToolchain-WinBuild2025.ps1](template/Install-CIToolchain-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.
### 3.4 [P3] [ ] Pre-warm pool di cloni
File: `F:\CI\WarmPool\` (hypothetical); scheduled task via `scripts/Register-CIScheduledTasks.ps1`.
Mantenere 2 cloni avviati in background, pronti per il binding a job. Salta la fase di
`New-BuildVM + Wait-VMReady` (~30-60s) nel percorso critico. Richiede una VM "watcher" che
monitora il pool e ricrea cloni dopo uso.
**Stato attuale**: Disegnato ma non implementato. Dipende da §2.1 (IP allocator).
**Rifiori quando**: Profiling mostra che New-BuildVM+Wait-VMReady è bottleneck >30% nel
tempo totale di job. Con build >2 minuti, overhead stimato ~40% (rilevante solo per 1-min builds).
### 3.5 [P2] [ ] vCPU/RAM tuning per workload
File: [docs/OPTIMIZATION.md](docs/OPTIMIZATION.md), VMX template config.
Esporre parametri `-VMCpu` / `-VMMemory` a `Invoke-CIJob.ps1` per permettere override
da workflow. Attualmente fisso a `numvcpus=4, memsize=6144` — subottimo per build leggeri
(waste) e pesanti (throttle).
**Stato attuale**: Progettato, non implementato. Template supporta la modifica.
**Rifiori quando**: Performance profiling mostra contentione CPU/RAM è bottleneck per la
workload target. Per home lab, 4 vCPU/6 GB è ragionevole compromesso.
### 8.1 [P3] [ ] Mirror GitHub Actions su Gitea locale (rimuovi dipendenza github.com)
File: [.gitea/workflows/lint.yml](.gitea/workflows/lint.yml), Gitea (org `actions`).
Dipendenza residua da `github.com`: `actions/checkout@v4` in `lint.yml` (2×) —
il codice dell'action è scaricato da github.com a runtime (con
`[actions] DEFAULT_ACTIONS_URL = github`). La dipendenza per gli **artifact**
è già stata eliminata (upload/download-artifact rimossi; handoff via
filesystem host — vedi commit `refactor(ci): drop upload/download-artifact`).
Questo task è l'**alternativa "fix 1"** non scelta: mirrorare le action
GitHub usate (`checkout`) in una org `actions` sull'istanza Gitea e
referenziarle via URL Gitea completo, eliminando del tutto github.com.
**Stato attuale**: Deferred. Artifact già github-free (scelta "fix 2"
applicata). Resta solo `actions/checkout@v4` in lint; act_runner la cacha
dopo il primo fetch, quindi outage GitHub impatta solo cold cache.
**Rifiori quando**: Host air-gapped, GitHub non raggiungibile dall'host CI,
o si vuole indipendenza totale da github.com (anche per `checkout`).
---
## 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\WinBuild2025\WinBuild2025.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 |