# 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"]
