Headless Linux dev container: Godot + .NET + Xvfb for autonomous testing
Claude Code running inside the project's dev container can now build the game, launch a real Godot instance under Xvfb, and drive the automation harness end-to-end — no Windows dependency. Dockerfile adds (as root, before USER node): - X11 / Mesa software GL / audio runtime deps + python3 - .NET SDK 9.0 via upstream dot.net install script -> /usr/local/dotnet - Godot 4.6.2-stable mono Linux x86_64 -> /opt/godot/godot - /usr/local/bin/godot-xvfb wrapper: auto-wraps invocations in xvfb-run -a --server-args="-screen 0 1280x720x24 ..." harness.py picks GODOT_BIN from env, defaults to /opt/godot/godot on Linux, and auto-wraps the subprocess in xvfb-run when DISPLAY is unset. Windows code path unchanged. init-firewall.sh adds api.nuget.org to the allowlist so dotnet restore works post-boot. Godot + .NET SDK are fetched at image build time, before the firewall exists. New docs: - autonomous_plan.md: design rationale, alternatives considered - README.md: launch instructions for Windows terminal / Docker Desktop / VS Code Dev Containers / WSL2 natif - CLAUDE.md already documents the harness (done in previous commit) Validation: docker build succeeds; inside the container, dotnet --version =9.0.313, godot --version=4.6.2.stable.mono, dotnet test=102/102, python3 tools/automation/smoke.py passes end-to-end with 14 non-black 1280x720 PNGs. Mission 1 screenshot is visually identical to the Windows build, and Xvfb determinism is a bonus (det_a.png ≡ det_b.png bytewise).
This commit is contained in:
parent
8f3b1b39e7
commit
c451a50349
6 changed files with 442 additions and 4 deletions
|
|
@ -25,8 +25,80 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||
jq \
|
||||
nano \
|
||||
vim \
|
||||
ca-certificates \
|
||||
curl \
|
||||
wget \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Chessistics: headless Godot + .NET SDK
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# 1. Xvfb + Mesa software GL + X/audio runtime deps for Godot's GL-compatibility renderer
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
xvfb \
|
||||
xauth \
|
||||
x11-utils \
|
||||
libx11-6 \
|
||||
libxcursor1 \
|
||||
libxinerama1 \
|
||||
libxrandr2 \
|
||||
libxi6 \
|
||||
libxext6 \
|
||||
libxrender1 \
|
||||
libxfixes3 \
|
||||
libxss1 \
|
||||
libxkbcommon0 \
|
||||
libxkbcommon-x11-0 \
|
||||
libgl1 \
|
||||
libglx-mesa0 \
|
||||
libgl1-mesa-dri \
|
||||
libglu1-mesa \
|
||||
libegl1 \
|
||||
libgles2 \
|
||||
libasound2 \
|
||||
libpulse0 \
|
||||
libfontconfig1 \
|
||||
libfreetype6 \
|
||||
libdbus-1-3 \
|
||||
libudev1 \
|
||||
fonts-dejavu-core \
|
||||
python3 \
|
||||
python3-pip \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 2. .NET SDK 9.0 via the upstream install script (arch-agnostic, no apt repo needed)
|
||||
RUN curl -fsSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh \
|
||||
&& bash /tmp/dotnet-install.sh --channel 9.0 --install-dir /usr/local/dotnet \
|
||||
&& ln -s /usr/local/dotnet/dotnet /usr/local/bin/dotnet \
|
||||
&& rm /tmp/dotnet-install.sh
|
||||
ENV DOTNET_ROOT=/usr/local/dotnet
|
||||
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||
ENV DOTNET_NOLOGO=1
|
||||
|
||||
# 3. Godot 4.6.2-stable Mono for Linux x86_64
|
||||
# The zip contains a directory like "Godot_v..._mono_linux_x86_64/" with
|
||||
# an executable whose exact filename has varied across releases
|
||||
# ("Godot_v..._mono_linux.x86_64" on 4.x). Locate it dynamically.
|
||||
ARG GODOT_VERSION=4.6.2-stable
|
||||
RUN mkdir -p /opt/godot \
|
||||
&& cd /tmp \
|
||||
&& wget -q "https://github.com/godotengine/godot/releases/download/${GODOT_VERSION}/Godot_v${GODOT_VERSION}_mono_linux_x86_64.zip" \
|
||||
&& unzip -q "Godot_v${GODOT_VERSION}_mono_linux_x86_64.zip" -d /opt/godot \
|
||||
&& GODOT_EXE="$(find /opt/godot -maxdepth 3 -type f \( -name 'Godot_v*mono_linux*x86_64' -o -name 'Godot_v*mono_linux*x86_64' \) | head -1)" \
|
||||
&& if [ -z "$GODOT_EXE" ]; then echo "Godot executable not found in zip" && ls -R /opt/godot && exit 1; fi \
|
||||
&& chmod +x "$GODOT_EXE" \
|
||||
&& ln -sf "$GODOT_EXE" /opt/godot/godot \
|
||||
&& rm "Godot_v${GODOT_VERSION}_mono_linux_x86_64.zip"
|
||||
ENV GODOT_BIN=/opt/godot/godot
|
||||
ENV PATH=$PATH:/opt/godot:/usr/local/dotnet
|
||||
|
||||
# 4. xvfb wrapper — any Godot invocation gets its own virtual 1280x720x24 display.
|
||||
# Usage: `godot-xvfb --path /workspace ...` or let tools/automation/harness.py
|
||||
# invoke it automatically on Linux.
|
||||
COPY godot-xvfb.sh /usr/local/bin/godot-xvfb
|
||||
RUN chmod +x /usr/local/bin/godot-xvfb
|
||||
|
||||
# Ensure default node user has access to /usr/local/share
|
||||
RUN mkdir -p /usr/local/share/npm-global && \
|
||||
chown -R node:node /usr/local/share
|
||||
|
|
|
|||
15
.devcontainer/godot-xvfb.sh
Normal file
15
.devcontainer/godot-xvfb.sh
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#!/bin/bash
|
||||
# Wrap a Godot invocation in a fresh Xvfb 1280x720x24 display so the GL
|
||||
# renderer has something to draw into. If DISPLAY is already set (real
|
||||
# display / nested X server), skip xvfb-run and exec Godot directly.
|
||||
set -euo pipefail
|
||||
|
||||
: "${GODOT_BIN:=/opt/godot/godot}"
|
||||
|
||||
if [[ -n "${DISPLAY:-}" ]]; then
|
||||
exec "$GODOT_BIN" "$@"
|
||||
fi
|
||||
|
||||
exec xvfb-run -a \
|
||||
--server-args="-screen 0 1280x720x24 -ac +extension GLX +render -noreset" \
|
||||
"$GODOT_BIN" "$@"
|
||||
|
|
@ -69,7 +69,8 @@ for domain in \
|
|||
"api.anthropic.com" \
|
||||
"sentry.io" \
|
||||
"statsig.anthropic.com" \
|
||||
"statsig.com"; do
|
||||
"statsig.com" \
|
||||
"api.nuget.org"; do
|
||||
echo "Resolving $domain..."
|
||||
ips=$(dig +noall +answer A "$domain" | awk '$4 == "A" {print $5}')
|
||||
if [ -z "$ips" ]; then
|
||||
|
|
|
|||
174
README.md
Normal file
174
README.md
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
# Chessistics
|
||||
|
||||
Jeu de logistique sur échiquier en Godot 4 / C#. Le joueur place des pièces
|
||||
d'échecs sur un plateau ; elles se déplacent automatiquement et transportent
|
||||
des ressources entre des productions et des demandes.
|
||||
|
||||
Voir [`CLAUDE.md`](CLAUDE.md) pour l'architecture (black-box simulation) et
|
||||
les conventions internes.
|
||||
|
||||
## Arborescence
|
||||
|
||||
```
|
||||
Chessistics/
|
||||
├─ Scripts/ # Code Godot (présentation)
|
||||
│ ├─ Automation/ # Harness autonome (CLI --automation=<dir>)
|
||||
│ ├─ Board/ Input/ UI/ Pieces/ Presentation/
|
||||
│ └─ Main.cs
|
||||
├─ chessistics-engine/ # Moteur pur .NET, sans Godot
|
||||
├─ chessistics-tests/ # Tests unitaires xUnit (sans Godot)
|
||||
├─ Data/
|
||||
│ ├─ campaigns/ # campaign_01.json (7 missions)
|
||||
│ └─ levels/ # niveaux legacy
|
||||
├─ tools/automation/ # Harness Python stdlib pour piloter Godot
|
||||
├─ .devcontainer/ # Image Docker + firewall + Xvfb + Godot Linux
|
||||
├─ Scenes/ icon.svg project.godot …
|
||||
```
|
||||
|
||||
## Lancer le jeu (poste Windows direct)
|
||||
|
||||
Prérequis : Godot 4.6 mono + .NET 9 SDK installés localement.
|
||||
|
||||
```powershell
|
||||
dotnet build Chessistics.csproj
|
||||
"C:\Apps\godot\Godot_v4.6.2-stable_mono_win64.exe" --path .
|
||||
```
|
||||
|
||||
Tests headless du moteur :
|
||||
|
||||
```powershell
|
||||
dotnet test chessistics-tests/
|
||||
```
|
||||
|
||||
Smoke test du harness d'automatisation :
|
||||
|
||||
```powershell
|
||||
python tools/automation/smoke.py
|
||||
```
|
||||
|
||||
## Dev container autonome (recommandé pour Claude Code)
|
||||
|
||||
Le dossier `.devcontainer/` contient une image Docker qui embarque :
|
||||
|
||||
- **.NET SDK 9.0** (build + tests)
|
||||
- **Godot 4.6.2-stable mono Linux x86_64** sous `/opt/godot/godot`
|
||||
- **Xvfb** + Mesa software GL pour un framebuffer virtuel 1280×720
|
||||
- **Python 3** pour le harness d'automatisation
|
||||
- **Claude Code** installé automatiquement (`@anthropic-ai/claude-code`)
|
||||
- Un firewall `iptables` qui restreint les sorties réseau à une allow-list
|
||||
(GitHub, npm, Anthropic, NuGet, Sentry, Statsig)
|
||||
|
||||
Claude Code, lancé à l'intérieur, peut donc compiler le projet, exécuter
|
||||
les tests, **démarrer une vraie instance de Godot en headless** et lire les
|
||||
captures PNG produites — sans dépendance Windows.
|
||||
|
||||
Voir [`autonomous_plan.md`](autonomous_plan.md) pour le design détaillé.
|
||||
|
||||
### Option 1 — Docker Desktop + terminal Windows (le plus simple)
|
||||
|
||||
1. Installer [Docker Desktop pour Windows](https://www.docker.com/products/docker-desktop/).
|
||||
Docker Desktop utilise WSL2 en coulisses ; aucune configuration WSL
|
||||
manuelle n'est nécessaire.
|
||||
2. Installer la CLI devcontainers :
|
||||
|
||||
```powershell
|
||||
npm install -g @devcontainers/cli
|
||||
```
|
||||
|
||||
3. Depuis PowerShell ou Terminal Windows, à la racine du repo :
|
||||
|
||||
```powershell
|
||||
cd C:\Projets\Chessistics
|
||||
devcontainer up --workspace-folder .
|
||||
devcontainer exec --workspace-folder . zsh
|
||||
```
|
||||
|
||||
La première commande construit l'image (long le premier coup : Godot +
|
||||
.NET à télécharger). La seconde ouvre un shell interactif dans le
|
||||
container.
|
||||
|
||||
4. Dans le container :
|
||||
|
||||
```bash
|
||||
dotnet build Chessistics.csproj
|
||||
python3 tools/automation/smoke.py
|
||||
claude # lance Claude Code inside the container
|
||||
```
|
||||
|
||||
### Option 2 — VS Code + extension Dev Containers
|
||||
|
||||
1. Installer Docker Desktop + [VS Code](https://code.visualstudio.com/) +
|
||||
l'extension **Dev Containers**.
|
||||
2. Ouvrir le dossier du repo dans VS Code.
|
||||
3. Palette de commandes → `Dev Containers: Reopen in Container`.
|
||||
4. Le terminal intégré est déjà dans le container. Lancer `claude` pour
|
||||
démarrer Claude Code.
|
||||
|
||||
### Option 3 — WSL2 natif (plus performant si Docker Desktop rame)
|
||||
|
||||
Les bind-mounts Docker Desktop sur `C:\` sont plus lents qu'un fichier
|
||||
stocké directement dans le système de fichiers WSL2. Pour un gros projet
|
||||
c'est négligeable, mais si la lenteur devient visible :
|
||||
|
||||
1. Installer WSL2 + Ubuntu depuis le Microsoft Store.
|
||||
2. Cloner le repo **à l'intérieur** de WSL2 (pas sur `/mnt/c/`) :
|
||||
|
||||
```bash
|
||||
cd ~ && git clone <url> chessistics && cd chessistics
|
||||
```
|
||||
|
||||
3. Installer Docker dans WSL2 (via `docker.io` apt ou Docker Desktop avec
|
||||
intégration WSL2 activée).
|
||||
4. Installer la CLI devcontainers :
|
||||
|
||||
```bash
|
||||
npm install -g @devcontainers/cli
|
||||
devcontainer up --workspace-folder .
|
||||
devcontainer exec --workspace-folder . zsh
|
||||
```
|
||||
|
||||
Avantage : I/O disque natif Linux. Inconvénient : pas d'accès Explorer
|
||||
direct aux fichiers (`\\wsl$\Ubuntu\home\…`).
|
||||
|
||||
### Vérifier que tout fonctionne dans le container
|
||||
|
||||
```bash
|
||||
dotnet --version # -> 9.0.x
|
||||
godot --version # -> 4.6.2.stable.mono.official.*
|
||||
dotnet test chessistics-tests/ # 102/102
|
||||
python3 tools/automation/smoke.py # Godot boots, PNG screenshots written
|
||||
ls .automation_runs/smoke/screens/ # 01_loaded.png, 02_placed.png, ...
|
||||
```
|
||||
|
||||
Le harness Python détecte automatiquement Linux et enveloppe Godot dans
|
||||
`xvfb-run`. Aucune variable d'environnement à positionner.
|
||||
|
||||
### Personnaliser la version de Godot
|
||||
|
||||
L'image par défaut pose Godot 4.6.2-stable. Pour changer, modifier
|
||||
l'argument de build :
|
||||
|
||||
```jsonc
|
||||
// .devcontainer/devcontainer.json
|
||||
"build": {
|
||||
"args": {
|
||||
"GODOT_VERSION": "4.7.0-stable"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Puis `devcontainer up --workspace-folder . --build-no-cache`.
|
||||
|
||||
## Dépannage
|
||||
|
||||
| Symptôme | Cause probable | Fix |
|
||||
|----------|----------------|-----|
|
||||
| `godot --version` donne *no such file* | PATH non mis à jour | `source /etc/profile` ou relancer le shell |
|
||||
| Screenshot tout noir | Aucun DISPLAY + pas d'xvfb | Vérifier `which xvfb-run` ; utiliser `godot-xvfb` au lieu de `godot` directement |
|
||||
| `dotnet restore` bloque | Firewall bloque `api.nuget.org` | Vérifier que `init-firewall.sh` s'est bien exécuté avec les changements récents |
|
||||
| Build Docker échoue au download de Godot | Réseau restreint côté hôte | Retry, ou installer Godot manuellement et commenter les lignes correspondantes |
|
||||
| Claude Code demande `--dangerously-skip-permissions` | Comportement normal en container sandbox | Accepter si tu es conscient du modèle de confiance |
|
||||
|
||||
## Licence
|
||||
|
||||
Voir les fichiers du repo.
|
||||
149
autonomous_plan.md
Normal file
149
autonomous_plan.md
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
# Headless Linux dev container for autonomous Chessistics testing
|
||||
|
||||
## Why
|
||||
|
||||
Today the automation harness only runs on Windows because the Godot binary is
|
||||
hardcoded to `C:\Apps\godot\Godot_v4.6.2-stable_mono_win64_console.exe`. When
|
||||
Claude Code runs inside the project's dev container (Linux / `node:20`), it
|
||||
can read source code and run `dotnet test`, but it **cannot launch the actual
|
||||
game** — there's no Godot binary, no .NET SDK, and no display server for the
|
||||
renderer.
|
||||
|
||||
The goal: make the dev container a self-contained environment where Claude
|
||||
Code can build the project, launch a real Godot instance in headless Linux
|
||||
mode, drive it via the automation harness, and read back 1280×720 PNG
|
||||
screenshots — all without any Windows dependency.
|
||||
|
||||
## Design
|
||||
|
||||
### Pieces required
|
||||
|
||||
1. **Godot 4.6.2-stable Mono for Linux** — matches the Windows editor the
|
||||
project already uses. Installed once at image build time to `/opt/godot/`
|
||||
with a symlink `/opt/godot/godot`.
|
||||
2. **.NET SDK 9.0** — the project targets `net9.0`. Installed via the
|
||||
upstream `dot.net` install script to `/usr/local/dotnet/`.
|
||||
3. **Xvfb + Mesa software GL** — a virtual framebuffer at `:99` so Godot's
|
||||
GL-compatibility renderer has somewhere to draw. `xvfb-run` wraps any
|
||||
command transparently.
|
||||
4. **Python 3** — the automation harness is stdlib-only Python.
|
||||
5. **Minimal X / audio runtime deps** — `libx11`, `libxcursor`, `libxrandr`,
|
||||
`libxi`, `libgl1`, `libgles2`, `libasound2`, `libxkbcommon0`, etc.
|
||||
Without these, Godot exits on startup with `libXext not found`-style errors.
|
||||
|
||||
### How Godot reaches the framebuffer
|
||||
|
||||
Two options considered:
|
||||
|
||||
- **(A)** Run `Xvfb :99 -screen 0 1280x720x24 &` as a background process,
|
||||
export `DISPLAY=:99`, launch Godot normally. Persistent display, shared by
|
||||
many Godot runs.
|
||||
- **(B)** Use `xvfb-run -a --server-args="-screen 0 1280x720x24"` as a prefix
|
||||
on every Godot invocation. A fresh display per launch; cleans up
|
||||
automatically on exit.
|
||||
|
||||
**Chosen: (B)**, because the automation harness already spawns Godot once per
|
||||
`Harness.launch()` and cleans up on context exit — matches the per-launch
|
||||
lifecycle naturally, no daemon to keep alive, no race on the display number.
|
||||
|
||||
A tiny wrapper `/usr/local/bin/godot-xvfb` wraps `xvfb-run … $GODOT_BIN
|
||||
"$@"`, so the harness (or a human) only has to invoke one path.
|
||||
|
||||
### Integration with the existing harness
|
||||
|
||||
`tools/automation/harness.py` currently hardcodes the Windows Godot path. We
|
||||
teach it two things:
|
||||
|
||||
1. Read `GODOT_BIN` from the environment first; fall back to the platform
|
||||
default (Windows path on Windows, `/opt/godot/godot` on Linux).
|
||||
2. On Linux, auto-prepend `["xvfb-run", "-a", "--server-args=-screen 0
|
||||
1280x720x24"]` to the Godot launch command unless `DISPLAY` is already
|
||||
set (someone has a real display, skip the wrap).
|
||||
|
||||
With those two tweaks, every existing Python helper (`smoke.py`,
|
||||
`run_game.py`, `solve_*.py`) works unchanged inside the container.
|
||||
|
||||
### Firewall considerations
|
||||
|
||||
The container's `init-firewall.sh` runs at `postStartCommand`, **after** the
|
||||
image is built, and drops all outbound traffic except to a small allowlist
|
||||
(GitHub, npmjs, Anthropic, Sentry, Statsig). Impact on our pieces:
|
||||
|
||||
- **Godot binary + .NET SDK**: downloaded during `docker build`, which runs
|
||||
_before_ the firewall exists → works unconditionally.
|
||||
- **`dotnet restore`** (runtime, e.g. after a `git pull`): needs
|
||||
`api.nuget.org`. Added to the allowlist.
|
||||
- **Godot runtime**: no outbound traffic required — the engine runs fully
|
||||
offline once installed.
|
||||
|
||||
### Build sequence inside the Dockerfile
|
||||
|
||||
As `root`, before the existing `USER node` switch:
|
||||
|
||||
```
|
||||
# 1. X/GL/audio runtime + python + xvfb
|
||||
apt-get install xvfb xauth libx11-6 libxcursor1 libxinerama1 libxrandr2 \
|
||||
libxi6 libxext6 libgl1 libglx-mesa0 libgl1-mesa-dri libglu1-mesa \
|
||||
libasound2 libxkbcommon0 libxkbcommon-x11-0 libfontconfig1 libdbus-1-3 \
|
||||
python3 python3-pip
|
||||
|
||||
# 2. .NET SDK 9.0 via upstream install script
|
||||
curl -sSL https://dot.net/v1/dotnet-install.sh | bash -s -- \
|
||||
--channel 9.0 --install-dir /usr/local/dotnet
|
||||
ln -s /usr/local/dotnet/dotnet /usr/local/bin/dotnet
|
||||
|
||||
# 3. Godot 4.6.2-stable mono, Linux x86_64
|
||||
wget https://github.com/godotengine/godot/releases/download/${VERSION}/\
|
||||
Godot_v${VERSION}_mono_linux_x86_64.zip
|
||||
unzip … -d /opt/godot
|
||||
ln -s /opt/godot/Godot_v…/Godot_v…_mono_linux.x86_64 /opt/godot/godot
|
||||
```
|
||||
|
||||
Then:
|
||||
- `ENV GODOT_BIN=/opt/godot/godot`
|
||||
- `ENV PATH=$PATH:/opt/godot:/usr/local/dotnet`
|
||||
- Drop in `/usr/local/bin/godot-xvfb` wrapper
|
||||
|
||||
## File-by-file change list
|
||||
|
||||
| File | Change |
|
||||
|------|--------|
|
||||
| `.devcontainer/Dockerfile` | Add Godot / dotnet / xvfb installs before `USER node` |
|
||||
| `.devcontainer/init-firewall.sh` | Append `api.nuget.org` to the domain allowlist |
|
||||
| `.devcontainer/godot-xvfb.sh` *(new)* | `exec xvfb-run -a … "$GODOT_BIN" "$@"` |
|
||||
| `tools/automation/harness.py` | Env-aware Godot path + Linux xvfb auto-wrap |
|
||||
| `README.md` *(new)* | Windows / WSL2 launch instructions for the dev container |
|
||||
|
||||
Nothing inside `Scripts/` or `chessistics-engine/` changes. The harness
|
||||
contract (inbox/outbox/screens) is platform-agnostic already.
|
||||
|
||||
## Verification
|
||||
|
||||
After rebuild:
|
||||
|
||||
1. `docker build .devcontainer -t chessistics-dev` succeeds.
|
||||
2. `devcontainer up --workspace-folder .` starts the container and
|
||||
post-start firewall passes.
|
||||
3. Inside: `dotnet --version` → `9.0.x`, `godot --version` →
|
||||
`4.6.2.stable.mono.official.*`.
|
||||
4. `dotnet build Chessistics.csproj` → green.
|
||||
5. `dotnet test chessistics-tests/` → 102 / 102.
|
||||
6. `python3 tools/automation/smoke.py` → loads mission 1, takes PNG
|
||||
screenshots that are non-black, quits cleanly.
|
||||
7. `Read` one of the PNGs — it should show the same mission 1 UI as the
|
||||
Windows run (title bar, board, objectives, stock panel).
|
||||
|
||||
## Out of scope (explicit non-goals)
|
||||
|
||||
- **GPU acceleration**: we use Mesa software rendering. Xvfb + llvmpipe is
|
||||
enough for 1280×720 at a few FPS, which is what the harness needs.
|
||||
- **Real display forwarding** (X11 forwarding, VNC, noVNC): doable but
|
||||
unnecessary — Claude reads PNGs, not a live video feed.
|
||||
- **Multi-arch images**: we ship x86_64 only. ARM (Apple Silicon via
|
||||
Docker Desktop emulation) would need `Godot_v…_mono_linux_arm64.zip` —
|
||||
straightforward to add if needed, not done here.
|
||||
- **Shrinking the image**: Godot + .NET SDK adds ~500 MB. Worth it;
|
||||
multi-stage builds could trim later.
|
||||
- **Keeping Xvfb warm across launches**: the single-launch pattern is clean
|
||||
enough. If someone ever scripts dozens of rapid Godot starts and the
|
||||
xvfb-run startup cost shows up, revisit approach (A).
|
||||
|
|
@ -36,10 +36,37 @@ from typing import Any
|
|||
|
||||
# Resolve defaults relative to the repo root (parent of tools/).
|
||||
_REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||
_DEFAULT_GODOT = Path(r"C:\Apps\godot\Godot_v4.6.2-stable_mono_win64_console.exe")
|
||||
_DEFAULT_RUNS = _REPO_ROOT / ".automation_runs"
|
||||
|
||||
|
||||
def _default_godot_exe() -> Path:
|
||||
"""Locate a Godot binary. Env var wins; otherwise platform defaults."""
|
||||
env = os.environ.get("GODOT_BIN")
|
||||
if env:
|
||||
return Path(env)
|
||||
if sys.platform.startswith("linux"):
|
||||
return Path("/opt/godot/godot")
|
||||
return Path(r"C:\Apps\godot\Godot_v4.6.2-stable_mono_win64_console.exe")
|
||||
|
||||
|
||||
_DEFAULT_GODOT = _default_godot_exe()
|
||||
|
||||
|
||||
def _wrap_with_xvfb(argv: list[str]) -> list[str]:
|
||||
"""On Linux without an existing DISPLAY, wrap Godot in xvfb-run so the
|
||||
GL-compatibility renderer has a framebuffer to target.
|
||||
"""
|
||||
if not sys.platform.startswith("linux"):
|
||||
return argv
|
||||
if os.environ.get("DISPLAY"):
|
||||
return argv
|
||||
return [
|
||||
"xvfb-run", "-a",
|
||||
"--server-args=-screen 0 1280x720x24 -ac +extension GLX +render -noreset",
|
||||
*argv,
|
||||
]
|
||||
|
||||
|
||||
class HarnessError(RuntimeError):
|
||||
"""Raised when a command fails or times out."""
|
||||
|
||||
|
|
@ -98,11 +125,11 @@ class Harness:
|
|||
if not self.project_path.exists():
|
||||
raise HarnessError(f"Project path not found: {self.project_path}")
|
||||
|
||||
args = [
|
||||
args = _wrap_with_xvfb([
|
||||
str(self.godot_exe),
|
||||
"--path", str(self.project_path),
|
||||
f"--automation={self.root}",
|
||||
]
|
||||
])
|
||||
print(f"[harness] launching: {' '.join(args)}", file=sys.stderr)
|
||||
# Inherit stdout/stderr so GD.Print output is visible.
|
||||
self._proc = subprocess.Popen(args)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue