diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index 1e84be276c..4d1abc70dc 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -561,6 +561,22 @@ def dockerfile_binfile_fixups binfixups end + def dockerfile_base_packages + # Add curl to work with the default healthcheck strategy in Kamal + packages = ["curl"] + + # ActiveRecord databases + packages << base_package_for_database unless skip_active_record? + + # ActiveStorage preview support + packages << "libvips" unless skip_active_storage? + + # jemalloc for memory optimization + packages << "libjemalloc2" + + packages.compact.sort + end + def dockerfile_build_packages # start with the essentials packages = %w(build-essential git pkg-config) @@ -568,11 +584,6 @@ def dockerfile_build_packages # add database support packages << build_package_for_database unless skip_active_record? - # ActiveStorage preview support - packages << "libvips" unless skip_active_storage? - - packages << "curl" if using_js_runtime? - packages << "unzip" if using_bun? # node support, including support for building native modules @@ -585,22 +596,6 @@ def dockerfile_build_packages packages.compact.sort end - def dockerfile_deploy_packages - # Add curl to work with the default healthcheck strategy in Kamal - packages = ["curl"] - - # ActiveRecord databases - packages << deploy_package_for_database unless skip_active_record? - - # ActiveStorage preview support - packages << "libvips" unless skip_active_storage? - - # jemalloc for memory optimization - packages << "libjemalloc2" - - packages.compact.sort - end - def css_gemfile_entry return if options[:api] return unless options[:css] diff --git a/railties/lib/rails/generators/database.rb b/railties/lib/rails/generators/database.rb index 2236092317..86bf023021 100644 --- a/railties/lib/rails/generators/database.rb +++ b/railties/lib/rails/generators/database.rb @@ -27,6 +27,16 @@ def gem_for_database(database = options[:database]) end end + def docker_for_database_base(database = options[:database]) + case database + when "mysql" then "curl default-mysql-client libvips" + when "trilogy" then "curl libvips" + when "postgresql" then "curl libvips postgresql-client" + when "sqlite3" then "curl libsqlite3-0 libvips" + else nil + end + end + def docker_for_database_build(database = options[:database]) case database when "mysql" then "build-essential default-libmysqlclient-dev git" @@ -37,16 +47,6 @@ def docker_for_database_build(database = options[:database]) end end - def docker_for_database_deploy(database = options[:database]) - case database - when "mysql" then "curl default-mysql-client libvips" - when "trilogy" then "curl libvips" - when "postgresql" then "curl libvips postgresql-client" - when "sqlite3" then "curl libsqlite3-0 libvips" - else nil - end - end - def convert_database_option_for_jruby if defined?(JRUBY_VERSION) opt = options.dup @@ -59,15 +59,7 @@ def convert_database_option_for_jruby end end - def build_package_for_database(database = options[:database]) - case database - when "mysql" then "default-libmysqlclient-dev" - when "postgresql" then "libpq-dev" - else nil - end - end - - def deploy_package_for_database(database = options[:database]) + def base_package_for_database(database = options[:database]) case database when "mysql" then "default-mysql-client" when "postgresql" then "postgresql-client" @@ -76,6 +68,14 @@ def deploy_package_for_database(database = options[:database]) end end + def build_package_for_database(database = options[:database]) + case database + when "mysql" then "default-libmysqlclient-dev" + when "postgresql" then "libpq-dev" + else nil + end + end + private def mysql_socket @mysql_socket ||= [ diff --git a/railties/lib/rails/generators/rails/app/templates/Dockerfile.tt b/railties/lib/rails/generators/rails/app/templates/Dockerfile.tt index e9c897d043..7401793a7e 100644 --- a/railties/lib/rails/generators/rails/app/templates/Dockerfile.tt +++ b/railties/lib/rails/generators/rails/app/templates/Dockerfile.tt @@ -7,19 +7,21 @@ FROM registry.docker.com/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 <%= dockerfile_base_packages.join(" ") %> + # 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<%= using_node? ? " and node modules" : "" %> -RUN apt-get update -qq && \ - apt-get install --no-install-recommends -y <%= dockerfile_build_packages.join(" ") %> +RUN apt-get install --no-install-recommends -y <%= dockerfile_build_packages.join(" ") %> <% if using_node? -%> # Install JavaScript dependencies @@ -79,13 +81,9 @@ RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile # Final stage for app image FROM base -<% unless dockerfile_deploy_packages.empty? -%> -# Install packages needed for deployment -RUN apt-get update -qq && \ - apt-get install --no-install-recommends -y <%= dockerfile_deploy_packages.join(" ") %> && \ - rm -rf /var/lib/apt/lists /var/cache/apt/archives +# Clean up installation packages to reduce image size +RUN rm -rf /var/lib/apt/lists /var/cache/apt/archives -<% end -%> # Copy built artifacts: gems, application COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}" COPY --from=build /rails /rails diff --git a/railties/lib/rails/generators/rails/app/templates/github/ci.yml.tt b/railties/lib/rails/generators/rails/app/templates/github/ci.yml.tt index 08657f3744..32d4a1c123 100644 --- a/railties/lib/rails/generators/rails/app/templates/github/ci.yml.tt +++ b/railties/lib/rails/generators/rails/app/templates/github/ci.yml.tt @@ -99,7 +99,7 @@ jobs: <%- end -%> steps: - name: Install packages - run: sudo apt-get update && sudo apt-get install --no-install-recommends -y google-chrome-stable <%= (dockerfile_deploy_packages + [build_package_for_database]).join(" ") %> + run: sudo apt-get update && sudo apt-get install --no-install-recommends -y google-chrome-stable <%= (dockerfile_base_packages + [build_package_for_database]).join(" ") %> - name: Checkout code uses: actions/checkout@v4 diff --git a/railties/lib/rails/generators/rails/db/system/change/change_generator.rb b/railties/lib/rails/generators/rails/db/system/change/change_generator.rb index 5bfb56bef1..1619d5e047 100644 --- a/railties/lib/rails/generators/rails/db/system/change/change_generator.rb +++ b/railties/lib/rails/generators/rails/db/system/change/change_generator.rb @@ -44,14 +44,14 @@ def edit_dockerfile dockerfile_path = File.expand_path("Dockerfile", destination_root) return unless File.exist?(dockerfile_path) + base_name = docker_for_database_base build_name = docker_for_database_build - deploy_name = docker_for_database_deploy + if base_name + gsub_file("Dockerfile", all_docker_bases_regex, base_name) + end if build_name gsub_file("Dockerfile", all_docker_builds_regex, build_name) end - if deploy_name - gsub_file("Dockerfile", all_docker_deploys_regex, deploy_name) - end end private @@ -59,12 +59,12 @@ def all_database_gems DATABASES.map { |database| gem_for_database(database) } end - def all_docker_builds - DATABASES.map { |database| docker_for_database_build(database).nil? ? nil : docker_for_database_build(database) }.compact! + def all_docker_bases + DATABASES.map { |database| docker_for_database_base(database).nil? ? nil : docker_for_database_base(database) }.compact! end - def all_docker_deploys - DATABASES.map { |database| docker_for_database_deploy(database).nil? ? nil : docker_for_database_deploy(database) }.compact! + def all_docker_builds + DATABASES.map { |database| docker_for_database_build(database).nil? ? nil : docker_for_database_build(database) }.compact! end def all_database_gems_regex @@ -72,12 +72,12 @@ def all_database_gems_regex /(\b#{all_database_gem_names.join('\b|\b')}\b)/ end - def all_docker_builds_regex - /(\b#{all_docker_builds.join('\b|\b')}\b)/ + def all_docker_bases_regex + /(\b#{all_docker_bases.join('\b|\b')}\b)/ end - def all_docker_deploys_regex - /(\b#{all_docker_deploys.join('\b|\b')}\b)/ + def all_docker_builds_regex + /(\b#{all_docker_builds.join('\b|\b')}\b)/ end def gem_entry_regex_for(gem_name) diff --git a/railties/test/fixtures/Dockerfile.test b/railties/test/fixtures/Dockerfile.test index 731448f0b3..45112df40f 100644 --- a/railties/test/fixtures/Dockerfile.test +++ b/railties/test/fixtures/Dockerfile.test @@ -7,21 +7,21 @@ FROM registry.docker.com/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 libsqlite3-0 libvips + # 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 git libvips pkg-config - - +RUN apt-get install --no-install-recommends -y build-essential git pkg-config # Install application gems COPY Gemfile Gemfile.lock ./ @@ -29,8 +29,6 @@ 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 . . @@ -40,14 +38,11 @@ RUN bundle exec bootsnap precompile app/ lib/ # Precompiling assets for production without requiring secret RAILS_MASTER_KEY RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile - # Final stage for app image FROM base -# Install packages needed for deployment -RUN apt-get update -qq && \ - apt-get install --no-install-recommends -y curl libsqlite3-0 libvips && \ - rm -rf /var/lib/apt/lists /var/cache/apt/archives +# Clean up installation packages to reduce image size +RUN rm -rf /var/lib/apt/lists /var/cache/apt/archives # Copy built artifacts: gems, application COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}" diff --git a/railties/test/generators/db_system_change_generator_test.rb b/railties/test/generators/db_system_change_generator_test.rb index 77ca07fba1..aff01cdb96 100644 --- a/railties/test/generators/db_system_change_generator_test.rb +++ b/railties/test/generators/db_system_change_generator_test.rb @@ -85,7 +85,7 @@ class ChangeGeneratorTest < Rails::Generators::TestCase end assert_file("Dockerfile") do |content| - assert_match "build-essential git libvips", content + assert_match "build-essential git", content assert_match "curl libsqlite3-0 libvips", content end end