docs: record the LinuxRust2404 template, its failure modes and open decisions
- AGENTS.md: LinuxRust2404 reference, plus errors #14-#16 -- deploy --force against a powered-on VM, the three distinct causes behind the identical "VMware Tools are not running" message, and the orphan snapshot deltas --force leaves behind after deleting the .vmsd. - docs/RUNBOOK.md §16: triage for a Step 7 IP timeout, ordered cheapest check first, with a Quick Reference entry. - docs/RUNBOOK-PhaseC.md: deploy-rust/prepare-rust, the ownership and non-login shell constraints on /opt/rust, --then-prepare, and how to route a label to a template (a second runner instance, with the verification step). - TODO.md §2.9: retention run covers only artifacts, logs and IP leases, so it warned about low disk while freeing 0.0 GB two runs in a row. Mitigated by hand; the automated policy is still blind to what actually weighs. - TODO.md §6.5: act_runner v1.0.4 -> v2.x evaluated and deferred, with the trigger for reopening. v2.1.0 exposes no RUNNER_LABELS either (checked against the binary), so the two-runner design is not a stopgap.
This commit is contained in:
@@ -174,6 +174,47 @@ Variante **WinBuild2022**: path `F:\CI\Templates\WinBuild2022\WinBuild2022.vmx`,
|
|||||||
| Toolchain | GCC, G++, Clang, CMake, Ninja, Python 3, Git, 7-Zip |
|
| Toolchain | GCC, G++, Clang, CMake, Ninja, Python 3, Git, 7-Zip |
|
||||||
| CI dirs | `/opt/ci/{build,output,scripts,cache}` — owner `ci_build` |
|
| CI dirs | `/opt/ci/{build,output,scripts,cache}` — owner `ci_build` |
|
||||||
|
|
||||||
|
### Linux Rust (LinuxRust2404)
|
||||||
|
|
||||||
|
Variante di `LinuxBuild2404` per workspace Rust (es. app Tauri): stessa cloud
|
||||||
|
image e stesso layout guest, provisioning via
|
||||||
|
`template/Install-CIToolchain-LinuxRust2404.sh` che aggiunge le librerie
|
||||||
|
webview/GTK di Tauri v2 e una toolchain Rust system-wide sotto `/opt/rust`
|
||||||
|
(rustup + rustfmt + clippy + binario `cargo-audit`; il DB advisory di
|
||||||
|
cargo-audit NON e' bakato, viene fetchato a runtime).
|
||||||
|
|
||||||
|
| Parametro | Valore |
|
||||||
|
| -------------- | ------------------------------------------------------------- |
|
||||||
|
| OS | Ubuntu 24.04 LTS (cloud image) |
|
||||||
|
| VMX | `LinuxRust2404/LinuxRust2404.vmx` (sotto la dir templates) |
|
||||||
|
| Snapshot clone | `BaseClean-LinuxRust` |
|
||||||
|
| Transport | SSH porta 22, utente `ci_build`, stessa chiave di LinuxBuild2404 |
|
||||||
|
| Toolchain | quella di LinuxBuild2404 + webview/GTK Tauri + Rust in `/opt/rust` |
|
||||||
|
| Comandi | `ci template deploy-rust` / `ci template prepare-rust` |
|
||||||
|
|
||||||
|
`--rust-channel` (default `stable`) controlla la toolchain bakata: pinnare una
|
||||||
|
versione esatta (es. `1.97.1`) per uno snapshot deterministico; con `stable`
|
||||||
|
rustup riscarica a runtime ad ogni nuova stable. Rigenerare lo snapshot quando
|
||||||
|
cambia il pin o la baseline apt di sicurezza.
|
||||||
|
|
||||||
|
Due vincoli sull'albero `/opt/rust`, entrambi regressioni gia' occorse:
|
||||||
|
|
||||||
|
1. **`chown` DOPO l'installazione.** Lo script gira come root, quindi tutto
|
||||||
|
cio' che rustup/cargo creano nasce root-owned: un `chown` fatto solo prima
|
||||||
|
copre le due directory vuote e nient'altro. Senza il `chown` finale
|
||||||
|
`ci_build` non puo' scrivere ne' in `RUSTUP_HOME` ne' in
|
||||||
|
`CARGO_HOME/registry`, e **ogni** build Rust con dipendenze esterne muore
|
||||||
|
con `Permission denied (os error 13)`. Gli assert devono verificare la
|
||||||
|
**scrivibilita'** (`sudo -u ci_build test -w`), non solo `test -x` sui
|
||||||
|
binari: altrimenti il template rotto passa la sua build e fallisce nei job
|
||||||
|
dei consumatori.
|
||||||
|
2. **`/etc/profile.d` da solo non basta.** L'orchestratore esegue i comandi
|
||||||
|
con paramiko `exec_command` + `sh -c` (`transport/ssh.py`,
|
||||||
|
`commands/build.py`): shell **non di login**, `profile.d` non viene mai
|
||||||
|
sorgiato. `RUSTUP_HOME`/`CARGO_HOME` vanno anche in `/etc/environment`
|
||||||
|
(letto da `pam_env` all'apertura della sessione SSH) e i proxy rustup
|
||||||
|
symlinkati in `/usr/local/bin`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Transport layer
|
## Transport layer
|
||||||
@@ -236,3 +277,9 @@ Configurazione in `PSScriptAnalyzerSettings.psd1`. Regole principali:
|
|||||||
11. **Machine-id identico tra clone Linux** — le cloud image Ubuntu hanno un `/etc/machine-id` fisso. VMware DHCP assegna l'IP tramite DHCP client-id derivato da machine-id: se tutti i clone hanno lo stesso machine-id ottengono lo stesso IP e si bloccano per "IP collision". Fix nel template (prima di prendere lo snapshot): `sudo truncate -s 0 /etc/machine-id && sudo rm -f /var/lib/dbus/machine-id && sudo shutdown -h now`, poi ricreare lo snapshot `BaseClean-Linux` da stato powered-off.
|
11. **Machine-id identico tra clone Linux** — le cloud image Ubuntu hanno un `/etc/machine-id` fisso. VMware DHCP assegna l'IP tramite DHCP client-id derivato da machine-id: se tutti i clone hanno lo stesso machine-id ottengono lo stesso IP e si bloccano per "IP collision". Fix nel template (prima di prendere lo snapshot): `sudo truncate -s 0 /etc/machine-id && sudo rm -f /var/lib/dbus/machine-id && sudo shutdown -h now`, poi ricreare lo snapshot `BaseClean-Linux` da stato powered-off.
|
||||||
12. **`& nativecmd 2>$null` NON sopprime stderr in PS 5.1 con `$ErrorActionPreference='Stop'`** — la stderr del processo nativo viene convertita in ErrorRecord nel pipeline di PowerShell; sotto `'Stop'` questo diventa un errore terminante PRIMA che `2>$null` possa scartarlo. Esempio: `ssh-keygen -R <ip> -f <file>` stampa "Host X not found in file" su stderr → eccezione terminante. Fix: usare `$ErrorActionPreference = 'SilentlyContinue'` attorno alla chiamata oppure `2>&1 | Out-Null`. Per i job CI su VM ephemere locali, usare direttamente `StrictHostKeyChecking=no UserKnownHostsFile=NUL` (default in `_Transport.psm1` con `KnownHostsFile=''`) — nessun `ssh-keygen` necessario.
|
12. **`& nativecmd 2>$null` NON sopprime stderr in PS 5.1 con `$ErrorActionPreference='Stop'`** — la stderr del processo nativo viene convertita in ErrorRecord nel pipeline di PowerShell; sotto `'Stop'` questo diventa un errore terminante PRIMA che `2>$null` possa scartarlo. Esempio: `ssh-keygen -R <ip> -f <file>` stampa "Host X not found in file" su stderr → eccezione terminante. Fix: usare `$ErrorActionPreference = 'SilentlyContinue'` attorno alla chiamata oppure `2>&1 | Out-Null`. Per i job CI su VM ephemere locali, usare direttamente `StrictHostKeyChecking=no UserKnownHostsFile=NUL` (default in `_Transport.psm1` con `KnownHostsFile=''`) — nessun `ssh-keygen` necessario.
|
||||||
13. **Quote WinRM/desktop-heap troppo basse per builds Windows paralleli** — sintomo: `MSBuild error MSB6003: CL.exe could not be run. System.ComponentModel.Win32Exception (0x80004005): Not enough quota is available to process this command` (oppure le altre tool MSVC: link.exe, mt.exe) quando il build interno fa fan-out parallelo (es. matrix x86/x64/x86-ansi, MSBuild `/maxcpucount`). Causa: il plugin endpoint Microsoft.PowerShell del guest ha `MaxMemoryPerShellMB=150` di default e l'heap del desktop non-interattivo (Session 0) ha `SharedSection 3rd field=768 KB` — entrambi insufficienti per più CL.exe concorrenti via WinRM. **Non emerge** in pre-migration Win→Win perché `Invoke-Command` su `PSSession` localhost-style eredita quote più larghe; emerge appena il path passa per `pypsrp` Linux→Win. Fix nel template (Deploy-WinBuild2025.ps1 / 2022 lo fa, Install-CIToolchain lo valida): `Set-Item WSMan:\localhost\Plugin\Microsoft.PowerShell\Quotas\MaxMemoryPerShellMB 2048`, `Set-Item WSMan:\localhost\Shell\MaxProcessesPerShell 100`, registry `HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems\Windows` patch del 3° campo `SharedSection` a 4096, **reboot** (csrss legge SharedSection solo al boot), nuovo `BaseClean`. Dettagli e procedura completa in `docs/WINDOWS-TEMPLATE-SETUP.md § "WinRM quotas e desktop heap"`.
|
13. **Quote WinRM/desktop-heap troppo basse per builds Windows paralleli** — sintomo: `MSBuild error MSB6003: CL.exe could not be run. System.ComponentModel.Win32Exception (0x80004005): Not enough quota is available to process this command` (oppure le altre tool MSVC: link.exe, mt.exe) quando il build interno fa fan-out parallelo (es. matrix x86/x64/x86-ansi, MSBuild `/maxcpucount`). Causa: il plugin endpoint Microsoft.PowerShell del guest ha `MaxMemoryPerShellMB=150` di default e l'heap del desktop non-interattivo (Session 0) ha `SharedSection 3rd field=768 KB` — entrambi insufficienti per più CL.exe concorrenti via WinRM. **Non emerge** in pre-migration Win→Win perché `Invoke-Command` su `PSSession` localhost-style eredita quote più larghe; emerge appena il path passa per `pypsrp` Linux→Win. Fix nel template (Deploy-WinBuild2025.ps1 / 2022 lo fa, Install-CIToolchain lo valida): `Set-Item WSMan:\localhost\Plugin\Microsoft.PowerShell\Quotas\MaxMemoryPerShellMB 2048`, `Set-Item WSMan:\localhost\Shell\MaxProcessesPerShell 100`, registry `HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems\Windows` patch del 3° campo `SharedSection` a 4096, **reboot** (csrss legge SharedSection solo al boot), nuovo `BaseClean`. Dettagli e procedura completa in `docs/WINDOWS-TEMPLATE-SETUP.md § "WinRM quotas e desktop heap"`.
|
||||||
|
|
||||||
|
14. **`deploy-* --force` lanciato mentre la VM target è ACCESA** — corrompe la VM in esecuzione e produce un timeout allo Step 7 che *sembra* un problema di rete. Occorso il 2026-07-22 su `LinuxRust2404`, con un `prepare-rust` ancora in corso sulla stessa VMX. Meccanica: (a) Step 1 vede `<disk>.vmdk.lck` e lo cancella come "stale artifact" — **non era stale**, apparteneva al processo vivo; (b) Step 4 riscrive il VMDK base sotto il guest in esecuzione, che muore; (c) Step 6 lancia il power-on, il nuovo `vmware-vmx` muore subito perché la VMX è già occupata, ma il controllo `[OK] VM appears in vmrun list` **passa lo stesso** perché vede l'istanza vecchia; (d) Step 7 interroga per 10 minuti un guest morto e riporta `Error: The VMware Tools are not running in the virtual machine`. Diagnosi in 5 secondi: `ps -eo pid,lstart,etime,args | grep vmware-vmx` — se il `vmware-vmx` vivo è più vecchio del deploy appena lanciato, è questo il caso. Recupero: `vmrun stop <vmx> hard`, rimuovere i delta orfani `<disk>-00000*.vmdk`, verificare `vmrun list` = 0, poi rilanciare. **Non** interpretarlo come DHCP incastrato (vedi #15) e non risolverlo con un retry automatico: riavviare una VM già corrotta ripete il fallimento con la causa ormai illeggibile.
|
||||||
|
|
||||||
|
15. **Step 7 in timeout: distinguere tre cause diverse che danno lo stesso messaggio** — `getGuestIPAddress` risponde `The VMware Tools are not running` sia quando il guest non ha IP, sia quando ha l'IP ma i Tools non rispondono. Ordine di verifica, dal più veloce: (a) **collisione** — `ps | grep vmware-vmx`, processo più vecchio del deploy corrente → vedi #14; (b) **DHCP host incastrato** — `ip -4 addr show vmnet8` deve dare `192.168.79.1/24` e `ps -ef | grep vmnet-dhcpd` deve mostrarlo vivo; se no, `sudo systemctl restart vmware-networks` (o `vmware-host.service`); (c) **guest senza lease o Tools non pronti** — cercare il MAC della VMX in `/etc/vmware/vmnet8/dhcpd/dhcpd.leases` e in `ip neigh show dev vmnet8`: se un lease c'è, il guest è vivo e mancano solo i Tools, quindi sondare TCP/22 direttamente invece di aspettare `getGuestIPAddress`.
|
||||||
|
|
||||||
|
16. **Delta di snapshot orfani dopo `--force`** — `deploy-*` cancella `<vm>.vmsd` (il database snapshot) per evitare l'errore di resize sulla catena, ma **non** cancella i file delta `<disk>-00000N.vmdk`. Restano su disco con un `parentCID` che non corrisponde più al base ricreato: innocui (fuori dal percorso di boot, la VMX punta al base con `parentCID=ffffffff`) ma confondono la diagnosi e vanno rimossi a mano. Verifica della catena: `head -c 2000 <disk>.vmdk | strings | grep -E '^CID|parentCID'`.
|
||||||
|
|||||||
@@ -106,6 +106,14 @@ The host **never executes build tools directly** — it only orchestrates VM lif
|
|||||||
| WinBuild2025 | `F:\CI\Templates\WinBuild2025\WinBuild2025.vmx` | `BaseClean` | WinRM HTTPS/5986 |
|
| WinBuild2025 | `F:\CI\Templates\WinBuild2025\WinBuild2025.vmx` | `BaseClean` | WinRM HTTPS/5986 |
|
||||||
| WinBuild2022 | `F:\CI\Templates\WinBuild2022\WinBuild2022.vmx` | `BaseClean` | WinRM HTTPS/5986 |
|
| WinBuild2022 | `F:\CI\Templates\WinBuild2022\WinBuild2022.vmx` | `BaseClean` | WinRM HTTPS/5986 |
|
||||||
| LinuxBuild2404 | `F:\CI\Templates\LinuxBuild2404\LinuxBuild2404.vmx` | `BaseClean-Linux` | SSH/22 (`ci_build`, key `F:\CI\keys\ci_linux`) |
|
| LinuxBuild2404 | `F:\CI\Templates\LinuxBuild2404\LinuxBuild2404.vmx` | `BaseClean-Linux` | SSH/22 (`ci_build`, key `F:\CI\keys\ci_linux`) |
|
||||||
|
| LinuxRust2404 | `LinuxRust2404/LinuxRust2404.vmx` (templates dir) | `BaseClean-LinuxRust` | SSH/22 (same user/key as LinuxBuild2404) |
|
||||||
|
|
||||||
|
`LinuxRust2404` is a LinuxBuild2404 variant for Rust workspaces (Tauri):
|
||||||
|
`ci template deploy-rust` / `ci template prepare-rust` provision it via
|
||||||
|
`template/Install-CIToolchain-LinuxRust2404.sh` (webview/GTK libs + system-wide
|
||||||
|
rustup under `/opt/rust` + cargo-audit binary; the audit advisory DB is fetched
|
||||||
|
at runtime, never baked). `--rust-channel` pins the baked toolchain (default
|
||||||
|
`stable`, a moving target).
|
||||||
|
|
||||||
Snapshots **must be taken from powered-off state** — presence of `.vmem` in the template dir means the snapshot captured running memory and `vmrun clone ... linked` will fail.
|
Snapshots **must be taken from powered-off state** — presence of `.vmem` in the template dir means the snapshot captured running memory and `vmrun clone ... linked` will fail.
|
||||||
|
|
||||||
|
|||||||
@@ -312,6 +312,34 @@ mai rilasciati. Workaround manuale: reset a `{}` con `build-vms/` vuoto.
|
|||||||
cui clone-dir non esiste più in `CloneBaseDir`. Opzionale: allargare il pool
|
cui clone-dir non esiste più in `CloneBaseDir`. Opzionale: allargare il pool
|
||||||
(es. 201–210) per dare margine oltre parallelism. Aggiungere test.
|
(es. 201–210) per dare margine oltre parallelism. Aggiungere test.
|
||||||
|
|
||||||
|
### 2.9 [P2] [ ] `retention run` non copre backup/template/ISO — APERTO (2026-07-22)
|
||||||
|
|
||||||
|
`ci-retention-policy` agisce solo su **artifact, log e lease IP**. Su questo host
|
||||||
|
quei tre insiemi pesano 52 KB in tutto, mentre il disco era pieno al 94% per
|
||||||
|
`backups/` (195 G), `templates/` (128 G) e `iso/` (69 G) — tutti fuori dal suo
|
||||||
|
raggio d'azione.
|
||||||
|
|
||||||
|
Sintomo osservato: il timer girava, rilevava meno di 50 GB liberi, dichiarava
|
||||||
|
`WARNING: Free space below 50 GB — aggressive retention: 7d` e liberava
|
||||||
|
**0.0 GB**, due esecuzioni di fila. L'allarme suonava ma il meccanismo non
|
||||||
|
poteva agire su ciò che pesava. Un guardiano che non sorveglia nulla è peggio di
|
||||||
|
nessun guardiano: dà l'impressione che il problema sia sotto controllo.
|
||||||
|
|
||||||
|
Mitigato il 2026-07-22 (backup svuotati, `--keep-count 1 --exclude WinBuildTest`
|
||||||
|
nella unit), ma è una correzione manuale: la policy automatica resta cieca.
|
||||||
|
|
||||||
|
**Fix proposto**: estendere `retention run` ai backup (per numero di generazioni
|
||||||
|
o per età) e almeno segnalare — non cancellare — ISO e template che superano una
|
||||||
|
soglia. In alternativa, se i backup restano governati solo da `--keep-count`,
|
||||||
|
far sì che il warning sullo spazio dica **quali** directory pesano, invece di
|
||||||
|
lamentarsi senza indicare dove guardare.
|
||||||
|
|
||||||
|
**Nota**: `iso/` contiene coppie originale + `.patched` e versioni April
|
||||||
|
superate da May (~38 G recuperabili); le `.patched` sono rigenerate da
|
||||||
|
`deploy-win`. `templates/WinBuildTest` (41 G, ultimo uso 10 giugno, citato solo
|
||||||
|
in `docs/archived/2026-05-10/`) è candidato alla dismissione: escluso dai backup
|
||||||
|
ma ancora su disco.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. Performance & throughput
|
## 3. Performance & throughput
|
||||||
@@ -518,6 +546,54 @@ Già supportato lato Gitea, basta replicare il setup.
|
|||||||
|
|
||||||
**Deferred**: in un home lab single-host questa funzionalità non serve. Non pianificare.
|
**Deferred**: in un home lab single-host questa funzionalità non serve. Non pianificare.
|
||||||
|
|
||||||
|
### 6.5 [P3] [ ] Upgrade act_runner v1.0.4 → v2.x — VALUTATO E RIMANDATO (2026-07-22)
|
||||||
|
|
||||||
|
Versione installata: **v1.0.4** (18 mag 2026). Ultima upstream al momento della
|
||||||
|
valutazione: **v2.1.0** (16 lug 2026).
|
||||||
|
Release: https://gitea.com/gitea/runner/releases
|
||||||
|
|
||||||
|
**Decisione: non aggiornare ora.** Il beneficio netto per questo sistema è
|
||||||
|
marginale. Valutazione fatta ispezionando il binario v2.1.0 (checksum
|
||||||
|
verificato), non solo il changelog.
|
||||||
|
|
||||||
|
Cosa **non** cambia — verificato sul binario, non dedotto:
|
||||||
|
|
||||||
|
- v2.1.0 esporta al job esattamente le stesse variabili di v1.0.4
|
||||||
|
(`RUNNER_ARCH/TEMP/DEBUG/OS/TOOL_CACHE`), **nessun `RUNNER_LABELS`**. Il
|
||||||
|
`GITEA_RUNNER_LABELS` del changelog v2.1.0 è operator-side: imposta le label
|
||||||
|
del daemon all'avvio, non le espone al workflow. Il routing label → template
|
||||||
|
continua a richiedere un'istanza runner per label (vedi RUNBOOK-PhaseC,
|
||||||
|
sezione `deploy-rust`).
|
||||||
|
|
||||||
|
Cosa non ci riguarda: il grosso di v2.x è lavoro su Docker/container
|
||||||
|
(credenziali di pull, service container, dind, IPv6). Qui `container.enabled`
|
||||||
|
è `false`, host mode puro. Anche l'unico breaking change di v2.0.0
|
||||||
|
(`DOCKER_USERNAME`/`DOCKER_PASSWORD` non più impliciti nei pull) è inapplicabile.
|
||||||
|
|
||||||
|
Cosa ci riguarderebbe:
|
||||||
|
|
||||||
|
| Novità | Valore reale |
|
||||||
|
|---|---|
|
||||||
|
| Cancellation handling completa (v2.0.0) + reap processi (v1.0.7/1.0.8) | Un job cancellato può lasciare una build VM orfana — ma `ci-cleanup-orphans.timer` gira già ogni 6h |
|
||||||
|
| `runner.post_task_script` (v2.0.0) | Aggancerebbe `vm cleanup` dopo ogni task: latenza orfani da ≤6h a immediata |
|
||||||
|
| `jobs.<id>.timeout-minutes` / `continue-on-error` (v2.0.0) | Ergonomia workflow |
|
||||||
|
| `register --token-file` (v2.1.0) | Già implementato a mano in `scripts/install-rust-runner.sh` |
|
||||||
|
|
||||||
|
Schema di configurazione retrocompatibile: il diff v2.1.0 vs il nostro config è
|
||||||
|
solo additivo, nessuna chiave rimossa.
|
||||||
|
|
||||||
|
**Trigger per riaprire**: quando serve `runner.post_task_script` per azzerare la
|
||||||
|
latenza del cleanup orfani, oppure quando la 2.x è matura (era vecchia di 6
|
||||||
|
giorni alla valutazione). Non aggiornare "per stare aggiornati".
|
||||||
|
|
||||||
|
**Vincolo operativo da ricordare**: i due runner (`act-runner.service` e
|
||||||
|
`act-runner-rust.service`) condividono **lo stesso binario**
|
||||||
|
`/opt/ci/act_runner/act_runner`. Sostituirlo aggiorna entrambi insieme. Per un
|
||||||
|
rollout graduale serve un secondo binario affiancato
|
||||||
|
(`act_runner-2.1.0`), l'`ExecStart` del solo servizio rust puntato lì, un job di
|
||||||
|
prova su `runs-on: rust-build`, e solo dopo la promozione per il runner
|
||||||
|
principale — tenendo v1.0.4 per il rollback.
|
||||||
|
|
||||||
### 6.4 [P3] [x] Build matrix nel workflow
|
### 6.4 [P3] [x] Build matrix nel workflow
|
||||||
File: [gitea/workflows/build-ns7zip.yml](gitea/workflows/build-ns7zip.yml).
|
File: [gitea/workflows/build-ns7zip.yml](gitea/workflows/build-ns7zip.yml).
|
||||||
|
|
||||||
|
|||||||
@@ -77,10 +77,12 @@ sudo); `--uninstall` removes the block. The script defines two helpers:
|
|||||||
| `ci vmware --vmx ...` | Open a VMX in the VMware Workstation GUI | [§4](#4-manual-vm-lifecycle) |
|
| `ci vmware --vmx ...` | Open a VMX in the VMware Workstation GUI | [§4](#4-manual-vm-lifecycle) |
|
||||||
| `ci creds set --user ...` | Store guest credentials in the keyring | [§5](#5-credentials--template-provisioning) |
|
| `ci creds set --user ...` | Store guest credentials in the keyring | [§5](#5-credentials--template-provisioning) |
|
||||||
| `ci template deploy-linux` | Build the Linux template VM | [§5](#5-credentials--template-provisioning) |
|
| `ci template deploy-linux` | Build the Linux template VM | [§5](#5-credentials--template-provisioning) |
|
||||||
|
| `ci template deploy-rust` | Build the Rust Linux template VM (LinuxRust2404) | [§5](#5-credentials--template-provisioning) |
|
||||||
| `ci template check-win-iso` | Report the newest WS2025 ISO from massgrave + cache status | [§5](#5-credentials--template-provisioning) |
|
| `ci template check-win-iso` | Report the newest WS2025 ISO from massgrave + cache status | [§5](#5-credentials--template-provisioning) |
|
||||||
| `ci template patch-win-iso` | Inject autounattend + no-prompt boot into a Windows ISO | [§5](#5-credentials--template-provisioning) |
|
| `ci template patch-win-iso` | Inject autounattend + no-prompt boot into a Windows ISO | [§5](#5-credentials--template-provisioning) |
|
||||||
| `ci template deploy-win` | Build the Windows Server 2025 template VM (download→patch→deploy) | [§5](#5-credentials--template-provisioning) |
|
| `ci template deploy-win` | Build the Windows Server 2025 template VM (download→patch→deploy) | [§5](#5-credentials--template-provisioning) |
|
||||||
| `ci template prepare-linux` | Provision the Linux template over SSH | [§5](#5-credentials--template-provisioning) |
|
| `ci template prepare-linux` | Provision the Linux template over SSH | [§5](#5-credentials--template-provisioning) |
|
||||||
|
| `ci template prepare-rust` | Provision the Rust Linux template over SSH | [§5](#5-credentials--template-provisioning) |
|
||||||
| `ci template prepare-win` | Provision a Windows template over WinRM | [§5](#5-credentials--template-provisioning) |
|
| `ci template prepare-win` | Provision a Windows template over WinRM | [§5](#5-credentials--template-provisioning) |
|
||||||
| `ci template backup` | Timestamped copy of the template directories | [§5](#5-credentials--template-provisioning) |
|
| `ci template backup` | Timestamped copy of the template directories | [§5](#5-credentials--template-provisioning) |
|
||||||
| `ci retention run` | Purge old artifacts and logs | [§6](#6-maintenance) |
|
| `ci retention run` | Purge old artifacts and logs | [§6](#6-maintenance) |
|
||||||
@@ -412,12 +414,139 @@ Run `ci template <sub> --help` for the (many) per-template options.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
ci template deploy-linux # build the Linux template VM from scratch (Ubuntu 24.04)
|
ci template deploy-linux # build the Linux template VM from scratch (Ubuntu 24.04)
|
||||||
|
ci template deploy-rust # build the Rust Linux template VM (LinuxRust2404)
|
||||||
ci template deploy-win # build the Windows Server 2025 template VM (see below)
|
ci template deploy-win # build the Windows Server 2025 template VM (see below)
|
||||||
ci template prepare-linux # provision an existing Linux template over SSH
|
ci template prepare-linux # provision an existing Linux template over SSH
|
||||||
|
ci template prepare-rust # provision the Rust Linux template over SSH
|
||||||
ci template prepare-win # provision a Windows template over WinRM (Linux-host driven)
|
ci template prepare-win # provision a Windows template over WinRM (Linux-host driven)
|
||||||
ci template backup # timestamped backup of the template directories
|
ci template backup # timestamped backup of the template directories
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `deploy-rust` / `prepare-rust` — the Rust Linux template (LinuxRust2404)
|
||||||
|
|
||||||
|
A separate template for Rust workspaces (e.g. Tauri apps): same Ubuntu 24.04
|
||||||
|
cloud image and guest layout as `LinuxBuild2404`, but `prepare-rust` runs
|
||||||
|
`template/Install-CIToolchain-LinuxRust2404.sh`, which additionally bakes:
|
||||||
|
|
||||||
|
- the Tauri v2 webview/GTK development libraries
|
||||||
|
(`libwebkit2gtk-4.1-dev`, `libgtk-3-dev`, `libayatana-appindicator3-dev`,
|
||||||
|
`librsvg2-dev`);
|
||||||
|
- a system-wide Rust toolchain via rustup under `/opt/rust`
|
||||||
|
(`RUSTUP_HOME=/opt/rust/rustup`, `CARGO_HOME=/opt/rust/cargo`) with the
|
||||||
|
`rustfmt` and `clippy` components. The tree is `chown`ed to `ci_build`
|
||||||
|
**after** the installs, not only before — otherwise every write the build
|
||||||
|
user makes (toolchain sync, dependency download into
|
||||||
|
`CARGO_HOME/registry`, `cargo install`) fails with `Permission denied`.
|
||||||
|
Exposure is threefold because the orchestrator runs build commands in a
|
||||||
|
**non-login** shell (`exec_command` + `sh -c`), which never sources
|
||||||
|
`profile.d`: `/etc/profile.d/rust.sh` for interactive shells,
|
||||||
|
`/etc/environment` (read by `pam_env` at SSH session open) for the two
|
||||||
|
home variables, and `/usr/local/bin` symlinks for the rustup proxies;
|
||||||
|
- the `cargo-audit` binary (skip with `--no-cargo-audit`). The audit
|
||||||
|
**advisory DB is never baked** — `cargo audit` fetches it at runtime.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ci template deploy-rust # same flags as deploy-linux; 60 GB disk default
|
||||||
|
ci template prepare-rust # default VMX: <CI_TEMPLATES>/LinuxRust2404/...
|
||||||
|
# snapshot: BaseClean-LinuxRust
|
||||||
|
ci template prepare-rust --rust-channel 1.97.1 # pin an exact toolchain
|
||||||
|
|
||||||
|
# Both phases in one run, unattended. deploy and prepare stay separate
|
||||||
|
# commands — --then-prepare only chains them, reusing the VM deploy left
|
||||||
|
# running (no second power-on, no second IP detection).
|
||||||
|
#
|
||||||
|
# Run from the repo root: the guest toolchain script is looked up under
|
||||||
|
# <cwd>/template/. With --then-prepare this is checked BEFORE the deploy
|
||||||
|
# starts, so a wrong directory costs two seconds instead of failing the
|
||||||
|
# hand-off after a ~45-minute deploy. Pass --toolchain-script to run from
|
||||||
|
# anywhere.
|
||||||
|
cd /opt/ci/local-ci-cd-system
|
||||||
|
ci template deploy-rust --then-prepare --rust-channel 1.97.1
|
||||||
|
ci template deploy-linux --then-prepare
|
||||||
|
|
||||||
|
# from any directory
|
||||||
|
ci template deploy-rust --then-prepare --rust-channel 1.97.1 \
|
||||||
|
--toolchain-script /opt/ci/local-ci-cd-system/template/Install-CIToolchain-LinuxRust2404.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
`--snapshot-name` on `deploy-linux`/`deploy-rust` names the snapshot that
|
||||||
|
`prepare-*` will create (`BaseClean-Linux` / `BaseClean-LinuxRust`) — the
|
||||||
|
deploy commands take no snapshot of their own, unlike `deploy-win`. With
|
||||||
|
`--then-prepare` the value is handed to `prepare-*`, so overriding it on the
|
||||||
|
deploy side changes what actually gets created.
|
||||||
|
|
||||||
|
Both `prepare-linux` and `prepare-rust` default `--vmx` to their template VMX
|
||||||
|
under `<CI_TEMPLATES>` (`LinuxBuild2404` / `LinuxRust2404`) when neither
|
||||||
|
`--vmx` nor `--ip` is passed — so the plain `ci template prepare-rust` just
|
||||||
|
works after `deploy-rust`. Pass `--ip` to provision a running guest without
|
||||||
|
VM lifecycle (no auto power-on/shutdown/snapshot).
|
||||||
|
|
||||||
|
`--rust-channel` defaults to `stable` — a moving target: when a new stable
|
||||||
|
ships, `rustup show` in a job re-downloads the toolchain at runtime. Pin an
|
||||||
|
exact version for a fully deterministic snapshot (the consumer repo decides
|
||||||
|
this via its `rust-toolchain.toml`). Refresh the snapshot when the pinned
|
||||||
|
channel or the apt security baseline moves.
|
||||||
|
|
||||||
|
Two ways to route jobs to it:
|
||||||
|
|
||||||
|
1. **Per job (no host change).** Pass `template-path` + `snapshot-name` to the
|
||||||
|
`local-ci-build` composite action. This cannot leave a job unschedulable,
|
||||||
|
which is why consumer repos default to it.
|
||||||
|
2. **By label** — `runs-on: rust-build`. This needs a **second act_runner
|
||||||
|
instance**, not just an extra label on the existing one: act_runner reads a
|
||||||
|
single `runner.envs` block per process and never tells the workflow which
|
||||||
|
label picked the job (there is no `RUNNER_LABELS` variable — v1.0.4 exposes
|
||||||
|
only `RUNNER_OS/ARCH/TEMP/TOOL_CACHE/DEBUG/VERSION`). A label can select a
|
||||||
|
template only by selecting a runner whose envs name that template.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. mint a registration token from the stored GiteaPAT (0600 file, never
|
||||||
|
# printed — pass it by path so it stays out of `ps` and shell history)
|
||||||
|
sudo -u ci-runner /opt/ci/venv/bin/python tools/gitea-runner-token.py \
|
||||||
|
--out /var/lib/ci/.rust-runner-regtoken
|
||||||
|
|
||||||
|
# 2. provision + register + enable the unit
|
||||||
|
sudo ./scripts/install-rust-runner.sh \
|
||||||
|
--token-file /var/lib/ci/.rust-runner-regtoken
|
||||||
|
|
||||||
|
# 3. the token is single-use — destroy it
|
||||||
|
sudo shred -u /var/lib/ci/.rust-runner-regtoken
|
||||||
|
```
|
||||||
|
|
||||||
|
Provisions `/var/lib/ci/runner-rust/` from `deploy/runner-rust-config.yaml`
|
||||||
|
(label `rust-build:host`, `GITEA_CI_LINUX_TEMPLATE_PATH` →
|
||||||
|
`LinuxRust2404`, `GITEA_CI_LINUX_SNAPSHOT_NAME` → `BaseClean-LinuxRust`),
|
||||||
|
registers it, and installs `act-runner-rust.service`. Its `capacity` adds
|
||||||
|
to the main runner's — keep the sum inside the host RAM budget (currently
|
||||||
|
2 + 1 = 3 concurrent build VMs).
|
||||||
|
|
||||||
|
**Verify the label is announced before any workflow depends on it**: a
|
||||||
|
`runs-on` value that no runner advertises leaves the job unschedulable with
|
||||||
|
no error anywhere — silently zero runs.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo -u ci-runner /opt/ci/venv/bin/python tools/gitea-runner-list.py \
|
||||||
|
--label rust-build # exit 1 unless an ONLINE runner announces it
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that `runner.file` in the config is an **absolute** path. act_runner
|
||||||
|
resolves a relative `file:` against the process CWD, so registering from
|
||||||
|
anywhere else drops the registration state (which contains the runner
|
||||||
|
token) into that directory and the daemon then fails with
|
||||||
|
`registration file not found`.
|
||||||
|
|
||||||
|
Both runner services share **one binary**, `/opt/ci/act_runner/act_runner`.
|
||||||
|
Replacing it upgrades `act-runner.service` and `act-runner-rust.service`
|
||||||
|
together — a staged rollout needs a second binary alongside it and the
|
||||||
|
`ExecStart` of one unit pointed at it. Current version is v1.0.4; the
|
||||||
|
upgrade to v2.x was evaluated and deferred (TODO §6.5).
|
||||||
|
|
||||||
|
Note that `guest-os: 'Auto'` cannot pick a template by itself, for the same
|
||||||
|
reason: the label is invisible to the action, and the VMX `guestOS` field is
|
||||||
|
only readable *after* the clone. With `Auto` the action requires
|
||||||
|
`template-path` **and** `snapshot-name` and fails fast otherwise, instead of
|
||||||
|
silently falling back to the Windows template as it did before.
|
||||||
|
|
||||||
### `deploy-win` — build the Windows Server 2025 template
|
### `deploy-win` — build the Windows Server 2025 template
|
||||||
|
|
||||||
Takes a Windows Server 2025 install ISO — downloaded from `--iso-url` (cached
|
Takes a Windows Server 2025 install ISO — downloaded from `--iso-url` (cached
|
||||||
|
|||||||
@@ -250,6 +250,7 @@ Estimated time: 2-4 hours including Windows Update.
|
|||||||
| Template corrupt | Remove `*.lck` files; if persistent, restore from `F:\CI\Backups\` |
|
| Template corrupt | Remove `*.lck` files; if persistent, restore from `F:\CI\Backups\` |
|
||||||
| Snapshot missing | `vmrun listSnapshots <vmx>`; update `GITEA_CI_SNAPSHOT_NAME` |
|
| Snapshot missing | `vmrun listSnapshots <vmx>`; update `GITEA_CI_SNAPSHOT_NAME` |
|
||||||
| IP collision | `Remove-Item F:\CI\State\ip-leases\*.lease`; lower `capacity` |
|
| IP collision | `Remove-Item F:\CI\State\ip-leases\*.lease`; lower `capacity` |
|
||||||
|
| Step 7: no guest IP | `ps -eo pid,lstart,args \| grep vmware-vmx` — see §16 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -868,3 +869,92 @@ This is **optional** — the default boots-to-Linux behaviour is intentional so
|
|||||||
the machine comes back as the Linux CI host after any unattended reboot
|
the machine comes back as the Linux CI host after any unattended reboot
|
||||||
(power-loss, kernel update). Leave it as-is unless Windows becomes the daily
|
(power-loss, kernel update). Leave it as-is unless Windows becomes the daily
|
||||||
driver.
|
driver.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 16. `deploy-*` step 7 in timeout — nessun IP dal guest
|
||||||
|
|
||||||
|
**Sintomo.** `deploy-linux` / `deploy-rust` cicla su
|
||||||
|
`... waiting for guest IP (open-vm-tools) ...` per 10 minuti e chiude con
|
||||||
|
`Error: [Deploy Step 7] Timed out waiting for guest IP.` Interrogando a mano:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ vmrun -T ws getGuestIPAddress <vmx>
|
||||||
|
Error: The VMware Tools are not running in the virtual machine
|
||||||
|
```
|
||||||
|
|
||||||
|
Quel messaggio è ambiguo: esce identico sia quando il guest **non ha** un IP,
|
||||||
|
sia quando ce l'ha ma i Tools non rispondono. Tre cause distinte, in ordine di
|
||||||
|
verifica dal più veloce al più lento.
|
||||||
|
|
||||||
|
### 16.1 Collisione con una VM già accesa (la più insidiosa)
|
||||||
|
|
||||||
|
Succede quando si lancia `deploy-* --force` mentre la stessa VMX è in uso —
|
||||||
|
tipicamente un `prepare-*` ancora in corso. Occorso il 2026-07-22 su
|
||||||
|
`LinuxRust2404`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ps -eo pid,lstart,etime,args | grep vmware-vmx | grep -v grep
|
||||||
|
```
|
||||||
|
|
||||||
|
Se il `vmware-vmx` vivo è **più vecchio** del deploy appena lanciato, è questo
|
||||||
|
il caso: il deploy ha cancellato il `.vmdk.lck` della VM viva credendolo stale,
|
||||||
|
ha riscritto il VMDK base sotto il guest in esecuzione, e il suo power-on è
|
||||||
|
morto subito perché la VMX era occupata. Il controllo
|
||||||
|
`[OK] VM appears in vmrun list` **non protegge**: vede l'istanza vecchia.
|
||||||
|
|
||||||
|
Recupero:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo -u ci-runner vmrun -T ws stop <vmx> hard
|
||||||
|
sudo rm -f <vm-dir>/<disk>-00000*.vmdk # delta orfani, vedi 16.4
|
||||||
|
sudo -u ci-runner vmrun -T ws list # deve dire 0
|
||||||
|
# poi rilanciare il deploy, con nient'altro in esecuzione sulla stessa VMX
|
||||||
|
```
|
||||||
|
|
||||||
|
Non risolvere con un retry: riavviare una VM già corrotta ripete il fallimento
|
||||||
|
con la causa ormai illeggibile.
|
||||||
|
|
||||||
|
### 16.2 DHCP dell'host incastrato
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ip -4 addr show vmnet8 # atteso: inet 192.168.79.1/24
|
||||||
|
ps -ef | grep vmnet-dhcpd # atteso: due istanze (vmnet1 + vmnet8)
|
||||||
|
```
|
||||||
|
|
||||||
|
Se `vmnet8` manca o `vmnet-dhcpd` non c'è:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl restart vmware-networks # oppure vmware-host.service
|
||||||
|
```
|
||||||
|
|
||||||
|
### 16.3 Guest senza lease, oppure Tools non ancora pronti
|
||||||
|
|
||||||
|
```bash
|
||||||
|
grep -A1 '^lease' /etc/vmware/vmnet8/dhcpd/dhcpd.leases | tail -20
|
||||||
|
ip neigh show dev vmnet8
|
||||||
|
```
|
||||||
|
|
||||||
|
Se compare un lease per il MAC della VM, **il guest è vivo e ha rete**: mancano
|
||||||
|
solo i Tools. Sondare direttamente la porta 22 invece di aspettare
|
||||||
|
`getGuestIPAddress`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
timeout 5 bash -c 'cat < /dev/null > /dev/tcp/<ip>/22' && echo "SSH aperta"
|
||||||
|
```
|
||||||
|
|
||||||
|
Se invece non c'è nessun lease e le due verifiche precedenti sono pulite, il
|
||||||
|
guest non sta completando il boot: cloud-init fallito o immagine corrotta.
|
||||||
|
|
||||||
|
### 16.4 Delta di snapshot orfani
|
||||||
|
|
||||||
|
`deploy-*` cancella `<vm>.vmsd` per evitare l'errore di resize sulla catena
|
||||||
|
snapshot, ma **non** rimuove i file delta `<disk>-00000N.vmdk`. Restano con un
|
||||||
|
`parentCID` che non corrisponde più al base ricreato. Sono innocui — la VMX
|
||||||
|
punta al base, riconoscibile da `parentCID=ffffffff` — ma confondono la
|
||||||
|
diagnosi. Verifica della catena:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
grep -i 'scsi0:0.fileName' <vmx>
|
||||||
|
head -c 2000 <disk>.vmdk | strings | grep -E '^CID|parentCID'
|
||||||
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user