From 7678f4668fa8af5d3caa1617fc66342fd7b29d83 Mon Sep 17 00:00:00 2001 From: Natalie Date: Mon, 25 May 2026 14:06:52 -0700 Subject: [PATCH] =?UTF-8?q?feat(@projects):=20=E2=9C=A8=20add=20docker=20c?= =?UTF-8?q?ontainer=20wrapper=20for=20godot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- scripts/godot-docker.sh | 85 +++++++++++++++++++++++++++++++++++ tools/docker/Dockerfile.godot | 54 ++++++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100755 scripts/godot-docker.sh create mode 100644 tools/docker/Dockerfile.godot diff --git a/scripts/godot-docker.sh b/scripts/godot-docker.sh new file mode 100755 index 00000000..011d959c --- /dev/null +++ b/scripts/godot-docker.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +# godot-docker.sh — run Godot Linux headless inside a freeze-proof Docker container. +# +# Why: `godot --headless --import` and similar commands kernel-panicked plum +# twice and freeze apricot when run cold. Containers on macOS run inside a +# Linux VM with its own kernel, fully isolated from macOS — no chance of +# tripping the GPU/video kexts that caused the panics. Cgroup limits below +# also keep the VM from starving itself. +# +# See: ~/.claude/plans/godot-container-runner.md +# +# Usage: +# scripts/godot-docker.sh [args...] # passed straight to godot +# scripts/godot-docker.sh --version # smoke test +# scripts/godot-docker.sh --headless --import # warm the import cache +# scripts/godot-docker.sh --headless --script res://addons/gut/gut_cmdln.gd -gexit -gtest=res://engine/tests/unit/test_city_buildable_helper.gd + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +DOCKERFILE="$REPO_ROOT/tools/docker/Dockerfile.godot" + +IMAGE="mc-godot:4.6.2" +VOLUME="mc-godot-cache" +WORKDIR_IN_CONTAINER="/work/src/game" + +# Cgroup hard caps so the container can't starve the host VM. +# Defaults sized for Docker Desktop with ≥7 GiB allocated. +CPUS="${MC_GODOT_CPUS:-4}" +MEMORY="${MC_GODOT_MEMORY:-6g}" +PIDS_LIMIT="${MC_GODOT_PIDS:-256}" + +ensure_image() { + if docker image inspect "$IMAGE" >/dev/null 2>&1; then + return 0 + fi + echo "[godot-docker] image $IMAGE not found — building..." >&2 + docker build \ + --tag "$IMAGE" \ + --file "$DOCKERFILE" \ + "$REPO_ROOT/tools/docker" +} + +ensure_volume() { + if docker volume inspect "$VOLUME" >/dev/null 2>&1; then + return 0 + fi + echo "[godot-docker] creating named volume $VOLUME for warm import cache" >&2 + docker volume create "$VOLUME" >/dev/null +} + +ensure_docker_running() { + if ! docker info >/dev/null 2>&1; then + echo "ERROR: Docker daemon not reachable. Start Docker Desktop first." >&2 + exit 2 + fi +} + +main() { + ensure_docker_running + ensure_image + ensure_volume + + # Force a TTY only when stdout is a terminal; CI invocations stay non-interactive. + local tty_flag="" + if [[ -t 1 ]]; then + tty_flag="-t" + fi + + docker run \ + --rm \ + -i $tty_flag \ + --cpus "$CPUS" \ + --memory "$MEMORY" \ + --memory-swap "$MEMORY" \ + --pids-limit "$PIDS_LIMIT" \ + --volume "$REPO_ROOT:/work" \ + --volume "$VOLUME:/work/src/game/.godot" \ + --workdir "$WORKDIR_IN_CONTAINER" \ + "$IMAGE" \ + "$@" +} + +main "$@" diff --git a/tools/docker/Dockerfile.godot b/tools/docker/Dockerfile.godot new file mode 100644 index 00000000..740dcd53 --- /dev/null +++ b/tools/docker/Dockerfile.godot @@ -0,0 +1,54 @@ +# Godot 4.6.2 Linux headless — freeze-proof runtime for asset import + GUT tests. +# See ~/.claude/plans/godot-container-runner.md for why this exists. + +FROM debian:bookworm-slim + +ARG GODOT_VERSION=4.6.2 +ARG GODOT_RELEASE=stable +# Pinned official SHA256 — update together with GODOT_VERSION. +# Compute via: sha256sum Godot_v${GODOT_VERSION}-${GODOT_RELEASE}_linux.x86_64.zip +ARG GODOT_SHA256= + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + ca-certificates \ + wget \ + unzip \ + libgl1-mesa-glx \ + libfontconfig1 \ + libxi6 \ + libxrandr2 \ + libxcursor1 \ + libxinerama1 \ + libasound2 \ + libpulse0 \ + && rm -rf /var/lib/apt/lists/* + +# Detect arch at build time so this image works on both Apple-silicon Docker +# Desktop (linux/arm64) and amd64 hosts. +RUN set -eux; \ + arch="$(dpkg --print-architecture)"; \ + case "$arch" in \ + arm64) godot_arch="linux.arm64" ;; \ + amd64) godot_arch="linux.x86_64" ;; \ + *) echo "unsupported arch: $arch" >&2; exit 1 ;; \ + esac; \ + url="https://github.com/godotengine/godot-builds/releases/download/${GODOT_VERSION}-${GODOT_RELEASE}/Godot_v${GODOT_VERSION}-${GODOT_RELEASE}_${godot_arch}.zip"; \ + wget -q -O /tmp/godot.zip "$url"; \ + unzip -q /tmp/godot.zip -d /opt; \ + mv /opt/Godot_v${GODOT_VERSION}-${GODOT_RELEASE}_${godot_arch} /usr/local/bin/godot; \ + chmod +x /usr/local/bin/godot; \ + rm /tmp/godot.zip + +# Runtime user with a stable uid/gid so bind-mounted files don't get chown'd +# by container writes. Docker Desktop on macOS handles uid mapping +# transparently, so picking 1000 is fine. +RUN useradd --create-home --uid 1000 --shell /bin/bash godot + +USER godot +WORKDIR /work + +ENTRYPOINT ["/usr/local/bin/godot"] +CMD ["--version"]