docs: update documentation for Linux VM support (Sprint 9)

AGENTS.md (new):
- Environment reference for AI agents: host specs, PS 5.1 constraints,
  dir structure, VM templates, transport layer, style rules
- Lessons #9 (snapshot cold state) and #10 (vmrun list vs getGuestIPAddress)

ARCHITECTURE.md:
- Add SSH/SCP transport section (Linux VMs)
- Update system diagram to include _Transport.psm1, SSH transport path
- Network layout: add SSH port 22 for Linux VMs
- Timeline: Linux readiness check phases documented

CI-FLOW.md:
- Prerequisites table: add Template VM (Linux), SSH key row
- Step 6 (readiness): document Windows vs Linux phases
- Step 7 (build) and Step 8 (artifacts): document Linux/SSH paths
- ci-report-ip guestinfo mechanism referenced

HOST-SETUP.md:
- Directory tree: add LinuxBuild2404 template dir and keys/
- Post-setup checklist: add step 5 (SSH key generation + Linux template)
- Fix duplicate step numbering (56, 67)
- Reference Paths table: add Linux template VMX, snapshot, SSH key

LINUX-TEMPLATE-SETUP.md:
- Rewritten as implementation record (no longer a plan)
- Fase A user-data: minimal (user+SSH key only, no packages/runcmd)
- All phases marked complete with actual paths and snapshot names

WINDOWS-TEMPLATE-SETUP.md: minor updates for consistency

TODO.md:
- Summary: update last-updated to 2026-05-11, Sprint 9 description
- Linux Build VM row: all 5 items complete
- Prossimi passi: Test Plan + §6.6 Tier-2 as next priorities
This commit is contained in:
Simone
2026-05-11 18:35:11 +02:00
parent 383d6864ce
commit 1c8fdcf028
7 changed files with 376 additions and 165 deletions
+57 -56
View File
@@ -1,6 +1,6 @@
# Linux Template VM Setup Piano di implementazione
> **Status**: pianificato non ancora implementato. Vedi [TODO.md §6.1](../TODO.md) (P2).
> **Status**: implementato (2026-05-11) — Fasi A-E complete. Fase F (test e2e) richiede provisioning fisico VM. Vedi [TODO.md §6.1](../TODO.md) (P2).
> Distro scelta: **Ubuntu 24.04 LTS** (cloud image, cloud-init). Approccio simmetrico a Windows.
---
@@ -12,7 +12,7 @@
| `template/Deploy-LinuxBuild2404.ps1` | **Host** (PS) | Crea VM da cloud image VMDK, genera seed ISO cloud-init, avvia, aspetta SSH |
| `template/Prepare-LinuxBuild2404.ps1` | **Host** (PS) | Copia + esegue setup via SSH, valida stato, prende snapshot |
| `template/Setup-LinuxBuild2404.sh` | **Guest** (Bash) | Installa toolchain CI, dirs, hardening, gate finale |
| `scripts/_Transport.psm1` | **Host** (PS) | Funzioni SSH-only per il branch Linux (WinRM esistente invariato per ora) |
| `scripts/_Transport.psm1` | **Host** (PS) | Funzioni SSH-only per il branch Linux (WinRM esistente invariato per ora) |
Perche tre script separati (non tutto-in-uno): cloud-init copre solo il bootstrap OS
(utente, SSH key, pacchetti base). Il toolchain CI e le validazioni per step sono piu
@@ -26,7 +26,7 @@ Deploy/Setup/Prepare di Windows.
| Parametro | Valore |
| ------------- | ---------------------------------------------------- |
| OS | Ubuntu 24.04 LTS (cloud image, non ISO installer) |
| Immagine base | `ubuntu-24.04-server-cloudimg-amd64.vmdk` |
| Immagine base | `noble-server-cloudimg-amd64.vmdk` |
| Download | `https://cloud-images.ubuntu.com/noble/current/` |
| vCPU | 4 |
| RAM | 4096 MB |
@@ -44,19 +44,19 @@ Linux ha il driver vmxnet3 nativo.
## Prerequisiti host
- [ ] **SSH key CI dedicata** generare una volta sola (no passphrase):
- [x] **SSH key CI dedicata** generare una volta sola (no passphrase):
```powershell
New-Item -ItemType Directory -Force F:\CI\keys | Out-Null
ssh-keygen -t ed25519 -f F:\CI\keys\ci_linux -N '""' -C 'ci-linux-runner'
# Risultato: F:\CI\keys\ci_linux (privata) + F:\CI\keys\ci_linux.pub (pubblica)
```
- [ ] **OpenSSH client** verificato presente sull'host (default su Windows 11):
- [x] **OpenSSH client** verificato presente sull'host (default su Windows 11):
```powershell
Get-WindowsCapability -Online -Name OpenSSH.Client* | Select-Object State
# Atteso: State = Installed
```
- [ ] **`F:\CI\Templates\LinuxBuild2404\`** creata (Setup-Host.ps1 o manuale)
- [ ] **`F:\CI\ISO\`** creata (il VMDK viene scaricato automaticamente da Deploy se mancante)
- [x] **`F:\CI\Templates\LinuxBuild2404\`** creata (Setup-Host.ps1 o manuale)
- [x] **`F:\CI\ISO\`** creata (il VMDK viene scaricato automaticamente da Deploy se mancante)
---
@@ -65,7 +65,7 @@ Linux ha il driver vmxnet3 nativo.
Script host-side. Produce una VM pronta con cloud-init completato e SSH raggiungibile.
Non richiede installazione OS parte dal cloud VMDK pre-built.
- [ ] **Step 1 Validate prerequisites**
- [x] **Step 1 Validate prerequisites**
- Parametro `-VMwareWorkstationDir` (default `C:\Program Files (x86)\VMware\VMware Workstation`) — stesso pattern di Deploy-WinBuild2025.ps1
- `vmrun.exe` presente in `$VMwareWorkstationDir`
- `vmware-vdiskmanager.exe` presente in `$VMwareWorkstationDir`
@@ -74,10 +74,10 @@ Non richiede installazione OS parte dal cloud VMDK pre-built.
- Cartella destinazione VMX non gia occupata (o `-Force` per sovrascrivere)
- (il VMDK sorgente viene gestito in Step 1b — nessun prerequisito manuale)
- [ ] **Step 1b Download cloud VMDK se assente** (`F:\CI\ISO\` come cache locale)
- [x] **Step 1b Download cloud VMDK se assente** (`F:\CI\ISO\` come cache locale)
```powershell
$VmdkUrl = 'https://cloud-images.ubuntu.com/noble/current/ubuntu-24.04-server-cloudimg-amd64.vmdk'
$VmdkPath = 'F:\CI\ISO\ubuntu-24.04-server-cloudimg-amd64.vmdk'
$VmdkUrl = 'https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.vmdk'
$VmdkPath = 'F:\CI\ISO\noble-server-cloudimg-amd64.vmdk'
if (-not (Test-Path $VmdkPath)) {
Write-Host "[Deploy] VMDK non trovato, avvio download..."
New-Item -ItemType Directory -Force F:\CI\ISO | Out-Null
@@ -99,32 +99,33 @@ Non richiede installazione OS parte dal cloud VMDK pre-built.
- In caso di errore: il file `.part` viene rimosso, l'eccezione termina lo script
- Parametro opzionale `-VmdkUrl` per sovrascrivere l'URL (es. mirror locale)
- [ ] **Step 2 Genera cloud-init user-data + meta-data** (inline, no template esterno)
- [x] **Step 2 Genera cloud-init user-data + meta-data** (inline, no template esterno)
- `meta-data`: `instance-id: linuxbuild-001`, `local-hostname: ci-linux-template`
- `user-data` (YAML):
- `hostname: ci-linux-template`, `timezone: Europe/Rome`
- `hostname: ci-linux-template`
- `users`: utente `ci_build`, shell `/bin/bash`, `sudo: ALL=(ALL) NOPASSWD:ALL`,
`ssh_authorized_keys` con contenuto di `ci_linux.pub`
- `packages`: `[openssh-server, curl, ca-certificates, p7zip-full]`
- `package_update: true`
- `runcmd`: `swapoff -a`, commenta swap in `/etc/fstab`,
`sed PasswordAuthentication no` in sshd_config, `systemctl restart sshd`
- `power_state`: `mode: reboot` dopo primo provisioning
- Nessun `packages`, nessun `package_update`, nessun `runcmd`:
tutto il provisioning (apt, toolchain, SSH hardening, swap) e' delegato a
`Prepare-LinuxBuild2404.ps1` via `Setup-LinuxBuild2404.sh`
- Nessun `power_state`: il reboot non e' necessario, cloud-init completa tutto al primo boot
- [ ] **Step 3 Costruisce seed ISO nocloud** via IMAPI2FS COM
- [x] **Step 3 Costruisce seed ISO nocloud** via IMAPI2FS COM
- Due file: `user-data` + `meta-data` (volume label: `cidata`)
- Stesso codice COM gia usato in Deploy-WinBuild2025.ps1 Step 3
- Output: `F:\CI\Templates\LinuxBuild2404\cloud-init-seed.iso`
- [ ] **Step 4 Copia e ridimensiona cloud VMDK**
- Copia `ubuntu-24.04-server-cloudimg-amd64.vmdk` → `LinuxBuild2404.vmdk`
- [x] **Step 4 Converti e ridimensiona cloud VMDK**
- Il cloud VMDK e' in formato **streamOptimized** (distribuzione), non direttamente resizeable.
- Converti a **monolithicSparse** (tipo 0) contestualmente alla copia:
`& $vdiskMgr -r noble-server-cloudimg-amd64.vmdk -t 0 LinuxBuild2404.vmdk`
- Ridimensiona a 40 GB: `& $vdiskMgr -x 40GB LinuxBuild2404.vmdk`
(stessa variabile `$vdiskMgr` di Deploy-WinBuild2025.ps1, valorizzata in Step 1)
- Il resize avviene PRIMA del primo boot: l'immagine Ubuntu 24.04 ha i moduli
cloud-init `growpart` + `resizefs` abilitati di default — espandono automaticamente
la partizione root e il filesystem durante il primo avvio, senza `runcmd` aggiuntivi
- [ ] **Step 5 Genera .vmx**
- [x] **Step 5 Genera .vmx**
- `guestOS = "ubuntu-64"`, `firmware = "efi"`
(Ubuntu 24.04 cloud image usa GPT con partizione EFI; il boot sotto BIOS legacy fallirebbe)
- NIC: `vmxnet3`, VMnet8
@@ -133,9 +134,9 @@ Non richiede installazione OS parte dal cloud VMDK pre-built.
- Nessun USB controller, nessuna sound card
- `memsize = 4096`, `numvcpus = 4`
- [ ] **Step 6 Power on** (`vmrun start nogui`)
- [x] **Step 6 Power on** (`vmrun start nogui`)
- [ ] **Step 7 Polling SSH port 22** fino a ready
- [x] **Step 7 Polling SSH port 22** fino a ready
- `Test-NetConnection -ComputerName $IP -Port 22` ogni 10 secondi
- Timeout: 5 minuti (cloud-init + reboot finale)
- IP rilevato via `vmrun getGuestIPAddress`
@@ -143,7 +144,7 @@ Non richiede installazione OS parte dal cloud VMDK pre-built.
> open-vm-tools e pre-installato nell'Ubuntu 24.04 cloud image.
> `vmrun getGuestIPAddress` funziona senza installazione aggiuntiva.
- [ ] **Step 8 Verifica cloud-init completato**
- [x] **Step 8 Verifica cloud-init completato**
```powershell
ssh -i F:\CI\keys\ci_linux -o StrictHostKeyChecking=accept-new `
-o ConnectTimeout=5 ci_build@$IP `
@@ -151,11 +152,11 @@ Non richiede installazione OS parte dal cloud VMDK pre-built.
# Atteso: OK
```
- [ ] **Step 9 Rimuove seed ISO dal VMX**
- [x] **Step 9 Rimuove seed ISO dal VMX**
- Setta `ide1:0.present = "FALSE"` nel VMX
- Evita che linked clone ricevano il seed ISO e ri-eseguano cloud-init
- [ ] **Step 10 Print summary** (VMX path, IP, SSH fingerprint)
- [x] **Step 10 Print summary** (VMX path, IP, SSH fingerprint)
---
@@ -164,23 +165,23 @@ Non richiede installazione OS parte dal cloud VMDK pre-built.
Script host-side. Copia il setup script, lo esegue via SSH, valida stato, prende snapshot.
Equivalente di `Prepare-WinBuild2025.ps1` con SSH al posto di WinRM.
- [ ] **Step 1 Pre-flight**
- [x] **Step 1 Pre-flight**
- VMX presente; se VM spenta, avvia con `vmrun start nogui`
- SSH key `F:\CI\keys\ci_linux` presente
- `ssh.exe` e `scp.exe` nel PATH host
- TCP/22 raggiungibile (`Test-NetConnection`)
- [ ] **Step 2 Auto-detect IP** via `vmrun getGuestIPAddress $VMXPath`
- [x] **Step 2 Auto-detect IP** via `vmrun getGuestIPAddress $VMXPath`
- Se `-VMIPAddress` e fornito esplicitamente, salta il rilevamento automatico
- [ ] **Step 3 Aggiungi host key a known_hosts**
- [x] **Step 3 Aggiungi host key a known_hosts**
```powershell
ssh -i F:\CI\keys\ci_linux -o StrictHostKeyChecking=accept-new `
ci_build@$IP "echo connected" | Out-Null
```
Solo al primo run; idempotente se gia presente.
- [ ] **Step 4 Copia Setup-LinuxBuild2404.sh in VM**
- [x] **Step 4 Copia Setup-LinuxBuild2404.sh in VM**
```powershell
scp -i F:\CI\keys\ci_linux `
"$PSScriptRoot\Setup-LinuxBuild2404.sh" `
@@ -188,7 +189,7 @@ Equivalente di `Prepare-WinBuild2025.ps1` con SSH al posto di WinRM.
```
Valida: confronta size locale vs remota via `ssh ... stat -c %s /tmp/...`
- [ ] **Step 5 Esegui setup script** nel guest
- [x] **Step 5 Esegui setup script** nel guest
```powershell
ssh -i F:\CI\keys\ci_linux ci_build@$IP `
"chmod +x /tmp/Setup-LinuxBuild2404.sh && sudo /tmp/Setup-LinuxBuild2404.sh"
@@ -196,7 +197,7 @@ Equivalente di `Prepare-WinBuild2025.ps1` con SSH al posto di WinRM.
```
Flag opzionali passabili: `--skip-update`, `--dotnet`, `--mingw`
- [ ] **Step 6 Post-setup remote validation** (batch via singola sessione SSH)
- [x] **Step 6 Post-setup remote validation** (batch via singola sessione SSH)
- `systemctl is-active ssh` = `active`
- `id ci_build` contiene `sudo`
- `sudo -n true` exit 0 (passwordless sudo)
@@ -205,18 +206,18 @@ Equivalente di `Prepare-WinBuild2025.ps1` con SSH al posto di WinRM.
- `swapon --show` output vuoto (swap disabilitato)
- `grep -q "^PasswordAuthentication no" /etc/ssh/sshd_config`
- [ ] **Step 7 Shutdown VM**
- [x] **Step 7 Shutdown VM**
```powershell
& $vmrun stop $VMXPath soft
# Polling vmrun list fino a VM non piu presente
```
- [ ] **Step 8 Snapshot BaseClean-Linux**
- [x] **Step 8 Snapshot BaseClean-Linux**
```powershell
& $vmrun snapshot $VMXPath 'BaseClean-Linux'
```
- [ ] **Final Print summary** (VMX, snapshot name, toolchain versions rilevate)
- [x] **Final Print summary** (VMX, snapshot name, toolchain versions rilevate)
---
@@ -228,13 +229,13 @@ Eseguito da Prepare via SSH come `ci_build` con sudo.
Header: `#!/usr/bin/env bash` + `set -euo pipefail`
Helper `assert_step()`: stampa `[OK] [Step N] desc` su successo, `exit 1` su fallimento.
- [ ] **Step 1 apt update + upgrade** (saltabile con `--skip-update`)
- [x] **Step 1 apt update + upgrade** (saltabile con `--skip-update`)
```bash
sudo DEBIAN_FRONTEND=noninteractive apt-get update -qq
sudo DEBIAN_FRONTEND=noninteractive apt-get upgrade -y -qq
```
- [ ] **Step 2 Toolchain build essentials**
- [x] **Step 2 Toolchain build essentials**
```bash
sudo apt-get install -y -qq \
build-essential gcc g++ clang \
@@ -243,20 +244,20 @@ Helper `assert_step()`: stampa `[OK] [Step N] desc` su successo, `exit 1` su fal
```
- assert: `command -v gcc`, `command -v clang`, `command -v cmake`
- [ ] **Step 3 Python**
- [x] **Step 3 Python**
```bash
sudo apt-get install -y -qq python3 python3-pip python3-venv
sudo ln -sf /usr/bin/python3 /usr/local/bin/python
```
- assert: `python3 --version`, `pip3 --version`
- [ ] **Step 4 Tier-1 Toolchain** (speculare a §6.6 Windows: Git + 7-Zip)
- [x] **Step 4 Tier-1 Toolchain** (speculare a §6.6 Windows: Git + 7-Zip)
```bash
sudo apt-get install -y -qq git p7zip-full curl wget ca-certificates
```
- assert: `command -v git`, `command -v 7z`
- [ ] **Step 4b .NET SDK** (opzionale, flag `--dotnet`)
- [x] **Step 4b .NET SDK** (opzionale, flag `--dotnet`)
```bash
curl -fsSL https://dotnet.microsoft.com/download/dotnet/scripts/v1/dotnet-install.sh \
| sudo bash -s -- --channel 10.0 --install-dir /usr/local/dotnet
@@ -264,20 +265,20 @@ Helper `assert_step()`: stampa `[OK] [Step N] desc` su successo, `exit 1` su fal
```
- assert: `dotnet --version`
- [ ] **Step 4c mingw-w64** (opzionale, flag `--mingw` cross-compile per Windows)
- [x] **Step 4c mingw-w64** (opzionale, flag `--mingw` cross-compile per Windows)
```bash
sudo apt-get install -y -qq mingw-w64
```
- assert: `command -v x86_64-w64-mingw32-gcc`
- [ ] **Step 5 CI directories**
- [x] **Step 5 CI directories**
```bash
sudo mkdir -p /opt/ci/{build,output,scripts,cache}
sudo chown -R ci_build:ci_build /opt/ci
```
- assert: `test -d /opt/ci/build`, `test -d /opt/ci/output`, `test -d /opt/ci/scripts`
- [ ] **Step 6 SSH hardening** (PasswordAuth off gia impostato da cloud-init, ri-validato)
- [x] **Step 6 SSH hardening** (PasswordAuth off gia impostato da cloud-init, ri-validato)
```bash
sudo sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo sed -i 's/^#*PubkeyAuthentication.*/PubkeyAuthentication yes/' /etc/ssh/sshd_config
@@ -285,21 +286,21 @@ Helper `assert_step()`: stampa `[OK] [Step N] desc` su successo, `exit 1` su fal
```
- assert: `grep -q "^PasswordAuthentication no" /etc/ssh/sshd_config`
- [ ] **Step 7 Disabilita swap permanente**
- [x] **Step 7 Disabilita swap permanente**
```bash
sudo swapoff -a
sudo sed -i '/ swap / s/^/#/' /etc/fstab
```
- assert: `swapon --show` output vuoto
- [ ] **Step 8 Disabilita apt auto-update** (no lock durante build)
- [x] **Step 8 Disabilita apt auto-update** (no lock durante build)
```bash
sudo systemctl mask apt-daily.service apt-daily-upgrade.service \
apt-daily.timer apt-daily-upgrade.timer
sudo apt-get remove -y unattended-upgrades 2>/dev/null || true
```
- [ ] **Step 9 Cleanup pre-snapshot**
- [x] **Step 9 Cleanup pre-snapshot**
```bash
sudo apt-get clean
sudo rm -rf /var/lib/apt/lists/*
@@ -309,7 +310,7 @@ Helper `assert_step()`: stampa `[OK] [Step N] desc` su successo, `exit 1` su fal
cat /dev/null > ~/.bash_history
```
- [ ] **Final Gate pre-snapshot** (exit 1 se qualcosa manca)
- [x] **Final Gate pre-snapshot** (exit 1 se qualcosa manca)
- Ogni tool richiesto raggiungibile con `command -v`
- `/opt/ci/{build,output,scripts}` esistenti con owner `ci_build`
- `swapon --show` vuoto
@@ -325,7 +326,7 @@ Helper `assert_step()`: stampa `[OK] [Step N] desc` su successo, `exit 1` su fal
## Fase D Adattamento script CI
- [ ] **`scripts/_Transport.psm1`** nuovo modulo SSH-only per il branch Linux.
- [x] **`scripts/_Transport.psm1`** nuovo modulo SSH-only per il branch Linux.
Gli script Windows esistenti NON vengono modificati ora; il refactoring WinRM e' rinviato.
```powershell
function Invoke-SshCommand { param($IP, $User, $KeyPath, $Command, ...) }
@@ -335,18 +336,18 @@ Helper `assert_step()`: stampa `[OK] [Step N] desc` su successo, `exit 1` su fal
Gli script CI (`Invoke-CIJob`, `Invoke-RemoteBuild`, `Get-BuildArtifacts`) chiameranno
queste funzioni nel branch Linux senza toccare il branch WinRM esistente.
- [ ] **`scripts/Invoke-CIJob.ps1`** aggiungere parametro `-GuestOS Windows|Linux`
- [x] **`scripts/Invoke-CIJob.ps1`** aggiungere parametro `-GuestOS Windows|Linux`
- Auto-detect da VMX `guestOS` field se non fornito
- Branch su transport (WinRM vs SSH) in ogni fase
- [ ] **`scripts/Wait-VMReady.ps1`** aggiungere `-Transport SSH`
- [x] **`scripts/Wait-VMReady.ps1`** aggiungere `-Transport SSH`
- Probe: `Test-NetConnection -Port 22` + `ssh -o ConnectTimeout=5 ... "echo ok"`
- [ ] **`scripts/Invoke-RemoteBuild.ps1`** branch SSH+scp per Linux
- [x] **`scripts/Invoke-RemoteBuild.ps1`** branch SSH+scp per Linux
- Transfer: `scp` invece di `Copy-Item -ToSession`
- Build: `ssh ... "cd /opt/ci/build && $BuildCommand"`
- [ ] **`scripts/Get-BuildArtifacts.ps1`** branch `scp` per Linux
- [x] **`scripts/Get-BuildArtifacts.ps1`** branch `scp` per Linux
- `scp -r ci_build@$IP:/opt/ci/output/* $LocalArtifactDir\`
- [ ] **`scripts/New-BuildVM.ps1`** gia OS-agnostic (vmrun clone)
@@ -358,17 +359,17 @@ Helper `assert_step()`: stampa `[OK] [Step N] desc` su successo, `exit 1` su fal
## Fase E Runner e workflow Gitea
- [ ] **Label `linux-build`** aggiunta al runner in `runner/config.yaml`:
- [x] **Label `linux-build`** aggiunta al runner in `runner/config.yaml`:
```yaml
labels:
- 'windows-build:host'
- 'linux-build:host'
```
- [ ] **Variabile d'ambiente** `GITEA_CI_LINUX_TEMPLATE_PATH` nel servizio act_runner:
- [x] **Variabile d'ambiente** `GITEA_CI_LINUX_TEMPLATE_PATH` nel servizio act_runner:
`F:\CI\Templates\LinuxBuild2404\LinuxBuild2404.vmx`
- [ ] **Workflow matrix** di esempio (`gitea/workflow-example.yml` aggiornato):
- [x] **Workflow matrix** di esempio (`gitea/workflow-example.yml` aggiornato):
```yaml
strategy:
matrix: