hr_deploy 0.0.2 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|