Every Gitea API call from Python 403s with "error code: 1010" unless a User-Agent is set, including endpoints that need no auth — and it looks like a token/scope problem, which is where this went wrong once. Registered next to the other environment traps, with the two-command check that tells them apart.
290 lines
23 KiB
Markdown
290 lines
23 KiB
Markdown
# AGENTS.md — Environment Reference
|
|
|
|
Questo file descrive l'ambiente su cui e' costruito il progetto.
|
|
Leggerlo prima di generare o modificare qualsiasi script.
|
|
|
|
> **Istruzione per gli agenti**: questo file va tenuto aggiornato.
|
|
> Se durante il lavoro si scopre un errore nuovo, un vincolo non documentato,
|
|
> un pattern che ha causato problemi o una best-practice utile, aggiungerla
|
|
> nella sezione "Errori frequenti da evitare" o in una sezione apposita.
|
|
> Non aggiungere dettagli ovvi o gia' coperti; solo informazioni che
|
|
> impedirebbero errori concreti in sessioni future.
|
|
|
|
---
|
|
|
|
## Host
|
|
|
|
| Parametro | Valore |
|
|
| ------------------ | ----------------------------------------------------------------------- |
|
|
| OS | Windows 11 |
|
|
| Hardware | i9-10900X, 64 GB RAM, NVMe SSD |
|
|
| PowerShell | **5.1** (Windows PowerShell — NON Core/7) |
|
|
| VMware Workstation | Pro, `vmrun.exe` in `C:\Program Files (x86)\VMware\VMware Workstation\` |
|
|
| SSH client | `ssh.exe` / `scp.exe` built-in Windows 11 (PATH) |
|
|
| act_runner | v1.0.2 — `F:\CI\act_runner\act_runner.exe` |
|
|
| Gitea | `http://10.10.20.11:3100` / `https://gitea.emulab.it` |
|
|
|
|
---
|
|
|
|
## PowerShell 5.1 — Vincoli critici
|
|
|
|
Tutti gli script devono girare su **Windows PowerShell 5.1**. Le seguenti
|
|
sintassi sono disponibili solo in PS 7+ e NON vanno usate:
|
|
|
|
| Costrutto PS 7+ | Alternativa PS 5.1 corretta |
|
|
| ------------------------- | ----------------------------------------- |
|
|
| `$x ??= 'default'` | `if ($null -eq $x) { $x = 'default' }` |
|
|
| `$a ?? $b` | `if ($null -ne $a) { $a } else { $b }` |
|
|
| `cmd1 && cmd2` | `cmd1; if ($LASTEXITCODE -eq 0) { cmd2 }` |
|
|
| `cmd1 \|\| cmd2` | `cmd1; if ($LASTEXITCODE -ne 0) { cmd2 }` |
|
|
| `$x is [int]` | `$x -is [int]` |
|
|
| Ternary `$a ? $b : $c` | `if ($a) { $b } else { $c }` |
|
|
| `foreach-object` con `&&` | pipeline classico con `-ErrorAction Stop` |
|
|
|
|
Ogni script deve avere in testa:
|
|
|
|
```powershell
|
|
#Requires -Version 5.1
|
|
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = 'Stop'
|
|
```
|
|
|
|
---
|
|
|
|
## Python development
|
|
|
|
L'orchestratore CI vive in `src/ci_orchestrator/` (package `ci-orchestrator`,
|
|
entry point `python -m ci_orchestrator`). Tutta la logica applicativa
|
|
nuova va scritta in Python, non in PowerShell. Gli script PS in `scripts/`
|
|
sono ridotti a shim a 3 righe verso la CLI Python e sono mantenuti solo
|
|
per compatibilita' con Task Scheduler e caller esterni.
|
|
|
|
| Parametro | Valore |
|
|
| ------------------------ | ------------------------------------------------------------------------------------- |
|
|
| Python | **3.11+** richiesto (`requires-python` in `pyproject.toml`) |
|
|
| Venv produzione | `F:\CI\python\venv\` — install **non-editable**: `pip install .` (act_runner=LocalSystem; `-e` rompe con `No module named ci_orchestrator`). Ri-deploy a ogni modifica codice. |
|
|
| Venv dev locale | `.venv\` (NON committare; gitignored) |
|
|
| Install (dev locale) | `python -m pip install -e .[dev]` — editable solo nel `.venv` dev |
|
|
| Test | `python -m pytest tests\python -q --cov=ci_orchestrator --cov-fail-under=90` |
|
|
| Lint | `python -m ruff check src tests\python` |
|
|
| Type-check | `python -m mypy --strict src` |
|
|
|
|
**Convenzioni Python (enforced da ruff + mypy --strict)**:
|
|
|
|
- Type hints obbligatori su funzioni pubbliche e helper esportati. Evitare
|
|
`Any` se esiste un tipo concreto/Protocol; `Any` solo per fixture pytest
|
|
e mock.
|
|
- Niente variabili globali. Lo state vive in oggetti passati esplicitamente
|
|
(factory `backends.load_backend(config)`, `Config` dataclass).
|
|
- Niente `print()`: usare `click.echo` (rispetta il flag `-q` di click e
|
|
il redirect `err=True` per stderr).
|
|
- Niente `subprocess.run(..., shell=True)`. Niente shell-out per cose che
|
|
esistono come libreria Python (paramiko per SSH, pypsrp per WinRM,
|
|
keyring per credenziali).
|
|
- Encoding stdio sempre UTF-8: i moduli Python lo assumono e
|
|
`runner/config.yaml` esporta `PYTHONIOENCODING=utf-8` ad act_runner.
|
|
|
|
**Mappatura script PowerShell → sotto-comando Python** (Fase A2/A3/A4 + C).
|
|
I `.ps1` qui sotto sono **archiviati** in `scripts/archived/` (congelati, esenti
|
|
da lint, nessun caller vivo — l'action Gitea chiama Python direttamente). Non
|
|
chiamarli né estenderli; vedi `scripts/archived/README.md`.
|
|
|
|
| PowerShell archiviato | Python sub-command |
|
|
| ----------------------------------- | ------------------------------------------ |
|
|
| `Wait-VMReady.ps1` | `python -m ci_orchestrator wait-ready` |
|
|
| `New-BuildVM.ps1` | `python -m ci_orchestrator vm new` |
|
|
| `Remove-BuildVM.ps1` | `python -m ci_orchestrator vm remove` |
|
|
| `Cleanup-OrphanedBuildVMs.ps1` | `python -m ci_orchestrator vm cleanup` |
|
|
| `Invoke-RemoteBuild.ps1` | `python -m ci_orchestrator build run` |
|
|
| `Get-BuildArtifacts.ps1` | `python -m ci_orchestrator artifacts collect` |
|
|
| `Watch-DiskSpace.ps1` | `python -m ci_orchestrator monitor disk` |
|
|
| `Watch-RunnerHealth.ps1` | `python -m ci_orchestrator monitor runner` |
|
|
| `Get-CIJobSummary.ps1` | `python -m ci_orchestrator report job` |
|
|
| `Invoke-CIJob.ps1` | `python -m ci_orchestrator job` |
|
|
| `Start-BurnInTest.ps1` / `-Linux` | `python -m ci_orchestrator bench run` |
|
|
| `Test-CapacityBurnIn.ps1` | `python -m ci_orchestrator bench run` |
|
|
| `Test-Smoke.ps1` | `python -m ci_orchestrator smoke run` |
|
|
| `Validate-HostState.ps1` | `python -m ci_orchestrator validate host` |
|
|
| `Set-CIGuestCredential.ps1` | `python -m ci_orchestrator creds set` |
|
|
|
|
Restano in PowerShell (girano sull'host Windows fuori dal flusso CI o nel guest,
|
|
non ancora portati): `Setup-Host.ps1`, `scripts/Register-CIScheduledTasks.ps1`,
|
|
`scripts/Backup-CITemplate.ps1`, `scripts/Invoke-RetentionPolicy.ps1`,
|
|
`scripts/Set-TemplateSharedFolders.ps1`, `scripts/Test-CIGuestWinRM.ps1`, gli
|
|
harness e2e `scripts/Test-E2E-Section3.3.ps1` / `Test-Ns7zipBuild-Linux.ps1` /
|
|
`Test-NsinnounpBuild*.ps1`, tutti i `template/Deploy-*.ps1`,
|
|
`template/Prepare-*.ps1`, `template/Install-CIToolchain-*.ps1`.
|
|
|
|
**Test gate**: coverage totale ≥90% (alzato in A5, poi a 90%). Nuovi moduli devono
|
|
includere unit test con mock; gli errori frequenti di questa stessa
|
|
sezione sono coperti da `tests/python/test_agents_errors.py` come
|
|
regression test — non rimuoverli senza aggiornare la voce in
|
|
"Errori frequenti da evitare" e motivare la rimozione.
|
|
|
|
**Closeout fasi A**: vedi `plans/A1-closeout.md`, `plans/A2-closeout.md`,
|
|
`plans/A3-closeout.md`, `plans/A4-closeout.md`, `plans/A5-closeout.md`
|
|
per stato per-fase, decisioni prese e voci pendenti a carico utente.
|
|
|
|
---
|
|
|
|
## Struttura directory host (fissa)
|
|
|
|
```
|
|
F:\CI\
|
|
act_runner\ — binario e stato act_runner
|
|
Templates\
|
|
WinBuild2025\ — template Windows Server 2025
|
|
WinBuild2022\ — template Windows Server 2022
|
|
LinuxBuild2404\ — template Ubuntu 24.04
|
|
BuildVMs\ — clone efimeri (creati/distrutti per ogni job)
|
|
Artifacts\ — artifact raccolti (per job-id)
|
|
ISO\ — ISO/VMDK di bootstrap (cache download)
|
|
keys\
|
|
ci_linux — chiave SSH privata per guest Linux (no passphrase)
|
|
ci_linux.pub — chiave pubblica corrispondente
|
|
```
|
|
|
|
---
|
|
|
|
## VM Template
|
|
|
|
### Windows (WinBuild2025 — primario)
|
|
|
|
| Parametro | Valore |
|
|
| -------------- | ------------------------------------------------------------------------------- |
|
|
| OS | Windows Server 2025 |
|
|
| VMX | `F:\CI\Templates\WinBuild2025\WinBuild2025.vmx` |
|
|
| Snapshot clone | `BaseClean` |
|
|
| Transport | WinRM HTTPS porta 5986, cert self-signed (lab-only) |
|
|
| Credenziali | Windows Credential Manager target `BuildVMGuest` |
|
|
| Rete | VMnet8 NAT — `192.168.79.0/24` |
|
|
| Toolchain | VS Build Tools 2026 (MSBuild 18.5 / v145), .NET SDK 10, Python 3.13, Git, 7-Zip |
|
|
|
|
Variante **WinBuild2022**: path `F:\CI\Templates\WinBuild2022\WinBuild2022.vmx`, stesso snapshot `BaseClean`.
|
|
|
|
### Linux (LinuxBuild2404)
|
|
|
|
| Parametro | Valore |
|
|
| -------------- | ------------------------------------------------------------- |
|
|
| OS | Ubuntu 24.04 LTS (cloud image) |
|
|
| VMX | `F:\CI\Templates\LinuxBuild2404\LinuxBuild2404.vmx` |
|
|
| Snapshot clone | `BaseClean-Linux` |
|
|
| Transport | SSH porta 22, utente `ci_build`, chiave `F:\CI\keys\ci_linux` |
|
|
| Rete | VMnet8 NAT — `192.168.79.0/24` |
|
|
| Toolchain | GCC, G++, Clang, CMake, Ninja, Python 3, Git, 7-Zip |
|
|
| 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
|
|
|
|
- **Windows guest**: WinRM (`New-PSSession` con `New-CISessionOption` da `_Common.psm1`).
|
|
Usare `Invoke-Command -Session`, `Copy-Item -ToSession / -FromSession`.
|
|
- **Linux guest**: SSH/SCP via `ssh.exe` / `scp.exe`.
|
|
Usare le funzioni di `scripts/_Transport.psm1`:
|
|
`Invoke-SshCommand`, `Copy-SshItem`, `Test-SshReady`.
|
|
- **vmrun.exe**: sempre invocato tramite `Invoke-Vmrun` da `_Common.psm1`
|
|
(wrapper uniforme che gestisce `$LASTEXITCODE` e output).
|
|
|
|
NON modificare il branch WinRM esistente quando si lavora sul branch Linux e viceversa.
|
|
|
|
---
|
|
|
|
## act_runner / Gitea Actions
|
|
|
|
- Labels runner: `windows-build:host`, `linux-build:host`
|
|
- Env vars iniettate in ogni job (da `runner/config.yaml`):
|
|
- `GITEA_CI_TEMPLATE_PATH` — VMX Windows template
|
|
- `GITEA_CI_LINUX_TEMPLATE_PATH` — VMX Linux template
|
|
- `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`
|
|
- `CI_PYTHON_LAUNCHER` — absolute path al Python di sistema usato per
|
|
bootstrap del venv produzione (`F:\CI\python\venv`). act_runner gira come
|
|
SYSTEM con PATH sanitizzato (no `python`/`py.exe` risolvibili), quindi i
|
|
workflow Python leggono questo env come single source of truth. Aggiornare
|
|
solo quando si fa upgrade dell'interprete.
|
|
|
|
---
|
|
|
|
## Regole di stile (PSScriptAnalyzer)
|
|
|
|
Configurazione in `PSScriptAnalyzerSettings.psd1`. Regole principali:
|
|
|
|
- Indentazione: **4 spazi** (no tab)
|
|
- `Write-Host` e' permesso (output catturato da act_runner; `Write-Output` interferisce con i return value)
|
|
- `Invoke-Expression` vietato
|
|
- Funzioni state-changing devono supportare `-WhatIf` / `ShouldProcess`
|
|
- Credenziali solo come `[PSCredential]`, mai plain-text string
|
|
- Nessuna variabile globale (`$global:`)
|
|
|
|
---
|
|
|
|
## Errori frequenti da evitare
|
|
|
|
1. **Sintassi PS 7+** in script PS 5.1 — vedi tabella sopra.
|
|
2. **`$LASTEXITCODE` non controllato** dopo `vmrun`, `ssh`, `scp` — controllare sempre e fare `throw` se non zero.
|
|
3. **Percorsi hardcoded senza parametro** — ogni path critico deve avere un parametro con default documentato.
|
|
4. **Modificare entrambi i branch (WinRM e SSH) nello stesso script** — mantenerli separati con `if ($GuestOS -eq 'Linux')`.
|
|
5. **Usare `New-PSSession` / WinRM verso host Linux** — Linux usa solo SSH tramite `_Transport.psm1`.
|
|
6. **Non rimuovere il seed ISO dal VMX** dopo il primo boot Linux — `ide1:0.present = "FALSE"` deve essere settato dopo cloud-init.
|
|
7. **`[ordered]@{}` non disponibile in PS 2** — non e' un problema qui (PS 5.1), ma ricordare che `[ordered]` e' PS 3+.
|
|
8. **`ForEach-Object -Parallel`** — disponibile solo in PS 7+; usare `foreach` classico o `Start-Job`.
|
|
9. **Snapshot template catturato con VM accesa** — produce file `*.vmem` / `*.vmsn` con memoria; `vmrun clone ... linked -snapshot=<name>` rifiuta con `Error: The virtual machine should not be powered on. It is already running.` anche se `vmrun list` mostra 0 VM. Catturare snapshot SOLO da stato fully powered-off (verificare assenza di `.vmem` nella dir template).
|
|
10. **`vmrun getGuestIPAddress` per check "VM running"** — richiede VMware Tools attivi nel guest; in headless Tools puo' impiegare 30-60s a rispondere e il check appare bloccato. Usare `vmrun list` e cercare il path del VMX nell'output per `is_running`. Per recuperare l'IP usare `getGuestIPAddress -wait` (blocca internamente finché Tools registrano l'IP) e filtrare le righe che iniziano con `"Error:"` perché vmrun in modalità headless può tornare exit code 0 con `"Error: The VMware Tools are not running..."` su stdout invece di usare un exit code non-zero.
|
|
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.
|
|
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'`.
|
|
|
|
17. **`vmrun list` è per-utente e mente se lo lanci con l'utente sbagliato** — le VM della CI le avvia `ci-runner`; `vmrun -T ws list` eseguito come `simone` (o come root) risponde `Total running VMs: 0` **anche mentre un clone sta girando**, perché vede solo l'istanza VMware dell'utente corrente. Occorso il 2026-07-22 durante la validazione della cache cargo: `vmrun list` diceva 0 e `ps` mostrava un `vmware-vmx` vivo da 19 minuti. Conseguenza pratica: il check "0 VM accese" prima di un `deploy --force` (vedi #14) **non è affidabile** se non lo si esegue come `ci-runner`. Usare `sudo -u ci-runner vmrun -T ws list`, oppure — meglio, perché indipendente dall'utente — `ps -eo pid,lstart,etime,args | grep '[v]mware-vmx'`, che è anche la diagnosi già raccomandata in #14 e #15.
|
|
|
|
18. **Gitea è dietro Cloudflare: `Python-urllib` riceve 403 prima di arrivare a Gitea** — qualunque chiamata a `https://gitea.emulab.it/api/v1/...` fatta con `urllib.request` senza `User-Agent` esplicito torna `403` con body `error code: 1010` (Cloudflare "browser banned"), **su ogni endpoint**, incluso `/api/v1/version` che non richiede autenticazione. Sembra identico a un problema di token/scope e manda la diagnosi fuori strada. Verifica in due mosse: `curl -s https://gitea.emulab.it/api/v1/version` risponde 200 (curl manda il suo UA) mentre la stessa chiamata in Python risponde 403; gli header di risposta mostrano `server: cloudflare` + `cf-ray`. Fix: header `User-Agent` esplicito — qualunque stringa normale passa (vedi `operations/executions.py::_USER_AGENT`). Corollario generale: quando un endpoint HTTP risponde 4xx, **leggere il body** prima di ipotizzare la causa — Gitea scrive il permesso mancante (`required=[read:user], token scope=read:admin,read:repository`), Cloudflare scrive il suo codice.
|