The pin was 4.3, chosen without checking; 4.7.2 is the current stable (released 2026-08-18). Getting this right matters more here than usual because the project diffs Godot screenshots against a reference renderer, so the engine version is part of the measurement. No Godot MCP server. Not because they are bad -- the mature ones need a LIVE editor plus a WebSocket plugin and a Python server, which is a daemon, an editor process and a second runtime added to an unattended loop. And their headline feature, scene-tree introspection and node manipulation, is built for someone hand-authoring scenes in the editor. This port GENERATES screens from exported JSON: the agent writes a loader, not a scene tree, so the feature that justifies the complexity does not apply. What it actually needs to verify itself -- run headless, screenshot, diff against sylpheed-cli screen render -- is already a bash job. Third-party skill packs are the opposite trade: pure context, no runtime. Worth revisiting, deliberately not installed now, because a skill is instructions injected into an agent running with approvals disabled -- a supply-chain decision, not a default -- and because P0/P1 need no advanced GDScript. The agent may propose one, naming the milestone it unblocks, for a human to vendor and review. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
101 lines
5.0 KiB
Docker
101 lines
5.0 KiB
Docker
# Autonomous port agent for the Sylpheed Godot menu shell.
|
|
#
|
|
# DELIBERATELY SMALL. The reverse-engineering container next door is 4.36 GB
|
|
# because it builds Xenia Canary and drives it under a software Vulkan stack.
|
|
# This agent has no emulator, no oracle and no C++ build: it converts already-
|
|
# decoded assets and drives Godot. Keeping it light is what lets both containers
|
|
# run on one 12-core / 15 GB box without the memory pressure that has crashed it.
|
|
#
|
|
# What it needs, and nothing else: Rust (the exporter), Godot 4 (the runtime),
|
|
# ffmpeg (the transcode), and a headless display to screenshot Godot for
|
|
# comparison against the reference renderer.
|
|
|
|
FROM ubuntu:24.04
|
|
|
|
ENV DEBIAN_FRONTEND=noninteractive \
|
|
LANG=C.UTF-8 \
|
|
TZ=Etc/UTC
|
|
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
# toolchain for the exporter and for building sylpheed-cli from /reborn
|
|
build-essential pkg-config git curl ca-certificates \
|
|
libssl-dev \
|
|
# Godot 4 needs these even headless; the windowed run needs the X libs
|
|
libx11-6 libxcursor1 libxinerama1 libxrandr2 libxi6 libgl1 \
|
|
libasound2t64 libpulse0 libfontconfig1 \
|
|
# the transcode target (libtheora + libvorbis ship in Ubuntu's ffmpeg)
|
|
ffmpeg \
|
|
# headless display + the screenshot path, for diffing Godot's output
|
|
# against `sylpheed-cli screen render`
|
|
xvfb x11-utils openbox imagemagick \
|
|
# everyday
|
|
python3 jq ripgrep unzip file less nano tini sudo procps \
|
|
# expect drives Claude Code's one-time interactive gates
|
|
expect \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# ── Godot 4 ──────────────────────────────────────────────────────────────────
|
|
# Pinned. An engine version bump changes rendering, and this project compares
|
|
# screenshots against a reference renderer — so an upgrade must be a deliberate,
|
|
# stated act rather than a silent drift.
|
|
ARG GODOT_VERSION=4.7.2
|
|
RUN cd /tmp \
|
|
&& curl -fsSLO "https://github.com/godotengine/godot/releases/download/${GODOT_VERSION}-stable/Godot_v${GODOT_VERSION}-stable_linux.x86_64.zip" \
|
|
&& unzip -q "Godot_v${GODOT_VERSION}-stable_linux.x86_64.zip" \
|
|
&& mv "Godot_v${GODOT_VERSION}-stable_linux.x86_64" /usr/local/bin/godot \
|
|
&& chmod +x /usr/local/bin/godot \
|
|
&& printf '#!/bin/sh\nexec /usr/local/bin/godot --headless "$@"\n' > /usr/local/bin/godot-headless \
|
|
&& chmod +x /usr/local/bin/godot-headless \
|
|
&& rm -f "Godot_v${GODOT_VERSION}-stable_linux.x86_64.zip"
|
|
|
|
# ── Node + Claude Code ───────────────────────────────────────────────────────
|
|
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
|
|
&& apt-get install -y --no-install-recommends nodejs \
|
|
&& npm install -g @anthropic-ai/claude-code \
|
|
&& npm cache clean --force \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# ── The agent user ───────────────────────────────────────────────────────────
|
|
# NOT root: Claude Code refuses --dangerously-skip-permissions with root
|
|
# privileges. Ubuntu 24.04 ships its own `ubuntu` account at uid 1000, so the
|
|
# common case — matching a host user who is also 1000 — collides with it.
|
|
ARG AGENT_UID=1000
|
|
ARG AGENT_GID=1000
|
|
RUN if getent passwd "${AGENT_UID}" >/dev/null; then \
|
|
userdel -r "$(getent passwd "${AGENT_UID}" | cut -d: -f1)" 2>/dev/null || true; \
|
|
fi; \
|
|
if getent group "${AGENT_GID}" >/dev/null; then \
|
|
groupdel "$(getent group "${AGENT_GID}" | cut -d: -f1)" 2>/dev/null || true; \
|
|
fi; \
|
|
groupadd -g "${AGENT_GID}" agent \
|
|
&& useradd -m -u "${AGENT_UID}" -g "${AGENT_GID}" -s /bin/bash -d /sylph-home/port agent \
|
|
&& mkdir -p /sylph-home/port /work /reborn \
|
|
&& chown -R "${AGENT_UID}:${AGENT_GID}" /sylph-home \
|
|
&& echo 'agent ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/agent
|
|
|
|
COPY bin/ /usr/local/bin/
|
|
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
|
|
RUN chmod +x /usr/local/bin/* /usr/local/bin/entrypoint.sh
|
|
|
|
USER agent
|
|
WORKDIR /work
|
|
|
|
# CARGO_TARGET_DIR points OUTSIDE the bind-mounted repo so the host and the
|
|
# container do not invalidate each other's incremental state on every switch.
|
|
ENV RUSTUP_HOME=/sylph-home/port/.rustup \
|
|
CARGO_HOME=/sylph-home/port/.cargo \
|
|
CARGO_TARGET_DIR=/sylph-home/port/target-container \
|
|
PATH=/sylph-home/port/.cargo/bin:/usr/local/bin:/usr/bin:/bin
|
|
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
|
|
| sh -s -- -y --default-toolchain stable --profile minimal --component clippy --component rustfmt
|
|
|
|
RUN mkdir -p /sylph-home/port/target-container /sylph-home/port/.claude
|
|
|
|
ENV HOME=/sylph-home/port \
|
|
DISPLAY=:97 \
|
|
SCREEN_GEOMETRY=1280x720x24 \
|
|
PROJECT_DIR=/work
|
|
|
|
ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/entrypoint.sh"]
|
|
CMD ["bash"]
|