Give the suite somewhere to run that is not the deploy host - #120
Give the suite somewhere to run that is not the deploy host#120openipc-ai wants to merge 2 commits into
Conversation
The Gemfile pins Ruby 3.1.7 and the app needs MariaDB, libvips and libheif.
Almost no development machine has that combination, so verification has been
happening in CI or on the deploy host -- which means the loop between writing a
change and finding out it fails is a push and a wait.
This adds a dev/test stack: compose.yaml plus docker/Dockerfile.dev, deliberately
separate from the root Dockerfile, which is the production build and should not
grow development concerns. The image carries only the OS libraries the gems bind
to; the tree is bind-mounted and gems live in a volume, so a source edit never
rebuilds anything. Its build context is ./docker rather than the repository root,
so the daemon is not handed node_modules on every build.
Three things it has to get right, all of them found by running it:
- libvips and libheif come from the OS packages, matching the production
runtime stage. The 'vips' gem is gone from the Gemfile and must stay gone.
- Yarn 4, via corepack, because yarn.lock carries the Berry __metadata header
that a v1 yarn cannot read.
- RUBYOPT=--disable-error_highlight. Ruby 3.1 activates its own bundled
error_highlight 0.3.0 before Bundler runs, and the development group asks
for >= 0.4.0, so bin/rails dies in config/boot.rb with "You have already
activated error_highlight 0.3.0". Neither the production image nor CI hits
this because both exclude the development group; this container is the only
place that installs it, and it has to, since rubocop and i18n-tasks are
there. Upgrading RubyGems does not help -- gem update --system 3.4.22 was
tried and the conflict is unchanged.
rubocop-performance joins the Gemfile. .rubocop.yml has had
`require: rubocop-performance` all along, so rubocop could not start at all
without it; that went unnoticed for the same reason the rest of this did.
development.rb gets two settings that only ever mattered off-localhost. LAN
hosts, so the dev server can be opened from a phone -- written
/.*\.local(:\d+)?\z/ because HostAuthorization matches a Regexp against the Host
header including the port, while Strings and IPAddrs are matched port-stripped,
so the obvious spelling 403s every request to a host:3010. And
i18n.fallbacks = true, which production already sets: without it development is
the only environment that renders "translation missing", so a gap in ru or zh
looks broken here and invisible there.
Verified in the container: 231 runs, 873 assertions, 0 failures. rubocop runs
(742 offences over 111 files, the pre-existing baseline); development.rb goes
from 4 offences to 3, having lost a stray blank line next to the new stanza.
i18n-tasks reports 0 missing and 102 unused.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015hvzXBErEjEGMyRme2K3hi
PR Summary by QodoAdd containerized Rails development and test stack
AI Description
Diagram
High-Level Assessment
Files changed (6)
|
Code Review by Qodo
1.
|
The stack as first written ran as root, so everything it put into the
bind-mounted tree -- app/assets/builds, log/, tmp/, the output of
yarn build:fonts -- landed on the host owned by root. Using it for one afternoon
left public/fonts/ owned by nobody:nogroup and a git checkout that could not
remove it: "warning: unable to unlink", twenty times.
The image now creates a user and runs as it, and chowns /bundle to it so bundler
can still install into the volume. The ids come from DEV_UID/DEV_GID build args
defaulting to 1000, which is the usual first user on a Linux desktop; anyone
else writes them into a .env beside compose.yaml, which Compose reads on its own
and which is now gitignored.
The node_modules named volume goes. A named volume is created root-owned and
Docker seeds it from the image, so an empty one over a path the image does not
have stays root-owned and yarn fails on the first link step with EACCES. /bundle
does not have this problem precisely because the image creates and chowns it
before the volume is populated from it. node_modules lives in the bind mount
instead, where it belongs to the developer and is already gitignored.
Migrating an existing checkout needs one command, since the old root-owned files
resist the new user:
docker compose run --rm --user 0:0 web chown -R "$(id -u):$(id -g)" \
/app/.yarn /app/node_modules /app/app/assets/builds /app/log /app/tmp
Verified: container reports uid 1002, a file it touches in the tree reads
ai:ai on the host and deletes without sudo, and the suite is unchanged at
231 runs, 873 assertions, 0 failures.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015hvzXBErEjEGMyRme2K3hi
First of five PRs rebasing the June 11 relaunch work onto current master. This
one carries no relaunch content — it exists so the four that follow can be
verified locally instead of by pushing and waiting.
What and why
The Gemfile pins Ruby 3.1.7 and the app needs MariaDB, libvips and libheif.
Almost no development machine has that combination, so verification has been
happening in CI or on the deploy host.
compose.yaml+docker/Dockerfile.devare a dev/test stack, deliberatelyseparate from the root
Dockerfile, which is the production build and shouldnot grow development concerns. The image carries only the OS libraries the gems
bind to; the tree is bind-mounted and gems live in a volume, so a source edit
never rebuilds anything. Its build context is
./docker, so the daemon is nothanded
node_moduleson every build.Three things it has to get right, all found by running it:
vipsgem is gone from the Gemfile and must stay gone.yarn.lockcarries the Berry__metadataheader that a v1 yarn cannot read.
RUBYOPT=--disable-error_highlight. Ruby 3.1 activates its bundlederror_highlight 0.3.0before Bundler runs, while the development group asksfor
>= 0.4.0, sobin/railsdies inconfig/boot.rb. Neither the productionimage nor CI hits this — both exclude the development group. This container is
the only place that installs it, and it has to, since rubocop and i18n-tasks
live there. Upgrading RubyGems does not help;
gem update --system 3.4.22was tried and the conflict is unchanged.
Two fixes that fell out
rubocop-performancejoins the Gemfile..rubocop.ymlhas hadrequire: rubocop-performanceall along, so rubocop could not start at allwithout it.
development.rbgets two settings that only ever mattered off-localhost:/.*\.local(:\d+)?\z/—HostAuthorizationmatches aRegexpagainst the Host header including the port while Strings andIPAddrs are matched port-stripped, so the obvious spelling 403s every request
to a
host:3010.i18n.fallbacks = true, which production already sets. Without it developmentis the only environment that renders "translation missing", so a gap in ru or
zh looks broken here and invisible there.
Verification
Run in the container, not asserted from reading:
bin/rails testrubocopconfig/environments/development.rbi18n-tasks missing/unusedNote for reviewers: the numbers in bd6fb59's message ("15 offences", "4 missing")
were scoped to that change's touched files. The whole-repo baselines above are
what to measure future changes against, and are now recorded in
CLAUDE.md.🤖 Generated with Claude Code
https://claude.ai/code/session_015hvzXBErEjEGMyRme2K3hi