dockerfile-rails 1.0.18 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,40 +1,45 @@
1
- require 'erb'
2
- require_relative '../dockerfile-rails/scanner.rb'
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+ require_relative "../dockerfile-rails/scanner.rb"
3
5
 
4
6
  class DockerfileGenerator < Rails::Generators::Base
5
7
  include DockerfileRails::Scanner
6
8
 
7
- OPTION_DEFAULTS = OpenStruct.new(
8
- 'bin-cd' => false,
9
- 'cache' => false,
10
- 'ci' => false,
11
- 'compose' => false,
12
- 'fullstaq' => false,
13
- 'jemalloc' => false,
14
- 'label' => {},
15
- 'mysql' => false,
16
- 'nginx' => false,
17
- 'parallel' => false,
18
- 'platform' => nil,
19
- 'postgresql' => false,
20
- 'precompile' => nil,
21
- 'prepare' => true,
22
- 'redis' => false,
23
- 'root' => false,
24
- 'swap' => nil,
25
- 'yjit' => false,
26
- )
9
+ OPTION_DEFAULTS = {
10
+ "bin-cd" => false,
11
+ "cache" => false,
12
+ "ci" => false,
13
+ "compose" => false,
14
+ "fullstaq" => false,
15
+ "jemalloc" => false,
16
+ "label" => {},
17
+ "mysql" => false,
18
+ "nginx" => false,
19
+ "parallel" => false,
20
+ "platform" => nil,
21
+ "postgresql" => false,
22
+ "precompile" => nil,
23
+ "prepare" => true,
24
+ "redis" => false,
25
+ "root" => false,
26
+ "sqlite3" => false,
27
+ "swap" => nil,
28
+ "yjit" => false,
29
+ }.then { |hash| Struct.new(*hash.keys.map(&:to_sym)).new(*hash.values) }
27
30
 
28
31
  @@labels = {}
29
- @@packages = {"base" => [], "build" => [], "deploy" => []}
32
+ @@packages = { "base" => [], "build" => [], "deploy" => [] }
33
+ @@vars = { "base" => {}, "build" => {}, "deploy" => {} }
34
+ @@args = { "base" => {}, "build" => {}, "deploy" => {} }
30
35
 
31
36
  # load defaults from config file
32
- if File.exist? 'config/dockerfile.yml'
33
- options = YAML.safe_load(IO.read('config/dockerfile.yml'), symbolize_names: true)[:options]
37
+ if File.exist? "config/dockerfile.yml"
38
+ options = YAML.safe_load(IO.read("config/dockerfile.yml"), symbolize_names: true)[:options]
34
39
 
35
40
  if options
36
41
  OPTION_DEFAULTS.to_h.each do |option, value|
37
- OPTION_DEFAULTS[option] = options[option] if options.include? option
42
+ OPTION_DEFAULTS[option] = options[option] if options.include? option
38
43
  end
39
44
 
40
45
  if options[:packages]
@@ -43,90 +48,124 @@ class DockerfileGenerator < Rails::Generators::Base
43
48
  end
44
49
  end
45
50
 
51
+ if options[:envs]
52
+ options[:envs].each do |stage, vars|
53
+ @@vars[stage.to_s] = vars.stringify_keys
54
+ end
55
+ end
56
+
57
+ if options[:args]
58
+ options[:args].each do |stage, vars|
59
+ @@args[stage.to_s] = vars.stringify_keys
60
+ end
61
+ end
62
+
46
63
  @@labels = options[:label].stringify_keys if options.include? :label
47
64
  end
48
65
  end
49
66
 
50
67
  class_option :ci, type: :boolean, default: OPTION_DEFAULTS.ci,
51
- desc: 'include test gems in bundle'
68
+ desc: "include test gems in bundle"
52
69
 
53
70
  class_option :precompile, type: :string, default: OPTION_DEFAULTS.precompile,
54
71
  desc: 'if set to "defer", assets:precompile will be done at deploy time'
55
72
 
56
- class_option 'bin-cd', type: :boolean, default: OPTION_DEFAULTS['bin-cd'],
57
- desc: 'modify binstubs to set working directory'
73
+ class_option "bin-cd", type: :boolean, default: OPTION_DEFAULTS["bin-cd"],
74
+ desc: "modify binstubs to set working directory"
58
75
 
59
76
  class_option :cache, type: :boolean, default: OPTION_DEFAULTS.cache,
60
- desc: 'use build cache to speed up installs'
77
+ desc: "use build cache to speed up installs"
61
78
 
62
79
  class_option :prepare, type: :boolean, default: OPTION_DEFAULTS.prepare,
63
- desc: 'include db:prepare step'
80
+ desc: "include db:prepare step"
64
81
 
65
82
  class_option :parallel, type: :boolean, default: OPTION_DEFAULTS.parallel,
66
- desc: 'use build stages to install gems and node modules in parallel'
83
+ desc: "use build stages to install gems and node modules in parallel"
67
84
 
68
85
  class_option :swap, type: :string, default: OPTION_DEFAULTS.swap,
69
- desc: 'allocate swapspace'
86
+ desc: "allocate swapspace"
70
87
 
71
88
  class_option :compose, type: :boolean, default: OPTION_DEFAULTS.compose,
72
- desc: 'generate a docker-compose.yml file'
89
+ desc: "generate a docker-compose.yml file"
73
90
 
74
91
  class_option :redis, type: :boolean, default: OPTION_DEFAULTS.redis,
75
- desc: 'include redis libraries'
92
+ desc: "include redis libraries"
76
93
 
77
- class_option :sqlite3, aliases: '--sqlite', type: :boolean, default: OPTION_DEFAULTS.sqlite3,
78
- desc: 'include sqlite3 libraries'
94
+ class_option :sqlite3, aliases: "--sqlite", type: :boolean, default: OPTION_DEFAULTS.sqlite3,
95
+ desc: "include sqlite3 libraries"
79
96
 
80
- class_option :postgresql, aliases: '--postgres', type: :boolean, default: OPTION_DEFAULTS.postgresql,
81
- desc: 'include postgresql libraries'
97
+ class_option :postgresql, aliases: "--postgres", type: :boolean, default: OPTION_DEFAULTS.postgresql,
98
+ desc: "include postgresql libraries"
82
99
 
83
100
  class_option :mysql, type: :boolean, default: OPTION_DEFAULTS.mysql,
84
- desc: 'include mysql libraries'
101
+ desc: "include mysql libraries"
85
102
 
86
103
  class_option :platform, type: :string, default: OPTION_DEFAULTS.platform,
87
- desc: 'image platform (example: linux/arm64)'
104
+ desc: "image platform (example: linux/arm64)"
88
105
 
89
106
  class_option :jemalloc, type: :boolean, default: OPTION_DEFAULTS.jemalloc,
90
- desc: 'use jemalloc alternative malloc implementation'
91
-
107
+ desc: "use jemalloc alternative malloc implementation"
108
+
92
109
  class_option :fullstaq, type: :boolean, default: OPTION_DEFAULTS.fullstaq,
93
- descr: 'use Fullstaq Ruby image from Quay.io'
110
+ descr: "use Fullstaq Ruby image from Quay.io"
94
111
 
95
112
  class_option :yjit, type: :boolean, default: OPTION_DEFAULTS.yjit,
96
- desc: 'enable YJIT optimizing compiler'
113
+ desc: "enable YJIT optimizing compiler"
97
114
 
98
115
  class_option :label, type: :hash, default: {},
99
- desc: 'Add Docker label(s)'
116
+ desc: "Add Docker label(s)"
100
117
 
101
118
  class_option :nginx, type: :boolean, default: OPTION_DEFAULTS.nginx,
102
- desc: 'Serve static files with nginx'
119
+ desc: "Serve static files with nginx"
103
120
 
104
121
  class_option :root, type: :boolean, default: OPTION_DEFAULTS.root,
105
- desc: 'Run application as root user'
122
+ desc: "Run application as root user"
123
+
124
+
125
+ class_option "add-base", type: :array, default: [],
126
+ desc: "additional packages to install for both build and deploy"
127
+
128
+ class_option "add-build", type: :array, default: [],
129
+ desc: "additional packages to install for use during build"
130
+
131
+ class_option "add-deploy", aliases: "--add", type: :array, default: [],
132
+ desc: "additional packages to install for deployment"
133
+
134
+ class_option "remove-base", type: :array, default: [],
135
+ desc: "remove from list of base packages"
136
+
137
+ class_option "remove-build", type: :array, default: [],
138
+ desc: "remove from list of build packages"
139
+
140
+ class_option "remove-deploy", aliases: "--remove", type: :array, default: [],
141
+ desc: "remove from list of deploy packages"
142
+
106
143
 
107
- class_option 'add-base', type: :array, default: [],
108
- desc: 'additional packages to install for both build and deploy'
144
+ class_option "env-base", type: :hash, default: {},
145
+ desc: "additional environment variables for both build and deploy"
109
146
 
110
- class_option 'add-build', type: :array, default: [],
111
- desc: 'additional packages to install for use during build'
147
+ class_option "env-build", type: :hash, default: {},
148
+ desc: "additional environment variables to set during build"
112
149
 
113
- class_option 'add-deploy', aliases: '--add', type: :array, default: [],
114
- desc: 'additional packages to install for deployment'
150
+ class_option "env-deploy", aliases: "--env", type: :hash, default: {},
151
+ desc: "additional environment variables to set for deployment"
115
152
 
116
- class_option 'remove-base', type: :array, default: [],
117
- desc: 'remove from list of base packages'
118
153
 
119
- class_option 'remove-build', type: :array, default: [],
120
- desc: 'remove from list of build packages'
154
+ class_option "arg-base", aliases: "--arg", type: :hash, default: {},
155
+ desc: "additional build arguments for both build and deploy"
156
+
157
+ class_option "arg-build", type: :hash, default: {},
158
+ desc: "additional build arguments to set during build"
159
+
160
+ class_option "arg-deploy", type: :hash, default: {},
161
+ desc: "additional build arguments to set for deployment"
121
162
 
122
- class_option 'remove-deploy', aliases: '--remove', type: :array, default: [],
123
- desc: 'remove from list of deploy packages'
124
163
 
125
164
  def generate_app
126
- source_paths.push File.expand_path('./templates', __dir__)
165
+ source_paths.push File.expand_path("./templates", __dir__)
127
166
 
128
167
  # merge options
129
- options.label.replace(@@labels.merge(options.label).select {|key, value| value != ''})
168
+ options.label.replace(@@labels.merge(options.label).select { |key, value| value != "" })
130
169
 
131
170
  # gather up options for config file
132
171
  @dockerfile_config = OPTION_DEFAULTS.dup.to_h.stringify_keys
@@ -134,57 +173,68 @@ class DockerfileGenerator < Rails::Generators::Base
134
173
  @dockerfile_config[option] = value if @dockerfile_config.include? option
135
174
  end
136
175
 
137
- # apply requested package changes
138
176
  %w(base build deploy).each do |phase|
139
177
  @@packages[phase] += options["add-#{phase}"]
140
178
  @@packages[phase] -= options["remove-#{phase}"]
141
179
  @@packages[phase].uniq!
142
180
  @@packages.delete phase if @@packages[phase].empty?
181
+
182
+ @@vars[phase].merge! options["env-#{phase}"]
183
+ @@vars[phase].delete_if { |key, value| value.blank? }
184
+ @@vars.delete phase if @@vars[phase].empty?
185
+
186
+ @@args[phase].merge! options["arg-#{phase}"]
187
+ @@args[phase].delete_if { |key, value| value.blank? }
188
+ @@args.delete phase if @@args[phase].empty?
143
189
  end
144
190
 
145
- @dockerfile_config['packages'] = @@packages
191
+ @dockerfile_config["packages"] = @@packages
192
+ @dockerfile_config["envs"] = @@vars
193
+ @dockerfile_config["args"] = @@args
146
194
 
147
195
  scan_rails_app
148
196
 
149
197
  Bundler.with_original_env { install_gems }
150
198
 
151
- template 'Dockerfile.erb', 'Dockerfile'
152
- template 'dockerignore.erb', '.dockerignore'
199
+ template "Dockerfile.erb", "Dockerfile"
200
+ template "dockerignore.erb", ".dockerignore"
153
201
 
154
- if using_node? and node_version =~ /\A\d+\.\d+\.\d+\z/
155
- template 'node-version.erb', '.node-version'
202
+ if using_node? && node_version =~ (/\A\d+\.\d+\.\d+\z/)
203
+ template "node-version.erb", ".node-version"
156
204
  end
157
205
 
158
- template 'docker-entrypoint.erb', 'bin/docker-entrypoint'
206
+ template "docker-entrypoint.erb", "bin/docker-entrypoint"
159
207
  chmod "bin/docker-entrypoint", 0755 & ~File.umask, verbose: false
160
208
 
161
- template 'docker-compose.yml.erb', 'docker-compose.yml' if options.compose
209
+ template "docker-compose.yml.erb", "docker-compose.yml" if options.compose
162
210
 
163
- template 'dockerfile.yml.erb', 'config/dockerfile.yml', force: true
211
+ template "dockerfile.yml.erb", "config/dockerfile.yml", force: true
164
212
 
165
- if @gemfile.include?('vite_ruby')
166
- package = JSON.load_file('package.json')
167
- unless package.dig('scripts', 'build')
168
- package['scripts'] ||= {}
169
- package['scripts']['build'] = 'vite build --outDir public'
213
+ if @gemfile.include?("vite_ruby")
214
+ package = JSON.load_file("package.json")
215
+ unless package.dig("scripts", "build")
216
+ package["scripts"] ||= {}
217
+ package["scripts"]["build"] = "vite build --outDir public"
170
218
 
171
- say_status :update, 'package.json'
172
- IO.write('package.json', JSON.pretty_generate(package))
219
+ say_status :update, "package.json"
220
+ IO.write("package.json", JSON.pretty_generate(package))
173
221
  end
174
222
  end
175
223
  end
176
224
 
177
225
  private
178
-
179
226
  def render(options)
180
227
  scope = (Class.new do
181
228
  def initialize(obj, locals)
182
229
  @_obj = obj
183
- @_locals = OpenStruct.new(locals)
230
+ @_locals = locals.then do |hash|
231
+ return nil if hash.empty?
232
+ Struct.new(*hash.keys.map(&:to_sym)).new(*hash.values)
233
+ end
184
234
  end
185
235
 
186
236
  def method_missing(method, *args, &block)
187
- if @_locals.respond_to? method
237
+ if @_locals&.respond_to? method
188
238
  @_locals.send method, *args, &block
189
239
  else
190
240
  @_obj.send method, *args, &block
@@ -197,7 +247,7 @@ private
197
247
  end).new(self, options[:locals] || {})
198
248
 
199
249
  template = IO.read(File.join(source_paths.last, "_#{options[:partial]}.erb"))
200
- ERB.new(template, trim_mode: '-').result(scope.get_binding).strip
250
+ ERB.new(template, trim_mode: "-").result(scope.get_binding).strip
201
251
  end
202
252
 
203
253
  def platform
@@ -209,28 +259,28 @@ private
209
259
  end
210
260
 
211
261
  def run_as_root?
212
- true # options.root? || options.nginx?
262
+ options.root? || options.nginx? # needed to access /dev/stdout
213
263
  end
214
264
 
215
265
  def using_node?
216
266
  return @using_node if @using_node != nil
217
- @using_node = File.exist? 'package.json'
267
+ @using_node = File.exist? "package.json"
218
268
  end
219
269
 
220
270
  def using_redis?
221
- options.redis? or @redis or @gemfile.include?('sidekiq')
271
+ options.redis? or @redis or @gemfile.include?("sidekiq")
222
272
  end
223
273
 
224
274
  def using_execjs?
225
- @gemfile.include?('execjs') or @gemfile.include?('grover')
275
+ @gemfile.include?("execjs") or @gemfile.include?("grover")
226
276
  end
227
277
 
228
278
  def using_puppeteer?
229
- @gemfile.include?('grover') or @gemfile.include?('puppeteer-ruby')
279
+ @gemfile.include?("grover") or @gemfile.include?("puppeteer-ruby")
230
280
  end
231
281
 
232
282
  def using_sidekiq?
233
- @gemfile.include?('sidekiq')
283
+ @gemfile.include?("sidekiq")
234
284
  end
235
285
 
236
286
  def parallel?
@@ -239,26 +289,26 @@ private
239
289
 
240
290
  def keeps?
241
291
  return @keeps if @keeps != nil
242
- @keeps = !!Dir['**/.keep']
292
+ @keeps = !!Dir["**/.keep"]
243
293
  end
244
294
 
245
295
  def install_gems
246
- if options.postgresql? or @postgresql
247
- system "bundle add pg" unless @gemfile.include? 'pg'
296
+ if options.postgresql? || @postgresql
297
+ system "bundle add pg" unless @gemfile.include? "pg"
248
298
  end
249
299
 
250
- if options.mysql? or @mysql
251
- system "bundle add mysql2" unless @gemfile.include? 'mysql2'
300
+ if options.mysql? || @mysql
301
+ system "bundle add mysql2" unless @gemfile.include? "mysql2"
252
302
  end
253
303
 
254
- if options.redis? or using_redis?
255
- system "bundle add redis" unless @gemfile.include? 'redis'
304
+ if options.redis? || using_redis?
305
+ system "bundle add redis" unless @gemfile.include? "redis"
256
306
  end
257
307
  end
258
308
 
259
309
  def base_packages
260
310
  packages = []
261
- packages += @@packages['base'] if @@packages['base']
311
+ packages += @@packages["base"] if @@packages["base"]
262
312
 
263
313
  if using_execjs?
264
314
  packages += %w(curl unzip)
@@ -270,10 +320,10 @@ private
270
320
 
271
321
  # charlock_holmes. Placed here as the library itself is
272
322
  # libicu63 in buster, libicu67 in bullseye, libiclu72 in bookworm...
273
- packages << "libicu-dev" if @gemfile.include? 'charlock_holmes'
323
+ packages << "libicu-dev" if @gemfile.include? "charlock_holmes"
274
324
 
275
325
 
276
- if @gemfile.include? 'webp-ffi'
326
+ if @gemfile.include? "webp-ffi"
277
327
  # https://github.com/le0pard/webp-ffi#requirements
278
328
  packages += %w(libjpeg-dev libpng-dev libtiff-dev libwebp-dev)
279
329
  end
@@ -283,38 +333,38 @@ private
283
333
 
284
334
  def base_requirements
285
335
  requirements = []
286
- requirements << 'nodejs' if using_execjs?
287
- requirements << 'chrome' if using_puppeteer?
288
- requirements << "charlock_holmes" if @gemfile.include? 'charlock_holmes'
289
- requirements.join(' and ')
336
+ requirements << "nodejs" if using_execjs?
337
+ requirements << "chrome" if using_puppeteer?
338
+ requirements << "charlock_holmes" if @gemfile.include? "charlock_holmes"
339
+ requirements.join(" and ")
290
340
  end
291
341
 
292
342
  def build_packages
293
343
  # start with the essentials
294
344
  packages = %w(build-essential)
295
- packages += @@packages['build'] if @@packages['build']
345
+ packages += @@packages["build"] if @@packages["build"]
296
346
 
297
347
  # add databases: sqlite3, postgres, mysql
298
- packages << 'pkg-config' if options.sqlite3? or @sqlite3
299
- packages << 'libpq-dev' if options.postgresql? or @postgresql
300
- packages << 'default-libmysqlclient-dev' if options.mysql? or @mysql
348
+ packages << "pkg-config" if options.sqlite3? || @sqlite3
349
+ packages << "libpq-dev" if options.postgresql? || @postgresql
350
+ packages << "default-libmysqlclient-dev" if options.mysql? || @mysql
301
351
 
302
352
  # add git if needed to install gems
303
- packages << 'git' if @git
353
+ packages << "git" if @git
304
354
 
305
355
  # add redis if Action Cable, caching, or sidekiq are used
306
- packages << "redis" if options.redis? or using_redis?
356
+ packages << "redis" if options.redis? || using_redis?
307
357
 
308
358
  # ActiveStorage preview support
309
- packages << "libvips" if @gemfile.include? 'ruby-vips'
359
+ packages << "libvips" if @gemfile.include? "ruby-vips"
310
360
 
311
361
  # Rmagick gem
312
- packages += %w[pkg-config libmagickwand-dev] if @gemfile.include? 'rmagick'
362
+ packages += %w[pkg-config libmagickwand-dev] if @gemfile.include? "rmagick"
313
363
 
314
364
  # node support, including support for building native modules
315
365
  if using_node?
316
366
  packages += %w(node-gyp pkg-config)
317
- packages += %w(curl unzip) unless using_execjs? or using_puppeteer?
367
+ packages += %w(curl unzip) unless using_execjs? || using_puppeteer?
318
368
 
319
369
  # module build process depends on Python, and debian changed
320
370
  # how python is installed with the bullseye release. Below
@@ -343,29 +393,29 @@ private
343
393
 
344
394
  def deploy_packages
345
395
  packages = []
346
- packages += @@packages['deploy'] if @@packages['deploy']
396
+ packages += @@packages["deploy"] if @@packages["deploy"]
347
397
 
348
398
  # start with databases: sqlite3, postgres, mysql
349
- packages << 'libsqlite3-0' if options.sqlite3? or @sqlite3
350
- packages << 'postgresql-client' if options.postgresql? or @postgresql
351
- packages << 'default-mysql-client' if options.mysql or @mysql
399
+ packages << "libsqlite3-0" if options.sqlite3? || @sqlite3
400
+ packages << "postgresql-client" if options.postgresql? || @postgresql
401
+ packages << "default-mysql-client" if options.mysql || @mysql
352
402
 
353
403
  # add redis in case Action Cable, caching, or sidekiq are added later
354
404
  packages << "redis" if using_redis?
355
405
 
356
406
  # ActiveStorage preview support
357
- packages << "libvips" if @gemfile.include? 'ruby-vips'
407
+ packages << "libvips" if @gemfile.include? "ruby-vips"
358
408
 
359
409
  # Rmagick gem
360
- if @gemfile.include?('rmagick') or @gemfile.include?('mini_magick')
361
- packages << 'imagemagick'
410
+ if @gemfile.include?("rmagick") || @gemfile.include?("mini_magick")
411
+ packages << "imagemagick"
362
412
  end
363
413
 
364
414
  # Puppeteer
365
- packages << 'google-chrome-stable' if using_puppeteer?
415
+ packages << "google-chrome-stable" if using_puppeteer?
366
416
 
367
417
  # nginx
368
- packages << 'nginx' if options.nginx?
418
+ packages << "nginx" if options.nginx?
369
419
 
370
420
  packages.sort
371
421
  end
@@ -382,57 +432,133 @@ private
382
432
  end
383
433
 
384
434
  if repos.empty?
385
- ''
435
+ ""
386
436
  else
387
437
  repos.join(" \\\n ") + " && \\\n "
388
438
  end
389
439
  end
390
440
 
441
+ def base_env
442
+ env = {
443
+ "RAILS_ENV" => "production",
444
+ "BUNDLE_PATH" => "vendor/bundle",
445
+ "BUNDLE_WITHOUT" => options.ci? ? "development" : "development:test"
446
+ }
447
+
448
+ if @@args["base"]
449
+ env.merge! @@args["base"].to_h { |key, value| [key, "$#{key}"] }
450
+ end
451
+
452
+ env.merge! @@vars["base"] if @@vars["base"]
453
+
454
+ env.map { |key, value| "#{key}=#{value.inspect}" }
455
+ end
456
+
457
+ def build_env
458
+ env = {}
459
+
460
+ if @@args["build"]
461
+ env.merge! @@args["build"].to_h { |key, value| [key, "$#{key}"] }
462
+ end
463
+
464
+ env.merge! @@vars["build"] if @@vars["build"]
465
+
466
+ env.map { |key, value| "#{key}=#{value.inspect}" }
467
+ end
468
+
391
469
  def deploy_env
392
- env = []
470
+ env = {}
393
471
 
394
- env << 'PORT="3001"' if options.nginx?
472
+ env["PORT"] = "3001" if options.nginx?
395
473
 
396
- if Rails::VERSION::MAJOR<7 || Rails::VERSION::STRING.start_with?('7.0')
397
- env << 'RAILS_LOG_TO_STDOUT="1"'
398
- env << 'RAILS_SERVE_STATIC_FILES="true"' unless options.nginx?
474
+ if Rails::VERSION::MAJOR < 7 || Rails::VERSION::STRING.start_with?("7.0")
475
+ env["RAILS_LOG_TO_STDOUT"] = "1"
476
+ env["RAILS_SERVE_STATIC_FILES"] = "true" unless options.nginx?
399
477
  end
400
478
 
401
479
  if options.yjit?
402
- env << 'RUBY_YJIT_ENABLE="1"'
480
+ env["RUBY_YJIT_ENABLE"] = "1"
403
481
  end
404
482
 
405
- if options.jemalloc? and not options.fullstaq?
406
- if (options.platform || Gem::Platform::local.cpu).include? 'arm'
407
- env << 'LD_PRELOAD="/usr/lib/aarch64-linux-gnu/libjemalloc.so.2"'
483
+ if options.jemalloc? && (not options.fullstaq?)
484
+ if (options.platform || Gem::Platform.local.cpu).include? "arm"
485
+ env["LD_PRELOAD"] = "/usr/lib/aarch64-linux-gnu/libjemalloc.so.2"
408
486
  else
409
- env << 'LD_PRELOAD="/usr/lib/x86_64-linux-gnu/libjemalloc.so.2"'
487
+ env["LD_PRELOAD"] = "/usr/lib/x86_64-linux-gnu/libjemalloc.so.2"
410
488
  end
411
489
 
412
- env << 'MALLOC_CONF="dirty_decay_ms:1000,narenas:2,background_thread:true"'
490
+ env["MALLOC_CONF"] = "dirty_decay_ms:1000,narenas:2,background_thread:true"
413
491
  end
414
492
 
415
493
  if using_puppeteer?
416
- env << 'GROVER_NO_SANDBOX="true"' if @gemfile.include? 'grover'
417
- env << 'PUPPETEER_RUBY_NO_SANDBOX="1"' if @gemfile.include? 'puppeteer-ruby'
418
- env << 'PUPPETEER_EXECUTABLE_PATH="/usr/bin/google-chrome"'
494
+ if options.root?
495
+ env["GROVER_NO_SANDBOX"] = "true" if @gemfile.include? "grover"
496
+ env["PUPPETEER_RUBY_NO_SANDBOX"] = "1" if @gemfile.include? "puppeteer-ruby"
497
+ end
498
+
499
+ env["PUPPETEER_EXECUTABLE_PATH"] = "/usr/bin/google-chrome"
500
+ end
501
+
502
+ if @@args["deploy"]
503
+ env.merge! @@args["deploy"].to_h { |key, value| [key, "$#{key}"] }
504
+ end
505
+
506
+ env.merge! @@vars["base"] if @@vars["base"]
507
+
508
+ env.map { |key, value| "#{key}=#{value.inspect}" }
509
+ end
510
+
511
+ def base_args
512
+ args = {}
513
+
514
+ args.merge! @@args["base"] if @@args["base"]
515
+
516
+ args
517
+ end
518
+
519
+ def build_args
520
+ args = {}
521
+
522
+ args.merge! @@args["build"] if @@args["build"]
523
+
524
+ args
525
+ end
526
+
527
+ def deploy_args
528
+ args = {}
529
+
530
+ args.merge! @@args["deploy"] if @@args["deploy"]
531
+
532
+ args
533
+ end
534
+
535
+ def all_args
536
+ args = {}
537
+
538
+ unless options.root?
539
+ args[:UID] = "${UID:-1000}".html_safe
540
+ args[:GID] = "${GID:-${UID:-1000}}".html_safe
419
541
  end
420
542
 
421
- env
543
+ args.merge! base_args
544
+ args.merge! build_args
545
+ args.merge! deploy_args
546
+
547
+ args
422
548
  end
423
549
 
424
550
  def binfile_fixups
425
551
  # binfiles may have OS specific paths to ruby. Normalize them.
426
552
  shebangs = Dir["bin/*"].map { |file| IO.read(file).lines.first }.join
427
553
  rubies = shebangs.scan(%r{#!/usr/bin/env (ruby.*)}).flatten.uniq
428
-
554
+
429
555
  binfixups = (rubies - %w(ruby)).map do |ruby|
430
556
  "sed -i 's/#{Regexp.quote(ruby)}$/ruby/' bin/*"
431
557
  end
432
-
558
+
433
559
  # Windows line endings will cause scripts to fail. If any
434
- # or found OR this generation is run on a windows platform
435
- # and there are other binfixups required, then convert
560
+ # or found OR this generation is run on a windows platform
561
+ # and there are other binfixups required, then convert
436
562
  # line endings. This avoids adding unnecessary fixups if
437
563
  # none are required, but prepares for the need to do the
438
564
  # fix line endings if other fixups are required.
@@ -440,7 +566,7 @@ private
440
566
  if has_cr || (Gem.win_platform? && !binfixups.empty?)
441
567
  binfixups.unshift 'sed -i "s/\r$//g" bin/*'
442
568
  end
443
-
569
+
444
570
  # Windows file systems may not have the concept of executable.
445
571
  # In such cases, fix up during the build.
446
572
  unless Dir["bin/*"].all? { |file| File.executable? file }
@@ -448,7 +574,7 @@ private
448
574
  end
449
575
 
450
576
  # optionally, adjust cwd
451
- if options['bin-cd']
577
+ if options["bin-cd"]
452
578
  binfixups.push %{grep -l '#!/usr/bin/env ruby' /rails/bin/* | xargs sed -i '/^#!/aDir.chdir File.expand_path("..", __dir__)'}
453
579
  end
454
580
 
@@ -456,50 +582,50 @@ private
456
582
  end
457
583
 
458
584
  def deploy_database
459
- if options.postgresql? or @postgresql
460
- 'postgresql'
461
- elsif options.mysql or @mysql
462
- 'mysql'
585
+ if options.postgresql? || @postgresql
586
+ "postgresql"
587
+ elsif options.mysql || @mysql
588
+ "mysql"
463
589
  else
464
- 'sqlite3'
590
+ "sqlite3"
465
591
  end
466
592
  end
467
593
 
468
594
  def node_version
469
595
  version = nil
470
596
 
471
- if File.exist? '.node-version'
472
- version = IO.read('.node-version')[/\d+\.\d+\.\d+/]
597
+ if File.exist? ".node-version"
598
+ version = IO.read(".node-version")[/\d+\.\d+\.\d+/]
473
599
  end
474
600
 
475
- if !version and File.exist? 'package.json'
476
- version = JSON.parse(IO.read('package.json')).dig("engines", "node")
477
- version = nil unless version =~ /\A(\d+\.)+(\d+|x)\z/
601
+ if !version && File.exist?("package.json")
602
+ version = JSON.parse(IO.read("package.json")).dig("engines", "node")
603
+ version = nil unless /\A(\d+\.)+(\d+|x)\z/.match?(version)
478
604
  end
479
605
 
480
606
  version || `node --version`[/\d+\.\d+\.\d+/]
481
607
  rescue
482
- "lts"
608
+ "lts"
483
609
  end
484
610
 
485
611
  def yarn_version
486
- package = JSON.parse(IO.read('package.json'))
612
+ package = JSON.parse(IO.read("package.json"))
487
613
 
488
- if ENV['RAILS_ENV'] == 'test'
614
+ if ENV["RAILS_ENV"] == "test"
489
615
  # yarn install instructions changed in v2
490
- version = '1.22.19'
491
- elsif package['packageManager'].to_s.start_with? "yarn@"
492
- version = package['packageManager'].sub('yarn@', '')
616
+ version = "1.22.19"
617
+ elsif package["packageManager"].to_s.start_with? "yarn@"
618
+ version = package["packageManager"].sub("yarn@", "")
493
619
  else
494
- version = `yarn --version`[/\d+\.\d+\.\d+/] || '1.22.19'
620
+ version = `yarn --version`[/\d+\.\d+\.\d+/] || "1.22.19"
495
621
  system "yarn set version #{version}"
496
- package = JSON.parse(IO.read('package.json'))
622
+ package = JSON.parse(IO.read("package.json"))
497
623
  # apparently not all versions of yarn will update package.json...
498
624
  end
499
625
 
500
- unless package['packageManager']
501
- package['packageManager'] = "yarn@#{version}"
502
- IO.write('package.json', JSON.pretty_generate(package))
626
+ unless package["packageManager"]
627
+ package["packageManager"] = "yarn@#{version}"
628
+ IO.write("package.json", JSON.pretty_generate(package))
503
629
  end
504
630
 
505
631
  version
@@ -508,7 +634,7 @@ private
508
634
  end
509
635
 
510
636
  def depend_on_bootsnap?
511
- @gemfile.include? 'bootsnap'
637
+ @gemfile.include? "bootsnap"
512
638
  end
513
639
 
514
640
  def api_only?
@@ -519,13 +645,13 @@ private
519
645
  # for specific directories.
520
646
  def api_client_dir
521
647
  if api_only?
522
- scan = '*/package.json'
648
+ scan = "*/package.json"
523
649
  else
524
- scan = '{client,frontend}/package.json'
650
+ scan = "{client,frontend}/package.json"
525
651
  end
526
652
 
527
653
  file = Dir[scan].find do |file|
528
- JSON.load_file(file).dig('scripts', 'build')
654
+ JSON.load_file(file).dig("scripts", "build")
529
655
  end
530
656
 
531
657
  file && File.dirname(file)
@@ -540,9 +666,9 @@ private
540
666
 
541
667
  def dbprep_command
542
668
  if Rails::VERSION::MAJOR >= 6
543
- 'db:prepare'
669
+ "db:prepare"
544
670
  else
545
- 'db:migrate'
671
+ "db:migrate"
546
672
  end
547
673
  end
548
674
 
@@ -550,23 +676,25 @@ private
550
676
  if options.nginx?
551
677
  {
552
678
  nginx: 'nginx -g "daemon off;"',
553
- rails: './bin/rails server -p 3001'
679
+ rails: "./bin/rails server -p 3001"
554
680
  }
555
681
  else
556
682
  {
557
- rails: './bin/rails server'
683
+ rails: "./bin/rails server"
558
684
  }
559
685
  end
560
686
  end
561
687
 
562
688
  def more_docker_ignores
563
- more = ''
689
+ more = ""
564
690
 
565
- if @gemfile.include?('vite_ruby')
566
- lines = IO.read('.gitignore')[/^# Vite.*?\n\n/m].to_s.chomp.lines -
691
+ if @gemfile.include?("vite_ruby")
692
+ lines = IO.read(".gitignore")[/^# Vite.*?\n\n/m].to_s.chomp.lines -
567
693
  ["node_modules\n"]
568
694
 
569
695
  more += "\n" + lines.join
570
696
  end
697
+
698
+ more
571
699
  end
572
700
  end