Skip to content

Commit bee3fa4

Browse files
authored
Add Kamal by default to Rails 8 (rails#51798)
* Add Kamal by default
1 parent 8eae753 commit bee3fa4

File tree

9 files changed

+140
-0
lines changed

9 files changed

+140
-0
lines changed

railties/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
1+
* Use Kamal for deployment by default, which includes generating a Rails-specific config/deploy.yml.
2+
This can be skipped using --skip-kamal. See more: https://kamal-deploy.org/
3+
4+
*DHH*
15

26
Please check [7-2-stable](https://github.com/rails/rails/blob/7-2-stable/railties/CHANGELOG.md) for previous changes.

railties/lib/rails/generators/app_base.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,9 @@ def self.add_shared_options_for(name)
112112
class_option :skip_devcontainer, type: :boolean, default: false,
113113
desc: "Skip devcontainer files"
114114

115+
class_option :skip_kamal, type: :boolean, default: false,
116+
desc: "Skip Kamal setup"
117+
115118
class_option :dev, type: :boolean, default: nil,
116119
desc: "Set up the #{name} with Gemfile pointing to your Rails checkout"
117120

@@ -407,6 +410,10 @@ def skip_devcontainer?
407410
options[:skip_devcontainer]
408411
end
409412

413+
def skip_kamal?
414+
options[:skip_kamal]
415+
end
416+
410417
class GemfileEntry < Struct.new(:name, :version, :comment, :options, :commented_out)
411418
def initialize(name, version, comment, options = {}, commented_out = false)
412419
super
@@ -718,6 +725,16 @@ def run_css
718725
end
719726
end
720727

728+
def run_kamal
729+
return if options[:skip_kamal] || !bundle_install?
730+
731+
bundle_command "binstubs kamal"
732+
bundle_command "exec kamal init"
733+
734+
template "env.erb", ".env.erb"
735+
template "config/deploy.yml", force: true
736+
end
737+
721738
def add_bundler_platforms
722739
if bundle_install?
723740
# The vast majority of Rails apps will be deployed on `x86_64-linux`.

railties/lib/rails/generators/rails/app/app_generator.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,7 @@ def finish_template
570570
public_task :run_javascript
571571
public_task :run_hotwire
572572
public_task :run_css
573+
public_task :run_kamal
573574

574575
def run_after_bundle_callbacks
575576
@after_bundle_callbacks.each(&:call)

railties/lib/rails/generators/rails/app/templates/Gemfile.tt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ gem "tzinfo-data", platforms: %i[ <%= bundler_windows_platforms %> jruby ]
1919
# Reduces boot times through caching; required in config/boot.rb
2020
gem "bootsnap", require: false
2121
<% end -%>
22+
<% unless options.skip_kamal? -%>
23+
24+
# Deploy this application anywhere as a Docker container [https://kamal-deploy.org]
25+
gem "kamal", require: false
26+
<% end -%>
2227
<% unless skip_active_storage? -%>
2328

2429
# Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images]

railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ test:
2121
database: storage/test.sqlite3
2222

2323

24+
<%- if options.skip_kamal? -%>
2425
# SQLite3 write its data on the local filesystem, as such it requires
2526
# persistent disks. If you are deploying to a managed service, you should
2627
# make sure it provides disk persistence, as many don't.
@@ -30,3 +31,10 @@ test:
3031
production:
3132
<<: *default
3233
# database: path/to/persistent/storage/production.sqlite3
34+
<%- else -%>
35+
# Store production database in the storage/ directory, which by default
36+
# is mounted as a persistent Docker volume in config/deploy.yml.
37+
production:
38+
<<: *default
39+
database: storage/production.sqlite3
40+
<%- end -%>
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Name of your application. Used to uniquely configure containers.
2+
service: <%= app_name %>
3+
4+
# Name of the container image.
5+
image: your-user/<%= app_name %>
6+
7+
# Deploy to these servers.
8+
servers:
9+
web:
10+
- 192.168.0.1
11+
# job:
12+
# hosts:
13+
# - 192.168.0.1
14+
# cmd: bin/solid_queue work
15+
16+
# Credentials for your image host.
17+
registry:
18+
# Specify the registry server, if you're not using Docker Hub
19+
# server: registry.digitalocean.com / ghcr.io / ...
20+
username: your-user
21+
22+
# Always use an access token rather than real password when possible.
23+
password:
24+
- KAMAL_REGISTRY_PASSWORD
25+
26+
# Inject ENV variables into containers (secrets come from .env).
27+
# Remember to run `kamal env push` after making changes!
28+
env:
29+
secret:
30+
- RAILS_MASTER_KEY
31+
# clear:
32+
# DB_HOST: 192.168.0.2
33+
34+
# Use a persistent storage volume for sqlite database files and local Active Storage files.
35+
# Recommended to change this to a mounted volume path that is backed up off server.
36+
volumes:
37+
- "<%= app_name %>_storage:/rails/storage"
38+
39+
# Bridge fingerprinted assets, like JS and CSS, between versions to avoid
40+
# hitting 404 on in-flight requests. Combines all files from new and old
41+
# version inside the asset_path.
42+
asset_path: /rails/public/assets
43+
44+
# Use a different ssh user than root
45+
# ssh:
46+
# user: app
47+
48+
# Configure builder setup (defaults to multi-arch images).
49+
# builder:
50+
# # Build same-arch image locally (use for x86->x86)
51+
# multiarch: false
52+
#
53+
# # Build diff-arch image via remote server
54+
# remote:
55+
# arch: amd64
56+
# host: ssh://[email protected]
57+
#
58+
# args:
59+
# RUBY_VERSION: <%= ENV["RBENV_VERSION"] || ENV["rvm_ruby_string"] || "#{RUBY_ENGINE}-#{RUBY_ENGINE_VERSION}" %>
60+
# secrets:
61+
# - GITHUB_TOKEN
62+
# - RAILS_MASTER_KEY
63+
64+
# Use accessory services (secrets come from .env).
65+
# accessories:
66+
# db:
67+
# image: mysql:8.0
68+
# host: 192.168.0.2
69+
# port: 3306
70+
# env:
71+
# clear:
72+
# MYSQL_ROOT_HOST: '%'
73+
# secret:
74+
# - MYSQL_ROOT_PASSWORD
75+
# files:
76+
# - config/mysql/production.cnf:/etc/mysql/my.cnf
77+
# - db/production.sql:/docker-entrypoint-initdb.d/setup.sql
78+
# directories:
79+
# - data:/var/lib/mysql
80+
# redis:
81+
# image: redis:7.0
82+
# host: 192.168.0.2
83+
# port: 6379
84+
# directories:
85+
# - data:/data
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<%%# This env template should lookup values from a password store or from ENV -%>
2+
# Generated by kamal envify from .env.erb
3+
KAMAL_REGISTRY_PASSWORD=<%%= ENV["KAMAL_REGISTRY_PASSWORD"] %>
4+
RAILS_MASTER_KEY=<%%= File.read("config/master.key") %>
5+
# GITHUB_TOKEN=<%%#= `gh config get -h github.com oauth_token`.strip %>

railties/lib/rails/generators/rails/plugin/plugin_generator.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ def generate_test_dummy(force = false)
115115
opts[:skip_brakeman] = true
116116
opts[:skip_bundle] = true
117117
opts[:skip_ci] = true
118+
opts[:skip_kamal] = true
118119
opts[:skip_git] = true
119120
opts[:skip_hotwire] = true
120121
opts[:skip_rubocop] = true

railties/test/generators/app_generator_test.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,20 @@ def test_ci_files_are_skipped_if_required
654654
assert_no_file ".github/dependabot.yml"
655655
end
656656

657+
def test_inclusion_of_kamal_files
658+
run_generator_and_bundler [destination_root]
659+
660+
assert_file "config/deploy.yml"
661+
assert_file ".env.erb"
662+
end
663+
664+
def test_kamal_files_are_skipped_if_required
665+
run_generator_and_bundler [destination_root, "--skip-kamal"]
666+
667+
assert_no_file "config/deploy.yml"
668+
assert_no_file ".env.erb"
669+
end
670+
657671
def test_usage_read_from_file
658672
assert_called(File, :read, returns: "USAGE FROM FILE") do
659673
assert_equal "USAGE FROM FILE", Rails::Generators::AppGenerator.desc

0 commit comments

Comments
 (0)