capistrano-cookbook 0.0.4 → 5.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +13 -0
  3. data/README.md +39 -18
  4. data/capistrano-cookbook.gemspec +3 -1
  5. data/lib/capistrano/cookbook.rb +2 -2
  6. data/lib/capistrano/cookbook/helpers/setup_config_values.rb +27 -35
  7. data/lib/capistrano/cookbook/helpers/{template.rb → smart_template.rb} +21 -6
  8. data/lib/capistrano/cookbook/puma_systemd.rb +1 -0
  9. data/lib/capistrano/cookbook/sidekiq_systemd.rb +1 -0
  10. data/lib/capistrano/cookbook/tasks/compile_assets_locally.cap +1 -0
  11. data/lib/capistrano/cookbook/tasks/monit.cap +10 -1
  12. data/lib/capistrano/cookbook/tasks/nginx.cap +8 -6
  13. data/lib/capistrano/cookbook/tasks/puma_systemd.cap +17 -0
  14. data/lib/capistrano/cookbook/tasks/setup_config.cap +39 -15
  15. data/lib/capistrano/cookbook/tasks/sidekiq_systemd.cap +15 -0
  16. data/lib/capistrano/cookbook/version.rb +1 -1
  17. data/lib/generators/capistrano/reliably_deploying_rails/bootstrap_generator.rb +68 -0
  18. data/lib/generators/capistrano/reliably_deploying_rails/templates/Capfile.erb +46 -0
  19. data/lib/generators/capistrano/reliably_deploying_rails/templates/deploy.rb.erb +41 -0
  20. data/lib/generators/capistrano/reliably_deploying_rails/templates/nginx_conf.erb +105 -0
  21. data/lib/generators/capistrano/reliably_deploying_rails/templates/production.rb.erb +30 -0
  22. data/lib/generators/capistrano/reliably_deploying_rails/templates/puma.rb.erb +53 -0
  23. data/lib/generators/capistrano/reliably_deploying_rails/templates/puma.service.erb +29 -0
  24. data/lib/generators/capistrano/reliably_deploying_rails/templates/puma_monit.conf.erb +4 -0
  25. data/lib/generators/capistrano/reliably_deploying_rails/templates/sidekiq.service.capistrano.erb +30 -0
  26. data/lib/generators/capistrano/reliably_deploying_rails/templates/sidekiq_monit.erb +4 -0
  27. data/lib/generators/capistrano/reliably_deploying_rails/templates/staging.rb.erb +30 -0
  28. metadata +50 -15
  29. data/lib/capistrano/cookbook/restart.rb +0 -1
  30. data/lib/capistrano/cookbook/tasks/restart.cap +0 -10
  31. data/lib/capistrano/cookbook/templates/mongoid.example.yml.erb +0 -69
  32. data/lib/capistrano/cookbook/templates/monit.erb +0 -22
  33. data/lib/capistrano/cookbook/templates/nginx.conf.erb +0 -57
  34. data/lib/capistrano/cookbook/templates/unicorn.rb.erb +0 -41
  35. data/lib/capistrano/cookbook/templates/unicorn_init.sh.erb +0 -86
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 192d0bf014509cb56e6c84c30fd8be3a76719913
4
- data.tar.gz: 2e9ab7ae1a1190ab67a3163d2dbc4e0f823296d5
2
+ SHA256:
3
+ metadata.gz: ccf86ecd4d2c04f71dd86f6aaf38877d57c1b7482e21435986b58dcbd768e18c
4
+ data.tar.gz: 1a9f6bc210c07faf2c998ad13f438b97ac4efbccdb3734993e173f94cf7953be
5
5
  SHA512:
6
- metadata.gz: e0cefe15ebd19d73f8db9c3708f39e21c4a4d1164e97193b4dc279397ffbdd3cadbf33cd1bfa86792a17ea6abe5bce4bb334ba9573f79acf424aba39c6ea804e
7
- data.tar.gz: d62605469084670f78a9184e7274bd146283ab8b66817e491371b85b38329884e9ee2929b710fcd6eb798a186d3a26d457f29f6eaae26df1d58ad85b9a993986
6
+ metadata.gz: 936557739c95b9b6c4d2f7bc92fa300778437e0c335b84670c7b4d9e67dce28daa65196f0713cf2e9babdb3fe913777c181bf5587f9378880b4567f091453893
7
+ data.tar.gz: e55cb0581cfdde734cdea13b675ccb89aea40f1fab79c1280b1eda86590b37fb0cb3c22af6565d56e94d7fb4466b80d6823cb398ccb39d5b16e4277bd85066a2
data/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+
3
+ ## 5.0.1 (March 2021)
4
+
5
+ - Adds full support for deploy (but not config creation) without sudo access
6
+ - Refactor config files to use single array of hashes with flags
7
+ - Refactor Sidekiq and Monit configurations to copy files directly rather than using symlinks to avoid potential root access leak
8
+ - Fixes bug where object identifier was outputted in logs rather than filename
9
+ - Fixes nginx not being reloaded after `setup_config` due to shared log directory not yet existing
10
+
11
+ ## 5.0.0 (March 2021)
12
+
13
+ - Full overhaul to support Rails 6 and Ubuntu 20.04
data/README.md CHANGED
@@ -6,7 +6,7 @@ A collection of Capistrano 3 Compatible tasks to make deploying Rails and Sinatr
6
6
 
7
7
  Add this line to your application's Gemfile:
8
8
 
9
- gem 'capistrano-cookbook'
9
+ gem 'capistrano-cookbook', require: false, group: :development
10
10
 
11
11
  And then execute:
12
12
 
@@ -16,9 +16,44 @@ Or install it yourself as:
16
16
 
17
17
  $ gem install capistrano-cookbook
18
18
 
19
+ ## Versioning
20
+
21
+ This gem is primarily intended to provide a batteries included approach to getting Rails applications up and running. It is tested with the server configuration in <https://github.com/TalkingQuickly/rails-server-template>.
22
+
23
+ Major versions of the rails server template above and this gem stay in sync. So Version X.*.* of this gem should provide a working out of the box capistrano configuration when used with version X.*.* of the server template.
24
+
25
+ Major and minor versions of this gem, the server template and the book [Reliably Deploying Rails Applications](https://leanpub.com/deploying_rails_applications) also stay in sync. Updates to the book are free for life so on a new project, it's always advisable to make sure you have the latest version.
26
+
19
27
  ## Usage
20
28
 
21
- ### Including Tasks
29
+ ### Boostrap
30
+
31
+ To generate a complete Capistrano configuration include the gem in your Gemfile and then use the following Rails Generator:
32
+
33
+ ```
34
+ bundle exec rails g capistrano:reliably_deploying_rails:bootstrap --sidekiq --production_hostname='YOUR_PRODUCTION_DOMAIN' --production_server_address='YOUR_PRODUCTION_SERVER'
35
+ ```
36
+
37
+ Replacing `YOUR_PRODUCTION_DOMAIN` with the domain name people will access the Rails app on and `YOUR_PRODUCTION_SERVER` with the address or ip that can be used to access the server via SSH, these may be the same thing but often will not be (e.g. if the domain has a CDN or Load Balancer between it and the server you're deploying to),
38
+
39
+ This will generate a complete Capistrano configuration.
40
+
41
+ You can then execute:
42
+
43
+ ```
44
+ bundle exec cap production deploy:setup_config
45
+ bundle exec cap production database:create
46
+ ```
47
+
48
+ To perform setup tasks and create a new empty database. Followed by:
49
+
50
+ ```
51
+ bundle exec cap production deploy
52
+ ```
53
+
54
+ To kick off you first deploy.
55
+
56
+ ### Including Tasks Manually
22
57
 
23
58
  To include all tasks from the gem, add the following to your `Capfile`:
24
59
 
@@ -35,7 +70,6 @@ require 'capistrano/cookbook/create_database'
35
70
  require 'capistrano/cookbook/logs'
36
71
  require 'capistrano/cookbook/monit'
37
72
  require 'capistrano/cookbook/nginx'
38
- require 'capistrano/cookbook/restart'
39
73
  require 'capistrano/cookbook/run_tests'
40
74
  require 'capistrano/cookbook/setup_config'
41
75
  ```
@@ -66,7 +100,8 @@ Add the following to `deploy.rb`
66
100
 
67
101
  Currently only works with Postgresql on configurations where your web server and db server are the same machine, e.g. single box deployments. This task will:
68
102
 
69
- * Check to see if a remote `database.yml` exists in `APP_PATH/shared/config`, if not attempt to copy one from `APP_PATH/shared/config`.
103
+ * Check to see if a remote `database.yml` exists in `APP_PATH/shared/config`, if not attempt to copy one from `APP_PATH/shared/config`
104
+ * If a new `database.yml` is created, it will include a username and database name based on the application name and a random password
70
105
  * Download the remote `database.yml`
71
106
  * Create the Postgres user specified in `database.yml` if it doesn't already exist
72
107
  * Create the Database specified in `database.yml` if it doesn't already exist
@@ -125,20 +160,6 @@ cap STAGE nginx:restart
125
160
  cap STAGE nginx:remove_default_vhost
126
161
  ```
127
162
 
128
- #### Restart
129
-
130
- Provides Commands for interacting with the Unicorn app server via an `init.d` script.
131
-
132
- Usage:
133
-
134
- ``` bash
135
- cap STAGE deploy:start
136
- cap STAGE deploy:stop
137
- cap STAGE deploy:force-stop
138
- cap STAGE deploy:restart
139
- cap STAGE deploy:upgrade
140
- ```
141
-
142
163
  #### Run Tests
143
164
 
144
165
  Allows a test suite to be automatically run with `rspec`, if the tests pass the deploy will continue, if they fail, the deploy will halt and the test output will be displayed.
@@ -17,7 +17,9 @@ Gem::Specification.new do |spec|
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
18
  spec.require_paths = ["lib"]
19
19
 
20
- spec.add_dependency 'capistrano', '~> 3.1'
20
+ spec.add_dependency 'capistrano', '~> 3.16'
21
+ spec.add_dependency 'capistrano3-puma', '~> 5.0.4'
22
+ spec.add_dependency 'capistrano-sidekiq', '~> 2.0'
21
23
 
22
24
  spec.add_development_dependency "bundler", "~> 1.5"
23
25
  spec.add_development_dependency "rake"
@@ -3,13 +3,13 @@ require "capistrano/cookbook/version"
3
3
  module Capistrano
4
4
  module Cookbook
5
5
  require 'capistrano/cookbook/check_revision'
6
- require 'capistrano/cookbook/compile_assets_locally'
7
6
  require 'capistrano/cookbook/logs'
8
7
  require 'capistrano/cookbook/monit'
9
8
  require 'capistrano/cookbook/nginx'
10
- require 'capistrano/cookbook/restart'
11
9
  require 'capistrano/cookbook/run_tests'
12
10
  require 'capistrano/cookbook/setup_config'
13
11
  require 'capistrano/cookbook/create_database'
12
+ require 'capistrano/cookbook/puma_systemd'
13
+ require 'capistrano/cookbook/sidekiq_systemd'
14
14
  end
15
15
  end
@@ -1,57 +1,49 @@
1
1
  module Capistrano
2
2
  module Cookbook
3
3
  class SetupConfigValues
4
- def symlinks
5
- fetch(:symlinks) || symlinks_defaults
6
- end
7
-
8
- def executable_config_files
9
- fetch(:executable_config_files) || executable_config_files_defaults
10
- end
11
-
12
4
  def config_files
13
5
  fetch(:config_files) || config_files_defaults
14
6
  end
15
7
 
16
8
  private
17
9
 
18
- def symlinks_defaults
19
- [
10
+ def config_files_defaults
11
+ base = [
20
12
  {
21
- source: "nginx.conf",
22
- link: "/etc/nginx/sites-enabled/{{full_app_name}}"
13
+ source: 'log_rotation',
14
+ destination: "/etc/logrotate.d/#{fetch(:full_app_name)}",
15
+ executable: false,
16
+ as_root: true
23
17
  },
24
18
  {
25
- source: "unicorn_init.sh",
26
- link: "/etc/init.d/unicorn_{{full_app_name}}"
27
- },
19
+ source: 'database.example.yml',
20
+ destination: "#{shared_path}/config/database.example.yml",
21
+ executable: false,
22
+ as_root: false
23
+ }
24
+ ]
25
+
26
+ return base unless sidekiq_enabled?
27
+
28
+ base + [
28
29
  {
29
- source: "log_rotation",
30
- link: "/etc/logrotate.d/{{full_app_name}}"
30
+ source: 'sidekiq.service.capistrano',
31
+ destination: "/home/#{fetch(:deploy_user)}/.config/systemd/user/#{fetch(:sidekiq_service_unit_name)}.service",
32
+ executable: false,
33
+ as_root: false
31
34
  },
32
35
  {
33
- source: "monit",
34
- link: "/etc/monit/conf.d/{{full_app_name}}.conf"
36
+ source: "sidekiq_monit",
37
+ destination: "/etc/monit/conf.d/#{fetch(:full_app_name)}_sidekiq.conf",
38
+ executable: false,
39
+ as_root: true
35
40
  }
36
41
  ]
37
42
  end
38
43
 
39
- def executable_config_files_defaults
40
- %w(
41
- unicorn_init.sh
42
- )
43
- end
44
-
45
- def config_files_defaults
46
- %w(
47
- nginx.conf
48
- database.example.yml
49
- log_rotation
50
- monit
51
- unicorn.rb
52
- unicorn_init.sh
53
- )
44
+ def sidekiq_enabled?
45
+ defined?(Capistrano::Sidekiq) == 'constant' && Capistrano::Sidekiq.class == Class
54
46
  end
55
47
  end
56
48
  end
57
- end
49
+ end
@@ -1,3 +1,6 @@
1
+ require 'securerandom'
2
+ require 'stringio'
3
+
1
4
  # will first try and copy the file:
2
5
  # config/deploy/#{full_app_name}/#{from}.erb
3
6
  # to:
@@ -11,25 +14,37 @@
11
14
  # ones to be over-ridden
12
15
  # if the target file name is the same as the source then
13
16
  # the second parameter can be left out
14
- def smart_template(from, to=nil)
15
- to ||= from
16
- full_to_path = "#{shared_path}/config/#{to}"
17
+ def smart_template(from, to, as_root=false)
17
18
  if from_erb_path = template_file(from)
18
19
  from_erb = StringIO.new(ERB.new(File.read(from_erb_path)).result(binding))
19
- upload! from_erb, full_to_path
20
- info "copying: #{from_erb} to: #{full_to_path}"
20
+ upload!(from_erb, to) unless as_root
21
+ sudo_upload!(from_erb, to) if as_root
22
+ info "copying: #{from} to: #{to}"
21
23
  else
22
24
  error "error #{from} not found"
23
25
  end
24
26
  end
25
27
 
26
28
  def template_file(name)
27
- if File.exist?((file = "config/deploy/#{fetch(:full_app_name)}/#{name}.erb"))
29
+ if File.exist?((file = "config/deploy/#{fetch(:stage)}/#{name}.erb"))
28
30
  return file
29
31
  elsif File.exist?((file = "config/deploy/shared/#{name}.erb"))
30
32
  return file
33
+ elsif File.exist?((file = "config/deploy/templates/#{name}.erb"))
34
+ return file
31
35
  elsif File.exist?(file = File.expand_path("../templates/#{name}.erb",File.dirname(__FILE__)))
32
36
  return file
33
37
  end
34
38
  return nil
35
39
  end
40
+
41
+ def sudo_upload!(file_path, remote_path, mode: '644', owner: 'root:root')
42
+ tmp_path = "/tmp/#{SecureRandom.uuid}"
43
+
44
+ upload!(file_path, tmp_path)
45
+
46
+ execute(:sudo, :mkdir, '-p', File.dirname(remote_path))
47
+ execute(:sudo, :mv, '-f', tmp_path, remote_path)
48
+ execute(:sudo, :chmod, mode, remote_path)
49
+ execute(:sudo, :chown, owner, remote_path)
50
+ end
@@ -0,0 +1 @@
1
+ load File.expand_path("tasks/puma_systemd.cap", File.dirname(__FILE__))
@@ -0,0 +1 @@
1
+ load File.expand_path("tasks/sidekiq_systemd.cap", File.dirname(__FILE__))
@@ -8,6 +8,7 @@ namespace :deploy do
8
8
  run_locally do
9
9
  execute"rsync -av ./public/assets/ #{role.user}@#{role.hostname}:#{release_path}/public/assets/;"
10
10
  end
11
+ sudo "chmod -R 755 #{release_path}/public/assets/"
11
12
  end
12
13
  run_locally do
13
14
  execute "rm -rf ./public/assets"
@@ -1,4 +1,5 @@
1
1
  namespace :monit do
2
+
2
3
  %w(start stop restart).each do |task_name|
3
4
  desc "#{task_name} Monit"
4
5
  task task_name do
@@ -7,4 +8,12 @@ namespace :monit do
7
8
  end
8
9
  end
9
10
  end
10
- end
11
+
12
+ desc "Reload Monit"
13
+ task 'reload' do
14
+ on roles(:app), in: :sequence, wait: 5 do
15
+ sudo "monit reload"
16
+ end
17
+ end
18
+
19
+ end
@@ -3,7 +3,7 @@ namespace :nginx do
3
3
  desc "#{task } Nginx"
4
4
  task task_name do
5
5
  on roles(:app), in: :sequence, wait: 5 do
6
- sudo "/etc/init.d/nginx #{task_name}"
6
+ sudo "systemctl #{task_name} nginx"
7
7
  end
8
8
  end
9
9
  end
@@ -11,11 +11,13 @@ namespace :nginx do
11
11
  desc "Remove default Nginx Virtual Host"
12
12
  task "remove_default_vhost" do
13
13
  on roles(:app) do
14
- if test("[ -f /etc/nginx/sites-enabled/default ]")
15
- sudo "rm /etc/nginx/sites-enabled/default"
16
- puts "removed default Nginx Virtualhost"
17
- else
18
- puts "No default Nginx Virtualhost to remove"
14
+ %w(/etc/nginx/sites-enabled/default /etc/nginx/sites-enabled/000-default).each do |default_filename|
15
+ if test("[ -f #{default_filename} ]")
16
+ sudo "rm #{default_filename}"
17
+ puts "Removed default Nginx Virtualhost: #{default_filename}"
18
+ else
19
+ puts "Default: #{default_filename} not found, not removed"
20
+ end
19
21
  end
20
22
  end
21
23
  end
@@ -0,0 +1,17 @@
1
+ namespace :puma do
2
+ namespace :systemd do
3
+ desc 'Reload the puma service via systemd by sending USR1 (e.g. trigger a zero downtime deploy)'
4
+ task :reload do
5
+ on roles(fetch(:puma_role)) do
6
+ if fetch(:puma_systemctl_user) == :system
7
+ sudo "#{fetch(:puma_systemctl_bin)} reload-or-restart #{fetch(:puma_service_unit_name)}"
8
+ else
9
+ execute :loginctl, "enable-linger", fetch(:puma_lingering_user) if fetch(:puma_enable_lingering)
10
+ execute "#{fetch(:puma_systemctl_bin)}", "--user", "reload-or-restart", fetch(:puma_service_unit_name)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ after 'deploy:finished', 'puma:systemd:reload'
@@ -1,6 +1,7 @@
1
+ require 'capistrano/dsl'
1
2
  require 'capistrano/cookbook/helpers/setup_config_values'
2
3
  require 'capistrano/cookbook/helpers/substitute_strings'
3
- require 'capistrano/cookbook/helpers/template'
4
+ require 'capistrano/cookbook/helpers/smart_template'
4
5
  require 'capistrano/cookbook/nginx'
5
6
  require 'capistrano/cookbook/monit'
6
7
  require 'securerandom'
@@ -8,38 +9,61 @@ require 'securerandom'
8
9
  namespace :deploy do
9
10
  task :setup_config do
10
11
  conf = ::Capistrano::Cookbook::SetupConfigValues.new
12
+
11
13
  on roles(:app) do
12
14
  # make the config dir
13
15
  execute :mkdir, "-p #{shared_path}/config"
14
- full_app_name = fetch(:full_app_name)
16
+ execute :mkdir, "-p /home/#{fetch(:deploy_user)}/.config/systemd/user"
15
17
 
16
18
  # config files to be uploaded to shared/config, see the
17
19
  # definition of smart_template for details of operation.
18
20
  conf.config_files.each do |file|
19
- smart_template file
21
+ smart_template(file[:source], file[:destination], file[:as_root])
22
+ execute(:chmod, "+x #{file[:destination]}") if file[:executable]
20
23
  end
21
24
 
22
25
  # which of the above files should be marked as executable
23
- conf.executable_config_files.each do |file|
24
- execute :chmod, "+x #{shared_path}/config/#{file}"
25
- end
26
+ # conf.executable_config_files.each do |file|
27
+ # execute :chmod, "+x #{shared_path}/config/#{file}"
28
+ # end
26
29
 
27
30
  # symlink stuff which should be... symlinked
28
- conf.symlinks.each do |symlink|
29
- sudo "ln -nfs #{shared_path}/config/#{symlink[:source]} #{sub_strings(symlink[:link])}"
31
+ # conf.symlinks.each do |symlink|
32
+ # sudo "ln -nfs #{shared_path}/config/#{symlink[:source]} #{sub_strings(symlink[:link])}"
33
+ # end
34
+
35
+ if File.exists?(File.join('config', 'master.key'))
36
+ upload! File.join('config', 'master.key'), File.join(shared_path, 'config', 'master.key')
30
37
  end
31
38
  end
32
39
  end
33
40
  end
34
41
 
35
- # remove the default nginx configuration as it will tend
36
- # to conflict with our configs.
42
+
43
+ # remove the default nginx configuration as it will tend to conflict with our configs
37
44
  before 'deploy:setup_config', 'nginx:remove_default_vhost'
38
45
 
39
- # reload nginx to it will pick up any modified vhosts from
40
- # setup_config
46
+ # make sure that shared directories etc exist before running otherwise the
47
+ # initial nginx reload won't work because of the nginx log file directory path
48
+ # not existing
49
+ before 'deploy:setup_config', 'deploy:check:directories'
50
+ before 'deploy:setup_config', 'deploy:check:linked_dirs'
51
+
52
+ # After setup config has generated and setup initial files, run the Capistrano Puma
53
+ # tasks responsible for uploading config files. Note that `setup_config` creates overrides
54
+ # for these in `config/deploy/templates` so we're not using the default ones from the gem
55
+ after 'deploy:setup_config', 'puma:config'
56
+ after 'deploy:setup_config', 'puma:nginx_config'
57
+ after 'deploy:setup_config', 'puma:monit:config'
58
+ after 'deploy:setup_config', 'puma:systemd:config'
59
+ after 'deploy:setup_config', 'puma:systemd:enable'
60
+
61
+ # Enable the sidekiq systemd service so that it's started automatically on (re)boot
62
+ after 'deploy:setup_config', 'sidekiq:systemd:enable' if (defined?(Capistrano::Sidekiq) == 'constant' && Capistrano::Sidekiq.class == Class)
63
+
64
+ # reload nginx to it will pick up any modified vhosts from setup_config
41
65
  after 'deploy:setup_config', 'nginx:reload'
42
66
 
43
- # Restart monit so it will pick up any monit configurations
44
- # we've added
45
- after 'deploy:setup_config', 'monit:restart'
67
+ # Restart monit so it will pick up any monit configurations we've added
68
+ after 'deploy:setup_config', 'monit:reload'
69
+