capistrano-ops 0.2.13 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -0
  3. data/README.md +121 -29
  4. data/capistrano-ops.gemspec +2 -0
  5. data/lib/capistrano/ops/backup/helper.rb +56 -0
  6. data/lib/capistrano/ops/{capistrano/v3/tasks/backup → backup/tasks}/database/create.rake +1 -2
  7. data/lib/capistrano/ops/{capistrano/v3/tasks/backup → backup/tasks}/database/pull.rake +1 -2
  8. data/lib/capistrano/ops/{capistrano/v3/tasks/backup → backup/tasks}/storage/create.rake +1 -2
  9. data/lib/capistrano/ops/{capistrano/v3/tasks/backup → backup/tasks}/storage/pull.rake +1 -2
  10. data/lib/capistrano/ops/backup.rb +14 -4
  11. data/lib/capistrano/ops/capistrano.rb +7 -10
  12. data/lib/capistrano/ops/figaro_yml/helpers.rb +184 -0
  13. data/lib/capistrano/ops/figaro_yml/paths.rb +19 -0
  14. data/lib/capistrano/ops/figaro_yml/tasks/backup.rake +26 -0
  15. data/lib/capistrano/ops/figaro_yml/tasks/check.rake +12 -0
  16. data/lib/capistrano/ops/figaro_yml/tasks/check_config_present.rake +12 -0
  17. data/lib/capistrano/ops/figaro_yml/tasks/check_figaro_file_exists.rake +12 -0
  18. data/lib/capistrano/ops/figaro_yml/tasks/check_git_tracking.rake +12 -0
  19. data/lib/capistrano/ops/figaro_yml/tasks/compare.rake +53 -0
  20. data/lib/capistrano/ops/figaro_yml/tasks/create_local.rake +27 -0
  21. data/lib/capistrano/ops/figaro_yml/tasks/figaro_yml_symlink.rake +10 -0
  22. data/lib/capistrano/ops/figaro_yml/tasks/get.rake +45 -0
  23. data/lib/capistrano/ops/figaro_yml/tasks/get_stage.rake +15 -0
  24. data/lib/capistrano/ops/figaro_yml/tasks/load.rake +10 -0
  25. data/lib/capistrano/ops/figaro_yml/tasks/rollback.rake +22 -0
  26. data/lib/capistrano/ops/figaro_yml/tasks/setup.rake +16 -0
  27. data/lib/capistrano/ops/figaro_yml/tasks/sort_local.rake +18 -0
  28. data/lib/capistrano/ops/figaro_yml.rb +29 -0
  29. data/lib/capistrano/ops/helper.rb +14 -0
  30. data/lib/capistrano/ops/invoke/tasks/invoke.rake +25 -0
  31. data/lib/capistrano/ops/invoke.rb +3 -0
  32. data/lib/capistrano/ops/logrotate/helpers.rb +50 -0
  33. data/lib/capistrano/ops/logrotate/paths.rb +47 -0
  34. data/lib/capistrano/ops/{capistrano/v3/tasks/logrotate → logrotate/tasks}/check.rake +3 -5
  35. data/lib/capistrano/ops/{capistrano/v3/tasks/logrotate → logrotate/tasks}/disable.rake +2 -5
  36. data/lib/capistrano/ops/logrotate/tasks/enable.rake +28 -0
  37. data/lib/capistrano/ops/logrotate/tasks/load.rake +10 -0
  38. data/lib/capistrano/ops/logrotate.rb +19 -0
  39. data/lib/capistrano/ops/logs/helpers.rb +16 -0
  40. data/lib/capistrano/ops/logs/paths.rb +23 -0
  41. data/lib/capistrano/ops/logs/tasks/load.rake +9 -0
  42. data/lib/capistrano/ops/logs/tasks/rails.rake +18 -0
  43. data/lib/capistrano/ops/logs/tasks/sidekiq.rake +42 -0
  44. data/lib/capistrano/ops/logs.rb +18 -0
  45. data/lib/capistrano/ops/{backup → rails/lib/backup}/s3.rb +1 -1
  46. data/lib/capistrano/ops/rails/lib/backup.rb +6 -0
  47. data/lib/capistrano/ops/rails/lib/notification.rb +9 -0
  48. data/lib/capistrano/ops/rails/lib/railtie.rb +17 -0
  49. data/lib/capistrano/ops/{tasks → rails/lib/tasks}/pg/postgres_helper.rb +1 -1
  50. data/lib/capistrano/ops/version.rb +1 -1
  51. data/lib/capistrano/ops/{capistrano/v3/tasks/whenever.rake → whenever/tasks/show_crontab.rake} +1 -3
  52. data/lib/capistrano/ops/whenever.rb +17 -0
  53. data/lib/capistrano/ops/wkhtmltopdf/helpers.rb +50 -0
  54. data/lib/capistrano/ops/wkhtmltopdf/tasks/setup.rake +17 -0
  55. data/lib/capistrano/ops/wkhtmltopdf.rb +17 -1
  56. data/lib/capistrano/ops.rb +4 -3
  57. metadata +88 -31
  58. data/lib/capistrano/ops/capistrano/tasks/wkhtmltopdf.rake +0 -48
  59. data/lib/capistrano/ops/capistrano/v3/tasks/backup/backup_helper.rb +0 -50
  60. data/lib/capistrano/ops/capistrano/v3/tasks/figaro_yml.rake +0 -139
  61. data/lib/capistrano/ops/capistrano/v3/tasks/invoke.rake +0 -23
  62. data/lib/capistrano/ops/capistrano/v3/tasks/logrotate/enable.rake +0 -24
  63. data/lib/capistrano/ops/capistrano/v3/tasks/logrotate/logrotate_helper.rb +0 -68
  64. data/lib/capistrano/ops/capistrano/v3/tasks/logs/rails.rake +0 -16
  65. data/lib/capistrano/ops/notification.rb +0 -9
  66. data/lib/capistrano/ops/railtie.rb +0 -15
  67. /data/lib/capistrano/ops/{capistrano/v3/tasks/logrotate → logrotate/tasks}/templates/logrotate.conf.erb +0 -0
  68. /data/lib/capistrano/ops/{capistrano/v3/tasks/logrotate → logrotate/tasks}/templates/schedule.rb.erb +0 -0
  69. /data/lib/capistrano/ops/{backup → rails/lib/backup}/api.rb +0 -0
  70. /data/lib/capistrano/ops/{backup → rails/lib/backup}/s3_helper.rb +0 -0
  71. /data/lib/capistrano/ops/{notification → rails/lib/notification}/api.rb +0 -0
  72. /data/lib/capistrano/ops/{notification → rails/lib/notification}/slack.rb +0 -0
  73. /data/lib/capistrano/ops/{notification → rails/lib/notification}/webhook.rb +0 -0
  74. /data/lib/capistrano/ops/{tasks → rails/lib/tasks}/pg/dump.rake +0 -0
  75. /data/lib/capistrano/ops/{tasks → rails/lib/tasks}/pg/remove_old_dumps.rake +0 -0
  76. /data/lib/capistrano/ops/{tasks → rails/lib/tasks}/storage/backup.rake +0 -0
  77. /data/lib/capistrano/ops/{tasks → rails/lib/tasks}/storage/remove_old_backups.rake +0 -0
@@ -1,48 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- namespace :wkhtmltopdf do
4
- after 'deploy:symlink:release', 'wkhtmltopdf:setup'
5
-
6
- desc 'unzip wkhtmltopdf if necessary'
7
- task :setup do
8
- on roles(:app) do
9
- within release_path do
10
- binary_path, version = binary_path_and_version
11
- info("setup wkhtmltopdf version #{version}")
12
- check_file_and_permissions(binary_path, version)
13
- end
14
- end
15
- end
16
- end
17
- def binary_path_and_version
18
- # get the binary path of wkhtmltopdf-binary
19
- gem_path = capture(:bundle, 'show', 'wkhtmltopdf-binary').strip
20
- binary_path = "#{gem_path}/bin"
21
-
22
- # get the use wkhtmltopdf_ubuntu version from the wicked_pdf initializer
23
- version = capture(:cat, 'config/initializers/wicked_pdf.rb').scan(/wkhtmltopdf_ubuntu_(\d+\.\d+)_amd64/).flatten.first
24
-
25
- [binary_path, version]
26
- end
27
-
28
- def check_file_and_permissions(binary_path, version)
29
- binary_file = "#{binary_path}/wkhtmltopdf_ubuntu_#{version}_amd64"
30
-
31
- if test("[ -f #{binary_file} ]")
32
- info('wkhtmltopdf binary already extracted')
33
-
34
- if test("[ $(stat -c '%a' #{binary_file}) = '777' ]")
35
- info('wkhtmltopdf binary has already the right permissions')
36
- else
37
- info('adding right permissions to wkhtmltopdf binary')
38
- execute("chmod 777 #{binary_file}")
39
- end
40
- else
41
- info('extracting wkhtmltopdf binary')
42
- # extract the binary but keep the gzip file
43
- execute("cd #{binary_path} && gzip -dk wkhtmltopdf_ubuntu_#{version}_amd64.gz")
44
- # add execute permission to the binary
45
- execute("chmod 777 #{binary_file}")
46
- end
47
- info('wkhtmltopdf setup finished')
48
- end
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module BackupHelper
4
- def backup_file_name(type)
5
- regex = type == 'storage' ? "'.{0,}\.tar.gz'" : "'.{0,}\.dump'"
6
- @backup_file_name ||= capture "cd #{shared_path}/backups && ls -lt | grep -E -i #{regex} | head -n 1 | awk '{print $9}'"
7
- end
8
-
9
- def backup_file_size
10
- @backup_file_size ||= capture "cd #{shared_path}/backups && wc -c #{@backup_file_name} | awk '{print $1}'"
11
- end
12
-
13
- def download_backup(backup_file, type)
14
- puts "Downloading #{type} backup"
15
- download! "#{shared_path}/backups/#{backup_file}", backup_file
16
- puts "Download finished\nDeleting temporary backup..."
17
- cleanup_backup(backup_file, "Download finished\nDeleting temporary backup...")
18
- end
19
-
20
- def cleanup_backup(backup_file, message)
21
- puts message
22
- execute "cd #{shared_path}/backups && rm #{backup_file}"
23
- puts 'Temporary backup deleted'
24
- end
25
-
26
- def question(question, default = 'n', &block)
27
- print "#{question} #{default.downcase == 'n' ? '(y/N)' : '(Y/n)'}: "
28
- input = $stdin.gets.strip.downcase
29
- answer = (input.empty? ? default : input).downcase.to_s
30
-
31
- if %w[y n].include?(answer)
32
- block.call(answer == 'y')
33
- else
34
- question(question, default, &block)
35
- end
36
- end
37
-
38
- def prepare_env
39
- @env = "RAILS_ENV=#{fetch(:stage)}"
40
- @path_cmd = "PATH=$HOME/.rbenv/versions/#{RUBY_VERSION}/bin:$PATH"
41
- @test_command = "cd #{release_path} && #{@path_cmd} && #{@env}"
42
- end
43
-
44
- def size_str(size)
45
- units = %w[B KB MB GB TB]
46
- e = (Math.log(size) / Math.log(1024)).floor
47
- s = format('%.2f', size.to_f / 1024**e)
48
- s.sub(/\.?0*$/, units[e])
49
- end
50
- end
@@ -1,139 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # rubocop:disable Metrics/BlockLength
4
- namespace :figaro_yml do
5
- # Defaults to :app role
6
- rake_roles = fetch(:rake_roles, :app)
7
-
8
- desc 'get the `application.yml` file from server and create local if it does not exist'
9
- task :get do
10
- env = fetch(:stage)
11
- if !File.exist?('config/application.yml')
12
- puts 'config/application.yml does not exist, creating it from all stages'
13
- run_locally do
14
- yamls = {}
15
- stages = Dir.glob('config/deploy/*.rb')
16
- puts "found #{stages.count} stages"
17
- stages.map do |f|
18
- stage = File.basename(f, '.rb')
19
- puts "download #{stage} application.yml"
20
- begin
21
- res = capture "cap #{stage} figaro_yml:get_stage"
22
- yamls = yamls.merge(YAML.safe_load(res))
23
- rescue StandardError
24
- puts "could not get #{stage} application.yml"
25
- end
26
- yamls
27
- end
28
- # write to new file
29
- puts 'writing to config/application.yml'
30
- write_to_file('config/application.yml', yamls.to_yaml)
31
- end
32
- else
33
- local_yml = YAML.safe_load(File.read('config/application.yml'))
34
- on roles(rake_roles) do
35
- remote = capture("cat #{shared_path}/config/application.yml")
36
- remote_yml = YAML.safe_load(remote)
37
- remote_stage = remote_yml[env.to_s]
38
- puts "remote application.yml stage '#{env}':\n\n"
39
- puts "#{remote}\r\n"
40
- puts "\r\n"
41
- loop do
42
- print "Overwrite local application.yml stage '#{env}'? (y/N): "
43
- input = $stdin.gets.strip.downcase
44
- answer = (input.empty? ? 'N' : input).downcase.to_s
45
-
46
- next unless %w[y n].include?(answer)
47
-
48
- if answer == 'y'
49
- puts 'Updating local application.yml'
50
- local_yml[env.to_s] = remote_stage
51
- write_to_file('config/application.yml', local_yml.to_yaml)
52
- exit
53
- end
54
- break
55
- end
56
- puts 'Nothing written to local application.yml'
57
- exit
58
- end
59
- end
60
- end
61
-
62
- task :get_stage do
63
- on roles(rake_roles) do
64
- puts capture "cat #{shared_path}/config/application.yml"
65
- end
66
- end
67
-
68
- desc 'compare and set the figaro_yml file on the server'
69
- task :compare do
70
- env = fetch(:stage)
71
- # read local application.yml
72
- local = File.read('config/application.yml')
73
-
74
- # convert to hash
75
- local_global_env = YAML.safe_load(local)
76
-
77
- # split into stage and global
78
- local_stage_env = local_global_env[env.to_s]
79
- local_global_env.delete('staging')
80
- local_global_env.delete('production')
81
-
82
- on roles(rake_roles) do
83
- # read remote application.yml
84
- remote = capture("cat #{shared_path}/config/application.yml")
85
-
86
- remote_global_env = YAML.safe_load(remote)
87
- remote_stage_env = remote_global_env[env.to_s]
88
- remote_global_env.delete(env.to_s)
89
-
90
- puts "with command 'cap #{env} figaro_yml:setup', following variables will be overwritten:"
91
- puts '--------------------------------------------------------------------------------'
92
- result1 = compare_hashes(local_global_env, remote_global_env)
93
- result2 = compare_hashes(local_stage_env, remote_stage_env)
94
- if !result1.empty? || !result2.empty?
95
- loop do
96
- print 'Update remote application.yml? (y/N): '
97
- input = $stdin.gets.strip.downcase
98
- answer = (input.empty? ? 'N' : input).downcase.to_s
99
-
100
- next unless %w[y n].include?(answer)
101
-
102
- if answer == 'y'
103
- puts 'Updating remote application.yml'
104
- invoke 'figaro_yml:setup'
105
- exit
106
- end
107
- break
108
- end
109
- puts 'remote application.yml not updated'
110
- exit
111
- end
112
- puts 'remote application.yml is up to date'
113
- end
114
- end
115
- def compare_hashes(hash1, hash2)
116
- changes = false
117
- local_server = hash1.to_a - hash2.to_a
118
- server_local = hash2.to_a - hash1.to_a
119
-
120
- [local_server + server_local].flatten(1).to_h.each_key do |k|
121
- new_value = hash1[k].to_s
122
- new_value = new_value.empty? ? 'nil' : new_value
123
- old_value = hash2[k].to_s
124
- old_value = old_value.empty? ? 'nil' : old_value
125
-
126
- if old_value != new_value
127
- puts "#{k}: #{old_value} => #{new_value} \r\n"
128
- changes = true
129
- end
130
- end
131
- end
132
-
133
- def write_to_file(file, content)
134
- File.open(file, 'w') do |f|
135
- f.write(content)
136
- end
137
- end
138
- end
139
- # rubocop:enable Metrics/BlockLength
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- namespace :invoke do
4
- # Defalut to :app roles
5
- rake_roles = fetch(:rake_roles, :app)
6
-
7
- desc 'Execute a rake task on a remote server (cap invoke:rake TASK=db:migrate)'
8
- task :rake do
9
- if ENV['TASK']
10
- on roles(rake_roles) do
11
- within current_path do
12
- with rails_env: fetch(:rails_env) do
13
- execute :rake, ENV['TASK']
14
- end
15
- end
16
- end
17
-
18
- else
19
- puts "\n\nFailed! You need to specify the 'TASK' parameter!",
20
- 'Usage: cap <stage> invoke:rake TASK=your:task'
21
- end
22
- end
23
- end
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'logrotate_helper'
4
-
5
- namespace :logrotate do
6
- include LogrotateHelper
7
- desc 'Enable logrotate for the application'
8
- task :enable do
9
- on roles(:app) do
10
- within release_path do
11
- set_config
12
- if logrotate_enabled
13
- puts "logrotate already enabled:\n========================="
14
- puts capture "cat #{@config_file_path}"
15
- else
16
- make_basepath
17
- upload! StringIO.new(@config_template), @config_file_path
18
- upload! StringIO.new(@schedule_template), @schedule_file_path
19
- whenever 'update' if logrotate_enabled
20
- end
21
- end
22
- end
23
- end
24
- end
@@ -1,68 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module LogrotateHelper
4
- # rubocop:disable Naming/MemoizedInstanceVariableName
5
- def set_config
6
- info ''
7
- @shared_path ||= shared_path
8
- @log_file_path ||= fetch(:log_file_path) || "#{@shared_path}/log"
9
- @template_config_filename ||= fetch(:logrotate_template_config_file) || 'templates/logrotate.conf.erb'
10
- @template_schedule_filename ||= fetch(:logrotate_template_schedule_file) || 'templates/schedule.rb.erb'
11
- @basepath ||= fetch(:logrotate_basepath) || "#{shared_path}/logrotate"
12
- @config_filename ||= File.basename(@template_config_filename, '.erb')
13
- @schedule_filename ||= File.basename(@template_schedule_filename, '.erb')
14
- @config_file_path ||= "#{@basepath}/#{@config_filename}"
15
- @schedule_file_path ||= "#{@basepath}/#{@schedule_filename}"
16
- @config_template ||= config_template
17
- @schedule_template ||= schedule_template
18
- end
19
- # rubocop:enable Naming/MemoizedInstanceVariableName
20
-
21
- def config
22
- {
23
- template_config_file: fetch(:logrotate_template_config_file) || 'templates/logrotate.conf.erb',
24
- template_schedule_file: fetch(:logrotate_template_schedule_file) || 'templates/schedule.rb.erb'
25
- }
26
- end
27
-
28
- def make_basepath
29
- puts capture "mkdir -pv #{@basepath}"
30
- end
31
-
32
- def delete_files
33
- puts capture "rm -rfv #{@basepath}"
34
- end
35
-
36
- def config_template
37
- return nil unless @template_config_filename && File.exist?(File.expand_path(@template_config_filename, __dir__))
38
-
39
- ERB.new(File.read(File.expand_path(@template_config_filename, __dir__))).result(binding)
40
- end
41
-
42
- def schedule_template
43
- return nil unless @template_schedule_filename && File.exist?(File.expand_path(@template_schedule_filename, __dir__))
44
-
45
- ERB.new(File.read(File.expand_path(@template_schedule_filename, __dir__))).result(binding)
46
- end
47
-
48
- def logrotate_enabled
49
- test("[ -f #{@config_file_path} ]") && test("[ -f #{@schedule_file_path} ]")
50
- end
51
-
52
- def logrotate_disabled
53
- !(test("[ -f #{@config_file_path} ]") && test("[ -f #{@schedule_file_path} ]"))
54
- end
55
-
56
- def whenever(type)
57
- case type
58
- when 'clear'
59
- puts capture :bundle, :exec, :whenever, '--clear-crontab',
60
- "-f #{@schedule_file_path} #{fetch(:whenever_identifier)}_logrotate"
61
- when 'update'
62
- puts capture :bundle, :exec, :whenever, '--update-crontab', "-f #{@schedule_file_path}",
63
- "-i #{fetch(:whenever_identifier)}_logrotate"
64
- else
65
- puts 'type not found'
66
- end
67
- end
68
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- namespace :logs do
4
- # Default to :app role
5
- rake_roles = fetch(:rake_roles, :app)
6
- desc 'tail rails logs'
7
- task :rails do
8
- on roles(rake_roles) do
9
- trap('SIGINT') do
10
- puts "\nDisconnecting..."
11
- exit
12
- end
13
- execute "tail -f #{shared_path}/log/#{fetch(:rails_env)}.log"
14
- end
15
- end
16
- end
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'capistrano/ops/notification/api'
4
- require 'capistrano/ops/notification/slack'
5
- require 'capistrano/ops/notification/webhook'
6
-
7
- module Notification
8
- class Error < StandardError; end
9
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'rails'
4
- # require 'capistrano/ops'
5
- module Capistrano
6
- module Ops
7
- class Railtie < ::Rails::Railtie
8
- railtie_name :ops
9
- rake_tasks do
10
- path = File.expand_path(__dir__)
11
- Dir.glob("#{path}/tasks/**/*.rake").each { |f| load f }
12
- end
13
- end
14
- end
15
- end