first round of changes: dedicated cache folder, cleaner base image, version management

This commit is contained in:
Justin
2026-06-20 10:56:44 -04:00
parent 4b3df1ddae
commit ecfdbd9f98
10 changed files with 122 additions and 110 deletions

View File

@ -1 +1,2 @@
.env
cache/

View File

@ -5,27 +5,46 @@
FROM safeclaude-base:latest
# System packages: what's needed to build Ruby, talk to Postgres, proxy the
# database (socat), and run browser tests (headless Chrome).
# database (socat), run browser tests (headless Chrome), and unpack the Node
# download below (xz-utils).
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
libssl-dev libreadline-dev zlib1g-dev libffi-dev libyaml-dev \
libpq-dev \
socat \
chromium chromium-driver \
&& rm -rf /var/lib/apt/lists/*
xz-utils
# Capybara/Selenium look for Chrome at these paths.
ENV CHROME_BIN=/usr/bin/chromium
ENV CHROMEDRIVER=/usr/bin/chromedriver
# rbenv and nvm get installed by the hooks at launch instead of here, because
# they live in the home folder — and that folder is swapped in fresh each run, so
# anything we installed there now would just be thrown away. Here we only set the
# paths and shell setup; the two lines below do nothing until a hook installs the
# matching tool.
ENV RBENV_ROOT=/home/coder/.rbenv
ENV NVM_DIR=/home/coder/.nvm
ENV PATH="$RBENV_ROOT/bin:$RBENV_ROOT/shims:$PATH"
# --- Ruby (one pinned version) ---
# A project only ever needs one Ruby, so we install it straight into /usr/local
# instead of running a version manager. ruby-build (the tool rbenv uses under the
# hood) does the download + compile. To change versions, bump RUBY_VERSION and
# rebuild with `safeclaude build`.
ARG RUBY_VERSION=3.3.6
RUN git clone --depth 1 https://github.com/rbenv/ruby-build.git /tmp/ruby-build && \
PREFIX=/usr/local /tmp/ruby-build/install.sh && \
rm -rf /tmp/ruby-build && \
ruby-build "$RUBY_VERSION" /usr/local && \
gem install bundler --no-document
RUN echo '[ -d "$RBENV_ROOT/bin" ] && eval "$(rbenv init - bash)"' >> /etc/bash.bashrc && \
echo '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"' >> /etc/bash.bashrc
# Gems install into the project's cache folder (which lives on the host), so they
# persist between runs and survive container/volume resets. The bundle hook
# relies on this too. BUNDLE_PATH is set here so both the hook and your app see it.
ENV BUNDLE_PATH=/code/.safeclaude/cache/bundle
# --- Node (one pinned version) ---
# Same idea: download one Node and unpack it into /usr/local. Bump NODE_VERSION
# and rebuild to change it.
ARG NODE_VERSION=22.11.0
RUN arch="$(uname -m)" && \
case "$arch" in \
x86_64) narch=x64 ;; \
aarch64) narch=arm64 ;; \
*) echo "unsupported arch: $arch" >&2; exit 1 ;; \
esac && \
curl -fsSL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-${narch}.tar.xz" \
| tar -xJ -C /usr/local --strip-components=1

View File

@ -1,31 +0,0 @@
#!/bin/bash
# Sets up rbenv and the project's Ruby. Safe to run every launch — it only does
# real work the first time, or when the Ruby version changes.
set -euo pipefail
# Grab rbenv (cloned into the home folder so it sticks around between runs).
if [ ! -d "$RBENV_ROOT/bin" ]; then
echo "[ruby] installing rbenv..."
git clone --depth=1 https://github.com/rbenv/rbenv.git "$RBENV_ROOT"
git clone --depth=1 https://github.com/rbenv/ruby-build.git "$RBENV_ROOT/plugins/ruby-build"
fi
eval "$(rbenv init - bash)"
# Pick the Ruby version: the project's .ruby-version wins, then whatever rbenv
# was last set to, then a sensible default.
if [ -f /code/.ruby-version ]; then
RUBY_VERSION="$(tr -d '[:space:]' < /code/.ruby-version)"
elif [ -f "$RBENV_ROOT/version" ]; then
RUBY_VERSION="$(cat "$RBENV_ROOT/version")"
else
RUBY_VERSION="3.3.6"
fi
if ! rbenv versions --bare 2>/dev/null | grep -qx "$RUBY_VERSION"; then
echo "[ruby] installing Ruby $RUBY_VERSION (first time only — takes a few minutes)..."
rbenv install "$RUBY_VERSION"
fi
rbenv global "$RUBY_VERSION"
# Make sure bundler is installed for this Ruby.
gem list bundler -i &>/dev/null || gem install bundler --no-document

View File

@ -1,15 +0,0 @@
#!/bin/bash
# OPTIONAL — only needed if your project pins a Node version via .nvmrc (the base
# already includes a working Node). Rename to 15-node.sh to turn it on.
set -euo pipefail
if [ ! -s "$NVM_DIR/nvm.sh" ]; then
echo "[node] installing nvm..."
curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/HEAD/install.sh \
| NVM_DIR="$NVM_DIR" PROFILE=/dev/null bash
fi
. "$NVM_DIR/nvm.sh"
if [ -f /code/.nvmrc ]; then
( cd /code && nvm install && nvm use )
fi

View File

@ -1,15 +1,19 @@
#!/bin/bash
# Installs gems, but only when Gemfile.lock has changed: it remembers the last
# version it installed (saved between runs), so an unchanged lockfile is a
# near-instant no-op.
# version it installed, so an unchanged lockfile is a near-instant no-op.
#
# Both the gems (via BUNDLE_PATH, set in the Dockerfile) and the marker below
# live in /code/.safeclaude/cache — on the host, so they persist between runs
# and stay out of git. Ruby is already on PATH from the Dockerfile, so there's
# no version manager to initialize here.
set -euo pipefail
[ -f /code/Gemfile ] || exit 0
eval "$(rbenv init - bash)"
CACHE=/code/.safeclaude/cache
mkdir -p "$CACHE"
LOCK=/code/Gemfile.lock
MARKER="$HOME/.safeclaude-deps/gemfile.sha"
mkdir -p "$(dirname "$MARKER")"
MARKER="$CACHE/gemfile.sha"
CUR="$( [ -f "$LOCK" ] && sha256sum "$LOCK" | cut -d' ' -f1 || echo no-lock )"
if [ "$(cat "$MARKER" 2>/dev/null || true)" != "$CUR" ]; then

View File

@ -0,0 +1 @@
0.1.0