diff --git a/Dockerfile b/Dockerfile index b485668bf..881d1daf4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,61 +1,77 @@ -FROM ruby:2.7.8-buster +# syntax=docker/dockerfile:1 +# check=error=true -# Install basic Linux packages -RUN apt-get update -qq && apt-get install -y \ +# Make sure RUBY_VERSION matches the Ruby version in .ruby-version +ARG RUBY_VERSION=3.3.8 +FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base + +# Rails app lives here +WORKDIR /rails + +# Install base packages +RUN apt-get update -qq && \ + apt-get install --no-install-recommends -y \ + curl \ + default-mysql-client \ + libjemalloc2 \ + libvips && \ + rm -rf /var/lib/apt/lists /var/cache/apt/archives + +# Set production environment +ENV RAILS_ENV="production" \ + BUNDLE_DEPLOYMENT="1" \ + BUNDLE_PATH="/usr/local/bundle" \ + BUNDLE_WITHOUT="development" + +# Throw-away build stage to reduce size of final image +FROM base AS build + +# Install packages needed to build gems +RUN apt-get update -qq && \ + apt-get install --no-install-recommends -y \ build-essential \ - libpq-dev \ - nodejs \ - yarn \ + default-libmysqlclient-dev \ git \ - curl \ - imagemagick \ - libvips \ - tzdata \ - libxml2-dev \ - libxslt1-dev \ - libffi-dev \ - libreadline-dev \ - libssl-dev \ - zlib1g-dev \ - libsqlite3-dev \ - sqlite3 - -# Set working directory -WORKDIR /app - -# Set environment variables -ENV RAILS_ENV=production \ - RACK_ENV=production \ - BUNDLE_PATH=/gems \ - BUNDLE_BIN=/gems/bin \ - PATH="/gems/bin:$PATH" - -# Install bundler -RUN gem install bundler -v 2.4.12 - -# Copy app code and install dependencies + libyaml-dev \ + pkg-config && \ + rm -rf /var/lib/apt/lists /var/cache/apt/archives + +# Install application gems +COPY Gemfile Gemfile.lock ./ +RUN bundle install && \ + rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git +# && bundle exec bootsnap precompile --gemfile + +# Copy application code COPY . . -RUN bundle install --without development test - -# These envs are used in the rails application. While they are entirely -# unrelated to the docker build process, they are required for the app to run. -# Without these build args the asset precompilation will fail. -ARG SECRET_KEY_BASE -ARG AWS_ACCESS_KEY_ID -ARG AWS_SECRET_ACCESS_KEY -ARG AWS_REGION -ARG AWS_S3_BUCKET -ARG SMTP_USERNAME -ARG SMTP_PASSWORD -ARG SMTP_SERVER -ARG SMTP_PORT - -# Precompile assets (if applicable) -RUN bundle exec rake assets:precompile - -# Expose port (default Rails) -EXPOSE 3000 +# Precompile bootsnap code for faster boot times +# RUN bundle exec bootsnap precompile app/ lib/ + +# Precompiling assets for production without requiring secret RAILS_MASTER_KEY +RUN SECRET_KEY_BASE=dummy \ + SMTP_SERVER=dummy \ + SMTP_PORT=dummy \ + SMTP_USERNAME=dummy \ + SMTP_PASSWORD=dummy \ + ./bin/rails assets:precompile + +# Final stage for app image +FROM base -# Start the server (customize to your app server if needed) -CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"] \ No newline at end of file +# Copy built artifacts: gems, application +COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}" +COPY --from=build /rails /rails + +# Run and own only the runtime files as a non-root user for security +RUN groupadd --system --gid 1000 rails && \ + useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \ + chown -R rails:rails db log tmp +USER 1000:1000 + +# Entrypoint prepares the database. +ENTRYPOINT ["/rails/bin/docker-entrypoint"] + +# Start server via Thruster by default, this can be overwritten at runtime +EXPOSE 3000 +CMD ["./bin/rails", "server"] diff --git a/bin/docker-entrypoint b/bin/docker-entrypoint new file mode 100755 index 000000000..57567d69b --- /dev/null +++ b/bin/docker-entrypoint @@ -0,0 +1,14 @@ +#!/bin/bash -e + +# Enable jemalloc for reduced memory usage and latency. +if [ -z "${LD_PRELOAD+x}" ]; then + LD_PRELOAD=$(find /usr/lib -name libjemalloc.so.2 -print -quit) + export LD_PRELOAD +fi + +# If running the rails server then create or migrate existing database +if [ "${@: -2:1}" == "./bin/rails" ] && [ "${@: -1:1}" == "server" ]; then + ./bin/rails db:prepare +fi + +exec "${@}" diff --git a/config/database.yml b/config/database.yml index 26b0116c0..452bca390 100644 --- a/config/database.yml +++ b/config/database.yml @@ -1,18 +1,26 @@ -development: - host: 127.0.0.1 +default: &default + host: <%= ENV.fetch('DB_HOST', 'localhost') %> username: root + port: 3306 adapter: mysql2 encoding: utf8 - pool: 5 + pool: 100 + +development: + <<: *default database: awbw_development # Warning: The database defined as "test" will be erased and # re-generated from your development database when you run "rake". # Do not set this db to the same as development or production. test: - host: 127.0.0.1 - username: root - adapter: mysql2 - encoding: utf8 - pool: 5 + <<: *default database: awbw_test + +production: + <<: *default + database: awbw_production + +staging: + <<: *default + database: awbw_staging diff --git a/db/seeds.rb b/db/seeds.rb index 05b34bf54..1da508d27 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,25 +1,13 @@ -# Resolve the correct DB credentials -config = YAML.load(ERB.new(File.read(Rails.root.join('config/database.yml'))).result) -env_config = config[Rails.env] - -# Extract credentials -adapter = env_config['adapter'] -database = env_config['database'] - # Only run if MySQL is the DB -if adapter.include?('mysql') - sql_file = Rails.root.join('db', 'awbw_dml_only.sql') +sql_file = Rails.root.join('db', 'awbw_dml_only.sql') - puts "Loading SQL dump from #{sql_file}..." +puts "Loading SQL dump from #{sql_file}..." - # Run the command - command = "rails dbconsole < #{sql_file}" - system(command) || raise("Failed to load SQL dump into #{database}") - - puts "SUCCESS! SQL dump loaded successfully." -else - puts "Skipping SQL dump: not using MySQL (adapter = #{adapter})" -end +# Run the command +command = "rails dbconsole < #{sql_file}" +system(command) || raise("Failed to load SQL dump") + +puts "SUCCESS! SQL dump loaded successfully." # wrapping in a tx for now ActiveRecord::Base.transaction do diff --git a/docker-compose.yml b/docker-compose.yml index c89990de5..8d6f0b36d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ services: ports: - 3306:3306 expose: - - '3306' + - "3306" volumes: - type: volume source: mysql-data @@ -24,7 +24,8 @@ services: web: build: context: . - dockerfile: Dockerfile.dev # <— use the dev Dockerfile + dockerfile: Dockerfile + target: build depends_on: database: condition: service_healthy @@ -32,7 +33,7 @@ services: environment: # Fallback so Rails can boot even if .env is minimal. # If you prefer traditional keys, set DB_* in .env and configure database.yml accordingly. - DATABASE_URL: mysql2://root@database:3306/awbw_development + DB_HOST: "database" RAILS_ENV: development RAILS_LOG_TO_STDOUT: "true" ports: @@ -48,4 +49,3 @@ services: volumes: mysql-data: -