capistrano-ops 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,63 +7,63 @@ namespace :figaro_yml do
7
7
 
8
8
  desc 'get the `application.yml` file from server and create local if it does not exist'
9
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
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
21
  res = capture "cap #{stage} figaro_yml:get_stage"
22
- yamls= yamls.merge(YAML.safe_load(res))
23
- rescue
24
- puts "could not get #{stage} application.yml"
25
- end
26
- yamls
22
+ yamls = yamls.merge(YAML.safe_load(res))
23
+ rescue StandardError
24
+ puts "could not get #{stage} application.yml"
27
25
  end
28
- # write to new file
29
- puts "writing to config/application.yml"
30
- write_to_file('config/application.yml', yamls.to_yaml)
26
+ yamls
31
27
  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.to_s}':\n\n"
39
- puts remote + "\r\n"
40
- puts "\r\n"
41
- loop do
42
- print "Overwrite local application.yml stage '#{env.to_s}'? (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
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
55
53
  end
56
- puts 'Nothing written to local application.yml'
57
- exit
54
+ break
58
55
  end
56
+ puts 'Nothing written to local application.yml'
57
+ exit
59
58
  end
60
59
  end
61
-
62
- task :get_stage do
63
- on roles(rake_roles) do
64
- puts capture "cat #{shared_path}/config/application.yml"
65
- end
60
+ end
61
+
62
+ task :get_stage do
63
+ on roles(rake_roles) do
64
+ puts capture "cat #{shared_path}/config/application.yml"
66
65
  end
66
+ end
67
67
 
68
68
  desc 'compare and set the figaro_yml file on the server'
69
69
  task :compare do
@@ -93,12 +93,12 @@ namespace :figaro_yml do
93
93
  result2 = compare_hashes(local_stage_env, remote_stage_env)
94
94
  if !result1.empty? || !result2.empty?
95
95
  loop do
96
- print 'Update remote application.yml? (y/N): '
96
+ print 'Update remote application.yml? (y/N): '
97
97
  input = $stdin.gets.strip.downcase
98
98
  answer = (input.empty? ? 'N' : input).downcase.to_s
99
99
 
100
- next unless %w(y n).include?(answer)
101
-
100
+ next unless %w[y n].include?(answer)
101
+
102
102
  if answer == 'y'
103
103
  puts 'Updating remote application.yml'
104
104
  invoke 'figaro_yml:setup'
@@ -116,25 +116,24 @@ namespace :figaro_yml do
116
116
  changes = false
117
117
  local_server = hash1.to_a - hash2.to_a
118
118
  server_local = hash2.to_a - hash1.to_a
119
-
120
- [local_server + server_local].flatten(1).to_h.keys.each do |k|
119
+
120
+ [local_server + server_local].flatten(1).to_h.each_key do |k|
121
121
  new_value = hash1[k].to_s
122
- new_value = new_value.empty? ? "nil" : new_value
122
+ new_value = new_value.empty? ? 'nil' : new_value
123
123
  old_value = hash2[k].to_s
124
- old_value = old_value.empty? ? "nil" : old_value
124
+ old_value = old_value.empty? ? 'nil' : old_value
125
125
 
126
126
  if old_value != new_value
127
- puts "#{k}: #{old_value} => #{new_value} \r\n"
128
- changes = true
127
+ puts "#{k}: #{old_value} => #{new_value} \r\n"
128
+ changes = true
129
129
  end
130
130
  end
131
- end
131
+ end
132
132
 
133
133
  def write_to_file(file, content)
134
134
  File.open(file, 'w') do |f|
135
135
  f.write(content)
136
136
  end
137
137
  end
138
-
139
138
  end
140
139
  # rubocop:enable Metrics/BlockLength
@@ -1,24 +1,23 @@
1
+ # frozen_string_literal: true
2
+
1
3
  namespace :invoke do
4
+ # Defalut to :app roles
5
+ rake_roles = fetch(:rake_roles, :app)
2
6
 
3
- # Defalut to :app roles
4
- rake_roles = fetch(:rake_roles, :app)
5
-
6
- desc "Execute a rake task on a remote server (cap invoke:rake TASK=db:migrate)"
7
- task :rake do
8
- if ENV['TASK']
9
- on roles(rake_roles) do
10
- within current_path do
11
- with rails_env: fetch(:rails_env) do
12
- execute :rake, ENV['TASK']
13
- end
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
14
  end
15
15
  end
16
-
17
- else
18
- puts "\n\nFailed! You need to specify the 'TASK' parameter!",
19
- "Usage: cap <stage> invoke:rake TASK=your:task"
20
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
21
  end
22
-
23
22
  end
24
-
23
+ end
@@ -8,6 +8,8 @@ if defined?(Capistrano::VERSION) && Gem::Version.new(Capistrano::VERSION).releas
8
8
  load File.expand_path('capistrano/v3/tasks/figaro_yml.rake', __dir__)
9
9
  load File.expand_path('capistrano/v3/tasks/logs.rake', __dir__)
10
10
  load File.expand_path('capistrano/v3/tasks/invoke.rake', __dir__)
11
+ path = File.expand_path(__dir__)
12
+ Dir.glob("#{path}/capistrano/v3/tasks/backup/**/*.rake").each { |f| load f }
11
13
  else
12
14
  puts 'Capistrano 3 is required to use this gem'
13
15
  end
@@ -1,30 +1,34 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Notification
2
- class Api
3
- attr_accessor :notification_type, :notification_level
4
+ class Api
5
+ attr_accessor :notification_type, :notification_level
6
+
7
+ def initialize(notification_type: ENV['NOTIFICATION_TYPE'], notification_level: ENV['NOTIFICATION_LEVEL'])
8
+ self.notification_type = notification_type
9
+ self.notification_level = notification_level || 'error'
10
+ end
4
11
 
5
- def initialize(notification_type: ENV['NOTIFICATION_TYPE'], notification_level: ENV['NOTIFICATION_LEVEL'])
6
- self.notification_type = notification_type
7
- self.notification_level = notification_level || 'error'
8
- end
12
+ def send_backup_notification(result, title, content, webhook_data = nil)
13
+ return if notification_type.nil?
14
+
15
+ case notification_type
16
+ when 'slack'
17
+ Slack.new.backup_notification(result, title, content, notification_level)
18
+ when 'webhook'
19
+ Webhook.new.backup_notification(result, webhook_data, notification_level)
20
+ end
21
+ end
9
22
 
10
- def send_backup_notification(result, title, content)
11
- return if notification_type.nil?
12
- case notification_type
13
- when 'slack'
14
- Slack.new.backup_notification(result, title, content, notification_level)
15
- when 'webhook'
16
- Webhook.new.backup_notification(result, title, content, notification_level)
17
- end
18
- end
23
+ def send_notification(message)
24
+ return if notification_type.nil?
19
25
 
20
- def send_notification(message)
21
- return if notification_type.nil?
22
- case notification_type
23
- when 'slack'
24
- Slack.new.notify(message)
25
- when 'webhook'
26
- p 'webhook'
27
- end
28
- end
26
+ case notification_type
27
+ when 'slack'
28
+ Slack.new.notify(message)
29
+ when 'webhook'
30
+ p 'webhook'
31
+ end
29
32
  end
30
- end
33
+ end
34
+ end
@@ -1,66 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require 'json'
5
+
1
6
  module Notification
2
- class Slack
3
- require 'uri'
4
- require 'net/http'
5
- require 'net/https'
6
- def initialize
7
- @slack_secret = ENV['SLACK_SECRET']
8
- @slack_channel = ENV['SLACK_CHANNEL']
9
- @slack_base_url ='https://slack.com/api/'
10
- end
7
+ class Slack
8
+ def initialize
9
+ @slack_secret = ENV['SLACK_SECRET']
10
+ @slack_channel = ENV['SLACK_CHANNEL']
11
+ @conn = Faraday.new(url: 'https://slack.com/api/') do |faraday|
12
+ faraday.headers['Content-Type'] = 'application/json'
13
+ faraday.headers['Authorization'] = "Bearer #{@slack_secret}"
14
+ end
15
+ end
16
+
17
+ def notify(message)
18
+ return if @slack_secret.nil? || @slack_channel.nil?
11
19
 
12
- def notify(message)
13
- return if @slack_secret.nil? || @slack_channel.nil?
14
- uri = URI.parse("#{@slack_base_url}chat.postMessage")
15
- http = Net::HTTP.new(uri.host, uri.port)
16
- http.use_ssl = true
17
- request = Net::HTTP::Post.new(uri.request_uri, initHeader = {'Content-Type' =>'application/json', 'Authorization' => 'Bearer ' + @slack_secret})
18
- request.body = {
19
- channel: @slack_channel,
20
- text: message
21
- }.to_json
22
- response = http.request(request)
23
- puts response.body
20
+ begin
21
+ res = @conn.post('chat.postMessage') do |req|
22
+ req.body = {
23
+ "channel": @slack_channel,
24
+ "text": message
25
+ }.to_json
24
26
  end
27
+ response = JSON.parse(res.body)
28
+ raise Notification::Error, response['error'] if response['ok'] == false
25
29
 
26
- def backup_notification(result, title, content, notification_level)
27
- return if @slack_secret.nil? || @slack_channel.nil?
28
- return if notification_level == 'error' && result
29
- uri = URI.parse("#{@slack_base_url}chat.postMessage")
30
- http = Net::HTTP.new(uri.host, uri.port)
31
- http.use_ssl = true
32
- request = Net::HTTP::Post.new(uri.request_uri, initHeader = {'Content-Type' =>'application/json', 'Authorization' => 'Bearer ' + @slack_secret})
33
-
34
- data = {
35
- channel: @slack_channel,
36
- blocks: [
37
- {
38
- type: 'header',
39
- text: {
40
- type: 'plain_text',
41
- text: title || "#{Rails.env} Message",
42
- emoji: true
43
- }
44
- },
45
- {
46
- type: 'section',
47
- text: {
48
- type: 'mrkdwn',
49
- text: content || 'No content'
50
- }
51
- }
52
- ]
53
- }
54
- request.body = data.to_json
55
- begin
56
- response = JSON.parse(http.request(request).body)
57
- if response['ok'] == false
58
- raise Notification::Error, response['error']
59
- end
60
- response
61
- rescue => e
62
- puts "Slack error: \n\t#{e.message}"
63
- end
30
+ response
31
+ rescue Notification::Error => e
32
+ puts "Slack error: \n\t#{e.message}"
33
+ end
34
+ end
35
+
36
+ def backup_notification(result, title, content, notification_level)
37
+ return if @slack_secret.nil? || @slack_channel.nil?
38
+ return if notification_level == 'error' && result
39
+
40
+ begin
41
+ res = @conn.post('chat.postMessage') do |req|
42
+ req.body = {
43
+ channel: @slack_channel,
44
+ blocks: [
45
+ {
46
+ type: 'header',
47
+ text: {
48
+ type: 'plain_text',
49
+ text: title || "#{Rails.env} Message",
50
+ emoji: true
51
+ }
52
+ },
53
+ {
54
+ type: 'section',
55
+ text: {
56
+ type: 'mrkdwn',
57
+ text: content || 'No content'
58
+ }
59
+ }
60
+ ]
61
+ }.to_json
64
62
  end
63
+ response = JSON.parse(res.body)
64
+ raise Notification::Error, response['error'] if response['ok'] == false
65
+
66
+ response
67
+ rescue Notification::Error => e
68
+ puts "Slack error: \n\t#{e.message}"
69
+ end
65
70
  end
71
+ end
66
72
  end
@@ -1,40 +1,47 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Notification
2
- class Webhook
3
- require 'uri'
4
- require 'net/http'
5
- require 'net/https'
6
- require 'openssl'
7
- require 'json'
8
-
9
- def initialize
10
- @webhook_url = ENV['WEBHOOK_URL']
11
- @secret = ENV['WEBHOOK_SECRET']
12
- end
13
-
14
- def generate_signature(payload_body)
15
- "md5=#{OpenSSL::HMAC.hexdigest('md5', ENV['WEBHOOK_SECRET'], payload_body)}"
16
- end
4
+ class Webhook
5
+ require 'faraday'
6
+ require 'openssl'
7
+ require 'json'
17
8
 
18
- def backup_notification(result, date, database, backup_path, notification_level)
19
- return if @webhook_url.nil? || @secret.nil?
20
- return if result && ENV['NOTIFICATION_LEVEL'] == 'error'
21
- data = {
22
- domain: ENV['DEFAULT_URL'] || "#{database} Backup",
23
- backupPath: result ? backup_path : nil,
24
- backupDate: date,
25
- }.to_json
26
-
27
- uri = URI.parse(@webhook_url)
28
- https = Net::HTTP.new(uri.host, uri.port)
29
- https.use_ssl = uri.scheme == "https"
30
- request = Net::HTTP::Post.new(uri.path.empty? ? "/" : uri.path, initHeader = {'Content-Type' =>'application/json', 'x-hub-signature' => generate_signature("#{data}")})
31
- request.body = "#{data}"
32
- begin
33
- response = https.request(request)
34
- response.to_hash
35
- rescue => e
36
- puts "Webhook error: \n\t#{e.message}"
37
- end
9
+ def initialize
10
+ @webhook_url = ENV['WEBHOOK_URL']
11
+ @secret = ENV['WEBHOOK_SECRET']
12
+ @conn = Faraday.new(url: @webhook_url) do |faraday|
13
+ faraday.headers['Content-Type'] = 'application/json'
14
+ end
15
+ end
16
+
17
+ def generate_signature(payload_body)
18
+ "md5=#{OpenSSL::HMAC.hexdigest('md5', ENV['WEBHOOK_SECRET'], payload_body)}"
19
+ end
20
+
21
+ def backup_notification(result, webhook_data, _notification_level)
22
+ return if @webhook_url.nil? || @secret.nil?
23
+ return if result && notification_level == 'error'
24
+
25
+ @date = webhook_data[:date]
26
+ @database = webhook_data[:database]
27
+ @backup_path = webhook_data[:backup_path]
28
+
29
+ @data = {
30
+ domain: ENV['DEFAULT_URL'] || "#{@database} Backup",
31
+ backupPath: result ? @backup_path : nil,
32
+ backupDate: @date
33
+ }.to_json
34
+
35
+ begin
36
+ @response = @conn.post do |req|
37
+ req.headers['x-hub-signature'] = generate_signature(@data.to_s)
38
+ req.body = @data
38
39
  end
40
+
41
+ @response.to_hash
42
+ rescue StandardError => e
43
+ puts "Webhook error: \n\t#{e.message}"
44
+ end
39
45
  end
40
- end
46
+ end
47
+ end
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'capistrano/ops/notification/api'
2
4
  require 'capistrano/ops/notification/slack'
3
5
  require 'capistrano/ops/notification/webhook'
4
6
 
5
7
  module Notification
6
- class Error < StandardError; end
7
- end
8
+ class Error < StandardError; end
9
+ end
@@ -12,4 +12,4 @@ module Capistrano
12
12
  end
13
13
  end
14
14
  end
15
- end
15
+ end
@@ -2,7 +2,6 @@
2
2
 
3
3
  # rubocop:disable Metrics/BlockLength
4
4
  namespace :pg do
5
-
6
5
  @database = Rails.configuration.database_configuration[Rails.env]['database']
7
6
  @username = Rails.configuration.database_configuration[Rails.env]['username']
8
7
  @password = Rails.configuration.database_configuration[Rails.env]['password']
@@ -10,34 +9,33 @@ namespace :pg do
10
9
  @portnumber = Rails.configuration.database_configuration[Rails.env]['port']
11
10
  @backup_path = Rails.root.join(Rails.env.development? ? 'tmp/backups' : '../../shared/backups').to_s
12
11
  backups_enabled = Rails.env.production? || ENV['BACKUPS_ENABLED'] == 'true'
13
-
12
+ external_backup = Rails.env.production? || ENV['EXTERNAL_BACKUP_ENABLED'] == 'true'
14
13
  task :dump do
15
14
  unless backups_enabled
16
- p 'dump: Backups are disabled'
15
+ puts 'dump: Backups are disabled'
17
16
  exit(0)
18
17
  end
19
18
 
20
19
  notification = Notification::Api.new
21
20
  commandlist = dump_cmd
22
-
21
+
23
22
  system "mkdir -p #{@backup_path}" unless Dir.exist?(@backup_path)
24
-
23
+
25
24
  result = system(commandlist.join(' && '))
26
-
27
- if ENV['BACKUP_PROVIDER'].present? && result
28
- p "Uploading #{@filename} to #{ENV['BACKUP_PROVIDER']}..."
25
+
26
+ if ENV['BACKUP_PROVIDER'].present? && external_backup && result
27
+ puts "Uploading #{@filename} to #{ENV['BACKUP_PROVIDER']}..."
29
28
  provider = Backup::Api.new
30
29
  begin
31
- provider.upload("#{@backup_path}/#{@filename}", "#{@filename}")
32
- p "#{@filename} uploaded to #{ENV['BACKUP_PROVIDER']}"
30
+ provider.upload("#{@backup_path}/#{@filename}", @filename.to_s)
31
+ puts "#{@filename} uploaded to #{ENV['BACKUP_PROVIDER']}"
33
32
  rescue StandardError => e
34
- p "#{@filename} upload failed: #{e.message}"
33
+ puts "#{@filename} upload failed: #{e.message}"
35
34
  end
36
35
  end
37
- notification.send_backup_notification(result,title,content(result))
38
- p result ? "Backup created: #{@backup_path}/#{@filename}" : "Backup failed removing dump file"
36
+ notification.send_backup_notification(result, title, content(result), { date: @date, database: @database, backup_path: @backup_path })
37
+ puts result ? "Backup created: #{@backup_path}/#{@filename}" : 'Backup failed removing dump file'
39
38
  system "rm #{@backup_path}/#{@filename}" unless result
40
-
41
39
  end
42
40
 
43
41
  def title
@@ -56,20 +54,19 @@ namespace :pg do
56
54
  end
57
55
 
58
56
  def dump_cmd
59
- date = Time.now.to_i
57
+ @date = Time.now.to_i
60
58
  options = []
61
59
  options << " -d #{@database}" if @database.present?
62
60
  options << " -U #{@username}" if @username.present?
63
61
  options << " -h #{@hostname}" if @hostname.present?
64
62
  options << " -p #{@portnumber}" if @portnumber.present?
65
-
66
- @filename = "#{@database}_#{date}.dump"
67
-
63
+
64
+ @filename = "#{@database}_#{@date}.dump"
65
+
68
66
  commandlist = []
69
67
  commandlist << "export PGPASSWORD='#{@password}'"
70
68
  commandlist << "cd #{@backup_path}"
71
69
  commandlist << "pg_dump -Fc #{options.join('')} > #{@filename}"
72
70
  end
73
-
74
71
  end
75
72
  # rubocop:enable Metrics/BlockLength