# Local CI/CD System Sistema CI/CD self-hosted su Windows per build isolati in VM efimere VMware. **Stato**: production-ready — build Windows e Linux verificati e2e; burn-in concorrente a 4 job PASS; §3.3 in-VM git clone attivabile via `-UseGitClone` --- ## Panoramica Il runner [act_runner](https://gitea.com/gitea/act_runner) riceve i job da Gitea Actions. Per ogni job, lo script `Invoke-CIJob.ps1` orchestra l'intero ciclo: ``` Gitea push/tag │ ▼ act_runner (label: windows-build | linux-build) │ ▼ Invoke-CIJob.ps1 │ ├─ 1. git clone repo su host (con submoduli) ├─ 2. Linked clone da snapshot BaseClean / BaseClean-Linux ├─ 3. Avvio VM headless ├─ 4. Attesa readiness (WinRM/5986 per Windows, SSH/22 per Linux) ├─ 5. Source transfer + build (WinRM per Windows, SSH/SCP per Linux) ├─ 6. Copia artifacts in F:\CI\Artifacts\\ └─ finally: stop + rimozione VM clone ``` Ogni build gira in una VM **usa e getta** ripristinata dallo snapshot: Windows Server 2025/2022 (WinRM) o Ubuntu 24.04 (SSH). --- ## Requisiti Host | Componente | Versione / Dettaglio | | ---------------------- | ----------------------------------------------------------------- | | OS | Windows 11 | | CPU/RAM | i9-10900X, 64 GB RAM | | VMware Workstation Pro | installato, `vmrun.exe` disponibile | | PowerShell | 5.1 | | act_runner | v1.0.2 — `F:\CI\act_runner\act_runner.exe` | | Gitea | self-hosted `http://10.10.20.11:3100` / `https://gitea.emulab.it` | ## Template VM ### Windows (snapshot `BaseClean`) | Componente | Versione | | -------------- | ------------------------------------------------------------------------------ | | OS | Windows Server 2025 | | VMX | `F:\CI\Templates\WinBuild2025\WinBuild2025.vmx` (env `GITEA_CI_TEMPLATE_PATH`) | | VS Build Tools | 2026 (MSBuild 18.5.4 / toolset v145) | | .NET SDK | 10.0.203 | | Python | 3.13.3 | | Rete | VMnet8 NAT — `192.168.79.0/24` | | WinRM | HTTPS/5986, Basic auth, self-signed cert (SkipCACheck — lab-only) | Variante **Windows Server 2022**: `*WinBuild2022.ps1` + VMX `F:\CI\Templates\WinBuild2022\WinBuild2022.vmx`. ### Linux (snapshot `BaseClean-Linux`) | Componente | Versione | | ------------ | ---------------------------------------------------------------------------------------- | | OS | Ubuntu 24.04 LTS (cloud image) | | VMX | `F:\CI\Templates\LinuxBuild2404\LinuxBuild2404.vmx` (env `GITEA_CI_LINUX_TEMPLATE_PATH`) | | Toolchain | GCC, G++, Clang, CMake, Ninja, Python 3, Git, 7-Zip | | Rete | VMnet8 NAT — `192.168.79.0/24` | | SSH | porta 22, utente `ci_build`, chiave `F:\CI\keys\ci_linux` | | Runner label | `linux-build:host` | --- ## Struttura repository ``` ├── Setup-Host.ps1 # Bootstrap host: dirs F:\CI\, credenziali, act_runner service ├── PSScriptAnalyzerSettings.psd1 # Regole linting condivise (§5.4) │ ├── scripts/ │ ├── _Common.psm1 # Shared module (vmrun wrapper, diagnostics) │ ├── _Transport.psm1 # SSH/SCP helpers per guest Linux (Invoke-SshCommand, Copy-SshItem) │ ├── Invoke-CIJob.ps1 # Orchestratore principale (Windows WinRM + Linux SSH, -UseGitClone) │ ├── New-BuildVM.ps1 # Linked clone da template │ ├── Wait-VMReady.ps1 # Polling readiness (WinRM/5986 o SSH/22) │ ├── Invoke-RemoteBuild.ps1 # Build remoto (WinRM per Windows, SSH/SCP per Linux) │ ├── Get-BuildArtifacts.ps1 # Raccolta artifacts (WinRM o SCP) │ ├── Remove-BuildVM.ps1 # Stop + rimozione VM clone (-WhatIf) │ ├── Cleanup-OrphanedBuildVMs.ps1 # Cleanup VM orfane (§2.2) │ ├── Invoke-RetentionPolicy.ps1 # Retention artifact + log (§2.3) │ ├── Watch-DiskSpace.ps1 # Alert spazio libero F: (§4.3) │ ├── Watch-RunnerHealth.ps1 # Health check runner (§2.7) │ ├── Backup-CITemplate.ps1 # Backup VMDK template (§2.6) │ ├── Set-TemplateSharedFolders.ps1 # Cache NuGet/pip via shared folders (§3.1) │ ├── Measure-CIBenchmark.ps1 # Benchmark fasi pipeline (§3.6) │ ├── Register-CIScheduledTasks.ps1 # Task Scheduler bootstrap │ ├── Test-CapacityBurnIn.ps1 # Burn-in concorrente N job × R round │ ├── Start-BurnInTest-Linux.ps1 # Wrapper burn-in specifico per LinuxBuild2404 │ └── Get-CIJobSummary.ps1 # Report sintetico ultima esecuzione job │ ├── template/ │ ├── autounattend.template.xml # Template XML per installazione Windows unattended │ ├── Deploy-WinBuild2025.ps1 # Deploy VM + install Windows 2025 unattended (HOST) │ ├── Prepare-WinBuild2025.ps1 # Provisioning toolchain 2025 via WinRM (HOST-side) │ ├── Install-CIToolchain-WinBuild2025.ps1 # Toolchain CI 2025 nella VM (GUEST-side) │ ├── Deploy-WinBuild2022.ps1 # Deploy VM + install Windows 2022 unattended (HOST) │ ├── Prepare-WinBuild2022.ps1 # Provisioning toolchain 2022 via WinRM (HOST-side) │ ├── Install-CIToolchain-WinBuild2022.ps1 # Toolchain CI 2022 nella VM (GUEST-side) │ ├── Deploy-LinuxBuild2404.ps1 # Deploy cloud image Ubuntu 24.04 su VMware (HOST) │ ├── Prepare-LinuxBuild2404.ps1# Provisioning toolchain Linux via SSH + snapshot (HOST-side) │ ├── Install-CIToolchain-Linux2404.sh # Toolchain CI Ubuntu 24.04 nel guest (GUEST-side) │ ├── Validate-DeployState.ps1 # Validazione stato post-Deploy (2025, 2022, Linux) │ └── Validate-SetupState.ps1 # Validazione stato post-Setup (pre-snapshot) │ ├── tests/ # Pester v5 unit tests (§5.1) │ ├── gitea/ │ ├── actions/ │ │ └── local-ci-build/action.yml # Composite action riusabile (wrap Invoke-CIJob.ps1) │ ├── workflows/ │ │ ├── build-nsInnoUnp.yml # Workflow matrix Windows+Linux per nsis-plugin-nsinnounp │ │ └── lint.yml # PSScriptAnalyzer su push/PR (§5.4) │ └── workflow-example.yml # Template generico adattabile │ ├── runner/ │ ├── config.yaml # Configurazione act_runner (no secrets) │ └── Install-Runner.ps1 │ ├── docs/ │ ├── ARCHITECTURE.md # Diagramma sistema e componenti │ ├── CI-FLOW.md # Flusso dettagliato passo-passo │ ├── HOST-SETUP.md # Bootstrap macchina host │ ├── WINDOWS-TEMPLATE-SETUP.md # Provisioning template Windows │ ├── LINUX-TEMPLATE-SETUP.md # Bozza template Linux (§6.1, non implementato) │ ├── OPTIMIZATION.md # Layout disco, snapshot, cache, tuning │ ├── BEST-PRACTICES.md # Sicurezza, WinRM, credenziali, threat model │ ├── RUNBOOK.md # Triage incident comuni (§4.4) │ ├── TEST-PLAN-v1.3-to-HEAD.md # Test plan completo features post-v1.3 │ ├── Setup-GiteaSSH.md # Configurazione SSH alias per Gitea │ └── archived/ # Doc storici / sign-off │ └── TODO.md # Roadmap, audit trail task completati, backlog ``` --- ## Python orchestrator (Phase A — in progress) A new Python 3.11+ rewrite of the orchestrator lives under `src/ci_orchestrator/`. Phase A (see [implementation-plan-A-B](plans/implementation-plan-A-B.md)) is on branch `feature/python-rewrite-and-linux-migration`. As of A5 the Python CLI is the production entry point invoked by `gitea/actions/local-ci-build/action.yml`; the PowerShell scripts in `scripts/` are 3-line shims that forward to the Python sub-commands listed below. ### Setup ```powershell # Production venv on the runner host (one-time) python -m venv F:\CI\python\venv F:\CI\python\venv\Scripts\python.exe -m pip install --upgrade pip F:\CI\python\venv\Scripts\python.exe -m pip install -e ".[dev]" # Local dev venv (NOT committed; see .gitignore) python -m venv .venv .\.venv\Scripts\python.exe -m pip install -e ".[dev]" ``` Configuration: copy [config.example.toml](config.example.toml) to `F:\CI\config.toml` (or set `$env:CI_CONFIG`). Environment variables (`CI_ROOT`, `CI_TEMPLATES`, `CI_BUILD_VMS`, `CI_ARTIFACTS`, `CI_KEYS`) override the file. Dependency manifest and tool config (ruff, mypy, pytest, coverage) live in [pyproject.toml](pyproject.toml). ### CLI sub-commands ```powershell # Discover commands python -m ci_orchestrator --help # Wait for a VM to become reachable (WinRM 5986 or SSH 22 — auto-detected) python -m ci_orchestrator wait-ready ` --vmx F:\CI\BuildVMs\smoke\smoke.vmx --guest-os windows --timeout 300 # Linked-clone lifecycle python -m ci_orchestrator vm new ` --template-path F:\CI\Templates\WinBuild2025\WinBuild2025.vmx ` --snapshot-name BaseClean ` --clone-base-dir F:\CI\BuildVMs ` --job-id smoke-001 python -m ci_orchestrator vm remove --vmx F:\CI\BuildVMs\Clone_smoke-001_*\Clone_smoke-001_*.vmx python -m ci_orchestrator vm cleanup --max-age-hours 12 # Build + artifact collection python -m ci_orchestrator build run ` --vmx F:\CI\BuildVMs\Clone_smoke-001_*\*.vmx ` --script .\build.ps1 --workdir C:\build --guest-os windows python -m ci_orchestrator artifacts collect ` --vmx F:\CI\BuildVMs\Clone_smoke-001_*\*.vmx ` --remote-path C:\build\dist --local-dir F:\CI\Artifacts\smoke-001 # Monitoring (driven by Task Scheduler / systemd timers in B5) python -m ci_orchestrator monitor disk --min-free-gb 50 --drive-letter F python -m ci_orchestrator monitor runner --state-dir F:\CI\act_runner # Reporting python -m ci_orchestrator report job --job-id smoke-001 # End-to-end orchestrator (replaces Invoke-CIJob.ps1) python -m ci_orchestrator job ` --job-id smoke-001 ` --repo-url ssh://gitea-ci/Simone/nsis-plugin-nsinnounp.git ` --branch main ` --template-path F:\CI\Templates\WinBuild2025\WinBuild2025.vmx ` --build-command "python build_plugin.py --final --dist-dir dist" ` --guest-artifact-source dist ``` The PowerShell wrappers (`scripts\Invoke-CIJob.ps1`, `scripts\New-BuildVM.ps1`, `scripts\Wait-VMReady.ps1`, `scripts\Remove-BuildVM.ps1`, `scripts\Cleanup-OrphanedBuildVMs.ps1`, `scripts\Invoke-RemoteBuild.ps1`, `scripts\Get-BuildArtifacts.ps1`, `scripts\Watch-DiskSpace.ps1`, `scripts\Watch-RunnerHealth.ps1`, `scripts\Get-CIJobSummary.ps1`) are kept as 3-line shims for Task Scheduler and external callers — they translate PascalCase parameters to the Python kebab-case CLI and propagate the exit code. ### Local validation gates ```powershell .\.venv\Scripts\python.exe -m ruff check src tests\python .\.venv\Scripts\python.exe -m mypy --strict src .\.venv\Scripts\python.exe -m pytest tests\python -q --cov=ci_orchestrator --cov-fail-under=80 ``` Coverage gate is **80%** as of Phase A5. Regression tests for the `AGENTS.md` "Errori frequenti da evitare" #9–#12 live in [tests/python/test_agents_errors.py](tests/python/test_agents_errors.py) and must stay green. --- ## Setup rapido ### 1a. Template VM Windows **Opzione A — Deploy automatico** (crea VM + installa Windows unattended da ISO): ```powershell cd N:\Code\Workspace\Local-CI-CD-System\template # Windows Server 2025 (corrente): .\Deploy-WinBuild2025.ps1 -ISOPath F:\CI\ISO\WinSrv2025.iso ` -VMPath F:\CI\Templates\WinBuild2025\WinBuild2025.vmx # Windows Server 2022 (alternativa): .\Deploy-WinBuild2022.ps1 -ISOPath F:\CI\ISO\WinSrv2022.iso ` -VMPath F:\CI\Templates\WinBuild2022\WinBuild2022.vmx ``` **Opzione B — installazione manuale**: installa Windows nella VM, abilita WinRM (`winrm quickconfig -q` + `Enable-PSRemoting -Force`), annota l'IP VMnet8. **Provisioning toolchain** (dopo A o B, dalla directory `template/`): ```powershell # Windows Server 2025: .\Prepare-WinBuild2025.ps1 -VMIPAddress 192.168.79.x -StoreCredential # Windows Server 2022: .\Prepare-WinBuild2022.ps1 -VMIPAddress 192.168.79.x -StoreCredential ``` Poi spegni la VM e prendi lo snapshot con nome esatto **`BaseClean`**. ### 1b. Template VM Linux ```powershell cd N:\Code\Workspace\Local-CI-CD-System\template # Deploy cloud image Ubuntu 24.04 (scarica ISO/cloud img, crea VM): .\Deploy-LinuxBuild2404.ps1 -VMPath F:\CI\Templates\LinuxBuild2404\LinuxBuild2404.vmx ``` **Provisioning toolchain** (SSH — la VM deve essere avviata e raggiungibile): ```powershell .\Prepare-LinuxBuild2404.ps1 -VMIPAddress 192.168.79.x ` -VMXPath F:\CI\Templates\LinuxBuild2404\LinuxBuild2404.vmx ` -TakeSnapshot ``` `-TakeSnapshot` esegue automaticamente: reset machine-id → shutdown → snapshot **`BaseClean-Linux`**. Dettagli: [docs/LINUX-TEMPLATE-SETUP.md](docs/LINUX-TEMPLATE-SETUP.md). ### 2. Credenziali guest ```powershell # PowerShell elevato — una volta sola sull'host Import-Module CredentialManager New-StoredCredential -Target "BuildVMGuest" -UserName "ci_build" ` -Password "" -Persist LocalMachine ``` ### 3. act_runner ```powershell # Registra il runner con Gitea (interattivo, usa il registration token da Gitea Admin) F:\CI\act_runner\act_runner.exe register # Installa come servizio Windows nssm install act_runner "F:\CI\act_runner\act_runner.exe" daemon nssm start act_runner ``` Imposta queste variabili d'ambiente nel servizio (`runner/config.yaml`): | Variabile | Valore | | ------------------------------ | --------------------------------------------------- | | `GITEA_CI_TEMPLATE_PATH` | `F:\CI\Templates\WinBuild2025\WinBuild2025.vmx` | | `GITEA_CI_LINUX_TEMPLATE_PATH` | `F:\CI\Templates\LinuxBuild2404\LinuxBuild2404.vmx` | | `GITEA_CI_CLONE_BASE_DIR` | `F:\CI\BuildVMs` | | `GITEA_CI_ARTIFACT_DIR` | `F:\CI\Artifacts` | | `GITEA_CI_GUEST_CRED_TARGET` | `BuildVMGuest` | | `GITEA_CI_SSH_KEY_PATH` | `F:\CI\keys\ci_linux` | Labels runner: `windows-build:host` (job Windows) e `linux-build:host` (job Linux). Dettagli completi: [docs/HOST-SETUP.md](docs/HOST-SETUP.md). ### 4. SSH per Gitea Vedi [docs/Setup-GiteaSSH.md](docs/Setup-GiteaSSH.md) per configurare l'alias `gitea-ci` (SSH port 2222, usato dai workflow live in `git clone ssh://gitea-ci/...`). --- ## Uso — build manuale (test) **Windows guest:** ```powershell & 'N:\Code\Workspace\Local-CI-CD-System\scripts\Invoke-CIJob.ps1' ` -JobId 'test-win-001' ` -RepoUrl 'ssh://gitea-ci/Simone/nsis-plugin-nsinnounp.git' ` -Branch 'main' ` -TemplatePath 'F:\CI\Templates\WinBuild2025\WinBuild2025.vmx' ` -Submodules ` -BuildCommand 'python build_plugin.py --final --dist-dir dist' ` -GuestArtifactSource 'dist' ` -GuestCredentialTarget 'BuildVMGuest' ``` **Linux guest:** ```powershell & 'N:\Code\Workspace\Local-CI-CD-System\scripts\Invoke-CIJob.ps1' ` -JobId 'test-linux-001' ` -RepoUrl 'ssh://gitea-ci/Simone/nsis-plugin-nsinnounp.git' ` -Branch 'main' ` -TemplatePath 'F:\CI\Templates\LinuxBuild2404\LinuxBuild2404.vmx' ` -SnapshotName 'BaseClean-Linux' ` -GuestOS 'Linux' ` -BuildCommand 'bash build.sh' ` -GuestArtifactSource '/opt/ci/output' ``` Artifacts raccolti in: `F:\CI\Artifacts\\`. Aggiungi `-UseGitClone` per clonare direttamente in VM (skip host-zip-transfer; richiede Git in template — già incluso nel toolchain). Aggiungi `-SkipArtifact` per job che non producono output (es. lint, test puri). --- ## Workflow Gitea Il file [gitea/workflows/build-nsInnoUnp.yml](gitea/workflows/build-nsInnoUnp.yml) è il workflow attivo per `Simone/nsis-plugin-nsinnounp`. Si attiva su push di tag `v*` o manualmente. Usa una **strategy matrix** `[windows, linux]` che esegue in parallelo su entrambi i runner. La composite action [gitea/actions/local-ci-build/action.yml](gitea/actions/local-ci-build/action.yml) asconde tutta la logica Invoke-CIJob.ps1 e accetta questi input principali: | Input | Descrizione | | ----------------- | ------------------------------------------------ | | `build-command` | Comando di build da eseguire nel guest | | `guest-os` | `Windows` (default) o `Linux` | | `artifact-source` | Path artifacts nel guest | | `skip-artifact` | `true` per job senza output | | `repo-url` | URL SSH del repository | | `job-id-suffix` | Suffisso per disambiguare artifact dir in matrix | Per adattare ad altri repository, copia [gitea/workflow-example.yml](gitea/workflow-example.yml) e aggiorna `build-command`, `artifact-source`, `repo-url`. --- ## Documentazione | File | Contenuto | | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | | [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | Diagramma componenti e flusso dati | | [docs/CI-FLOW.md](docs/CI-FLOW.md) | Descrizione dettagliata di ogni fase del pipeline | | [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) | | [docs/LINUX-TEMPLATE-SETUP.md](docs/LINUX-TEMPLATE-SETUP.md) | Template Ubuntu 24.04: Deploy-LinuxBuild2404, Prepare-LinuxBuild2404, snapshot BaseClean-Linux | | [docs/OPTIMIZATION.md](docs/OPTIMIZATION.md) | Layout NVMe, snapshot strategy, cache, tuning | | [docs/BEST-PRACTICES.md](docs/BEST-PRACTICES.md) | Sicurezza WinRM, threat model, credenziali | | [docs/RUNBOOK.md](docs/RUNBOOK.md) | Triage incident comuni (runner offline, clone fail, ecc.) | | [docs/TEST-PLAN-v1.3-to-HEAD.md](docs/TEST-PLAN-v1.3-to-HEAD.md) | Test plan completo features post-v1.3 | | [docs/Setup-GiteaSSH.md](docs/Setup-GiteaSSH.md) | Configurazione SSH alias `gitea-ci` | | [TODO.md](TODO.md) | Roadmap, audit trail task completati, backlog | --- ## Note di sicurezza WinRM usa **HTTPS/5986** con certificato self-signed e Basic auth. `-SkipCACheck`/`-SkipCNCheck` sono accettabili in lab isolato (il cert non è di una CA trusted). Per ambienti condivisi o di produzione, usare un certificato CA valida e rimuovere `-SkipCACheck`. Vedi [docs/BEST-PRACTICES.md](docs/BEST-PRACTICES.md).