diff --git a/Dockerfile.local b/Dockerfile.local new file mode 100644 index 0000000000..80d012bc16 --- /dev/null +++ b/Dockerfile.local @@ -0,0 +1,56 @@ +# This dockerfile is based on the production dockerfile, minus GYR Efiler Java deps + +### First build stage used for web, worker, and shakapacker containers +FROM ruby:3.2.2 AS base + +RUN apt-get update --allow-releaseinfo-change + +# System prerequisites +RUN apt-get update \ + && apt-get -y install ca-certificates libgnutls30 build-essential libpq-dev ghostscript default-jre poppler-utils curl \ + && curl -sL https://deb.nodesource.com/setup_16.x | bash - \ + && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ + && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \ + && apt-get update && apt-get install -y nodejs yarn python3 python3-pip python3-setuptools \ + && rm -rf /var/lib/apt/lists/* + +ENV SUPERCRONIC_URL=https://github.com/aptible/supercronic/releases/download/v0.1.9/supercronic-linux-amd64 \ + SUPERCRONIC=supercronic-linux-amd64 \ + SUPERCRONIC_SHA1SUM=5ddf8ea26b56d4a7ff6faecdd8966610d5cb9d85 + +RUN curl -fsSLO "$SUPERCRONIC_URL" \ + && echo "${SUPERCRONIC_SHA1SUM} ${SUPERCRONIC}" | sha1sum -c - \ + && chmod +x "$SUPERCRONIC" \ + && mv "$SUPERCRONIC" "/usr/local/bin/${SUPERCRONIC}" \ + && ln -s "/usr/local/bin/${SUPERCRONIC}" /usr/local/bin/supercronic + +ADD ./vendor/pdftk /app/vendor/pdftk +RUN /app/vendor/pdftk/install + +WORKDIR /app +ADD package.json yarn.lock /app/ +RUN NODE_ENV=production yarn install --frozen-lockfile +ADD .ruby-version Gemfile Gemfile.lock /app/ + +RUN gem install bundler:$(cat Gemfile.lock | tail -1 | tr -d " ") --no-document \ +&& bundle install + +RUN echo "IRB.conf[:USE_AUTOCOMPLETE] = false" > ./.irbrc + +# Install the OpenCV headless library +RUN pip3 install opencv-python-headless --break-system-packages + +# https://github.com/jwilder/dockerize +ENV DOCKERIZE_VERSION=v0.7.0 +RUN wget -O - https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz | tar xzf - -C /usr/local/bin + +ADD . /app + +### This build stage is used for the rails app only +FROM base AS app + +EXPOSE 3000 + +RUN chmod +x /app/bin/docker-entrypoint + +CMD ["bash", "./bin/docker-entrypoint"] diff --git a/README.md b/README.md index 904b7f9844..20b5c7317f 100644 --- a/README.md +++ b/README.md @@ -301,6 +301,27 @@ They'll be dumped into `public/assets/flow_explorer_screenshots` locally. You can upload them to the correct S3 bucket with the task `rake flow_explorer:upload_screenshots` +## Alternate setup + development with docker compose + +1. Make sure you have `development.key` in `config/credentials`. Ask a teammate if you need access. +1. Unpack the state schema files into `vendor/us_states/unpacked` +1. Run `docker compose up`. This will start the database, jobs, shakapacker, and rails app containers. (If you had the app running locally, you may need to stop your local postgres first.) + +### Run tests in a docker container +- Developent environment is set by default, and test environment is set by default when you run tests +- Run any of the test commands in an interactive shell in the container named `web`. For example, `docker exec -it web rspec` or `docker exec -it web yarn jest` +- Turbo tests don't work with the current docker setup, so running `docker exec -it web bin/test` won't work as expected + +### Useful docker commands +| Command | Meaning | +| ----- | ----- | +| `docker compose up -d --build` | Build, run, and detach from docker compose containers in the default profile | +| `docker compose --profile pgadmin up` | Start the pgadmin service | +| `docker exec -e ALLOWED_SCHEMAS=nj -it web rspec` | Run only the rspec tests that require the allowed schemas | +| `docker exec -it web yarn jest` | Run jest tests | +| `docker logs -f web` | Follow the logs of the rails container | +| `docker volume prune --filter label=vita-min_database` | Get rid of the database entirely | + ## Deploying the Application 🚀☁️ Notes on deployment can be found in [docs/deployment](docs/deployment.md). diff --git a/bin/docker-entrypoint b/bin/docker-entrypoint new file mode 100644 index 0000000000..c709cbaeda --- /dev/null +++ b/bin/docker-entrypoint @@ -0,0 +1,15 @@ +#!/bin/sh + +dockerize -wait tcp://db:5432 -timeout 20s -wait-retry-interval 10s bash -c "bin/rails db:create" +bin/rails db:prepare +bin/rails parallel:create +bin/rails parallel:prepare +bin/rails db:seed + +bundle exec rake assets:precompile + +if [ -f /app/tmp/pids/server.pid ]; then + rm /app/tmp/pids/server.pid +fi + +bundle exec rails s -b 0.0.0.0 -p 3000 \ No newline at end of file diff --git a/config/database.yml b/config/database.yml index c4463de218..fba3405c96 100644 --- a/config/database.yml +++ b/config/database.yml @@ -2,6 +2,10 @@ local_default: &local_default adapter: postgis pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i + 1 %> timeout: 5000 + host: <%= ENV.fetch("RAILS_DB_HOST", "localhost") %> + port: <%= ENV.fetch("RAILS_DB_PORT", "5432") %> + username: <%= ENV["RAILS_DB_USERNAME"] %> + password: <%= ENV["RAILS_DB_PASSWORD"] %> deploy_default: &deploy_default adapter: postgis diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000..b803b3754f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,85 @@ +name: vita-min + +services: + db: + container_name: db + image: postgis/postgis:13-3.4-alpine + user: postgres + ports: + - 5432:5432 + volumes: + - database:/var/lib/postgresql/data + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: password + worker: + container_name: worker + volumes: + - ./:/app + build: + dockerfile: Dockerfile.local + target: "base" + command: rails jobs:work + depends_on: + web: + condition: service_healthy + environment: + - DOCKER=true + - RAILS_DB_HOST=db + - RAILS_DB_USERNAME=postgres + - RAILS_DB_PORT=5432 + - RAILS_DB_PASSWORD=password + web: + container_name: web + build: + dockerfile: Dockerfile.local + target: "app" + volumes: + - ./:/app + ports: + - 3000:3000 + depends_on: + - db + - shakapacker + links: + - db + - shakapacker + healthcheck: + test: [ "CMD", "curl", "-f", "http://0.0.0.0:3000/up" ] + interval: 30s + timeout: 30s + retries: 15 + environment: + - DOCKER=true + - RAILS_DB_HOST=db + - RAILS_DB_USERNAME=postgres + - RAILS_DB_PORT=5432 + - RAILS_DB_PASSWORD=password + - SHAKAPACKER_DEV_SERVER_HOST=shakapacker + shakapacker: + container_name: shakapacker + build: + dockerfile: Dockerfile.local + target: "base" + volumes: + - ./:/app + ports: + - 3035:3035 + command: "bin/shakapacker-dev-server --client-web-socket-url auto://0.0.0.0:3035/ws" + environment: + - SHAKAPACKER_DEV_SERVER_HOST=shakapacker + chrome: + container_name: chrome + image: selenium/standalone-chrome:4.15.0 # this version should match that of the selenium-webdriver gem (see Gemfile) + ports: + - 4444:4444 + pgadmin: + container_name: pgadmin + profiles: ["pgadmin"] + image: chorss/docker-pgadmin4 + ports: + - 5050:5050 + depends_on: + - web +volumes: + database: \ No newline at end of file diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index e670d528d8..cb703e85cc 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -17,9 +17,15 @@ else Capybara.javascript_driver = :selenium_chrome_headless end + Capybara.default_max_wait_time = 5 Capybara.server = :puma, { Silent: true } Capybara.server_port = 9887 + ENV['TEST_ENV_NUMBER'].to_i +if ENV['DOCKER'] + Capybara.server_host = "web" +else + Capybara.server_host = "0.0.0.0" +end # Prevent database truncation if the environment is production abort("The Rails environment is running in production mode!") if Rails.env.production? require "rspec/rails" diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index c27f953a59..a7274793c5 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -8,7 +8,11 @@ puts "COVERAGE enabled! look at coverage/index.html for the result." end -WebMock.disable_net_connect!(allow_localhost: true) +if ENV['DOCKER'] + WebMock.allow_net_connect! +else + WebMock.disable_net_connect!(allow_localhost: true) +end RSpec::Matchers.define_negated_matcher :not_have_enqueued_job, :have_enqueued_job diff --git a/spec/support/selenium_chrome_headless.rb b/spec/support/selenium_chrome_headless.rb index 7081b41491..d6faefaff5 100644 --- a/spec/support/selenium_chrome_headless.rb +++ b/spec/support/selenium_chrome_headless.rb @@ -18,5 +18,14 @@ opts.add_option('goog:loggingPrefs', {browser: 'ALL'}) end - Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options) + if ENV['DOCKER'] + Capybara::Selenium::Driver.new( + app, + browser: :remote, + url: 'http://chrome:4444/wd/hub', + options: browser_options + ) + else + Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options) + end end diff --git a/spec/support/webmock.rb b/spec/support/webmock.rb index ed4fe6d6aa..71c65b21d5 100644 --- a/spec/support/webmock.rb +++ b/spec/support/webmock.rb @@ -1,3 +1,7 @@ require 'webmock/rspec' -WebMock.disable_net_connect!(allow_localhost: true) +if ENV['DOCKER'] + WebMock.allow_net_connect! +else + WebMock.disable_net_connect!(allow_localhost: true) +end \ No newline at end of file