dockerfile-rails 1.0.18 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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