227 lines
12 KiB
Docker
227 lines
12 KiB
Docker
# Per-repo harbor task Dockerfile for flaredown (rubyforgood, GPL-3). Polyglot symptom tracker:
|
|
# a backend/ Rails 7.1 API (Ruby 3.2.3, Mongoid 8.1 on MongoDB + Postgres + Redis + Sidekiq)
|
|
# and an Ember frontend/ (Node 14). Mirrors the explore stack; bakes the workspace + Claude Code
|
|
# (grader), git-commits a baseline. The app lives in subdirs — gems install in /workspace/backend.
|
|
#
|
|
# MongoDB 7.0 (not compose's EOL, arm64-less 4.4.9): Mongoid 8.1.3 + driver 2.20.1 support up to
|
|
# 7.0, which has native amd64 + aarch64 builds. Same wire protocol; the app is version-agnostic.
|
|
FROM ruby:3.2.3
|
|
ARG TOOLKIT_BUILD_ID=dev
|
|
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
postgresql postgresql-client libpq-dev \
|
|
redis-server \
|
|
build-essential pkg-config libyaml-dev \
|
|
python3 \
|
|
git sudo curl ca-certificates gnupg xz-utils jq procps \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# MongoDB 7.0 server binary (mongod), arch-aware ubuntu2204 build (runs on bookworm).
|
|
RUN set -eux; \
|
|
arch="$(dpkg --print-architecture)"; \
|
|
case "$arch" in amd64) marm=x86_64;; arm64) marm=aarch64;; *) echo "unsupported arch: $arch" >&2; exit 1;; esac; \
|
|
ver=7.0.14; \
|
|
curl -fsSL "https://fastdl.mongodb.org/linux/mongodb-linux-${marm}-ubuntu2204-${ver}.tgz" -o /tmp/mongo.tgz; \
|
|
tar -xzf /tmp/mongo.tgz -C /tmp; \
|
|
cp /tmp/mongodb-linux-${marm}-ubuntu2204-${ver}/bin/mongod /usr/local/bin/; \
|
|
rm -rf /tmp/mongo.tgz /tmp/mongodb-linux-*; \
|
|
mongod --version | head -1
|
|
|
|
# Node via nvm: 18 (default) + 14 (the Ember client; frontend/.nvmrc = v14.21.3). Pin npm 6
|
|
# in the v14 line — the frontend's .npmrc is engine-strict and requires npm 6.x (nvm's 14.21.3
|
|
# otherwise bundles npm 7, which fails engine-strict).
|
|
ENV NVM_DIR=/usr/local/nvm
|
|
RUN mkdir -p "$NVM_DIR" \
|
|
&& curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash \
|
|
&& bash -c '. "$NVM_DIR/nvm.sh" \
|
|
&& nvm install 18 \
|
|
&& nvm install 14.21.3 && nvm use 14.21.3 && npm install -g npm@6.14.18 \
|
|
&& nvm alias default 18' \
|
|
&& for b in node npm npx; do ln -sf "$NVM_DIR"/versions/node/v18.*/bin/"$b" /usr/local/bin/"$b"; done \
|
|
&& node --version
|
|
|
|
# phantomjs stub — the Ember client's phantomjs-prebuilt@2.1.16 (for `ember test`) has no arm64
|
|
# binary and is EOL; a version-reporting stub on PATH makes `npm install` skip the impossible
|
|
# download so the client's deps install and it can build/serve. `ember test` needs a real
|
|
# phantomjs (unavailable on arm64 upstream anyway); the rspec verifier doesn't touch the client.
|
|
RUN printf '#!/bin/bash\n[ "$1" = "--version" ] && { echo "2.1.1"; exit 0; }\nexit 0\n' > /usr/local/bin/phantomjs \
|
|
&& chmod +x /usr/local/bin/phantomjs
|
|
|
|
# Match backend/Gemfile.lock "BUNDLED WITH 2.5.6".
|
|
RUN gem install bundler -v 2.5.6
|
|
|
|
# Postgres trust auth (backend/config/database.yml connects as PG_DATABASE_USERNAME=postgres).
|
|
RUN PG_VERSION=$(ls /etc/postgresql) \
|
|
&& printf 'local all all trust\nhost all all 127.0.0.1/32 trust\nhost all all ::1/128 trust\nhost all all 0.0.0.0/0 trust\n' > "/etc/postgresql/${PG_VERSION}/main/pg_hba.conf" \
|
|
&& echo "listen_addresses='*'" >> "/etc/postgresql/${PG_VERSION}/main/postgresql.conf"
|
|
|
|
# Install Claude Code globally (grader runs `claude`); hard-gate on presence — a missing grader
|
|
# CLI silently zeros every reward, so a broken image must never be cached.
|
|
ARG CLAUDE_CODE_MIN=2.1.251
|
|
RUN for i in 1 2 3; do \
|
|
if curl -fsSL https://claude.ai/install.sh -o /tmp/claude-install.sh && bash /tmp/claude-install.sh; then break; fi; \
|
|
echo "WARNING: claude install attempt $i failed; retrying in 5s" >&2; sleep 5; \
|
|
done; \
|
|
rm -f /tmp/claude-install.sh; \
|
|
for p in /root/.claude-code/claude /root/.local/bin/claude "$(find /root -name claude -type f 2>/dev/null | head -1)"; do \
|
|
[ -n "$p" ] && [ -x "$p" ] && ln -sf "$p" /usr/local/bin/claude && break; \
|
|
done; \
|
|
command -v claude >/dev/null 2>&1 || { echo "FATAL: claude CLI not installed — the grader needs it" >&2; exit 1; }; \
|
|
_v="$(claude --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)"; \
|
|
[ "$(printf '%s\n%s\n' "$CLAUDE_CODE_MIN" "$_v" | sort -V | head -1)" = "$CLAUDE_CODE_MIN" ] \
|
|
|| { echo "FATAL: claude $_v is older than $CLAUDE_CODE_MIN, the minimum the grader needs" >&2; exit 1; }; \
|
|
echo "claude $_v installed at $(command -v claude)"
|
|
|
|
USER root
|
|
|
|
# --- Playwright + Chromium, when the task opts in ----------------------------
|
|
# Installed only when task.toml sets `[metadata] browser = true`. A Dockerfile cannot read
|
|
# task.toml, so build-workspace.sh writes that answer to environment/browser-optin.
|
|
# Self-contained under /opt — the member's own runtime is untouched.
|
|
ENV PLAYWRIGHT_BROWSERS_PATH=/opt/ms-playwright
|
|
COPY browser-optin /tmp/browser-optin
|
|
RUN set -eu; \
|
|
if [ "$(cat /tmp/browser-optin)" != "1" ]; then echo "browser: task did not opt in; skipping Playwright"; exit 0; fi; \
|
|
set -x; \
|
|
apt-get update -qq; \
|
|
apt-get install -y -qq --no-install-recommends \
|
|
xz-utils \
|
|
libxcomposite1 \
|
|
libxdamage1 \
|
|
libxfixes3 \
|
|
libxrandr2 \
|
|
libasound2 \
|
|
libatk1.0-0 \
|
|
libatk-bridge2.0-0 \
|
|
libatspi2.0-0 \
|
|
libcups2 \
|
|
libdbus-1-3 \
|
|
libgbm1 \
|
|
libnspr4 \
|
|
libnss3 \
|
|
libxkbcommon0 \
|
|
libpango-1.0-0 \
|
|
libcairo2 \
|
|
libxshmfence1 \
|
|
libx11-xcb1 \
|
|
libxcb-dri3-0 \
|
|
libdrm2; \
|
|
rm -rf /var/lib/apt/lists/*; \
|
|
arch="$(dpkg --print-architecture)"; \
|
|
case "$arch" in amd64) nodearch=x64;; arm64) nodearch=arm64;; *) echo "unsupported arch: $arch" >&2; exit 1;; esac; \
|
|
curl -fsSL "https://nodejs.org/dist/v20.19.5/node-v20.19.5-linux-${nodearch}.tar.xz" -o /tmp/pw-node.tar.xz; \
|
|
mkdir -p /opt/pw-node; \
|
|
tar -xJf /tmp/pw-node.tar.xz -C /opt/pw-node --strip-components=1; \
|
|
rm /tmp/pw-node.tar.xz; \
|
|
export npm_config_prefix=/opt/pw-node PATH="/opt/pw-node/bin:$PATH"; \
|
|
/opt/pw-node/bin/npm install -g playwright@1.56.0; \
|
|
test -d /opt/pw-node/lib/node_modules/playwright; \
|
|
/opt/pw-node/bin/node /opt/pw-node/lib/node_modules/playwright/cli.js install chromium; \
|
|
printf '#!/bin/sh\nNODE_PATH=/opt/pw-node/lib/node_modules exec /opt/pw-node/bin/node "$@"\n' > /usr/local/bin/pw; \
|
|
chmod +x /usr/local/bin/pw; \
|
|
printf 'const{chromium}=require("playwright");(async()=>{const b=await chromium.launch();const p=await b.newPage();await p.setContent("<h1 id=t>ok</h1>");if(await p.textContent("#t")!=="ok")throw new Error("bad render");await b.close();console.log("chromium OK");})()\n' > /tmp/pw-check.js; \
|
|
pw /tmp/pw-check.js; \
|
|
rm -f /tmp/pw-check.js
|
|
|
|
WORKDIR /workspace
|
|
COPY workspace/ .
|
|
|
|
RUN mkdir -p .claude && \
|
|
echo '{"permissions":{"deny":["WebFetch","WebSearch"]}}' > .claude/settings.json
|
|
|
|
# .env is gitignored; materialize from the committed backend/env-example (public dev secrets).
|
|
# env-example points PG at host `postgresql` (the compose service name) — rewrite to localhost
|
|
# (everything is on localhost in this single container). Redis is already localhost; Mongoid
|
|
# reads MONGODB_HOST (unset → localhost).
|
|
RUN if [ -f backend/env-example ] && [ ! -f backend/.env ]; then \
|
|
cp backend/env-example backend/.env && \
|
|
sed -i 's/^PG_DATABASE_HOST=.*/PG_DATABASE_HOST=localhost/' backend/.env; \
|
|
fi
|
|
|
|
RUN git init -q && \
|
|
git config user.email "dev@agent" && \
|
|
git config user.name "Dev" && \
|
|
git add -A && \
|
|
git commit -m "initial" --quiet
|
|
|
|
# Install backend gems (in backend/). Add linux platforms (host is typically darwin-arm64).
|
|
RUN cd backend \
|
|
&& bundle config set --local frozen false \
|
|
&& bundle lock --add-platform x86_64-linux \
|
|
&& bundle lock --add-platform aarch64-linux \
|
|
&& bundle install --jobs 4 --retry 3
|
|
|
|
# Install the Ember client deps (baked; non-fatal — the rspec verifier doesn't need them, and
|
|
# the Node-14/bower toolchain is fragile in a non-interactive build). OPENSSL_CONF=/dev/null
|
|
# for the old webpack md4 hashing on bookworm's OpenSSL 3.
|
|
# --unsafe-perm so npm (as root) runs the postinstall (patch-package + bower install) instead of
|
|
# skipping it; without it bower_components never populates and the client can't build.
|
|
RUN . "$NVM_DIR/nvm.sh" && nvm use 14.21.3 >/dev/null \
|
|
&& cd frontend && OPENSSL_CONF=/dev/null npm install --unsafe-perm --no-audit --no-fund \
|
|
|| echo "WARNING: frontend npm install failed (non-fatal — JS client isn't needed for grading)" >&2
|
|
|
|
# Fail loudly if any load-bearing tool is missing.
|
|
RUN for t in ruby bundle psql redis-server mongod node claude python3; do \
|
|
command -v "$t" >/dev/null 2>&1 || { echo "FATAL: required tool '$t' missing from image" >&2; exit 1; }; \
|
|
done; \
|
|
echo "toolchain OK: ruby=$(ruby --version) node=$(node --version) mongod=$(mongod --version | head -1)"
|
|
|
|
# Fold setup edits (.env, Gemfile.lock platform locks) into the baseline so the grader's
|
|
# working-tree diff attributes only the agent's changes.
|
|
RUN git add -A && git commit --amend --no-edit --quiet
|
|
|
|
# Startup: start Postgres + Redis + MongoDB, create the PG dev/test DBs, load the PG schema.
|
|
# Mongo collections are created lazily by Mongoid — nothing to load there.
|
|
RUN cat > /usr/local/bin/start-services.sh <<'EOF'
|
|
#!/bin/bash
|
|
set -e
|
|
service postgresql start
|
|
service redis-server start >/dev/null 2>&1 || redis-server --daemonize yes >/dev/null 2>&1 || true
|
|
mkdir -p /data/db && mongod --dbpath /data/db --bind_ip 127.0.0.1 --fork --logpath /tmp/mongod.log >/dev/null 2>&1 || true
|
|
until pg_isready -h localhost -p 5432 -U postgres >/dev/null 2>&1; do sleep 0.5; done
|
|
su postgres -c "psql -c \"CREATE DATABASE flaredown_development OWNER postgres;\"" >/dev/null 2>&1 || true
|
|
su postgres -c "psql -c \"CREATE DATABASE flaredown_test OWNER postgres;\"" >/dev/null 2>&1 || true
|
|
cd /workspace/backend && bundle exec rails db:schema:load >/tmp/schema-load-dev.log 2>&1 || echo "WARN: dev schema load failed - see /tmp/schema-load-dev.log" >&2
|
|
cd /workspace/backend && RAILS_ENV=test bundle exec rails db:schema:load >/tmp/schema-load-test.log 2>&1 || echo "WARN: test schema load failed - see /tmp/schema-load-test.log" >&2
|
|
exec "$@"
|
|
EOF
|
|
RUN chmod +x /usr/local/bin/start-services.sh
|
|
|
|
# Install the Codex CLI at BUILD time, for the same reason claude is: the agent-setup
|
|
# install needs the network, which the trial DNS jail blocks. Hard-fail rather than let a
|
|
# codex-less image cache and break every trial on that repo at agent-setup.
|
|
RUN for i in 1 2 3; do \
|
|
if curl -fsSL https://chatgpt.com/codex/install.sh -o /tmp/codex-install.sh \
|
|
&& CODEX_INSTALL_DIR=/usr/local/bin CODEX_NON_INTERACTIVE=true sh /tmp/codex-install.sh; then break; fi; \
|
|
echo "WARNING: codex install attempt $i failed; retrying in 5s" >&2; sleep 5; \
|
|
done; \
|
|
rm -f /tmp/codex-install.sh; \
|
|
if ! command -v codex >/dev/null 2>&1 && [ -x "$HOME/.local/bin/codex" ]; then \
|
|
ln -sf "$HOME/.local/bin/codex" /usr/local/bin/codex; \
|
|
fi; \
|
|
if ! command -v codex >/dev/null 2>&1 && command -v npm >/dev/null 2>&1; then \
|
|
npm install -g @openai/codex@latest || true; \
|
|
fi; \
|
|
command -v codex >/dev/null 2>&1 \
|
|
&& echo "codex installed at $(command -v codex)" \
|
|
|| echo "WARNING: codex CLI not installed (see the install output above)" >&2
|
|
|
|
# Restrict DNS to the model endpoint when DNSJAIL_ALLOW is set (the agent supplies it).
|
|
# Source: scripts/lib/dns-jail-container.sh, staged here by build-workspace.sh.
|
|
COPY dns-jail/ /opt/raccoon-dns-jail/
|
|
RUN if [ -f /opt/raccoon-dns-jail/dns-jail-container.sh ]; then \
|
|
install -m 0755 /opt/raccoon-dns-jail/dns-jail-container.sh /usr/local/bin/raccoon-dns-jail \
|
|
&& sh -n /usr/local/bin/raccoon-dns-jail; \
|
|
else echo "NOTE: no DNS jail script staged; trials on this image run unjailed" >&2; fi
|
|
|
|
|
|
ENTRYPOINT ["/usr/local/bin/start-services.sh"]
|
|
# Resolver for the trial DNS allowlist (scripts/lib/dns-jail.sh); if this
|
|
# does not land, trials just run unjailed.
|
|
RUN (command -v apk >/dev/null 2>&1 && apk add --no-cache dnsmasq bind-tools) \
|
|
|| (apt-get update && apt-get install -y --no-install-recommends dnsmasq-base dnsutils \
|
|
&& rm -rf /var/lib/apt/lists/*) \
|
|
|| true
|
|
|
|
CMD ["sleep", "infinity"]
|