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.
@@ -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
@@ -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 backup_db?
11
- !!@backup_db
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 default?
15
- !!@default
28
+ def pinging_interaction?
29
+ !!new_relic_disable_pinger_url
16
30
  end
17
31
 
18
32
  def requires_curl?
19
- !!disable_pinger_url
20
- end
21
-
22
- def requires_pinging_interaction?
23
- # If URL to disable pinging is set, we want to disable and enable pinging when deploying
24
- disable_pinger_url
25
- end
26
-
27
- def initialize(raw_target)
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