lib/generators/dockerfile_generator.rb in dockerfile-rails-1.0.18 vs lib/generators/dockerfile_generator.rb in dockerfile-rails-1.1.0

- old
+ new

@@ -1,192 +1,242 @@ -require 'erb' -require_relative '../dockerfile-rails/scanner.rb' +# frozen_string_literal: true +require "erb" +require_relative "../dockerfile-rails/scanner.rb" + class DockerfileGenerator < Rails::Generators::Base include DockerfileRails::Scanner - OPTION_DEFAULTS = OpenStruct.new( - 'bin-cd' => false, - 'cache' => false, - 'ci' => false, - 'compose' => false, - 'fullstaq' => false, - 'jemalloc' => false, - 'label' => {}, - 'mysql' => false, - 'nginx' => false, - 'parallel' => false, - 'platform' => nil, - 'postgresql' => false, - 'precompile' => nil, - 'prepare' => true, - 'redis' => false, - 'root' => false, - 'swap' => nil, - 'yjit' => false, - ) + OPTION_DEFAULTS = { + "bin-cd" => false, + "cache" => false, + "ci" => false, + "compose" => false, + "fullstaq" => false, + "jemalloc" => false, + "label" => {}, + "mysql" => false, + "nginx" => false, + "parallel" => false, + "platform" => nil, + "postgresql" => false, + "precompile" => nil, + "prepare" => true, + "redis" => false, + "root" => false, + "sqlite3" => false, + "swap" => nil, + "yjit" => false, + }.then { |hash| Struct.new(*hash.keys.map(&:to_sym)).new(*hash.values) } @@labels = {} - @@packages = {"base" => [], "build" => [], "deploy" => []} + @@packages = { "base" => [], "build" => [], "deploy" => [] } + @@vars = { "base" => {}, "build" => {}, "deploy" => {} } + @@args = { "base" => {}, "build" => {}, "deploy" => {} } # load defaults from config file - if File.exist? 'config/dockerfile.yml' - options = YAML.safe_load(IO.read('config/dockerfile.yml'), symbolize_names: true)[:options] + if File.exist? "config/dockerfile.yml" + options = YAML.safe_load(IO.read("config/dockerfile.yml"), symbolize_names: true)[:options] if options OPTION_DEFAULTS.to_h.each do |option, value| - OPTION_DEFAULTS[option] = options[option] if options.include? option + OPTION_DEFAULTS[option] = options[option] if options.include? option end if options[:packages] options[:packages].each do |stage, list| @@packages[stage.to_s] = list end end + if options[:envs] + options[:envs].each do |stage, vars| + @@vars[stage.to_s] = vars.stringify_keys + end + end + + if options[:args] + options[:args].each do |stage, vars| + @@args[stage.to_s] = vars.stringify_keys + end + end + @@labels = options[:label].stringify_keys if options.include? :label end end class_option :ci, type: :boolean, default: OPTION_DEFAULTS.ci, - desc: 'include test gems in bundle' + desc: "include test gems in bundle" class_option :precompile, type: :string, default: OPTION_DEFAULTS.precompile, desc: 'if set to "defer", assets:precompile will be done at deploy time' - class_option 'bin-cd', type: :boolean, default: OPTION_DEFAULTS['bin-cd'], - desc: 'modify binstubs to set working directory' + class_option "bin-cd", type: :boolean, default: OPTION_DEFAULTS["bin-cd"], + desc: "modify binstubs to set working directory" class_option :cache, type: :boolean, default: OPTION_DEFAULTS.cache, - desc: 'use build cache to speed up installs' + desc: "use build cache to speed up installs" class_option :prepare, type: :boolean, default: OPTION_DEFAULTS.prepare, - desc: 'include db:prepare step' + desc: "include db:prepare step" class_option :parallel, type: :boolean, default: OPTION_DEFAULTS.parallel, - desc: 'use build stages to install gems and node modules in parallel' + desc: "use build stages to install gems and node modules in parallel" class_option :swap, type: :string, default: OPTION_DEFAULTS.swap, - desc: 'allocate swapspace' + desc: "allocate swapspace" class_option :compose, type: :boolean, default: OPTION_DEFAULTS.compose, - desc: 'generate a docker-compose.yml file' + desc: "generate a docker-compose.yml file" class_option :redis, type: :boolean, default: OPTION_DEFAULTS.redis, - desc: 'include redis libraries' + desc: "include redis libraries" - class_option :sqlite3, aliases: '--sqlite', type: :boolean, default: OPTION_DEFAULTS.sqlite3, - desc: 'include sqlite3 libraries' + class_option :sqlite3, aliases: "--sqlite", type: :boolean, default: OPTION_DEFAULTS.sqlite3, + desc: "include sqlite3 libraries" - class_option :postgresql, aliases: '--postgres', type: :boolean, default: OPTION_DEFAULTS.postgresql, - desc: 'include postgresql libraries' + class_option :postgresql, aliases: "--postgres", type: :boolean, default: OPTION_DEFAULTS.postgresql, + desc: "include postgresql libraries" class_option :mysql, type: :boolean, default: OPTION_DEFAULTS.mysql, - desc: 'include mysql libraries' + desc: "include mysql libraries" class_option :platform, type: :string, default: OPTION_DEFAULTS.platform, - desc: 'image platform (example: linux/arm64)' + desc: "image platform (example: linux/arm64)" class_option :jemalloc, type: :boolean, default: OPTION_DEFAULTS.jemalloc, - desc: 'use jemalloc alternative malloc implementation' - + desc: "use jemalloc alternative malloc implementation" + class_option :fullstaq, type: :boolean, default: OPTION_DEFAULTS.fullstaq, - descr: 'use Fullstaq Ruby image from Quay.io' + descr: "use Fullstaq Ruby image from Quay.io" class_option :yjit, type: :boolean, default: OPTION_DEFAULTS.yjit, - desc: 'enable YJIT optimizing compiler' + desc: "enable YJIT optimizing compiler" class_option :label, type: :hash, default: {}, - desc: 'Add Docker label(s)' + desc: "Add Docker label(s)" class_option :nginx, type: :boolean, default: OPTION_DEFAULTS.nginx, - desc: 'Serve static files with nginx' + desc: "Serve static files with nginx" class_option :root, type: :boolean, default: OPTION_DEFAULTS.root, - desc: 'Run application as root user' + desc: "Run application as root user" - class_option 'add-base', type: :array, default: [], - desc: 'additional packages to install for both build and deploy' - class_option 'add-build', type: :array, default: [], - desc: 'additional packages to install for use during build' + class_option "add-base", type: :array, default: [], + desc: "additional packages to install for both build and deploy" - class_option 'add-deploy', aliases: '--add', type: :array, default: [], - desc: 'additional packages to install for deployment' + class_option "add-build", type: :array, default: [], + desc: "additional packages to install for use during build" - class_option 'remove-base', type: :array, default: [], - desc: 'remove from list of base packages' + class_option "add-deploy", aliases: "--add", type: :array, default: [], + desc: "additional packages to install for deployment" - class_option 'remove-build', type: :array, default: [], - desc: 'remove from list of build packages' + class_option "remove-base", type: :array, default: [], + desc: "remove from list of base packages" - class_option 'remove-deploy', aliases: '--remove', type: :array, default: [], - desc: 'remove from list of deploy packages' + class_option "remove-build", type: :array, default: [], + desc: "remove from list of build packages" + class_option "remove-deploy", aliases: "--remove", type: :array, default: [], + desc: "remove from list of deploy packages" + + + class_option "env-base", type: :hash, default: {}, + desc: "additional environment variables for both build and deploy" + + class_option "env-build", type: :hash, default: {}, + desc: "additional environment variables to set during build" + + class_option "env-deploy", aliases: "--env", type: :hash, default: {}, + desc: "additional environment variables to set for deployment" + + + class_option "arg-base", aliases: "--arg", type: :hash, default: {}, + desc: "additional build arguments for both build and deploy" + + class_option "arg-build", type: :hash, default: {}, + desc: "additional build arguments to set during build" + + class_option "arg-deploy", type: :hash, default: {}, + desc: "additional build arguments to set for deployment" + + def generate_app - source_paths.push File.expand_path('./templates', __dir__) + source_paths.push File.expand_path("./templates", __dir__) # merge options - options.label.replace(@@labels.merge(options.label).select {|key, value| value != ''}) + options.label.replace(@@labels.merge(options.label).select { |key, value| value != "" }) # gather up options for config file @dockerfile_config = OPTION_DEFAULTS.dup.to_h.stringify_keys options.to_h.each do |option, value| @dockerfile_config[option] = value if @dockerfile_config.include? option end - # apply requested package changes %w(base build deploy).each do |phase| @@packages[phase] += options["add-#{phase}"] @@packages[phase] -= options["remove-#{phase}"] @@packages[phase].uniq! @@packages.delete phase if @@packages[phase].empty? + + @@vars[phase].merge! options["env-#{phase}"] + @@vars[phase].delete_if { |key, value| value.blank? } + @@vars.delete phase if @@vars[phase].empty? + + @@args[phase].merge! options["arg-#{phase}"] + @@args[phase].delete_if { |key, value| value.blank? } + @@args.delete phase if @@args[phase].empty? end - @dockerfile_config['packages'] = @@packages + @dockerfile_config["packages"] = @@packages + @dockerfile_config["envs"] = @@vars + @dockerfile_config["args"] = @@args scan_rails_app Bundler.with_original_env { install_gems } - template 'Dockerfile.erb', 'Dockerfile' - template 'dockerignore.erb', '.dockerignore' + template "Dockerfile.erb", "Dockerfile" + template "dockerignore.erb", ".dockerignore" - if using_node? and node_version =~ /\A\d+\.\d+\.\d+\z/ - template 'node-version.erb', '.node-version' + if using_node? && node_version =~ (/\A\d+\.\d+\.\d+\z/) + template "node-version.erb", ".node-version" end - template 'docker-entrypoint.erb', 'bin/docker-entrypoint' + template "docker-entrypoint.erb", "bin/docker-entrypoint" chmod "bin/docker-entrypoint", 0755 & ~File.umask, verbose: false - template 'docker-compose.yml.erb', 'docker-compose.yml' if options.compose + template "docker-compose.yml.erb", "docker-compose.yml" if options.compose - template 'dockerfile.yml.erb', 'config/dockerfile.yml', force: true + template "dockerfile.yml.erb", "config/dockerfile.yml", force: true - if @gemfile.include?('vite_ruby') - package = JSON.load_file('package.json') - unless package.dig('scripts', 'build') - package['scripts'] ||= {} - package['scripts']['build'] = 'vite build --outDir public' + if @gemfile.include?("vite_ruby") + package = JSON.load_file("package.json") + unless package.dig("scripts", "build") + package["scripts"] ||= {} + package["scripts"]["build"] = "vite build --outDir public" - say_status :update, 'package.json' - IO.write('package.json', JSON.pretty_generate(package)) + say_status :update, "package.json" + IO.write("package.json", JSON.pretty_generate(package)) end end end private - def render(options) scope = (Class.new do def initialize(obj, locals) @_obj = obj - @_locals = OpenStruct.new(locals) + @_locals = locals.then do |hash| + return nil if hash.empty? + Struct.new(*hash.keys.map(&:to_sym)).new(*hash.values) + end end def method_missing(method, *args, &block) - if @_locals.respond_to? method + if @_locals&.respond_to? method @_locals.send method, *args, &block else @_obj.send method, *args, &block end end @@ -195,11 +245,11 @@ binding end end).new(self, options[:locals] || {}) template = IO.read(File.join(source_paths.last, "_#{options[:partial]}.erb")) - ERB.new(template, trim_mode: '-').result(scope.get_binding).strip + ERB.new(template, trim_mode: "-").result(scope.get_binding).strip end def platform if options.platform "--platform #{options.platform} " @@ -207,60 +257,60 @@ "" end end def run_as_root? - true # options.root? || options.nginx? + options.root? || options.nginx? # needed to access /dev/stdout end def using_node? return @using_node if @using_node != nil - @using_node = File.exist? 'package.json' + @using_node = File.exist? "package.json" end def using_redis? - options.redis? or @redis or @gemfile.include?('sidekiq') + options.redis? or @redis or @gemfile.include?("sidekiq") end def using_execjs? - @gemfile.include?('execjs') or @gemfile.include?('grover') + @gemfile.include?("execjs") or @gemfile.include?("grover") end def using_puppeteer? - @gemfile.include?('grover') or @gemfile.include?('puppeteer-ruby') + @gemfile.include?("grover") or @gemfile.include?("puppeteer-ruby") end def using_sidekiq? - @gemfile.include?('sidekiq') + @gemfile.include?("sidekiq") end def parallel? using_node? && options.parallel end def keeps? return @keeps if @keeps != nil - @keeps = !!Dir['**/.keep'] + @keeps = !!Dir["**/.keep"] end def install_gems - if options.postgresql? or @postgresql - system "bundle add pg" unless @gemfile.include? 'pg' + if options.postgresql? || @postgresql + system "bundle add pg" unless @gemfile.include? "pg" end - if options.mysql? or @mysql - system "bundle add mysql2" unless @gemfile.include? 'mysql2' + if options.mysql? || @mysql + system "bundle add mysql2" unless @gemfile.include? "mysql2" end - if options.redis? or using_redis? - system "bundle add redis" unless @gemfile.include? 'redis' + if options.redis? || using_redis? + system "bundle add redis" unless @gemfile.include? "redis" end end def base_packages packages = [] - packages += @@packages['base'] if @@packages['base'] + packages += @@packages["base"] if @@packages["base"] if using_execjs? packages += %w(curl unzip) end @@ -268,55 +318,55 @@ packages += %w(curl gnupg) end # charlock_holmes. Placed here as the library itself is # libicu63 in buster, libicu67 in bullseye, libiclu72 in bookworm... - packages << "libicu-dev" if @gemfile.include? 'charlock_holmes' + packages << "libicu-dev" if @gemfile.include? "charlock_holmes" - if @gemfile.include? 'webp-ffi' + if @gemfile.include? "webp-ffi" # https://github.com/le0pard/webp-ffi#requirements packages += %w(libjpeg-dev libpng-dev libtiff-dev libwebp-dev) end packages.sort.uniq end def base_requirements requirements = [] - requirements << 'nodejs' if using_execjs? - requirements << 'chrome' if using_puppeteer? - requirements << "charlock_holmes" if @gemfile.include? 'charlock_holmes' - requirements.join(' and ') + requirements << "nodejs" if using_execjs? + requirements << "chrome" if using_puppeteer? + requirements << "charlock_holmes" if @gemfile.include? "charlock_holmes" + requirements.join(" and ") end def build_packages # start with the essentials packages = %w(build-essential) - packages += @@packages['build'] if @@packages['build'] + packages += @@packages["build"] if @@packages["build"] # add databases: sqlite3, postgres, mysql - packages << 'pkg-config' if options.sqlite3? or @sqlite3 - packages << 'libpq-dev' if options.postgresql? or @postgresql - packages << 'default-libmysqlclient-dev' if options.mysql? or @mysql + packages << "pkg-config" if options.sqlite3? || @sqlite3 + packages << "libpq-dev" if options.postgresql? || @postgresql + packages << "default-libmysqlclient-dev" if options.mysql? || @mysql # add git if needed to install gems - packages << 'git' if @git + packages << "git" if @git # add redis if Action Cable, caching, or sidekiq are used - packages << "redis" if options.redis? or using_redis? + packages << "redis" if options.redis? || using_redis? # ActiveStorage preview support - packages << "libvips" if @gemfile.include? 'ruby-vips' + packages << "libvips" if @gemfile.include? "ruby-vips" # Rmagick gem - packages += %w[pkg-config libmagickwand-dev] if @gemfile.include? 'rmagick' + packages += %w[pkg-config libmagickwand-dev] if @gemfile.include? "rmagick" # node support, including support for building native modules if using_node? packages += %w(node-gyp pkg-config) - packages += %w(curl unzip) unless using_execjs? or using_puppeteer? + packages += %w(curl unzip) unless using_execjs? || using_puppeteer? # module build process depends on Python, and debian changed # how python is installed with the bullseye release. Below # is based on debian release included with the Ruby images on # Dockerhub. @@ -341,33 +391,33 @@ packages.sort.uniq end def deploy_packages packages = [] - packages += @@packages['deploy'] if @@packages['deploy'] + packages += @@packages["deploy"] if @@packages["deploy"] # start with databases: sqlite3, postgres, mysql - packages << 'libsqlite3-0' if options.sqlite3? or @sqlite3 - packages << 'postgresql-client' if options.postgresql? or @postgresql - packages << 'default-mysql-client' if options.mysql or @mysql + packages << "libsqlite3-0" if options.sqlite3? || @sqlite3 + packages << "postgresql-client" if options.postgresql? || @postgresql + packages << "default-mysql-client" if options.mysql || @mysql # add redis in case Action Cable, caching, or sidekiq are added later packages << "redis" if using_redis? # ActiveStorage preview support - packages << "libvips" if @gemfile.include? 'ruby-vips' + packages << "libvips" if @gemfile.include? "ruby-vips" # Rmagick gem - if @gemfile.include?('rmagick') or @gemfile.include?('mini_magick') - packages << 'imagemagick' + if @gemfile.include?("rmagick") || @gemfile.include?("mini_magick") + packages << "imagemagick" end # Puppeteer - packages << 'google-chrome-stable' if using_puppeteer? + packages << "google-chrome-stable" if using_puppeteer? # nginx - packages << 'nginx' if options.nginx? + packages << "nginx" if options.nginx? packages.sort end def deploy_repos @@ -380,154 +430,230 @@ 'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' ] end if repos.empty? - '' + "" else repos.join(" \\\n ") + " && \\\n " end end + def base_env + env = { + "RAILS_ENV" => "production", + "BUNDLE_PATH" => "vendor/bundle", + "BUNDLE_WITHOUT" => options.ci? ? "development" : "development:test" + } + + if @@args["base"] + env.merge! @@args["base"].to_h { |key, value| [key, "$#{key}"] } + end + + env.merge! @@vars["base"] if @@vars["base"] + + env.map { |key, value| "#{key}=#{value.inspect}" } + end + + def build_env + env = {} + + if @@args["build"] + env.merge! @@args["build"].to_h { |key, value| [key, "$#{key}"] } + end + + env.merge! @@vars["build"] if @@vars["build"] + + env.map { |key, value| "#{key}=#{value.inspect}" } + end + def deploy_env - env = [] + env = {} - env << 'PORT="3001"' if options.nginx? + env["PORT"] = "3001" if options.nginx? - if Rails::VERSION::MAJOR<7 || Rails::VERSION::STRING.start_with?('7.0') - env << 'RAILS_LOG_TO_STDOUT="1"' - env << 'RAILS_SERVE_STATIC_FILES="true"' unless options.nginx? + if Rails::VERSION::MAJOR < 7 || Rails::VERSION::STRING.start_with?("7.0") + env["RAILS_LOG_TO_STDOUT"] = "1" + env["RAILS_SERVE_STATIC_FILES"] = "true" unless options.nginx? end if options.yjit? - env << 'RUBY_YJIT_ENABLE="1"' + env["RUBY_YJIT_ENABLE"] = "1" end - if options.jemalloc? and not options.fullstaq? - if (options.platform || Gem::Platform::local.cpu).include? 'arm' - env << 'LD_PRELOAD="/usr/lib/aarch64-linux-gnu/libjemalloc.so.2"' + if options.jemalloc? && (not options.fullstaq?) + if (options.platform || Gem::Platform.local.cpu).include? "arm" + env["LD_PRELOAD"] = "/usr/lib/aarch64-linux-gnu/libjemalloc.so.2" else - env << 'LD_PRELOAD="/usr/lib/x86_64-linux-gnu/libjemalloc.so.2"' + env["LD_PRELOAD"] = "/usr/lib/x86_64-linux-gnu/libjemalloc.so.2" end - env << 'MALLOC_CONF="dirty_decay_ms:1000,narenas:2,background_thread:true"' + env["MALLOC_CONF"] = "dirty_decay_ms:1000,narenas:2,background_thread:true" end if using_puppeteer? - env << 'GROVER_NO_SANDBOX="true"' if @gemfile.include? 'grover' - env << 'PUPPETEER_RUBY_NO_SANDBOX="1"' if @gemfile.include? 'puppeteer-ruby' - env << 'PUPPETEER_EXECUTABLE_PATH="/usr/bin/google-chrome"' + if options.root? + env["GROVER_NO_SANDBOX"] = "true" if @gemfile.include? "grover" + env["PUPPETEER_RUBY_NO_SANDBOX"] = "1" if @gemfile.include? "puppeteer-ruby" + end + + env["PUPPETEER_EXECUTABLE_PATH"] = "/usr/bin/google-chrome" end - env + if @@args["deploy"] + env.merge! @@args["deploy"].to_h { |key, value| [key, "$#{key}"] } + end + + env.merge! @@vars["base"] if @@vars["base"] + + env.map { |key, value| "#{key}=#{value.inspect}" } end + def base_args + args = {} + + args.merge! @@args["base"] if @@args["base"] + + args + end + + def build_args + args = {} + + args.merge! @@args["build"] if @@args["build"] + + args + end + + def deploy_args + args = {} + + args.merge! @@args["deploy"] if @@args["deploy"] + + args + end + + def all_args + args = {} + + unless options.root? + args[:UID] = "${UID:-1000}".html_safe + args[:GID] = "${GID:-${UID:-1000}}".html_safe + end + + args.merge! base_args + args.merge! build_args + args.merge! deploy_args + + args + end + def binfile_fixups # binfiles may have OS specific paths to ruby. Normalize them. shebangs = Dir["bin/*"].map { |file| IO.read(file).lines.first }.join rubies = shebangs.scan(%r{#!/usr/bin/env (ruby.*)}).flatten.uniq - + binfixups = (rubies - %w(ruby)).map do |ruby| "sed -i 's/#{Regexp.quote(ruby)}$/ruby/' bin/*" end - + # Windows line endings will cause scripts to fail. If any - # or found OR this generation is run on a windows platform - # and there are other binfixups required, then convert + # or found OR this generation is run on a windows platform + # and there are other binfixups required, then convert # line endings. This avoids adding unnecessary fixups if # none are required, but prepares for the need to do the # fix line endings if other fixups are required. has_cr = Dir["bin/*"].any? { |file| IO.read(file).include? "\r" } if has_cr || (Gem.win_platform? && !binfixups.empty?) binfixups.unshift 'sed -i "s/\r$//g" bin/*' end - + # Windows file systems may not have the concept of executable. # In such cases, fix up during the build. unless Dir["bin/*"].all? { |file| File.executable? file } binfixups.unshift "chmod +x bin/*" end # optionally, adjust cwd - if options['bin-cd'] + if options["bin-cd"] binfixups.push %{grep -l '#!/usr/bin/env ruby' /rails/bin/* | xargs sed -i '/^#!/aDir.chdir File.expand_path("..", __dir__)'} end binfixups end def deploy_database - if options.postgresql? or @postgresql - 'postgresql' - elsif options.mysql or @mysql - 'mysql' + if options.postgresql? || @postgresql + "postgresql" + elsif options.mysql || @mysql + "mysql" else - 'sqlite3' + "sqlite3" end end def node_version version = nil - if File.exist? '.node-version' - version = IO.read('.node-version')[/\d+\.\d+\.\d+/] + if File.exist? ".node-version" + version = IO.read(".node-version")[/\d+\.\d+\.\d+/] end - if !version and File.exist? 'package.json' - version = JSON.parse(IO.read('package.json')).dig("engines", "node") - version = nil unless version =~ /\A(\d+\.)+(\d+|x)\z/ + if !version && File.exist?("package.json") + version = JSON.parse(IO.read("package.json")).dig("engines", "node") + version = nil unless /\A(\d+\.)+(\d+|x)\z/.match?(version) end version || `node --version`[/\d+\.\d+\.\d+/] rescue - "lts" + "lts" end def yarn_version - package = JSON.parse(IO.read('package.json')) + package = JSON.parse(IO.read("package.json")) - if ENV['RAILS_ENV'] == 'test' + if ENV["RAILS_ENV"] == "test" # yarn install instructions changed in v2 - version = '1.22.19' - elsif package['packageManager'].to_s.start_with? "yarn@" - version = package['packageManager'].sub('yarn@', '') + version = "1.22.19" + elsif package["packageManager"].to_s.start_with? "yarn@" + version = package["packageManager"].sub("yarn@", "") else - version = `yarn --version`[/\d+\.\d+\.\d+/] || '1.22.19' + version = `yarn --version`[/\d+\.\d+\.\d+/] || "1.22.19" system "yarn set version #{version}" - package = JSON.parse(IO.read('package.json')) + package = JSON.parse(IO.read("package.json")) # apparently not all versions of yarn will update package.json... end - unless package['packageManager'] - package['packageManager'] = "yarn@#{version}" - IO.write('package.json', JSON.pretty_generate(package)) + unless package["packageManager"] + package["packageManager"] = "yarn@#{version}" + IO.write("package.json", JSON.pretty_generate(package)) end version rescue "latest" end def depend_on_bootsnap? - @gemfile.include? 'bootsnap' + @gemfile.include? "bootsnap" end def api_only? Rails.application.config.api_only end # scan for node clients. Do a wide scan if api_only?, otherwise look # for specific directories. def api_client_dir if api_only? - scan = '*/package.json' + scan = "*/package.json" else - scan = '{client,frontend}/package.json' + scan = "{client,frontend}/package.json" end file = Dir[scan].find do |file| - JSON.load_file(file).dig('scripts', 'build') + JSON.load_file(file).dig("scripts", "build") end file && File.dirname(file) end @@ -538,35 +664,37 @@ Dir["#{client}/{package.json,package-lock.json,yarn.lock}"] end def dbprep_command if Rails::VERSION::MAJOR >= 6 - 'db:prepare' + "db:prepare" else - 'db:migrate' + "db:migrate" end end def procfile if options.nginx? { nginx: 'nginx -g "daemon off;"', - rails: './bin/rails server -p 3001' + rails: "./bin/rails server -p 3001" } else { - rails: './bin/rails server' + rails: "./bin/rails server" } end end def more_docker_ignores - more = '' + more = "" - if @gemfile.include?('vite_ruby') - lines = IO.read('.gitignore')[/^# Vite.*?\n\n/m].to_s.chomp.lines - + if @gemfile.include?("vite_ruby") + lines = IO.read(".gitignore")[/^# Vite.*?\n\n/m].to_s.chomp.lines - ["node_modules\n"] more += "\n" + lines.join end + + more end end