Files
project-work/worker-toolkit-flaredown/explore/.devcontainer/Dockerfile

167 lines
8.4 KiB
Docker

# Explore container for flaredown — rubyforgood chronic-illness symptom tracker.
# github.com/rubyforgood/Flaredown (GPL-3), pinned upstream at 5f859e8d. Polyglot, multi-service:
# - backend/ Rails 7.1 API, Ruby 3.2.3. Mongoid 8.1 on MongoDB (primary store) + Postgres
# (small relational slice) + Redis + Sidekiq.
# - frontend/ Ember.js client, Node 14.21.3 (npm 7).
# Adapted for live-mount: the source repo is bind-mounted at /workspace/repo; deps + DB set up
# by post-create.sh, and the three datastores are started by post-start.sh.
#
# Deliberate version choice: docker-compose pins MongoDB 4.4.9, which is EOL and ships no
# arm64 / Debian-bookworm packages. Mongoid 8.1.3 + the mongo ruby driver 2.20.1 support
# servers up to 7.0, so we run MongoDB 7.0 (native amd64 + aarch64, no emulation) instead of
# fighting a dead 4.4 build. Same wire protocol; the app is version-agnostic here.
FROM ruby:3.2.3
# System deps: Postgres + libpq (the pg gem), Redis (Sidekiq), plus build tooling. python3
# (bookworm ships 3.11 ≥ 3.10, which the reduced-toolset str_replace_editor needs). xz/curl/
# gnupg for the Node + Mongo downloads. libyaml for psych.
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 procps \
&& rm -rf /var/lib/apt/lists/*
# MongoDB 7.0 server binary (mongod) from the official tarball, arch-aware. The ubuntu2204
# build (glibc 2.35) runs fine on bookworm (glibc 2.36). Only mongod is needed — Mongoid
# connects over the wire; no mongosh required (post-start probes the port directly).
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 — toolkit tooling: create-snapshot hooks, `node -e` reads of
# toolkit.json) + 14 (the Ember app; frontend/.nvmrc = v14.21.3). Symlink v18 to /usr/local/bin
# so the toolkit's own node always resolves; run-app switches PATH to v14 for the client.
# The frontend's .npmrc sets engine-strict=true and its package.json requires npm 6.x, so pin
# npm 6 in the v14 line (nvm's 14.21.3 otherwise bundles npm 7, which fails engine-strict). The
# v18.* glob (not `nvm version`) avoids sourcing nvm.sh under Docker's /bin/sh (dash), bash-only.
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 depends on phantomjs-prebuilt@2.1.16, which has NO arm64
# binary and is EOL everywhere — its install script aborts `npm install` on Apple-Silicon
# hosts. A stub on PATH that reports the expected version makes the install script treat
# PhantomJS as "already installed" and skip the (impossible) download, so `npm install`
# completes and `ember build`/`ember serve` (what run-app uses) work. `ember test` runs on
# headless Chrome at this pin, wired up after the Playwright block below.
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 (default
# postgres). OVERWRITE pg_hba.conf (Debian's default `local all all peer` is first-match, so
# an appended trust rule never applies).
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"
USER root
# --- Playwright + Chromium, for driving the app in a real browser -------------
# Self-contained under /opt — the member's own runtime is untouched.
ENV PLAYWRIGHT_BROWSERS_PATH=/opt/ms-playwright
RUN 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/*
RUN set -eux; \
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
# `pw <script.js>` runs Node with `require("playwright")` resolvable (CommonJS).
RUN 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
# Fail the build if Chromium cannot start.
RUN 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
# `ember test` resolves its browser via CHROME_BIN, falling back to `google-chrome` on PATH
# (frontend/testem.js). Point both at the Chromium Playwright just installed. The glob is
# resolved at build time so a Playwright bump can't strand a hardcoded chromium-<build> path.
RUN set -eux; \
chrome="$(echo /opt/ms-playwright/chromium-*/chrome-linux/chrome)"; \
test -x "$chrome"; \
printf '#!/bin/bash\nexec %s --no-sandbox --disable-dev-shm-usage "$@"\n' "$chrome" \
> /usr/local/bin/google-chrome; \
chmod +x /usr/local/bin/google-chrome; \
google-chrome --version
ENV CHROME_BIN=/usr/local/bin/google-chrome
ENV IS_SANDBOX=1
RUN mkdir -p /root/.claude && \
echo '{"permissions":{"deny":["WebFetch","WebSearch"]}}' > /root/.claude/settings.json
# Startup for a direct `docker run` (the devcontainer path uses post-start.sh instead, which
# starts the same services). Bring up Postgres + Redis + MongoDB, then hand off.
RUN cat > /usr/local/bin/start-services.sh <<'EOF'
#!/bin/bash
set -e
service postgresql start || true
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
exec "$@"
EOF
RUN chmod +x /usr/local/bin/start-services.sh
WORKDIR /workspace/repo
# Resolver for the DNS jail (.devcontainer/dns-jail-container.sh, applied by
# post-start.sh); if this does not land, Explore just runs 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
ENTRYPOINT ["/usr/local/bin/start-services.sh"]
CMD ["sleep", "infinity"]