dockerfile-rails 1.0.17 → 1.1.0

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