Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
.git
.circleci
.github
.DS_Store
coverage
log/*
tmp/*
!log/.keep
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

# Ignore app config
.env
.env.local
.env.test
config/database.yml

# Ignore the default SQLite database.
Expand Down
21 changes: 8 additions & 13 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# syntax=docker/dockerfile:1
FROM ruby:4.0.4

RUN dpkg --add-architecture i386 \
Expand All @@ -19,24 +20,18 @@ RUN dpkg --add-architecture i386 \
chromium-driver \
&& rm -rf /var/lib/apt/lists/*

RUN gem install bundler -v 2.2.21

WORKDIR /app

COPY Gemfile Gemfile.lock ./
RUN bundle _2.2.21_ install --jobs=4 --retry=3

# Dual-boot: Gemfile.next targets the Rails version we're upgrading to.
# Remove this block (and Gemfile.next / Gemfile.next.lock) once the upgrade lands.
COPY Gemfile.next Gemfile.next.lock ./
RUN BUNDLE_GEMFILE=Gemfile.next bundle _2.2.21_ install --jobs=4 --retry=3
ARG BUNDLE_GEMFILE=/app/Gemfile

COPY . .
ENV BUNDLE_GEMFILE=${BUNDLE_GEMFILE} \
BUNDLE_JOBS=4 \
BUNDLE_RETRY=3

COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
COPY Gemfile Gemfile.lock Gemfile.next Gemfile.next.lock .ruby-version ./
RUN --mount=type=cache,target=/usr/local/bundle/cache,sharing=locked \
bundle install

EXPOSE 3000

ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]
24 changes: 16 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,25 @@ You can see it working in https://audit.fastruby.io

## Getting started (Docker)

The easiest way to run the app locally is with Docker Compose, which builds the app image and a Postgres database for you:
First, run:

docker compose up --build
```bash
bin/docker/setup && \
BUNDLE_GEMFILE=Gemfile.next bin/docker/setup
```

This starts:
This will build both sets of images for each version of Rails. Then, to start the containers:

- `db` — Postgres 16
- `web` — the app on http://localhost:3000, running against the default `Gemfile` (currently Rails 8.1)
- `web_next` — the same image, but with `BUNDLE_GEMFILE=Gemfile.next`, on http://localhost:3001 (see "Dual-boot Rails upgrades" below)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this line is being removed but it's the only line that mentions that the "next" server runs in port 3001

when you run the "next" app, the log shows port 3000 (which is the one inside the container)

BUNDLE_GEMFILE=Gemfile.next bin/docker/start
[+] Running 1/1
 ✔ Container audit-db-1  Running                                                                                                                                                                                                         0.0s 
Attaching to web_next-1
web_next-1  | => Booting Puma
web_next-1  | => Rails 8.1.3 application starting in development 
web_next-1  | => Run `bin/rails server --help` for more startup options
web_next-1  | Puma starting in single mode...
web_next-1  | * Puma version: 8.0.2 ("Into the Arena")
web_next-1  | * Ruby version: ruby 4.0.4 (2026-05-12 revision b89eb1bcbf) +PRISM [x86_64-linux]
web_next-1  | *  Min threads: 5
web_next-1  | *  Max threads: 5
web_next-1  | *  Environment: development
web_next-1  | *          PID: 1
web_next-1  | * Listening on http://0.0.0.0:3000
web_next-1  | Use Ctrl-C to stop

but if you click that link, it fails, because docker is exposing it as 3001 instead

I think we should change the command of the web-next service to include "-p 3001" and the port to be "3001:3001", so then the logs shows Listening on http://0.0.0.0:3001 with the correct port and clicking it opens the browser in the correct url

```bash
# For the current version of Rails
bin/docker/start

`docker/entrypoint.sh` copies `config/database.yml.sample` / `.env.sample` into place and runs `rails db:prepare` on boot, so no manual DB setup is needed.
# For the next version of Rails
BUNDLE_GEMFILE=Gemfile.next bin/docker/start
```

Keep in mind that if you change any of the dependencies of the application, you will need to
run the corresponding setup command.

## Getting started (without Docker)

Expand All @@ -35,7 +43,7 @@ You should be able to go to http://localhost:3000 and see the landing page.

Inside Docker:

docker compose run --rm -e RAILS_ENV=test -e DATABASE_HOST=db -e DATABASE_USERNAME=postgres -e DATABASE_PASSWORD=postgres web bin/rails test
bin/docker/run bin/rails test

Without Docker:

Expand Down
10 changes: 10 additions & 0 deletions bin/docker/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -e
cd "$(dirname "$0")/../.."
if [ "$(basename "$BUNDLE_GEMFILE")" = "Gemfile.next" ]; then
CONTAINER=web_next
else
CONTAINER=web
fi
exec docker compose run --rm "$CONTAINER" "$@"

39 changes: 39 additions & 0 deletions bin/docker/setup
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env ruby
require "pathname"
require "fileutils"
include FileUtils

# path to your application root.
APP_ROOT = Pathname.new File.expand_path("../..", __dir__)

def system!(*args)
system(*args) || abort("\n== Command #{args} failed ==")
end

CONTAINER = ENV["BUNDLE_GEMFILE"] == "Gemfile.next" ? "web_next" : "web"
DOCKER_PREFIX = "docker compose run --rm #{CONTAINER}"

chdir APP_ROOT do
# This script is a starting point to setup your application.
# Add necessary setup steps to this file.

puts "== Copy .env =="
# We use .env.local because we are using DotenvValidator and there's a known issue with docker-compose: [link](https://github.com/fastruby/dotenv_validator#if-you-use-docker-compose-read-this)
# The symlink is created because `.env.local` is not visible in testing environments but
# we don't want to maintain 2 separate files. This follows [this table](https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use)
unless File.exist?(".env.local")
cp ".env.sample", ".env.local"
system! "ln -s .env.local .env.test"
Comment on lines +25 to +26

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should add these files to the .gitignore file

end

puts "== Setup Database =="
puts "\n== Copying sample files =="
unless File.exist?("config/database.yml")
cp "config/database.yml.sample", "config/database.yml"
end

puts "== Build images =="
system! "docker compose build #{CONTAINER}"

system! "#{DOCKER_PREFIX} rails db:create db:migrate"
end
9 changes: 9 additions & 0 deletions bin/docker/start
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -e
cd "$(dirname "$0")/../.."
if [ "$(basename "$BUNDLE_GEMFILE")" = "Gemfile.next" ]; then
CONTAINER=web_next
else
CONTAINER=web
fi
exec docker compose up "$CONTAINER" "$@"
14 changes: 5 additions & 9 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,10 @@ services:
tty: true

web_next:
# Reuse the image built for `web` (compose tags it <project>-web, i.e.
# audit-web) instead of building a second image. Only BUNDLE_GEMFILE
# differs, so a separate build is unnecessary. (Not using `extends` here:
# it merges array fields like `ports` instead of overriding them, which
# would leak web's 3000:3000 mapping into this service too.)
image: audit-web
build:
context: .
args:
BUNDLE_GEMFILE: /app/Gemfile.next
platform: linux/amd64
# Distinct pidfile: web and web_next share the same bind-mounted /app, so
# they'd otherwise race on tmp/pids/server.pid and refuse to boot together.
Expand All @@ -53,12 +51,10 @@ services:
DATABASE_HOST: db
DATABASE_USERNAME: postgres
DATABASE_PASSWORD: postgres
BUNDLE_GEMFILE: Gemfile.next
BUNDLE_GEMFILE: /app/Gemfile.next
depends_on:
db:
condition: service_healthy
web:
condition: service_started
stdin_open: true
tty: true

Expand Down
Loading