Files
Sylpheed/docker/port/Dockerfile
MechaCat02 a8d2491366
Some checks failed
CI / Native — ubuntu-latest (push) Failing after 8m25s
CI / Native — macos-latest (push) Has been cancelled
CI / Native — windows-latest (push) Has been cancelled
CI / Formatting (push) Has been cancelled
CI / WASM — Web (push) Has been cancelled
audio: actually install the capture path I kept deferring
The audio work was three parts and I shipped two. The transcode-fidelity method
and the pinned 5.1 downmix landed; the null sink -- the only one that answers
"what does the GAME play" -- I deferred to "the next natural rebuild window" and
then rebuilt both images four times without doing it.

pulseaudio-utils is now in both, with tools/audio-capture wrapping it: a null
sink is a real device as far as an application is concerned, so Canary and Godot
open it normally and parec records what they emit.

This unblocks the decoder's Q8. The cue-to-event bindings are currently a name
match against the authors' own identifiers -- a plausible guess, not a
measurement -- and capturing what the game plays on a menu move converts them.

`audio-capture run` reports the peak level and warns when the capture is silent,
because silence is the failure that looks like success: a WAV of exactly the
right duration, full of zeroes, because the application opened a different sink.
A duration check alone passes it, which is how a confident wrong number gets
made.
2026-08-29 13:41:02 +02:00

108 lines
5.5 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 \
# PulseAudio, for capturing audio without a sound card. `module-null-sink`
# is a real device as far as any application is concerned, so the emulator
# and Godot open it normally and `parec` records what they play. Without
# it, "does this actually sound right" is unanswerable in a container --
# and the cue-to-event bindings stay a name match rather than a
# measurement. See docs/port/AUDIO-VERIFICATION.md.
pulseaudio pulseaudio-utils \
&& 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 /exchange /reborn \
&& chown -R "${AGENT_UID}:${AGENT_GID}" /sylph-home /work /exchange \
&& 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=/work/tools:/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"]