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.
- checksums.yaml +4 -4
- data/DEMO.md +61 -27
- data/MOTIVATION.md +58 -0
- data/README.md +25 -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 +302 -174
- data/lib/generators/templates/Dockerfile.erb +32 -6
- data/lib/generators/templates/docker-compose.yml.erb +9 -0
- metadata +3 -2
@@ -1,40 +1,45 @@
|
|
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
|
-
|
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?
|
33
|
-
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]
|
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:
|
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
|
57
|
-
desc:
|
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:
|
77
|
+
desc: "use build cache to speed up installs"
|
61
78
|
|
62
79
|
class_option :prepare, type: :boolean, default: OPTION_DEFAULTS.prepare,
|
63
|
-
desc:
|
80
|
+
desc: "include db:prepare step"
|
64
81
|
|
65
82
|
class_option :parallel, type: :boolean, default: OPTION_DEFAULTS.parallel,
|
66
|
-
desc:
|
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:
|
86
|
+
desc: "allocate swapspace"
|
70
87
|
|
71
88
|
class_option :compose, type: :boolean, default: OPTION_DEFAULTS.compose,
|
72
|
-
desc:
|
89
|
+
desc: "generate a docker-compose.yml file"
|
73
90
|
|
74
91
|
class_option :redis, type: :boolean, default: OPTION_DEFAULTS.redis,
|
75
|
-
desc:
|
92
|
+
desc: "include redis libraries"
|
76
93
|
|
77
|
-
class_option :sqlite3, aliases:
|
78
|
-
desc:
|
94
|
+
class_option :sqlite3, aliases: "--sqlite", type: :boolean, default: OPTION_DEFAULTS.sqlite3,
|
95
|
+
desc: "include sqlite3 libraries"
|
79
96
|
|
80
|
-
class_option :postgresql, aliases:
|
81
|
-
desc:
|
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:
|
101
|
+
desc: "include mysql libraries"
|
85
102
|
|
86
103
|
class_option :platform, type: :string, default: OPTION_DEFAULTS.platform,
|
87
|
-
desc:
|
104
|
+
desc: "image platform (example: linux/arm64)"
|
88
105
|
|
89
106
|
class_option :jemalloc, type: :boolean, default: OPTION_DEFAULTS.jemalloc,
|
90
|
-
desc:
|
91
|
-
|
107
|
+
desc: "use jemalloc alternative malloc implementation"
|
108
|
+
|
92
109
|
class_option :fullstaq, type: :boolean, default: OPTION_DEFAULTS.fullstaq,
|
93
|
-
descr:
|
110
|
+
descr: "use Fullstaq Ruby image from Quay.io"
|
94
111
|
|
95
112
|
class_option :yjit, type: :boolean, default: OPTION_DEFAULTS.yjit,
|
96
|
-
desc:
|
113
|
+
desc: "enable YJIT optimizing compiler"
|
97
114
|
|
98
115
|
class_option :label, type: :hash, default: {},
|
99
|
-
desc:
|
116
|
+
desc: "Add Docker label(s)"
|
100
117
|
|
101
118
|
class_option :nginx, type: :boolean, default: OPTION_DEFAULTS.nginx,
|
102
|
-
desc:
|
119
|
+
desc: "Serve static files with nginx"
|
103
120
|
|
104
121
|
class_option :root, type: :boolean, default: OPTION_DEFAULTS.root,
|
105
|
-
desc:
|
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
|
108
|
-
desc:
|
144
|
+
class_option "env-base", type: :hash, default: {},
|
145
|
+
desc: "additional environment variables for both build and deploy"
|
109
146
|
|
110
|
-
class_option
|
111
|
-
desc:
|
147
|
+
class_option "env-build", type: :hash, default: {},
|
148
|
+
desc: "additional environment variables to set during build"
|
112
149
|
|
113
|
-
class_option
|
114
|
-
desc:
|
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
|
120
|
-
desc:
|
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(
|
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[
|
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
|
152
|
-
template
|
199
|
+
template "Dockerfile.erb", "Dockerfile"
|
200
|
+
template "dockerignore.erb", ".dockerignore"
|
153
201
|
|
154
|
-
if using_node?
|
155
|
-
template
|
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
|
206
|
+
template "docker-entrypoint.erb", "bin/docker-entrypoint"
|
159
207
|
chmod "bin/docker-entrypoint", 0755 & ~File.umask, verbose: false
|
160
208
|
|
161
|
-
template
|
209
|
+
template "docker-compose.yml.erb", "docker-compose.yml" if options.compose
|
162
210
|
|
163
|
-
template
|
211
|
+
template "dockerfile.yml.erb", "config/dockerfile.yml", force: true
|
164
212
|
|
165
|
-
if @gemfile.include?(
|
166
|
-
package = JSON.load_file(
|
167
|
-
unless package.dig(
|
168
|
-
package[
|
169
|
-
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"
|
170
218
|
|
171
|
-
say_status :update,
|
172
|
-
IO.write(
|
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 =
|
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
|
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:
|
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
|
-
|
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?
|
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?(
|
271
|
+
options.redis? or @redis or @gemfile.include?("sidekiq")
|
222
272
|
end
|
223
273
|
|
224
274
|
def using_execjs?
|
225
|
-
@gemfile.include?(
|
275
|
+
@gemfile.include?("execjs") or @gemfile.include?("grover")
|
226
276
|
end
|
227
277
|
|
228
278
|
def using_puppeteer?
|
229
|
-
@gemfile.include?(
|
279
|
+
@gemfile.include?("grover") or @gemfile.include?("puppeteer-ruby")
|
230
280
|
end
|
231
281
|
|
232
282
|
def using_sidekiq?
|
233
|
-
@gemfile.include?(
|
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[
|
292
|
+
@keeps = !!Dir["**/.keep"]
|
243
293
|
end
|
244
294
|
|
245
295
|
def install_gems
|
246
|
-
if options.postgresql?
|
247
|
-
system "bundle add pg" unless @gemfile.include?
|
296
|
+
if options.postgresql? || @postgresql
|
297
|
+
system "bundle add pg" unless @gemfile.include? "pg"
|
248
298
|
end
|
249
299
|
|
250
|
-
if options.mysql?
|
251
|
-
system "bundle add mysql2" unless @gemfile.include?
|
300
|
+
if options.mysql? || @mysql
|
301
|
+
system "bundle add mysql2" unless @gemfile.include? "mysql2"
|
252
302
|
end
|
253
303
|
|
254
|
-
if options.redis?
|
255
|
-
system "bundle add redis" unless @gemfile.include?
|
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[
|
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?
|
323
|
+
packages << "libicu-dev" if @gemfile.include? "charlock_holmes"
|
274
324
|
|
275
325
|
|
276
|
-
if @gemfile.include?
|
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 <<
|
287
|
-
requirements <<
|
288
|
-
requirements << "charlock_holmes" if @gemfile.include?
|
289
|
-
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 ")
|
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[
|
345
|
+
packages += @@packages["build"] if @@packages["build"]
|
296
346
|
|
297
347
|
# add databases: sqlite3, postgres, mysql
|
298
|
-
packages <<
|
299
|
-
packages <<
|
300
|
-
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
|
301
351
|
|
302
352
|
# add git if needed to install gems
|
303
|
-
packages <<
|
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?
|
356
|
+
packages << "redis" if options.redis? || using_redis?
|
307
357
|
|
308
358
|
# ActiveStorage preview support
|
309
|
-
packages << "libvips" if @gemfile.include?
|
359
|
+
packages << "libvips" if @gemfile.include? "ruby-vips"
|
310
360
|
|
311
361
|
# Rmagick gem
|
312
|
-
packages += %w[pkg-config libmagickwand-dev] if @gemfile.include?
|
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?
|
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[
|
396
|
+
packages += @@packages["deploy"] if @@packages["deploy"]
|
347
397
|
|
348
398
|
# start with databases: sqlite3, postgres, mysql
|
349
|
-
packages <<
|
350
|
-
packages <<
|
351
|
-
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
|
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?
|
407
|
+
packages << "libvips" if @gemfile.include? "ruby-vips"
|
358
408
|
|
359
409
|
# Rmagick gem
|
360
|
-
if @gemfile.include?(
|
361
|
-
packages <<
|
410
|
+
if @gemfile.include?("rmagick") || @gemfile.include?("mini_magick")
|
411
|
+
packages << "imagemagick"
|
362
412
|
end
|
363
413
|
|
364
414
|
# Puppeteer
|
365
|
-
packages <<
|
415
|
+
packages << "google-chrome-stable" if using_puppeteer?
|
366
416
|
|
367
417
|
# nginx
|
368
|
-
packages <<
|
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
|
472
|
+
env["PORT"] = "3001" if options.nginx?
|
395
473
|
|
396
|
-
if Rails::VERSION::MAJOR<7 || Rails::VERSION::STRING.start_with?(
|
397
|
-
env
|
398
|
-
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?
|
399
477
|
end
|
400
478
|
|
401
479
|
if options.yjit?
|
402
|
-
env
|
480
|
+
env["RUBY_YJIT_ENABLE"] = "1"
|
403
481
|
end
|
404
482
|
|
405
|
-
if options.jemalloc?
|
406
|
-
if (options.platform || Gem::Platform
|
407
|
-
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"
|
408
486
|
else
|
409
|
-
env
|
487
|
+
env["LD_PRELOAD"] = "/usr/lib/x86_64-linux-gnu/libjemalloc.so.2"
|
410
488
|
end
|
411
489
|
|
412
|
-
env
|
490
|
+
env["MALLOC_CONF"] = "dirty_decay_ms:1000,narenas:2,background_thread:true"
|
413
491
|
end
|
414
492
|
|
415
493
|
if using_puppeteer?
|
416
|
-
|
417
|
-
|
418
|
-
|
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
|
-
|
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[
|
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?
|
460
|
-
|
461
|
-
elsif options.mysql
|
462
|
-
|
585
|
+
if options.postgresql? || @postgresql
|
586
|
+
"postgresql"
|
587
|
+
elsif options.mysql || @mysql
|
588
|
+
"mysql"
|
463
589
|
else
|
464
|
-
|
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?
|
472
|
-
version = IO.read(
|
597
|
+
if File.exist? ".node-version"
|
598
|
+
version = IO.read(".node-version")[/\d+\.\d+\.\d+/]
|
473
599
|
end
|
474
600
|
|
475
|
-
if !version
|
476
|
-
version = JSON.parse(IO.read(
|
477
|
-
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)
|
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(
|
612
|
+
package = JSON.parse(IO.read("package.json"))
|
487
613
|
|
488
|
-
if ENV[
|
614
|
+
if ENV["RAILS_ENV"] == "test"
|
489
615
|
# yarn install instructions changed in v2
|
490
|
-
version =
|
491
|
-
elsif package[
|
492
|
-
version = package[
|
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+/] ||
|
620
|
+
version = `yarn --version`[/\d+\.\d+\.\d+/] || "1.22.19"
|
495
621
|
system "yarn set version #{version}"
|
496
|
-
package = JSON.parse(IO.read(
|
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[
|
501
|
-
package[
|
502
|
-
IO.write(
|
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?
|
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 =
|
648
|
+
scan = "*/package.json"
|
523
649
|
else
|
524
|
-
scan =
|
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(
|
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
|
-
|
669
|
+
"db:prepare"
|
544
670
|
else
|
545
|
-
|
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:
|
679
|
+
rails: "./bin/rails server -p 3001"
|
554
680
|
}
|
555
681
|
else
|
556
682
|
{
|
557
|
-
rails:
|
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?(
|
566
|
-
lines = IO.read(
|
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
|