hr_deploy 0.0.2 → 0.0.6
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.
- checksums.yaml +4 -4
- data/bin/hr_deploy +2 -227
- data/lib/hr_deploy/application.rb +64 -0
- data/lib/hr_deploy/color.rb +21 -0
- data/lib/hr_deploy/config_handler.rb +122 -27
- data/lib/hr_deploy/deployer.rb +256 -303
- data/lib/hr_deploy/helper.rb +57 -0
- data/lib/hr_deploy/parser.rb +102 -0
- data/lib/hr_deploy/target.rb +48 -40
- data/lib/hr_deploy/tasks/backup_db_task.rb +25 -0
- data/lib/hr_deploy/tasks/check_heroku_task.rb +64 -0
- data/lib/hr_deploy/tasks/check_network_task.rb +29 -0
- data/lib/hr_deploy/tasks/clean_new_assets_task.rb +25 -0
- data/lib/hr_deploy/tasks/clean_old_assets_task.rb +25 -0
- data/lib/hr_deploy/tasks/disable_maintenance_task.rb +25 -0
- data/lib/hr_deploy/tasks/disable_pinger_task.rb +29 -0
- data/lib/hr_deploy/tasks/enable_maintenance_task.rb +29 -0
- data/lib/hr_deploy/tasks/enable_pinger_task.rb +25 -0
- data/lib/hr_deploy/tasks/migrate_db_task.rb +45 -0
- data/lib/hr_deploy/tasks/precompile_assets_task.rb +25 -0
- data/lib/hr_deploy/tasks/push_code_task.rb +33 -0
- data/lib/hr_deploy/tasks/restart_task.rb +26 -0
- data/lib/hr_deploy/tasks/task.rb +81 -0
- data/lib/hr_deploy/version.rb +1 -1
- metadata +20 -2
@@ -0,0 +1,57 @@
|
|
1
|
+
module HR_Deploy
|
2
|
+
|
3
|
+
class Helper
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
HELP_STRING = <<-eos
|
8
|
+
Usage: hr_deploy [command]
|
9
|
+
|
10
|
+
The following commands are supported:
|
11
|
+
|
12
|
+
generate Generate a config file which specifies targets to deploy to.
|
13
|
+
Won't overwrite existing config unless confirmed.
|
14
|
+
|
15
|
+
deploy [target] Deploy to [target]. If no [target] is specified, deploy
|
16
|
+
to default target specified in config.
|
17
|
+
This command supports the following options:
|
18
|
+
|
19
|
+
--no-confirm Skip confirmations while deploying.
|
20
|
+
This does not affect the initial
|
21
|
+
confirmation that triggers the deploy itself.
|
22
|
+
Default behavior is to confirm actions.
|
23
|
+
|
24
|
+
--dry-run Don't actually execute commands, only show
|
25
|
+
which commands would get executed on a real
|
26
|
+
deploy. This skips all confirmations,
|
27
|
+
including the initial confirmation that
|
28
|
+
triggers the deploy itself.
|
29
|
+
Default behavior is to actually run commands.
|
30
|
+
|
31
|
+
--no-db-change Skip database related commands: backing up,
|
32
|
+
migrating and restarting.
|
33
|
+
|
34
|
+
version Show gem version.
|
35
|
+
|
36
|
+
help Show help.
|
37
|
+
|
38
|
+
Verbose documentation is at:
|
39
|
+
https://github.com/enthrops/hr_deploy/blob/master/README.md
|
40
|
+
eos
|
41
|
+
|
42
|
+
public
|
43
|
+
|
44
|
+
def show_help(args)
|
45
|
+
after_failed_parse = args.fetch(:failed_parse, false)
|
46
|
+
|
47
|
+
if after_failed_parse
|
48
|
+
puts 'Unknown command'
|
49
|
+
puts
|
50
|
+
end
|
51
|
+
|
52
|
+
puts HELP_STRING
|
53
|
+
|
54
|
+
exit 1 if after_failed_parse
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module HR_Deploy
|
2
|
+
|
3
|
+
class Parser
|
4
|
+
|
5
|
+
def parse_cmd_args
|
6
|
+
command = {}
|
7
|
+
command[:action] = nil
|
8
|
+
command[:options] = {}
|
9
|
+
|
10
|
+
cmd_options = ARGV
|
11
|
+
|
12
|
+
if cmd_options == [] || cmd_options == ['help']
|
13
|
+
command[:action] = :help
|
14
|
+
return command
|
15
|
+
end
|
16
|
+
|
17
|
+
if cmd_options == ['version']
|
18
|
+
command[:action] = :version
|
19
|
+
return command
|
20
|
+
end
|
21
|
+
|
22
|
+
if cmd_options == ['generate']
|
23
|
+
command[:action] = :generate
|
24
|
+
return command
|
25
|
+
end
|
26
|
+
|
27
|
+
if cmd_options.first == 'deploy'
|
28
|
+
|
29
|
+
deploy_args = cmd_options[1, cmd_options.length]
|
30
|
+
|
31
|
+
if deploy_args_valid?(deploy_args)
|
32
|
+
|
33
|
+
# Don't dry run by default
|
34
|
+
dry_run = deploy_args.delete('--dry-run')
|
35
|
+
|
36
|
+
# Confirm actions on deploys by default
|
37
|
+
# If '--dry-run' was provided, skip confirmations
|
38
|
+
confirm = !(deploy_args.delete('--no-confirm') || dry_run)
|
39
|
+
|
40
|
+
# Run database change actions by default
|
41
|
+
db_change = !deploy_args.delete('--no-db-change')
|
42
|
+
|
43
|
+
# If no target specified, it will be set to 'nil'
|
44
|
+
target_name = deploy_args.first
|
45
|
+
|
46
|
+
command[:action] = :deploy
|
47
|
+
command[:options][:target_name] = target_name
|
48
|
+
command[:options][:confirm] = confirm
|
49
|
+
command[:options][:dry_run] = dry_run
|
50
|
+
command[:options][:db_change] = db_change
|
51
|
+
|
52
|
+
return command
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Can't parse command
|
57
|
+
command[:action] = :help
|
58
|
+
command[:options][:failed_parse] = true
|
59
|
+
command
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def deploy_args_valid?(args_to_deploy)
|
65
|
+
|
66
|
+
# We are going to mutate args later in the method
|
67
|
+
args_to_deploy = args_to_deploy.dup
|
68
|
+
|
69
|
+
# 0 to 3 additional options to 'deploy' command
|
70
|
+
# (target name, '--no-confirm', '--dry-run', '--no-db-change')
|
71
|
+
return false unless args_to_deploy.length <= 4
|
72
|
+
|
73
|
+
# Delete '--no-confirm' flag if it is present
|
74
|
+
if args_to_deploy.index('--no-confirm')
|
75
|
+
args_to_deploy.delete_at(args_to_deploy.index('--no-confirm'))
|
76
|
+
end
|
77
|
+
|
78
|
+
# Delete '--dry-run' flag if it is present
|
79
|
+
if args_to_deploy.index('--dry-run')
|
80
|
+
args_to_deploy.delete_at(args_to_deploy.index('--dry-run'))
|
81
|
+
end
|
82
|
+
|
83
|
+
# Delete '--no-db-change' flag if it is present
|
84
|
+
if args_to_deploy.index('--no-db-change')
|
85
|
+
args_to_deploy.delete_at(args_to_deploy.index('--no-db-change'))
|
86
|
+
end
|
87
|
+
|
88
|
+
# After we have removed '--no-confirm', '--dry-run' and '--no-db-change'
|
89
|
+
# we either have one param (target name) or nothing
|
90
|
+
return false unless args_to_deploy.length <= 1
|
91
|
+
|
92
|
+
# Explicit target should not be a command argument
|
93
|
+
if args_to_deploy.length == 1
|
94
|
+
if args_to_deploy.first.start_with?('--') || args_to_deploy.first.start_with?('-')
|
95
|
+
return false
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
true
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/hr_deploy/target.rb
CHANGED
@@ -3,53 +3,61 @@ module HR_Deploy
|
|
3
3
|
class Target
|
4
4
|
|
5
5
|
attr_reader :name
|
6
|
+
attr_reader :new_relic_disable_pinger_url
|
7
|
+
attr_reader :new_relic_enable_pinger_url
|
6
8
|
attr_reader :description
|
7
|
-
attr_reader :disable_pinger_url
|
8
|
-
attr_reader :enable_pinger_url
|
9
9
|
|
10
|
-
def
|
11
|
-
|
10
|
+
def initialize(args)
|
11
|
+
|
12
|
+
@name = args[:name]
|
13
|
+
@new_relic_disable_pinger_url = args[:new_relic_disable_pinger_url]
|
14
|
+
@new_relic_enable_pinger_url = args[:new_relic_enable_pinger_url]
|
15
|
+
@s3_asset_sync = args[:s3_asset_sync]
|
16
|
+
@default = args[:default]
|
17
|
+
|
18
|
+
# Use name for description, if no description given
|
19
|
+
@description = args[:description] || name
|
20
|
+
|
21
|
+
# Backup is off by default
|
22
|
+
@backup_db = args[:backup_db] == true
|
23
|
+
|
24
|
+
# Control maintenance mode is on by default
|
25
|
+
@maintenance = args[:maintenance] == false ? false : true
|
12
26
|
end
|
13
27
|
|
14
|
-
def
|
15
|
-
|
28
|
+
def pinging_interaction?
|
29
|
+
!!new_relic_disable_pinger_url
|
16
30
|
end
|
17
31
|
|
18
32
|
def requires_curl?
|
19
|
-
|
20
|
-
end
|
21
|
-
|
22
|
-
def
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
target_name = raw_target.keys.first
|
29
|
-
@name = target_name.to_s
|
30
|
-
|
31
|
-
options = raw_target[target_name]
|
32
|
-
# Target might have no options
|
33
|
-
if options
|
34
|
-
options.each do |option, value|
|
35
|
-
case option
|
36
|
-
when :description
|
37
|
-
@description = value
|
38
|
-
when :new_relic_disable_pinger_url
|
39
|
-
@disable_pinger_url = value
|
40
|
-
when :new_relic_enable_pinger_url
|
41
|
-
@enable_pinger_url = value
|
42
|
-
when :backup_db
|
43
|
-
@backup_db = value
|
44
|
-
when :default
|
45
|
-
@default = value
|
46
|
-
else
|
47
|
-
raise "Unknown option: #{option}"
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
@description = name unless description
|
33
|
+
pinging_interaction?
|
34
|
+
end
|
35
|
+
|
36
|
+
def requires_rake?
|
37
|
+
s3_asset_sync?
|
38
|
+
end
|
39
|
+
|
40
|
+
def s3_asset_sync?
|
41
|
+
!!s3_asset_sync
|
53
42
|
end
|
43
|
+
|
44
|
+
def backup_db?
|
45
|
+
!!backup_db
|
46
|
+
end
|
47
|
+
|
48
|
+
def maintenance_interaction?
|
49
|
+
!!maintenance
|
50
|
+
end
|
51
|
+
|
52
|
+
def default?
|
53
|
+
!!default
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
attr_reader :s3_asset_sync
|
59
|
+
attr_reader :maintenance
|
60
|
+
attr_reader :backup_db
|
61
|
+
attr_reader :default
|
54
62
|
end
|
55
63
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'hr_deploy/tasks/task'
|
2
|
+
|
3
|
+
module HR_Deploy
|
4
|
+
|
5
|
+
class BackupDBTask < HR_Deploy::Task
|
6
|
+
|
7
|
+
def run
|
8
|
+
print_stage 'Backing up database...'
|
9
|
+
|
10
|
+
execute_system_command("heroku pgbackups:capture --expire --remote #{target.name}")
|
11
|
+
|
12
|
+
if dry_run? || !confirm?
|
13
|
+
self.success = true
|
14
|
+
return
|
15
|
+
end
|
16
|
+
|
17
|
+
if confirm_successful?('Database backed up?')
|
18
|
+
self.success = true
|
19
|
+
else
|
20
|
+
self.success = false
|
21
|
+
self.error = 'Could not back up database'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'hr_deploy/tasks/task'
|
2
|
+
require 'uri'
|
3
|
+
require 'net/http'
|
4
|
+
require 'net/https'
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
module HR_Deploy
|
8
|
+
|
9
|
+
class CheckHerokuTask < HR_Deploy::Task
|
10
|
+
|
11
|
+
def run
|
12
|
+
print_stage 'Checking Heroku status...'
|
13
|
+
|
14
|
+
if dry_run?
|
15
|
+
self.success = true
|
16
|
+
return
|
17
|
+
end
|
18
|
+
|
19
|
+
uri = URI.parse('https://status.heroku.com/api/v3/current-status')
|
20
|
+
|
21
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
22
|
+
http.use_ssl = true
|
23
|
+
|
24
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
25
|
+
|
26
|
+
begin
|
27
|
+
response = http.request(request)
|
28
|
+
|
29
|
+
rescue
|
30
|
+
self.success = false
|
31
|
+
self.error = 'Could not fetch Heroku status'
|
32
|
+
|
33
|
+
else
|
34
|
+
if response.code == '200'
|
35
|
+
|
36
|
+
status = JSON.parse(response.body)
|
37
|
+
status = status['status'] if status
|
38
|
+
|
39
|
+
if status && status['Production'] && status['Development']
|
40
|
+
|
41
|
+
production_ok = status['Production'] == 'green'
|
42
|
+
development_ok = status['Development'] == 'green'
|
43
|
+
|
44
|
+
if production_ok && development_ok
|
45
|
+
print_success 'Heroku is operating correctly'
|
46
|
+
self.success = true
|
47
|
+
else
|
48
|
+
self.success = false
|
49
|
+
self.error = 'Heroku is having problems'
|
50
|
+
end
|
51
|
+
else
|
52
|
+
|
53
|
+
self.success = false
|
54
|
+
self.error = 'Could not parse Heroku status'
|
55
|
+
end
|
56
|
+
|
57
|
+
else
|
58
|
+
self.success = false
|
59
|
+
self.error = 'Could not fetch Heroku status'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'hr_deploy/tasks/task'
|
2
|
+
require 'open-uri'
|
3
|
+
|
4
|
+
module HR_Deploy
|
5
|
+
|
6
|
+
class CheckNetworkTask < HR_Deploy::Task
|
7
|
+
|
8
|
+
def run
|
9
|
+
print_stage 'Checking Internet connection...'
|
10
|
+
|
11
|
+
if dry_run?
|
12
|
+
self.success = true
|
13
|
+
return
|
14
|
+
end
|
15
|
+
|
16
|
+
begin
|
17
|
+
open('http://www.google.com')
|
18
|
+
|
19
|
+
rescue
|
20
|
+
self.error = 'Not connected to the Internet'
|
21
|
+
self.success = false
|
22
|
+
|
23
|
+
else
|
24
|
+
print_success 'Internet connection is fine'
|
25
|
+
self.success = true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'hr_deploy/tasks/task'
|
2
|
+
|
3
|
+
module HR_Deploy
|
4
|
+
|
5
|
+
class CleanNewAssetsTask < HR_Deploy::Task
|
6
|
+
|
7
|
+
def run
|
8
|
+
print_stage 'Cleaning up new assets...'
|
9
|
+
|
10
|
+
execute_system_command('rake assets:clean')
|
11
|
+
|
12
|
+
if dry_run? || !confirm?
|
13
|
+
self.success = true
|
14
|
+
return
|
15
|
+
end
|
16
|
+
|
17
|
+
if confirm_successful?('New assets cleaned up?')
|
18
|
+
self.success = true
|
19
|
+
else
|
20
|
+
self.success = false
|
21
|
+
self.error = 'Could not clean up new assets'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'hr_deploy/tasks/task'
|
2
|
+
|
3
|
+
module HR_Deploy
|
4
|
+
|
5
|
+
class CleanOldAssetsTask < HR_Deploy::Task
|
6
|
+
|
7
|
+
def run
|
8
|
+
print_stage 'Cleaning up old assets...'
|
9
|
+
|
10
|
+
execute_system_command('rake assets:clean')
|
11
|
+
|
12
|
+
if dry_run? || !confirm?
|
13
|
+
self.success = true
|
14
|
+
return
|
15
|
+
end
|
16
|
+
|
17
|
+
if confirm_successful?('Old assets cleaned up?')
|
18
|
+
self.success = true
|
19
|
+
else
|
20
|
+
self.success = false
|
21
|
+
self.error = 'Could not clean up old assets'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'hr_deploy/tasks/task'
|
2
|
+
|
3
|
+
module HR_Deploy
|
4
|
+
|
5
|
+
class DisableMaintenanceTask < HR_Deploy::Task
|
6
|
+
|
7
|
+
def run
|
8
|
+
print_stage 'Disabling maintenance mode...'
|
9
|
+
|
10
|
+
execute_system_command("heroku maintenance:off --remote #{target.name}")
|
11
|
+
|
12
|
+
if dry_run? || !confirm?
|
13
|
+
self.success = true
|
14
|
+
return
|
15
|
+
end
|
16
|
+
|
17
|
+
if confirm_successful?('Maintenance mode disabled?')
|
18
|
+
self.success = true
|
19
|
+
else
|
20
|
+
self.success = false
|
21
|
+
self.error = 'Could not disable maintenance mode'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|