# Linux Template VM Setup — DRAFT / TODO > **Status**: bozza. Non implementato. Vedi [TODO.md §6.1](../TODO.md) (P2). > Documento di pianificazione per estendere il sistema CI con build su Linux, > in parallelo al template Windows già operativo. --- ## Obiettivo Aggiungere supporto a build su Linux per: - Compilazione cross-platform (es. versione Linux di plugin C/C++, build check con GCC/Clang) - Test su distro Linux per progetti che lo richiedono - Build matrix `[windows, linux]` nei workflow Gitea Architettura simmetrica al template Windows: VM template → snapshot `BaseClean-Linux` → linked clone per ogni job → cleanup automatico. --- ## Decisioni di design (da validare) ### Distro | Opzione | Pro | Contro | | ------------------------ | ------------------------------------------------------------------ | -------------------------------------------- | | **Ubuntu 24.04 LTS** | Toolchain recente, supporto LTS fino al 2029, cloud-init ottimo | Snap default (lento per CI) | | Debian 12 | Stabile, leggero, no snap | Toolchain meno recente | | Rocky Linux 9 / Alma 9 | RHEL-compat, glibc moderno, dnf | Meno comune in workflow CI tipici | **Scelta proposta**: Ubuntu 24.04 LTS minimal. Rationale: maggior parità con runner GitHub ufficiali (`ubuntu-latest` mappa a 22.04/24.04), toolchain recente out-of-box, cloud-init maturo per provisioning automatico. ### Transport host → VM | Opzione | Pro | Contro | | ------------------------ | ------------------------------------------------------------------ | -------------------------------------------- | | **SSH (chiave pubblica)**| Standard Linux, supporto nativo OpenSSH su Win11/Server 2025 | Diversa API rispetto WinRM (PSSession) | | WinRM su Linux (PSCore) | Codice script unificato | Setup complesso, non standard, fragile | **Scelta proposta**: SSH key-based con `ssh.exe` + `scp.exe` (built-in Windows 10+) o modulo `Posh-SSH`. WinRM su Linux non è production-ready. ### Layer di astrazione Aggiungere `scripts/_Transport.psm1` con interfaccia comune: ```powershell # Pseudo-API function Invoke-RemoteCommand { param($Target, $ScriptBlock, $Args, [ValidateSet('WinRM','SSH')] $Transport) } function Copy-RemoteItem { param($Source, $Destination, $Target, $Direction, $Transport) } function Test-RemoteReady { param($Target, $TimeoutSec, $Transport) } ``` Implementazioni: - **WinRM** wrapper: `New-PSSession` + `Invoke-Command` + `Copy-Item -ToSession` - **SSH** wrapper: `ssh.exe` + `scp.exe` con `-i $keyfile -o StrictHostKeyChecking=accept-new` `Invoke-CIJob.ps1` riceve `-GuestOS Windows|Linux` (o auto-detect dal VMX `guestOS`), sceglie transport. --- ## Specifiche VM proposte | Parametro | Valore | | ----------------------- | --------------------------------------------------------------- | | OS | Ubuntu Server 24.04 LTS minimal | | vCPU | 4 | | RAM | 4096 MB (4 GB) — meno di Windows, footprint Linux più snello | | Disco | 40 GB thin | | NIC | VMnet8 (NAT) — internet per `apt` | | VMX path | `F:\CI\Templates\LinuxBuild\LinuxBuild.vmx` | | ISO | `ubuntu-24.04.x-live-server-amd64.iso` (in `F:\CI\ISO\`) | | Snapshot name | `BaseClean-Linux` | --- ## TODO — Implementazione ### Pre-requisiti host - [ ] **OpenSSH client** verificato presente su host (default Win11/Server 2025) ```powershell Get-WindowsCapability -Online -Name OpenSSH.Client* ``` - [ ] **Genera SSH key dedicata** per CI (no passphrase, separata da chiavi utente): ```powershell ssh-keygen -t ed25519 -f F:\CI\keys\ci_linux -N '""' -C 'ci-linux-runner' ``` - [ ] **Aggiungere a `Setup-Host.ps1`** uno step opzionale `-GenerateLinuxCIKey` che crea la chiave e la archivia con permessi corretti (`F:\CI\keys\` con ACL ristretta) ### Fase A — Crea VM e installa Ubuntu - [ ] Crea nuova VM in VMware Workstation (4 vCPU, 4 GB RAM, 40 GB thin) - [ ] NIC: VMnet8 (NAT) - [ ] Boot da ISO Ubuntu Server 24.04 LTS minimal - [ ] Durante install scegli: - [ ] **Minimal install** (no extra packages) - [ ] OpenSSH server: **Yes** - [ ] Username: `ci_build` - [ ] Hostname: `ci-linux-template` - [ ] LVM: opzionale (non necessario per template) - [ ] Annota IP DHCP (`ip a`) ### Fase B — Provisioning iniziale (manuale, una volta) Dentro la VM, come `ci_build`: - [ ] **Disabilita swap permanente** (overhead I/O su NVMe condivisa): ```bash sudo swapoff -a sudo sed -i '/ swap / s/^/#/' /etc/fstab ``` - [ ] **Disabilita servizi non necessari** (snap, motd-news, ecc.): ```bash sudo systemctl disable --now snapd.service snapd.socket snapd.seeded.service 2>/dev/null sudo systemctl disable --now motd-news.timer 2>/dev/null sudo systemctl disable --now apt-daily.timer apt-daily-upgrade.timer ``` Rationale: snap e timer apt rallentano boot e mangiano CPU su VM efimere. - [ ] **Sudo passwordless** per `ci_build` (parità con WinRM admin senza prompt UAC): ```bash echo 'ci_build ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/90-ci_build sudo chmod 440 /etc/sudoers.d/90-ci_build ``` - [ ] **Authorized SSH key** dell'host CI: ```bash mkdir -p ~/.ssh && chmod 700 ~/.ssh # Incolla la chiave pubblica generata su host (F:\CI\keys\ci_linux.pub) echo 'ssh-ed25519 AAAA... ci-linux-runner' >> ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys ``` - [ ] **SSH hardening** per template (ma non così stretto da bloccare CI): ```bash sudo sed -i 's/^#*PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config sudo sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config sudo systemctl restart sshd ``` ### Fase C — Setup-LinuxTemplateVM.sh (script equivalente a Setup-WinBuild2025.ps1) Da creare: `template/Setup-LinuxTemplateVM.sh` con `set -euo pipefail` + `assert_step()` helper. - [ ] **Helper `assert_step`** (throw on fail, log su success): ```bash assert_step() { local step="$1"; local desc="$2"; shift 2 if "$@" >/dev/null 2>&1; then echo " [OK] [$step] $desc" else echo " [FAIL] [$step] $desc" >&2; exit 1 fi } ``` - [ ] **Step 1 — `apt update` + upgrade** ```bash sudo DEBIAN_FRONTEND=noninteractive apt-get update -qq sudo DEBIAN_FRONTEND=noninteractive apt-get upgrade -y -qq assert_step 'apt' 'no errors' true ``` - [ ] **Step 2 — Toolchain build essentials** ```bash sudo apt-get install -y -qq \ build-essential gcc g++ clang \ make cmake ninja-build \ pkg-config autoconf automake libtool \ git curl wget ca-certificates \ python3 python3-pip python3-venv \ unzip zip xz-utils assert_step 'toolchain' 'gcc presente' command -v gcc assert_step 'toolchain' 'clang presente' command -v clang assert_step 'toolchain' 'cmake presente' command -v cmake assert_step 'toolchain' 'python3 presente' command -v python3 ``` - [ ] **Step 3 — Toolchain extra opzionale** - [ ] **mingw-w64** se serve cross-compile per Windows: `sudo apt-get install -y mingw-w64` - [ ] **.NET SDK** (per parità Windows): script `dotnet-install.sh` con channel pinned - [ ] **Node.js** se workflow lo richiede: nvm o nodesource repo - [ ] **Step 4 — CI working directories** ```bash sudo mkdir -p /opt/ci/{build,output,scripts,cache} sudo chown -R ci_build:ci_build /opt/ci assert_step 'dirs' 'tutte presenti' test -d /opt/ci/build -a -d /opt/ci/output ``` - [ ] **Step 5 — Disabilita auto-update apt** (no surprise reboot/lock): ```bash sudo apt-get remove -y unattended-upgrades 2>/dev/null sudo systemctl mask apt-daily.service apt-daily-upgrade.service ``` - [ ] **Step 6 — Cleanup pre-snapshot** ```bash sudo apt-get clean sudo rm -rf /var/lib/apt/lists/* sudo journalctl --vacuum-time=1d sudo rm -rf /tmp/* /var/tmp/* history -c && cat /dev/null > ~/.bash_history # Zero free space (riduce dim VMDK dopo compact, opzionale) # sudo dd if=/dev/zero of=/zero bs=1M; sudo rm /zero ``` - [ ] **Step 7 — Final pre-snapshot validation** - SSH server abilitato e in ascolto su 22 - `sudo` passwordless funzionante per `ci_build` - Tutti i tool elencati (`gcc --version`, `cmake --version`, ecc.) ritornano exit 0 - `/opt/ci/{build,output,scripts}` esistono con owner corretto - No leftover `/tmp/*`, history vuota ### Fase D — Prepare-LinuxTemplateSetup.ps1 (orchestratore host-side) Equivalente di `Prepare-WinBuild2025.ps1` per Linux. Usa SSH invece di WinRM. - [ ] **Pre-flight host**: - SSH key esiste (`F:\CI\keys\ci_linux`) - `ssh.exe` e `scp.exe` raggiungibili - IP target risponde a `Test-NetConnection -Port 22` - [ ] **Copia script in VM**: ```powershell scp -i F:\CI\keys\ci_linux -o StrictHostKeyChecking=accept-new ` template\Setup-LinuxTemplateVM.sh ci_build@${VMIPAddress}:/tmp/ ``` - [ ] **Esegui script in VM**: ```powershell ssh -i F:\CI\keys\ci_linux ci_build@${VMIPAddress} ` "chmod +x /tmp/Setup-LinuxTemplateVM.sh && sudo /tmp/Setup-LinuxTemplateVM.sh" ``` Cattura stdout + exit code, parse `[OK]`/`[FAIL]` lines. - [ ] **Post-setup remote validation** (parità Windows): query SSH per state checks: - `systemctl is-active ssh` = active - `id ci_build` ritorna gruppo + sudo - `gcc --version`, `cmake --version`, `python3 --version` exit 0 - `test -d /opt/ci/build` exit 0 ### Fase E — Snapshot - [ ] Solo se Prepare exit 0 → power off VM: ```bash sudo shutdown -h now ``` - [ ] Take snapshot in VMware Workstation: **`BaseClean-Linux`** (case-sensitive) ### Fase F — Adattamento script CI - [ ] **`scripts/_Transport.psm1`** (nuovo modulo) con interfaccia comune - [ ] **`scripts/Invoke-CIJob.ps1`** — aggiungere `-GuestOS Windows|Linux`, branching su transport - [ ] **`scripts/Wait-VMReady.ps1`** — supporto SSH probe (`Test-NetConnection -Port 22` + `ssh -o ConnectTimeout=5 ...`) - [ ] **`scripts/Invoke-RemoteBuild.ps1`** — branch SSH+scp per Linux - [ ] **`scripts/Get-BuildArtifacts.ps1`** — `scp` per Linux invece di `Copy-Item -FromSession` - [ ] **`scripts/New-BuildVM.ps1`** — already OS-agnostic (vmrun clone), verificare che `BaseClean-Linux` sia parametrizzabile - [ ] **`scripts/Remove-BuildVM.ps1`** — already OS-agnostic ### Fase G — Workflow Gitea - [ ] **Registrare label `linux-build`** sul runner (oppure secondo runner dedicato): ```yaml # runner/config.yaml — aggiungere a labels esistenti labels: - 'windows-build:host' - 'linux-build:host' ``` - [ ] **Workflow matrix**: ```yaml jobs: build: strategy: matrix: target: [windows, linux] runs-on: ${{ matrix.target }}-build steps: - uses: actions/checkout@v4 - run: pwsh -File scripts/Invoke-CIJob.ps1 -GuestOS ${{ matrix.target }} ``` ### Fase H — Test e2e - [ ] **Smoke test** clone + SSH ready: ```powershell .\scripts\New-BuildVM.ps1 -JobId 'linux-smoke' -SnapshotName 'BaseClean-Linux' .\scripts\Wait-VMReady.ps1 -JobId 'linux-smoke' -Transport SSH .\scripts\Remove-BuildVM.ps1 -JobId 'linux-smoke' ``` - [ ] **Build reale**: scegliere repo con build Linux nativo (es. `cmake` based) e eseguire job e2e via `Invoke-CIJob.ps1 -GuestOS Linux` - [ ] **Confronto artifact**: build dello stesso repo target Windows vs Linux, verifica che entrambi vengano raccolti in `F:\CI\Artifacts\\` --- ## Differenze chiave rispetto al template Windows | Aspetto | Windows | Linux | | ----------------------- | -------------------------------------- | ------------------------------------------- | | Transport | WinRM HTTP/5985 + Basic | SSH/22 + ed25519 key | | Auth fluid | `ci_build` admin + UAC off + LATFP=1 | `ci_build` + sudoers NOPASSWD | | Trasferimento file | `Copy-Item -ToSession` | `scp` o `rsync` | | Esecuzione remota | `Invoke-Command -Session` | `ssh user@host 'cmd'` | | AV/Firewall disable | Defender + Win Firewall off | ufw inattivo (default), no AV | | Update package | Windows Update (COM API + sched task) | `apt-get update && upgrade` non-interactive | | Toolchain | VS Build Tools 2026 + .NET SDK | gcc/g++/clang + cmake (+ .NET SDK opt) | | Snapshot name | `BaseClean` | `BaseClean-Linux` | | Footprint VM | 6 GB RAM, 80 GB disk | 4 GB RAM, 40 GB disk | | Boot time | ~30-60 s post-clone | ~10-20 s post-clone (atteso) | --- ## Backlog correlato (TODO.md) - **§6.1 [P2]** Linux Build VM — entry parent - **§6.4 [P3]** Build matrix `[windows, linux]` nel workflow - **§6.3 [P3]** Multi-host runner federation (se 4 VM Linux + 4 Windows non bastano) - **§5.2 [P2]** Modulo `_Transport.psm1` condiviso (prerequisito architetturale) --- ## Domande aperte 1. **.NET SDK su Linux** — sempre installato o solo se workflow lo richiede? Decisione: installare **se** workload richiede parità di build. Default off, opt-in via flag. 2. **Image size** — Ubuntu minimal + toolchain ≈ 5 GB. Compact post-snapshot per ridurre footprint VMDK? Decisione: sì, `dd if=/dev/zero` + `vmware-vdiskmanager -k` (manuale, una tantum). 3. **Pre-warm pool Linux** — boot Linux è veloce, beneficio minore. Decisione: implementare solo se profile dimostra > 30% overhead. 4. **Cloud-init vs script manuale** — cloud-init richiede ISO seed o NoCloud datasource. Più pulito ma overhead di setup. Decisione: script bash diretto (parità con flusso Windows). Cloud-init valutabile in v2.