#!/bin/bash set -e # Install nvm if not already present in the home volume. if [ ! -s "$NVM_DIR/nvm.sh" ]; then echo "nvm not found — running installer..." curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/HEAD/install.sh | NVM_DIR="$NVM_DIR" PROFILE=/dev/null bash echo "nvm installed successfully." fi . "$NVM_DIR/nvm.sh" # Install rbenv + ruby-build if not already present in the home volume. if [ ! -d "$RBENV_ROOT/bin" ]; then echo "rbenv not found — installing..." 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" echo "rbenv installed successfully." fi eval "$(rbenv init - bash)" # Determine the desired Ruby version: prefer .ruby-version in the workspace, # fall back to the rbenv global setting, then a hardcoded default. if [ -f "/code/.ruby-version" ]; then RUBY_VERSION=$(tr -d '[:space:]' /dev/null | grep -qx "$RUBY_VERSION"; then echo "Ruby $RUBY_VERSION not found — installing (this may take a few minutes)..." rbenv install "$RUBY_VERSION" echo "Ruby $RUBY_VERSION installed." fi rbenv global "$RUBY_VERSION" # Ensure bundler is available for this Ruby version. if ! gem list bundler -i &>/dev/null; then gem install bundler --no-document fi # Install gem dependencies for the linked workspace so rspec (and other gems) # are available without needing an explicit `bundle install` step. if [ -f "/code/Gemfile" ]; then echo "Gemfile found — running bundle install..." pushd /code # TODO: Elaborate or expand — wire secrets (e.g. a private gem registry token) # in from the environment instead of hardcoding. Compose's auto-loaded .env only # does YAML interpolation (next to the compose file), not container injection; # injecting needs `environment:`/`env_file:`. In the project-local model, point # this at the mounted project's .safeclaude/.env so secrets live with the project. BUNDLE_GEMS__GRAPHQL__PRO="${BUNDLE_GEMS__GRAPHQL__PRO:-}" bundle install popd fi # Install Claude Code if not already present in the home volume. # Because the home directory is a volume, this install persists across # container restarts and rebuilds. if ! command -v claude &>/dev/null; then echo "Claude Code not found — running installer..." curl -fsSL https://claude.ai/install.sh | bash echo "Claude Code installed successfully." else echo "Claude Code $(claude --version 2>/dev/null || echo '(version unknown)') ready." fi # Proxy host postgres to 127.0.0.1:5432 inside the container so the app can # use the same DATABASE_URL whether running inside or outside Docker. if ! ss -tlnp 2>/dev/null | grep -q ':5432'; then echo "Starting postgres proxy 127.0.0.1:5432 -> host.docker.internal:5432" socat TCP-LISTEN:5432,bind=127.0.0.1,fork,reuseaddr \ TCP:host.docker.internal:5432 & fi exec "$@"