diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..4e8f303 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,36 @@ +# Dev Container image for community_foundation. +# Matches the repo's .ruby-version (4.0.2) and the production Dockerfile base. +# Adds the build toolchain plus Chromium for Capybara/Selenium system tests. +ARG RUBY_VERSION=4.0.2 +FROM docker.io/library/ruby:${RUBY_VERSION}-slim + +# System packages: +# build-essential, git, libyaml-dev, pkg-config -> compiling native gems (sqlite3, etc.) +# libvips, sqlite3, curl -> runtime deps (Active Storage variants, DB CLI) +# chromium, chromium-driver -> headless system tests (driven_by :selenium) +# libjemalloc2 -> matches prod base (optional, harmless) +RUN apt-get update -qq && \ + apt-get install --no-install-recommends -y \ + build-essential git curl \ + libvips libyaml-dev pkg-config sqlite3 libjemalloc2 \ + chromium chromium-driver && \ + rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/* + +# The system test suite runs as root in this container, and Chromium refuses to +# start its sandbox as root ("Running as root without --no-sandbox is not +# supported", crbug.com/638180). The test files call `driven_by :selenium, using: +# :headless_chrome`, and Rails' own driver registration overwrites any Capybara +# driver configured in Ruby, so the flag can't be injected from the test setup +# without editing the test files. Instead, shim the browser binary itself: +# chromedriver auto-detects Chrome at /usr/bin/chromium, so move the real binary +# aside and put a wrapper there that always appends the container-required flags. +# (Upstream CI runs as non-root, where the sandbox works and this shim is absent.) +RUN mv /usr/bin/chromium /usr/bin/chromium.real && \ + printf '#!/bin/sh\nexec /usr/bin/chromium.real --no-sandbox --disable-dev-shm-usage "$@"\n' > /usr/bin/chromium && \ + chmod +x /usr/bin/chromium + +# Capybara/Selenium look up the browser + driver by these names. +ENV CHROME_BIN=/usr/bin/chromium \ + BUNDLE_PATH=/usr/local/bundle + +WORKDIR /workspace diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..ef2af15 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,17 @@ +{ + "name": "Community Foundation", + "dockerComposeFile": "docker-compose.yml", + "service": "app", + "workspaceFolder": "/workspace", + "remoteUser": "root", + "forwardPorts": [3000], + "containerEnv": { + "RAILS_MAX_THREADS": "5", + // Use the distro chromedriver instead of Selenium Manager's network download. + "SELENIUM_MANAGER_DISABLE": "true" + }, + // Install gems, prepare the dev + test databases, and seed dev data so the app + // can be browsed. No master.key is needed: HTTP Basic auth is production-only and + // require_master_key is unset, so dev/test boot without credentials. + "postCreateCommand": "bash -lc 'set -e; gem install bundler -v 4.0.12 --conservative; bundle install; bin/rails db:prepare; RAILS_ENV=test bin/rails db:test:prepare; bin/rails db:seed'" +} diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 0000000..1d6a25c --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,23 @@ +# Single-service Dev Container: a Rails 8.1 app on SQLite (Solid cache/queue/cable +# all run on SQLite too), so there is no separate database/redis service to wire up. +services: + app: + build: + context: .. + dockerfile: .devcontainer/Dockerfile + volumes: + # .devcontainer lives in the repo, so the parent dir is the repo root. + - ..:/workspace:cached + # Persist the installed gem bundle across rebuilds for faster iteration. + - cf-bundle:/usr/local/bundle + working_dir: /workspace + # Keep the container alive; the Dev Containers tooling execs into it. + command: sleep infinity + environment: + # Bind dev/system-test web servers to all interfaces so the host can reach them. + BINDING: "0.0.0.0" + # Selenium/Capybara browser discovery (mirrors the Dockerfile ENV). + CHROME_BIN: /usr/bin/chromium + +volumes: + cf-bundle: