dockerfile-rails 1.2.5 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6f8b3176721310f5c043a7c2642f12b59fa7bc2952fb73f6e6164d199d24bc5a
4
- data.tar.gz: 995b15167890754208baef9bc6ff48613e8cef121083f8d70c1be77593b5beb4
3
+ metadata.gz: 5d1b0d5e237b0a8c33a3b1263d34590db8912967420863332ad0b2c8f5661e5f
4
+ data.tar.gz: 000fd8db20304bc2ba8623aaa1a4bd19870ec4143cacc3cb6ea31b8690510782
5
5
  SHA512:
6
- metadata.gz: 7f05f65f85a29fcfc242682ab6464adb88f9e0725fffe1c65c192c342e12444c3dbaff9f855ff28ccb94ea13fa706918b71609af06a87673007407083e77dd2c
7
- data.tar.gz: 5bcca34771c67bf29e28be9a0342fbd9dac8a13694f3416d0b27a2070cabf0e38017ed195ecf2b1a80a03150698cb62502e73d63ec603500bb927fa627cd73aa
6
+ metadata.gz: 64b8702f00462a318af21238617528682be3831da82471bff0172ef9ef98d7e4a6a10346ffe297e87e76cc5d4b2699bf56b79d9257f1eee3c8cc24bc56012037
7
+ data.tar.gz: 9a9dc774d388e4276b08bbcdcce9ffd676800df7bb86b4cac34e6795fd0ee6f24ba2a57df393f7948f738a9bdafe8a1ed8073c0b91d9e6a2f9f94349a7d14fe4
data/DEMO.md CHANGED
@@ -132,7 +132,7 @@ docker compose up
132
132
  # Demo 4 - API only
133
133
 
134
134
  This demo deploys a [Create React App](https://create-react-app.dev/) client and a Rails API-only server. Ruby and Rails version information is retrieved from the server and displayed below a spinning React logo. Note that the build process installs the
135
- node moddules and ruby gems in parallel.
135
+ node modules and ruby gems in parallel.
136
136
 
137
137
  ```bash
138
138
  rails new demo --api
data/README.md CHANGED
@@ -40,6 +40,7 @@ bin/rails generate dockerfile
40
40
 
41
41
  * `--ci` - include test gems in deployed image
42
42
  * `--compose` - generate a `docker-compose.yml` file
43
+ * `--max-idle=n` - exit afer *n* seconds of inactivity. Supports [iso 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) and [sleep](https://man7.org/linux/man-pages/man1/sleep.1.html#DESCRIPTION) syntaxes. Uses passenger for now, awaiting [puma](https://github.com/puma/puma/issues/2580) support.
43
44
  * `--nginx` - serve static files via [nginx](https://www.nginx.com/). May require `--root` on some targets to access `/dev/stdout`
44
45
  * `--no-link` - don't add [--link](https://docs.docker.com/engine/reference/builder/#copy---link) to COPY statements. Some tools (like at the moment, [buildah](https://www.redhat.com/en/topics/containers/what-is-buildah)) don't yet support this feature.
45
46
  * `--no-lock` - don't add linux platforms, set `BUNDLE_DEPLOY`, or `--frozen-lockfile`. May be needed at times to work around a [rubygems bug](https://github.com/rubygems/rubygems/issues/6082#issuecomment-1329756343).
@@ -51,6 +52,7 @@ Generally the dockerfile generator will be able to determine what dependencies y
51
52
  are actually using. But should you be using DATABASE_URL, for example, at runtime
52
53
  additional support may be needed:
53
54
 
55
+ * `--litefs` - use [LiteFS](https://fly.io/docs/litefs/)
54
56
  * `--mysql` - add mysql libraries
55
57
  * `--postgresql` - add postgresql libraries
56
58
  * `--redis` - add redis libraries
@@ -65,16 +67,18 @@ Not all of your needs can be determined by scanning your application. For examp
65
67
  * `--env=name:value` - add an environment variable
66
68
  * `--remove package...` - remove package from "to be added" list
67
69
 
68
- Each of these can be tailored to a specific build phase by adding `-base`, `-build`, or `-deploy` after the flag name (e.g `--env-build:`). If no such suffix is found, the default for arg is `-base`, and the default for the rest is `-deploy`. Removal of an arg or environment variable is done by leaving the value blank.
70
+ Each of these can be tailored to a specific build phase by adding `-base`, `-build`, or `-deploy` after the flag name (e.g `--add-build freetds-dev --add-deploy freetds-bin`). If no such suffix is found, the default for arg is `-base`, and the default for the rest is `-deploy`. Removal of an arg or environment variable is done by leaving the value blank (e.g `--env-build=PORT:`).
69
71
 
70
72
  ### Configuration:
71
73
 
72
74
  * `--bin-cd` - adjust binstubs to set current working directory
75
+ [autocrlf](https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration#_core_autocrlf) enabled or may not be able to set bin stubs as executable.
73
76
  * `--label=name:value` - specify docker label. Can be used multiple times. See [LABEL](https://docs.docker.com/engine/reference/builder/#label) for detail
74
77
  * `--no-prepare` - omit `db:prepare`. Useful for cloud platforms with [release](https://devcenter.heroku.com/articles/release-phase) phases
75
78
  * `--platform=s` - specify target platform. See [FROM](https://docs.docker.com/engine/reference/builder/#from) for details
76
79
  * `--precompile=defer` - may be needed when your configuration requires access to secrets that are not available at build time. Results in larger images and slower deployments.
77
80
  * `--root` - run application as root
81
+ * `--windows` - make Dockerfile work for Windows users that may have set `git config --global core.autocrlf true`
78
82
 
79
83
  Options are saved between runs into `config/dockerfile.yml`. To invert a boolean options, add or remove a `no-` prefix from the option name.
80
84
 
@@ -107,9 +111,9 @@ If you are running a single test, the following environment variables settings m
107
111
  * `TEST_CAPTURE=1` will capture test results.
108
112
  * `TEST_KEEP=1` will leave the test app behind for inspection after the test completes.
109
113
 
110
- ## Links
114
+ ## Historical Links
111
115
 
112
- The following links relate to the current development status with respect to Rails 7.1 and will be removed once that is resolved.
116
+ The following links relate to the coordination between this package and Rails 7.1.
113
117
 
114
118
  * [Preparations for Rails 7.1](https://community.fly.io/t/preparations-for-rails-7-1/9512) - [Fly.io](https://fly.io/)'s plans and initial discussions with DHH
115
119
  * [Rails Dockerfile futures](https://discuss.rubyonrails.org/t/rails-dockerfile-futures/82091/1) - rationale for a generator
@@ -118,3 +122,7 @@ The following links relate to the current development status with respect to Rai
118
122
  * Fly.io [Cut over to Rails Dockerfile Generator on Sunday 29 Jan 2023](https://community.fly.io/t/cut-over-to-rails-dockerfile-generator-on-sunday-29-jan-2023/10350)
119
123
  * Fly.io [FAQ](https://fly.io/docs/rails/getting-started/dockerfiles/)
120
124
  * DDH's [target](https://github.com/rails/rails/pull/47372#issuecomment-1438971730)
125
+
126
+ Parallel efforts for Hanami:
127
+
128
+ * [Proposal](https://discourse.hanamirb.org/t/dockerfile-hanami/816)
data/Rakefile CHANGED
@@ -39,7 +39,7 @@ namespace :test do
39
39
  sh "docker run -p 3000:3000 -e RAILS_MASTER_KEY=#{key} --rm system:test"
40
40
  end
41
41
  ensure
42
- rm_rf "test/tmp/system_test"
42
+ rm_rf "test/tmp/system_test" unless ENV["TEST_KEEP"]
43
43
  end
44
44
 
45
45
  task all: %w(test:rubocop test test:system)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "erb"
4
+ require "json"
4
5
  require_relative "../dockerfile-rails/scanner.rb"
5
6
 
6
7
  class DockerfileGenerator < Rails::Generators::Base
@@ -15,10 +16,13 @@ class DockerfileGenerator < Rails::Generators::Base
15
16
  "jemalloc" => false,
16
17
  "label" => {},
17
18
  "link" => true,
19
+ "litefs" => false,
18
20
  "lock" => true,
21
+ "max-idle" => nil,
19
22
  "mysql" => false,
20
23
  "nginx" => false,
21
24
  "parallel" => false,
25
+ "passenger" => false,
22
26
  "platform" => nil,
23
27
  "postgresql" => false,
24
28
  "precompile" => nil,
@@ -28,6 +32,7 @@ class DockerfileGenerator < Rails::Generators::Base
28
32
  "sqlite3" => false,
29
33
  "sudo" => false,
30
34
  "swap" => nil,
35
+ "windows" => false,
31
36
  "yjit" => false,
32
37
  }.then { |hash| Struct.new(*hash.keys.map(&:to_sym)).new(*hash.values) }
33
38
 
@@ -84,6 +89,9 @@ class DockerfileGenerator < Rails::Generators::Base
84
89
  class_option "bin-cd", type: :boolean, default: OPTION_DEFAULTS["bin-cd"],
85
90
  desc: "modify binstubs to set working directory"
86
91
 
92
+ class_option "windows", type: :boolean, default: OPTION_DEFAULTS["windows"],
93
+ desc: "fixup CRLF in binstubs and make each executable"
94
+
87
95
  class_option :cache, type: :boolean, default: OPTION_DEFAULTS.cache,
88
96
  desc: "use build cache to speed up installs"
89
97
 
@@ -105,6 +113,9 @@ class DockerfileGenerator < Rails::Generators::Base
105
113
  class_option :sqlite3, aliases: "--sqlite", type: :boolean, default: OPTION_DEFAULTS.sqlite3,
106
114
  desc: "include sqlite3 libraries"
107
115
 
116
+ class_option :litefs, type: :boolean, default: OPTION_DEFAULTS.litefs,
117
+ desc: "replicate sqlite3 databases using litefs"
118
+
108
119
  class_option :postgresql, aliases: "--postgres", type: :boolean, default: OPTION_DEFAULTS.postgresql,
109
120
  desc: "include postgresql libraries"
110
121
 
@@ -129,6 +140,12 @@ class DockerfileGenerator < Rails::Generators::Base
129
140
  class_option :nginx, type: :boolean, default: OPTION_DEFAULTS.nginx,
130
141
  desc: "Serve static files with nginx"
131
142
 
143
+ class_option :passenger, type: :boolean, default: OPTION_DEFAULTS.passenger,
144
+ desc: "Serve Rails application with Phusion Passsenger"
145
+
146
+ class_option "max-idle", type: :string, default: OPTION_DEFAULTS["max-idle"],
147
+ desc: "Exit server after application has been idle for n seconds."
148
+
132
149
  class_option :root, type: :boolean, default: OPTION_DEFAULTS.root,
133
150
  desc: "Run application as root user"
134
151
 
@@ -221,6 +238,12 @@ class DockerfileGenerator < Rails::Generators::Base
221
238
 
222
239
  template "docker-compose.yml.erb", "docker-compose.yml" if options.compose
223
240
 
241
+ if using_litefs?
242
+ template "litefs.yml.erb", "config/litefs.yml"
243
+
244
+ fly_attach_consul
245
+ end
246
+
224
247
  if @gemfile.include?("vite_ruby")
225
248
  package = JSON.load_file("package.json")
226
249
  unless package.dig("scripts", "build")
@@ -284,12 +307,27 @@ private
284
307
  options.root?
285
308
  end
286
309
 
310
+ def using_litefs?
311
+ options.litefs?
312
+ end
313
+
287
314
  def using_node?
288
315
  return @using_node if @using_node != nil
289
316
  @using_node = File.exist? "package.json"
290
317
  end
291
318
 
292
319
  def using_redis?
320
+ # Note: If you have redis installed on your computer, 'rails new` will
321
+ # automatically add redis to your Gemfile, so having it in your Gemfile is
322
+ # not a reliable indicator of whether or not your application actually uses
323
+ # redis.
324
+
325
+ # using_redis? is currently used for two things: actually adding the redis
326
+ # gem if it is going to be needed in production, and adding a redis
327
+ # container to docker-compose.yml. Neither of these actions should be done
328
+ # unless there is an indication that redis is actually being used and not
329
+ # merely included in the Gemfile.
330
+
293
331
  options.redis? or @redis or @gemfile.include?("sidekiq")
294
332
  end
295
333
 
@@ -301,6 +339,10 @@ private
301
339
  @gemfile.include?("grover") or @gemfile.include?("puppeteer-ruby")
302
340
  end
303
341
 
342
+ def using_passenger?
343
+ options.passenger? or options["max-idle"]
344
+ end
345
+
304
346
  def using_sidekiq?
305
347
  @gemfile.include?("sidekiq")
306
348
  end
@@ -369,7 +411,9 @@ private
369
411
  gems = ["bundler"]
370
412
 
371
413
  if options.ci? && options.lock? && @gemfile.include?("debug")
372
- gems += %w(irb reline) - @gemfile
414
+ # https://github.com/rails/rails/pull/47515
415
+ # https://github.com/rubygems/rubygems/issues/6082#issuecomment-1329756343
416
+ gems += %w(irb reline) - @gemfile unless Gem.ruby_version >= "3.2.2"
373
417
  end
374
418
 
375
419
  gems.sort
@@ -391,7 +435,6 @@ private
391
435
  # libicu63 in buster, libicu67 in bullseye, libiclu72 in bookworm...
392
436
  packages << "libicu-dev" if @gemfile.include? "charlock_holmes"
393
437
 
394
-
395
438
  if @gemfile.include? "webp-ffi"
396
439
  # https://github.com/le0pard/webp-ffi#requirements
397
440
  packages += %w(libjpeg-dev libpng-dev libtiff-dev libwebp-dev)
@@ -464,7 +507,11 @@ private
464
507
  # start with databases: sqlite3, postgres, mysql
465
508
  packages << "libsqlite3-0" if options.sqlite3? || @sqlite3
466
509
  packages << "postgresql-client" if options.postgresql? || @postgresql
467
- packages << "default-mysql-client" if options.mysql || @mysql
510
+ packages << "default-mysql-client" if options.mysql? || @mysql
511
+ packages << "libjemalloc2" if options.jemalloc? && !options.fullstaq?
512
+
513
+ # litefs
514
+ packages += ["ca-certificates", "fuse3", "sudo"] if options.litefs?
468
515
 
469
516
  # ActiveStorage preview support
470
517
  packages << "libvips" if @gemfile.include? "ruby-vips"
@@ -483,8 +530,11 @@ private
483
530
  end
484
531
  end
485
532
 
533
+ # Passenger
534
+ packages += %w(passenger libnginx-mod-http-passenger) if using_passenger?
535
+
486
536
  # nginx
487
- packages << "nginx" if options.nginx?
537
+ packages << "nginx" if options.nginx? || using_passenger?
488
538
 
489
539
  # sudo
490
540
  packages << "sudo" if options.sudo?
@@ -494,18 +544,35 @@ private
494
544
 
495
545
  def deploy_repos
496
546
  repos = []
547
+ packages = []
497
548
 
498
549
  if using_puppeteer? && deploy_packages.include?("google-chrome-stable")
550
+ packages += %w(gnupg curl)
499
551
  repos += [
500
- "curl https://dl-ssl.google.com/linux/linux_signing_key.pub |",
501
- "gpg --dearmor > /etc/apt/trusted.gpg.d/google-archive.gpg &&",
552
+ "curl https://oss-binaries.phusionpassenger.com/auto-software-signing-gpg-key.txt |",
553
+ " gpg --dearmor > /etc/apt/trusted.gpg.d/google-archive.gpg &&",
502
554
  'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
503
555
  ]
504
556
  end
505
557
 
558
+ if using_passenger?
559
+ packages += %w(gnupg curl)
560
+ repos += [
561
+ "curl https://oss-binaries.phusionpassenger.com/auto-software-signing-gpg-key.txt |",
562
+ " gpg --dearmor > /etc/apt/trusted.gpg.d/phusion.gpg &&",
563
+ "bash -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger $(source /etc/os-release; echo $VERSION_CODENAME) main > /etc/apt/sources.list.d/passenger.list'"
564
+ ]
565
+ end
566
+
506
567
  if repos.empty?
507
568
  ""
508
569
  else
570
+ packages.sort!.uniq!
571
+ unless packages.empty?
572
+ repos.unshift "apt-get update -qq &&",
573
+ "apt-get install --no-install-recommends -y #{packages.join(" ")} &&"
574
+ end
575
+
509
576
  repos.join(" \\\n ") + " && \\\n "
510
577
  end
511
578
  end
@@ -552,18 +619,26 @@ private
552
619
  def deploy_env
553
620
  env = {}
554
621
 
555
- env["PORT"] = "3001" if options.nginx?
622
+ env["PORT"] = "3001" if (options.nginx? && !using_passenger?) || using_litefs?
556
623
 
557
624
  if Rails::VERSION::MAJOR < 7 || Rails::VERSION::STRING.start_with?("7.0")
558
625
  env["RAILS_LOG_TO_STDOUT"] = "1"
559
626
  env["RAILS_SERVE_STATIC_FILES"] = "true" unless options.nginx?
560
627
  end
561
628
 
629
+ if deploy_database == "sqlite3"
630
+ if using_litefs?
631
+ env["DATABASE_URL"] = "sqlite3:///litefs/production.sqlite3"
632
+ else
633
+ env["DATABASE_URL"] = "sqlite3:///data/production.sqlite3"
634
+ end
635
+ end
636
+
562
637
  if options.yjit?
563
638
  env["RUBY_YJIT_ENABLE"] = "1"
564
639
  end
565
640
 
566
- if options.jemalloc? && (not options.fullstaq?)
641
+ if options.jemalloc? && !options.fullstaq?
567
642
  if (options.platform || Gem::Platform.local.cpu).include? "arm"
568
643
  env["LD_PRELOAD"] = "/usr/lib/aarch64-linux-gnu/libjemalloc.so.2"
569
644
  else
@@ -590,7 +665,7 @@ private
590
665
 
591
666
  env.merge! @@vars["base"] if @@vars["base"]
592
667
 
593
- env.map { |key, value| "#{key}=#{value.inspect}" }
668
+ env.map { |key, value| "#{key}=#{value.inspect}" }.sort
594
669
  end
595
670
 
596
671
  def base_args
@@ -648,13 +723,13 @@ private
648
723
  # none are required, but prepares for the need to do the
649
724
  # fix line endings if other fixups are required.
650
725
  has_cr = Dir["bin/*"].any? { |file| IO.read(file).include? "\r" }
651
- if has_cr || (Gem.win_platform? && !binfixups.empty?)
726
+ if has_cr || (Gem.win_platform? && !binfixups.empty?) || options.windows?
652
727
  binfixups.unshift 'sed -i "s/\r$//g" bin/*'
653
728
  end
654
729
 
655
730
  # Windows file systems may not have the concept of executable.
656
731
  # In such cases, fix up during the build.
657
- unless Dir["bin/*"].all? { |file| File.executable? file }
732
+ if Dir["bin/*"].any? { |file| !File.executable?(file) } || options.windows?
658
733
  binfixups.unshift "chmod +x bin/*"
659
734
  end
660
735
 
@@ -667,9 +742,11 @@ private
667
742
  end
668
743
 
669
744
  def deploy_database
670
- if options.postgresql? || @postgresql
745
+ # note: as database can be overridden at runtime via DATABASE_URL,
746
+ # use presence of "pg" or "mysql2" in the bundle as evidence of intent.
747
+ if options.postgresql? || @postgresql || @gemfile.include?("pg")
671
748
  "postgresql"
672
- elsif options.mysql || @mysql
749
+ elsif options.mysql? || @mysql || @gemfile.include?("mysql2")
673
750
  "mysql"
674
751
  else
675
752
  "sqlite3"
@@ -758,7 +835,11 @@ private
758
835
  end
759
836
 
760
837
  def procfile
761
- if options.nginx?
838
+ if using_passenger?
839
+ {
840
+ nginx: "nginx"
841
+ }
842
+ elsif options.nginx?
762
843
  {
763
844
  nginx: '/usr/sbin/nginx -g "daemon off;"',
764
845
  rails: "./bin/rails server -p 3001"
@@ -782,4 +863,50 @@ private
782
863
 
783
864
  more
784
865
  end
866
+
867
+ def max_idle
868
+ option = options["max-idle"]
869
+
870
+ if option == nil || option.strip.downcase == "infinity"
871
+ nil
872
+ elsif /^\s*\d+(\.\d+)\s*/.match? option
873
+ option.to_f
874
+ elsif /^\s*P/.match? option
875
+ ActiveSupport::Duration.parse(option.strip).seconds
876
+ else
877
+ option.scan(/\d+\w/).map do |t|
878
+ ActiveSupport::Duration.parse("PT#{t.upcase}") rescue ActiveSupport::Duration.parse("P#{t.upcase}")
879
+ end.sum.seconds
880
+ end
881
+ rescue ArgumentError
882
+ nil
883
+ end
884
+
885
+ # if running on fly v2, make a best effort to attach consul
886
+ def fly_attach_consul
887
+ # certainly not fly unless there is a fly.toml
888
+ return unless File.exist? "fly.toml"
889
+
890
+ # Check fly.toml to guess if v1 or v2
891
+ toml = File.read("fly.toml")
892
+ return if toml.include?("enable_consul") # v1-ism
893
+ return unless toml.include?("primary_region") # v2
894
+
895
+ # see if flyctl is in the path
896
+ paths = ENV["PATH"].split(File::PATH_SEPARATOR)
897
+ cmds = %w(flyctl)
898
+ exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]
899
+ flyctl = Enumerator.product(paths, cmds, exts).
900
+ map { |path, cmd, ext| File.join(path, "#{cmd}#{ext}") }.
901
+ find { |path| File.executable? path }
902
+ return unless flyctl
903
+
904
+ # see if secret is already set?
905
+ secrets = JSON.parse(`#{flyctl} secrets list --json`)
906
+ return if secrets.any? { |secret| secret["Name"] == "FLY_CONSUL_URL" }
907
+
908
+ # attach consul
909
+ say_status "execute", "flyctl consul attach", :green
910
+ system "#{flyctl} consul attach"
911
+ end
785
912
  end
@@ -134,6 +134,12 @@ RUN SECRET_KEY_BASE<%= Rails::VERSION::MAJOR<7 || Rails::VERSION::STRING.start_w
134
134
  # Final stage for app image
135
135
  FROM base
136
136
 
137
+ <% end -%>
138
+ <% if using_litefs? -%>
139
+ # Install, configure litefs
140
+ COPY --from=flyio/litefs:0.4.0 /usr/local/bin/litefs /usr/local/bin/litefs
141
+ COPY<% if options.link? %> --link<% end %> config/litefs.yml /etc/litefs.yml
142
+
137
143
  <% end -%>
138
144
  <% unless deploy_args.empty? -%>
139
145
  # Deployment build arguments
@@ -145,38 +151,63 @@ ARG <%= deploy_args.map {|key, value| "#{key}=#{value.inspect}"}.join(" \\\n
145
151
  <%= render partial: 'apt_install', locals: {packages: deploy_packages, clean: true, repos: deploy_repos} %>
146
152
  <% end -%>
147
153
 
148
- <% if options.nginx? -%>
154
+ <% if using_passenger? -%>
155
+ <%= render partial: 'passenger' %>
156
+
157
+ <% elsif options.nginx? -%>
149
158
  <%= render partial: 'nginx' %>
150
159
 
151
160
  <% elsif procfile.size > 1 -%>
152
161
  RUN gem install foreman
153
162
 
154
163
  <% end -%>
155
- <% unless run_as_root? -%>
156
- # Run and own the application files as a non-root user for security
164
+ <% unless options.precompile == "defer" -%>
165
+ # Copy built artifacts: gems, application
166
+ COPY --from=build /usr/local/bundle /usr/local/bundle
167
+ COPY --from=build /rails /rails
168
+ <% if api_client_dir -%>
169
+
170
+ # Copy built client
171
+ COPY --from=client /rails/<%= api_client_dir %>/build /rails/public
172
+ <% end -%>
173
+
174
+ <% end -%>
175
+ <% if run_as_root? -%>
176
+ <% if deploy_database == 'sqlite3' -%>
177
+ RUN mkdir /data
178
+ <% end -%>
179
+ <% else -%>
180
+ # Run and own only the runtime files as a non-root user for security
157
181
  <% if options.compose? -%>
158
182
  ARG UID=1000 \
159
183
  GID=1000
160
184
  RUN groupadd -f -g $GID rails && \
161
- useradd -u $UID -g $GID rails<% else -%>
162
- RUN useradd rails<% end -%> --home /rails --shell /bin/bash<% if options.nginx? %> && \
163
- chown rails:rails /var/lib/nginx /var/log/nginx/*<% end %><% if deploy_packages.include?("sudo") && options.sudo? %> && \
185
+ useradd -u $UID -g $GID rails --create-home --shell /bin/bash && \
186
+ <% else -%>
187
+ RUN useradd rails --create-home --shell /bin/bash && \
188
+ <% end -%>
189
+ <% if options.nginx? -%>
190
+ chown rails:rails /var/lib/nginx /var/log/nginx/* && \
191
+ <% end -%>
192
+ <% if deploy_packages.include?("sudo") && options.sudo? -%>
164
193
  sed -i 's/env_reset/env_keep="*"/' /etc/sudoers && \
165
- chown rails:rails .<% end %>
166
- <% unless options.swap -%>
167
- USER rails:rails
168
194
  <% end -%>
169
-
195
+ <% if deploy_database == 'sqlite3' -%>
196
+ mkdir /data<% if using_litefs? %> /litefs<% end %> && \
197
+ chown -R rails:rails <%= Dir[*%w(db log storage tmp)].join(" ") %> /data<% if using_litefs? %> /litefs<% end %>
198
+ <% else -%>
199
+ chown -R rails:rails <%= Dir[*%w(db log storage tmp)].join(" ") %>
200
+ <% end -%>
201
+ <% unless options.swap? or using_passenger? or using_litefs? -%>
202
+ USER rails:rails
170
203
  <% end -%>
171
- <% unless options.precompile == "defer" -%>
172
- # Copy built artifacts: gems, application
173
- COPY --from=build /usr/local/bundle /usr/local/bundle
174
- COPY --from=build <% unless run_as_root? %>--chown=rails:rails <% end %>/rails /rails
175
- <% if api_client_dir -%>
176
204
 
177
- # Copy built client
178
- COPY --from=client <% unless run_as_root? %>--chown=rails:rails <% end %>/rails/<%= api_client_dir %>/build /rails/public
179
205
  <% end -%>
206
+ <% if using_litefs? and !run_as_root? -%>
207
+ # Authorize rails user to launch litefs
208
+ COPY <<-"EOF" /etc/sudoers.d/rails
209
+ rails ALL=(root) /usr/local/bin/litefs
210
+ EOF
180
211
 
181
212
  <% end -%>
182
213
  <% unless deploy_env.empty? -%>
@@ -201,9 +232,15 @@ EOF
201
232
 
202
233
  # Start the server by default, this can be overwritten at runtime
203
234
  EXPOSE 3000
235
+ <% if deploy_database == 'sqlite3' -%>
236
+ VOLUME /data
237
+ <% end -%>
204
238
  CMD ["foreman", "start", "--procfile=Procfile.prod"]
205
239
  <% else -%>
206
240
  # Start the server by default, this can be overwritten at runtime
207
241
  EXPOSE 3000
208
- CMD ["./bin/rails", "server"]
242
+ <% if deploy_database == 'sqlite3' -%>
243
+ VOLUME /data
244
+ <% end -%>
245
+ CMD <%= procfile.values.first.split(" ").inspect %>
209
246
  <% end -%>
@@ -0,0 +1,28 @@
1
+ # configure nginx and passenger
2
+ COPY <<-'EOF' /etc/nginx/sites-enabled/default
3
+ server {
4
+ listen 3000;
5
+ root /rails/public;
6
+ passenger_enabled on;
7
+ <% if options['max-idle'] -%>
8
+ passenger_ctl hook_detached_process /etc/nginx/hook_detached_process;
9
+ passenger_min_instances 0;
10
+ passenger_pool_idle_time <%= max_idle %>;
11
+ <% end -%>
12
+ }
13
+ <% if options['max-idle'] -%>
14
+ COPY <<-'EOF' /etc/nginx/sites-enabled/hook_detached_process
15
+ #!/usr/bin/env ruby
16
+ status = `passenger-status`
17
+ processes = status[/^Processes\s*:\s*(\d*)/, 1].to_i
18
+ system 'nginx -s stop' if processes == 0
19
+ EOF
20
+ <% end -%>
21
+ EOF
22
+ RUN echo "daemon off;" >> /etc/nginx/nginx.conf && \
23
+ sed -i 's/access_log\s.*;/access_log \/dev\/stdout;/' /etc/nginx/nginx.conf && \
24
+ sed -i 's/error_log\s.*;/error_log \/dev\/stderr info;/' /etc/nginx/nginx.conf && \
25
+ <% if options['max-idle'] -%>
26
+ chmod +sx /etc/nginx/sites-enabled/hook_detached_process && \
27
+ <% end -%>
28
+ mkdir /var/run/passenger-instreg
@@ -1,7 +1,7 @@
1
1
  #!/bin/bash -e
2
2
 
3
3
  <% if options.swap -%>
4
- <% if run_as_root? -%>
4
+ <% if run_as_root? or using_passenger? -%>
5
5
  <% @space = "" -%>
6
6
  <% else -%>
7
7
  <% @space = " " -%>
@@ -14,11 +14,21 @@ if [ $UID -eq 0 ]; then
14
14
  <%= @space %>echo 10 > /proc/sys/vm/swappiness
15
15
  <%= @space %>swapon /swapfile
16
16
  <%= @space %>echo 1 > /proc/sys/vm/overcommit_memory
17
- <% unless run_as_root? -%>
17
+ <% if using_litefs? -%>
18
+
19
+ <%= @space %># mount litefs
20
+ <%= @space %>litefs mount &
21
+ <% end -%>
22
+ <% unless run_as_root? or using_passenger? -%>
23
+
18
24
  exec su rails $0 $@
19
25
  fi
20
26
  <% end -%>
21
27
 
28
+ <% elsif using_litefs? -%>
29
+ # mount litefs
30
+ <% unless run_as_root? %>sudo -E <% end %>litefs mount &
31
+
22
32
  <% end -%>
23
33
  <% if options.prepare -%>
24
34
  <% if procfile.size > 1 -%>
@@ -26,7 +36,7 @@ fi
26
36
  if [ "${*}" == "foreman start --procfile=Procfile.prod" ]; then
27
37
  <% else -%>
28
38
  # If running the rails server then create or migrate existing database
29
- if [ "${*}" == "./bin/rails server" ]; then
39
+ if [ "${*}" == <%= procfile.values.first.inspect %> <% if using_litefs? %>-a "$FLY_REGION" == "$PRIMARY_REGION" <%end%>]; then
30
40
  <% end -%>
31
41
  <% if options.precompile == "defer" -%>
32
42
  ./bin/rails assets:precompile
@@ -0,0 +1,116 @@
1
+ # based on: https://github.com/superfly/litefs/blob/main/cmd/litefs/etc/litefs.yml
2
+
3
+ # The FUSE section handles settings on the FUSE file system. FUSE
4
+ # provides a layer for intercepting SQLite transactions on the
5
+ # primary node so they can be shipped to replica nodes transparently.
6
+ fuse:
7
+ # Required. This is the mount directory that applications will
8
+ # use to access their SQLite databases.
9
+ dir: "/litefs"
10
+
11
+ # Set this flag to true to allow non-root users to access mount.
12
+ # You must set the "user_allow_other" option in /etc/fuse.conf first.
13
+ allow-other: false
14
+
15
+ # The debug flag enables debug logging of all FUSE API calls.
16
+ # This will produce a lot of logging. Not for general use.
17
+ debug: false
18
+
19
+ # The data section specifies where internal LiteFS data is stored
20
+ # and how long to retain the transaction files.
21
+ #
22
+ # Transaction files are used to ship changes to replica nodes so
23
+ # they should persist long enough for replicas to retrieve them,
24
+ # even in the face of a short network interruption or a redeploy.
25
+ # Under high load, these files can grow large so it's not advised
26
+ # to extend retention too long.
27
+ data:
28
+ # Path to internal data storage.
29
+ dir: "/data"
30
+
31
+ # Duration to keep LTX files. Latest LTX file is always kept.
32
+ retention: "10m"
33
+
34
+ # Frequency with which to check for LTX files to delete.
35
+ retention-monitor-interval: "1m"
36
+
37
+ # If true, then LiteFS will not wait until the node becomes the
38
+ # primary or connects to the primary before starting the subprocess.
39
+ skip-sync: false
40
+
41
+ # If true, then LiteFS will not exit if there is a validation
42
+ # issue on startup. This can be useful for debugging issues as
43
+ # it avoids constantly restarting the node on ephemeral hosting.
44
+ exit-on-error: false
45
+
46
+ # This section defines settings for the LiteFS HTTP API server.
47
+ # This API server is how nodes communicate with each other.
48
+ http:
49
+ # Specifies the bind address of the HTTP API server.
50
+ addr: ":20202"
51
+
52
+ # This section defines settings for the option HTTP proxy.
53
+ # This proxy can handle primary forwarding & replica consistency
54
+ # for applications that use a single SQLite database.
55
+ proxy:
56
+ # Specifies the bind address of the proxy server.
57
+ addr: ":3000"
58
+
59
+ # The hostport of the target application.
60
+ target: "localhost:3001"
61
+
62
+ # The name of the database used for TXID tracking.
63
+ db: "production.sqlite3"
64
+
65
+ # If true, enables verbose logging of requests by the proxy.
66
+ debug: false
67
+
68
+ # List of paths that are ignored by the proxy. The asterisk is
69
+ # the only available wildcard. These requests are passed
70
+ # through to the target as-is.
71
+ passthrough: []
72
+
73
+ # The lease section defines how LiteFS creates a cluster and
74
+ # implements leader election. For dynamic clusters, use the
75
+ # "consul". This allows the primary to change automatically when
76
+ # the current primary goes down. For a simpler setup, use
77
+ # "static" which assigns a single node to be the primary and does
78
+ # not failover.
79
+ lease:
80
+ # Required. Must be either "consul" or "static".
81
+ type: "consul"
82
+
83
+ # Required. The URL for this node's LiteFS API.
84
+ # Should match HTTP port.
85
+ advertise-url: "http://${HOSTNAME}.vm.${FLY_APP_NAME}.internal:20202"
86
+
87
+ # Specifies whether the node can become the primary. If using
88
+ # "static" leasing, this should be set to true on the primary
89
+ # and false on the replicas.
90
+ candidate: ${FLY_REGION == PRIMARY_REGION}
91
+
92
+ # A Consul server provides leader election and ensures that the
93
+ # responsibility of the primary node can be moved in the event
94
+ # of a deployment or a failure.
95
+ consul:
96
+ # Required. The base URL of the Consul server.
97
+ url: "${FLY_CONSUL_URL}"
98
+
99
+ # Required. The key used for obtaining a lease by the primary.
100
+ # This must be unique for each cluster of LiteFS servers
101
+ key: "litefs/${FLY_APP_NAME}"
102
+
103
+ # Length of time before a lease expires. The primary will
104
+ # automatically renew the lease while it is alive, however,
105
+ # if it fails to renew in time then a new primary may be
106
+ # elected after the TTL. This only occurs for unexpected loss
107
+ # of the leader as normal operation will allow the leader to
108
+ # handoff the lease to another replica without downtime.
109
+ #
110
+ # Consul does not allow a TTL of less than 10 seconds.
111
+ ttl: "10s"
112
+
113
+ # Length of time after the lease expires before a candidate
114
+ # can become leader. This buffer is intended to prevent
115
+ # overlap in leadership due to clock skew or in-flight calls.
116
+ lock-delay: "1s"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dockerfile-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.5
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Ruby
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-05 00:00:00.000000000 Z
11
+ date: 2023-05-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -44,16 +44,18 @@ files:
44
44
  - lib/generators/templates/_nginx.erb
45
45
  - lib/generators/templates/_node_client.erb
46
46
  - lib/generators/templates/_npm_install.erb
47
+ - lib/generators/templates/_passenger.erb
47
48
  - lib/generators/templates/docker-compose.yml.erb
48
49
  - lib/generators/templates/docker-entrypoint.erb
49
50
  - lib/generators/templates/dockerfile.yml.erb
50
51
  - lib/generators/templates/dockerignore.erb
52
+ - lib/generators/templates/litefs.yml.erb
51
53
  - lib/generators/templates/node-version.erb
52
- homepage: https://github.com/rubys/dockerfile-rails
54
+ homepage: https://github.com/fly-apps/dockerfile-rails
53
55
  licenses:
54
56
  - MIT
55
57
  metadata:
56
- homepage_uri: https://github.com/rubys/dockerfile-rails
58
+ homepage_uri: https://github.com/fly-apps/dockerfile-rails
57
59
  post_install_message:
58
60
  rdoc_options: []
59
61
  require_paths:
@@ -69,7 +71,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
69
71
  - !ruby/object:Gem::Version
70
72
  version: '0'
71
73
  requirements: []
72
- rubygems_version: 3.4.6
74
+ rubygems_version: 3.4.8
73
75
  signing_key:
74
76
  specification_version: 4
75
77
  summary: Dockerfile generator for Rails