docs(agents): how the containers and agents are actually set up
All checks were successful
CI / Native — linux (pull_request) Successful in 42m12s
CI / WASM — Web (pull_request) Successful in 26m29s
CI / Formatting (pull_request) Successful in 35s

Written for the other machine, which asked. Everything in it was read off the
running host rather than remembered.

Also adds docker/ci/ -- the CI image recipe and a capped runner -- because the
image existed on exactly one host and its Dockerfile was in a scratch directory
under /tmp, which was swept. That is the same shape as every other thing this
consolidation has turned up: something correct that exists in one place.

  docker/ci/Dockerfile          rust 1.98.1 + the apt list copied from ci.yml
  docker/ci/Dockerfile.ffmpeg   + ffmpeg, which sylpheed-export shells out to
  docker/ci/run                 6 CPUs / 7 GB / NO SWAP, named cargo volumes

The rule the runner exists to enforce: every heavy command goes in the capped
container. CARGO_BUILD_JOBS caps codegen units, not rustc's threads, not the
linker, not the test harness -- a bare host build is unbounded and has frozen
this box repeatedly.

The document also records the two agent defects the other machine will meet:
a brief change does not reach a RESUMED session, and nothing brought an agent
back to its own red PR (fixed in #24, which is itself subject to the first).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-09-13 20:42:56 +02:00
parent 3977cc933e
commit 70d3a54acb
4 changed files with 244 additions and 0 deletions

33
docker/ci/Dockerfile Normal file
View File

@@ -0,0 +1,33 @@
# The CI image, reproduced locally.
#
# 🔴 WHY THIS FILE EXISTS. This image was built once, by hand, from a Dockerfile
# in a scratch directory under /tmp — and /tmp was swept. The image survived on
# one host and its recipe did not, which is the same shape as every other thing
# this consolidation found: something correct that exists in exactly one place.
#
# The apt list below is COPIED from `.github/workflows/ci.yml`'s "Install Linux
# system dependencies" step. If that list changes, this must change with it, or
# a local run stops predicting the runner.
#
# docker build -t sylph-ci:local docker/ci
#
# ⚠️ The Rust version is PINNED and the runner's is NOT — `dtolnay/rust-toolchain@stable`
# floats. That difference is issue #15: `collapsible_else_if` is `warn` on 1.92
# and `allow` on 1.98.1, so a local clippy pass and a runner clippy pass can
# legitimately disagree. When they do, the runner is the authority.
FROM rust:1.98.1-bookworm
RUN apt-get update && apt-get install -y --no-install-recommends \
libasound2-dev \
libudev-dev \
libwayland-dev \
libxkbcommon-dev \
libx11-dev \
libxi-dev \
pkg-config \
&& rm -rf /var/lib/apt/lists/*
RUN rustup component add clippy rustfmt \
&& rustup target add wasm32-unknown-unknown
WORKDIR /work

View File

@@ -0,0 +1,11 @@
# `sylph-ci:ffmpeg` — the CI image plus ffmpeg, for `sylpheed-export`.
#
# The exporter shells out to `ffprobe`/`ffmpeg` to transcode the two movies, so
# `cargo run -p sylpheed-export -- export` fails in the plain CI image with
# "run ffprobe -- is it on PATH?". CI itself never runs the exporter, which is
# why its own image does not carry this.
#
# docker build -t sylph-ci:ffmpeg -f docker/ci/Dockerfile.ffmpeg docker/ci
FROM sylph-ci:local
RUN apt-get update && apt-get install -y --no-install-recommends ffmpeg \
&& rm -rf /var/lib/apt/lists/*

43
docker/ci/run Executable file
View File

@@ -0,0 +1,43 @@
#!/usr/bin/env bash
# Run a command against this repository inside the capped CI container.
#
# docker/ci/run cargo check --workspace --all-targets
# docker/ci/run cargo clippy --workspace --all-targets --keep-going -- -D warnings
# docker/ci/run bash -c 'cargo fmt --all; cargo fmt --all -- --check | grep -c "^Diff in"'
#
# 🔴 EVERY HEAVY COMMAND GOES THROUGH HERE. A bare `cargo build` on the host is
# unbounded: `CARGO_BUILD_JOBS` caps codegen units, not rustc's own threads, not
# the linker, not the test harness. A full-parallel build has OOM-crashed this
# box, and unbounded host runs have frozen it since. The container is the cap
# that actually holds.
#
# Half the machine, no swap. `--memory-swap` equal to `--memory` means a build
# that would swap is killed instead, which is a fast failure rather than an
# hour of thrashing that takes the desktop with it.
set -euo pipefail
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
IMAGE="${SYLPH_CI_IMAGE:-sylph-ci:local}"
CPUS="${SYLPH_CI_CPUS:-6}"
MEM_GB="${SYLPH_CI_MEM_GB:-7}"
args=(
--rm
--cpus "$CPUS"
--memory "${MEM_GB}g"
--memory-swap "${MEM_GB}g"
--pids-limit 2048
-v "$REPO:/work"
# Named volumes, not bind mounts: the host tree keeps a 32 GB `target/` from
# earlier host-side builds, and mixing the two produces rebuilds that look
# like cache misses and are actually two toolchains fighting over one directory.
-v sylph-ci-cargo:/cargo -e CARGO_HOME=/cargo
-v sylph-ci-target:/target -e CARGO_TARGET_DIR=/target
-w /work
)
# The disc, read-only, when a disc-backed test or the exporter needs it.
DISC="${SYLPHEED_DISC:-$REPO/../sylph_extract}"
[ -d "$DISC" ] && args+=(-v "$DISC:/disc:ro" -e SYLPHEED_DISC=/disc)
exec docker run "${args[@]}" "$IMAGE" "$@"