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.
- checksums.yaml +4 -4
- data/DEMO.md +61 -27
- data/MOTIVATION.md +58 -0
- data/README.md +26 -9
- data/Rakefile +5 -3
- data/lib/dockerfile-rails/scanner.rb +29 -28
- data/lib/dockerfile-rails.rb +5 -3
- data/lib/generators/dockerfile_generator.rb +327 -153
- data/lib/generators/templates/Dockerfile.erb +36 -5
- data/lib/generators/templates/docker-compose.yml.erb +9 -0
- data/lib/generators/templates/docker-entrypoint.erb +1 -1
- metadata +3 -2
@@ -1,38 +1,63 @@
|
|
1
|
-
|
2
|
-
|
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 =
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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?
|
31
|
-
options = YAML.safe_load(IO.read(
|
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:
|
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
|
49
|
-
desc:
|
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:
|
77
|
+
desc: "use build cache to speed up installs"
|
53
78
|
|
54
79
|
class_option :prepare, type: :boolean, default: OPTION_DEFAULTS.prepare,
|
55
|
-
desc:
|
80
|
+
desc: "include db:prepare step"
|
56
81
|
|
57
82
|
class_option :parallel, type: :boolean, default: OPTION_DEFAULTS.parallel,
|
58
|
-
desc:
|
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:
|
86
|
+
desc: "allocate swapspace"
|
62
87
|
|
63
88
|
class_option :compose, type: :boolean, default: OPTION_DEFAULTS.compose,
|
64
|
-
desc:
|
89
|
+
desc: "generate a docker-compose.yml file"
|
65
90
|
|
66
91
|
class_option :redis, type: :boolean, default: OPTION_DEFAULTS.redis,
|
67
|
-
desc:
|
92
|
+
desc: "include redis libraries"
|
68
93
|
|
69
|
-
class_option :sqlite3, aliases:
|
70
|
-
desc:
|
94
|
+
class_option :sqlite3, aliases: "--sqlite", type: :boolean, default: OPTION_DEFAULTS.sqlite3,
|
95
|
+
desc: "include sqlite3 libraries"
|
71
96
|
|
72
|
-
class_option :postgresql, aliases:
|
73
|
-
desc:
|
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:
|
101
|
+
desc: "include mysql libraries"
|
77
102
|
|
78
103
|
class_option :platform, type: :string, default: OPTION_DEFAULTS.platform,
|
79
|
-
desc:
|
104
|
+
desc: "image platform (example: linux/arm64)"
|
80
105
|
|
81
106
|
class_option :jemalloc, type: :boolean, default: OPTION_DEFAULTS.jemalloc,
|
82
|
-
desc:
|
83
|
-
|
107
|
+
desc: "use jemalloc alternative malloc implementation"
|
108
|
+
|
84
109
|
class_option :fullstaq, type: :boolean, default: OPTION_DEFAULTS.fullstaq,
|
85
|
-
descr:
|
110
|
+
descr: "use Fullstaq Ruby image from Quay.io"
|
86
111
|
|
87
112
|
class_option :yjit, type: :boolean, default: OPTION_DEFAULTS.yjit,
|
88
|
-
desc:
|
113
|
+
desc: "enable YJIT optimizing compiler"
|
89
114
|
|
90
115
|
class_option :label, type: :hash, default: {},
|
91
|
-
desc:
|
116
|
+
desc: "Add Docker label(s)"
|
92
117
|
|
93
118
|
class_option :nginx, type: :boolean, default: OPTION_DEFAULTS.nginx,
|
94
|
-
desc:
|
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(
|
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
|
113
|
-
template
|
199
|
+
template "Dockerfile.erb", "Dockerfile"
|
200
|
+
template "dockerignore.erb", ".dockerignore"
|
114
201
|
|
115
|
-
if using_node?
|
116
|
-
template
|
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
|
206
|
+
template "docker-entrypoint.erb", "bin/docker-entrypoint"
|
120
207
|
chmod "bin/docker-entrypoint", 0755 & ~File.umask, verbose: false
|
121
208
|
|
122
|
-
template
|
209
|
+
template "docker-compose.yml.erb", "docker-compose.yml" if options.compose
|
123
210
|
|
124
|
-
template
|
211
|
+
template "dockerfile.yml.erb", "config/dockerfile.yml", force: true
|
125
212
|
|
126
|
-
if @gemfile.include?(
|
127
|
-
package = JSON.load_file(
|
128
|
-
unless package.dig(
|
129
|
-
package[
|
130
|
-
package[
|
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,
|
133
|
-
IO.write(
|
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 =
|
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
|
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:
|
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?
|
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?(
|
271
|
+
options.redis? or @redis or @gemfile.include?("sidekiq")
|
179
272
|
end
|
180
273
|
|
181
274
|
def using_execjs?
|
182
|
-
@gemfile.include?(
|
275
|
+
@gemfile.include?("execjs") or @gemfile.include?("grover")
|
183
276
|
end
|
184
277
|
|
185
278
|
def using_puppeteer?
|
186
|
-
@gemfile.include?(
|
279
|
+
@gemfile.include?("grover") or @gemfile.include?("puppeteer-ruby")
|
187
280
|
end
|
188
281
|
|
189
282
|
def using_sidekiq?
|
190
|
-
@gemfile.include?(
|
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[
|
292
|
+
@keeps = !!Dir["**/.keep"]
|
200
293
|
end
|
201
294
|
|
202
295
|
def install_gems
|
203
|
-
if options.postgresql?
|
204
|
-
system "bundle add pg" unless @gemfile.include?
|
296
|
+
if options.postgresql? || @postgresql
|
297
|
+
system "bundle add pg" unless @gemfile.include? "pg"
|
205
298
|
end
|
206
299
|
|
207
|
-
if options.mysql?
|
208
|
-
system "bundle add mysql2" unless @gemfile.include?
|
300
|
+
if options.mysql? || @mysql
|
301
|
+
system "bundle add mysql2" unless @gemfile.include? "mysql2"
|
209
302
|
end
|
210
303
|
|
211
|
-
if options.redis?
|
212
|
-
system "bundle add redis" unless @gemfile.include?
|
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?
|
323
|
+
packages << "libicu-dev" if @gemfile.include? "charlock_holmes"
|
230
324
|
|
231
325
|
|
232
|
-
if @gemfile.include?
|
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 <<
|
243
|
-
requirements <<
|
244
|
-
requirements << "charlock_holmes" if @gemfile.include?
|
245
|
-
requirements.join(
|
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 <<
|
254
|
-
packages <<
|
255
|
-
packages <<
|
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 <<
|
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?
|
356
|
+
packages << "redis" if options.redis? || using_redis?
|
262
357
|
|
263
358
|
# ActiveStorage preview support
|
264
|
-
packages << "libvips" if @gemfile.include?
|
359
|
+
packages << "libvips" if @gemfile.include? "ruby-vips"
|
265
360
|
|
266
361
|
# Rmagick gem
|
267
|
-
packages += %w[pkg-config libmagickwand-dev] if @gemfile.include?
|
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?
|
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 <<
|
304
|
-
packages <<
|
305
|
-
packages <<
|
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?
|
407
|
+
packages << "libvips" if @gemfile.include? "ruby-vips"
|
312
408
|
|
313
409
|
# Rmagick gem
|
314
|
-
if @gemfile.include?(
|
315
|
-
packages <<
|
410
|
+
if @gemfile.include?("rmagick") || @gemfile.include?("mini_magick")
|
411
|
+
packages << "imagemagick"
|
316
412
|
end
|
317
413
|
|
318
414
|
# Puppeteer
|
319
|
-
packages <<
|
415
|
+
packages << "google-chrome-stable" if using_puppeteer?
|
320
416
|
|
321
417
|
# nginx
|
322
|
-
packages <<
|
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
|
472
|
+
env["PORT"] = "3001" if options.nginx?
|
349
473
|
|
350
|
-
if Rails::VERSION::MAJOR<7 || Rails::VERSION::STRING.start_with?(
|
351
|
-
env
|
352
|
-
env
|
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
|
480
|
+
env["RUBY_YJIT_ENABLE"] = "1"
|
357
481
|
end
|
358
482
|
|
359
|
-
if options.jemalloc?
|
360
|
-
if (options.platform || Gem::Platform
|
361
|
-
env
|
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
|
487
|
+
env["LD_PRELOAD"] = "/usr/lib/x86_64-linux-gnu/libjemalloc.so.2"
|
364
488
|
end
|
365
489
|
|
366
|
-
env
|
490
|
+
env["MALLOC_CONF"] = "dirty_decay_ms:1000,narenas:2,background_thread:true"
|
367
491
|
end
|
368
492
|
|
369
493
|
if using_puppeteer?
|
370
|
-
|
371
|
-
|
372
|
-
|
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
|
-
|
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[
|
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?
|
414
|
-
|
415
|
-
elsif options.mysql
|
416
|
-
|
585
|
+
if options.postgresql? || @postgresql
|
586
|
+
"postgresql"
|
587
|
+
elsif options.mysql || @mysql
|
588
|
+
"mysql"
|
417
589
|
else
|
418
|
-
|
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?
|
426
|
-
version = IO.read(
|
597
|
+
if File.exist? ".node-version"
|
598
|
+
version = IO.read(".node-version")[/\d+\.\d+\.\d+/]
|
427
599
|
end
|
428
600
|
|
429
|
-
if !version
|
430
|
-
version = JSON.parse(IO.read(
|
431
|
-
version = nil unless
|
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(
|
612
|
+
package = JSON.parse(IO.read("package.json"))
|
441
613
|
|
442
|
-
if ENV[
|
614
|
+
if ENV["RAILS_ENV"] == "test"
|
443
615
|
# yarn install instructions changed in v2
|
444
|
-
version =
|
445
|
-
elsif package[
|
446
|
-
version = package[
|
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+/] ||
|
620
|
+
version = `yarn --version`[/\d+\.\d+\.\d+/] || "1.22.19"
|
449
621
|
system "yarn set version #{version}"
|
450
|
-
package = JSON.parse(IO.read(
|
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[
|
455
|
-
package[
|
456
|
-
IO.write(
|
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?
|
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 =
|
648
|
+
scan = "*/package.json"
|
477
649
|
else
|
478
|
-
scan =
|
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(
|
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
|
-
|
669
|
+
"db:prepare"
|
498
670
|
else
|
499
|
-
|
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:
|
679
|
+
rails: "./bin/rails server -p 3001"
|
508
680
|
}
|
509
681
|
else
|
510
682
|
{
|
511
|
-
rails:
|
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?(
|
520
|
-
lines = IO.read(
|
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
|