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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 93edb710600a30c90ce512be9dae066d6cfb64f5
4
- data.tar.gz: 9129a7c55e56c7fed6c3a269763c22ccc6a01ceb
3
+ metadata.gz: 269384c663c1693b844f227cbd8219d1e9e0ddaf
4
+ data.tar.gz: 3f9c00a090f410433ea4949950032515eeaa7f76
5
5
  SHA512:
6
- metadata.gz: d84c22c58a26875b651ef921ae5ac61c25e9e4791be78f523d0abf7ceb77f6089ad9a48a834302540b3f4bce6b316a577362e8391fd66699910cf62c867df29e
7
- data.tar.gz: 8447cc2204afa97ee798a40201a7ee2efecb13688f199f0fca99e9776a5f98fd010cc763598c5e2a3e5d24923c1f7b1d04fb45b1fcaa927df01a03d49f1a357c
6
+ metadata.gz: d1a59907ccd34338cd0abf9fbaa37b57ad4368207e318918a494743718ad6a4910888923037a495d464bbc1a49654413fcd255003566caecac853928090e6a65
7
+ data.tar.gz: b55cfd0cf957c4f195fb3511326dacaa3c2d67bb266a5d78020268b69d54b75670ca775e76d1e7d5aaaf30f8a1eabd4e37463052d45f774809c0391cb1f2671e
data/bin/hr_deploy CHANGED
@@ -1,230 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'hr_deploy/deployer'
4
- require 'hr_deploy/config_handler'
5
- require 'hr_deploy/target'
6
- require 'hr_deploy/version'
3
+ require 'hr_deploy/application'
7
4
 
8
- private
9
-
10
- def main
11
- command = parse(ARGV)
12
- if command
13
- react(command)
14
- else
15
- puts 'Unknown command'
16
- puts
17
- help(true)
18
- exit 1
19
- end
20
- end
21
-
22
- def parse(opts)
23
- if opts == [] || opts == ['help']
24
- { command: :help }
25
-
26
- elsif opts == ['version']
27
- { command: :version }
28
-
29
- elsif opts == ['generate']
30
- { command: :generate }
31
-
32
- elsif opts.first == 'deploy'
33
- # Deploy command
34
- # Possible combinations:
35
- #
36
- # (empty)
37
- # (target)
38
- # --no-confirm
39
- # (target) --no-confirm
40
- # --no-confirm target
41
-
42
- deploy_options = opts[1, opts.length]
43
-
44
- # 0 to 2 additional options to 'deploy' command
45
- return nil unless deploy_options.length <= 2
46
-
47
- confirm = true
48
- confirm = false if deploy_options.delete('--no-confirm')
49
-
50
- # After we have removed '--no-confirm', we either have one param (target) or nothing
51
- return nil unless deploy_options.length <= 1
52
-
53
- # 'nil' for no target (default target)
54
- target = deploy_options.first
55
-
56
- # Explicit target should not be a command argument
57
- return nil if target && target.start_with?('--')
58
-
59
- { command: :deploy, target: target, confirm: confirm }
60
-
61
- else
62
- nil
63
- end
64
- end
65
-
66
- def react(command)
67
- if command[:command] == :help
68
- help
69
-
70
- elsif command[:command] == :version
71
- version
72
-
73
- elsif command[:command] == :generate || command[:command] == :deploy
74
- if inside_app?
75
- if command[:command] == :generate
76
- generate
77
- else
78
- attempt_deploy(command[:target], command[:confirm])
79
- end
80
-
81
- else
82
- puts 'You should be inside a Heroku app to run this command'
83
- exit 1
84
- end
85
-
86
- else
87
- raise "Command was parsed but don't know how to deal with it"
88
- end
89
- end
90
-
91
- def generate
92
- generate = true
93
-
94
- if HR_Deploy::ConfigHandler.config_exists?
95
- print 'Config file already exists. Overwrite? [Y/n] > '
96
- generate = ($stdin.gets.chomp == 'Y')
97
- end
98
-
99
- if generate
100
- HR_Deploy::ConfigHandler.new.generate_config
101
- puts "Example config created at #{HR_Deploy::ConfigHandler::CONFIG_FILE_NAME}. Edit it to suit your needs"
102
- end
103
- end
104
-
105
- def attempt_deploy(target_name, confirm)
106
-
107
- # These tools are needed for all deploys
108
- exit 1 unless tools_available?('heroku', 'git')
109
-
110
- if HR_Deploy::ConfigHandler.config_exists?
111
- raw_targets = HR_Deploy::ConfigHandler.new.load_targets
112
- if raw_targets
113
- # Config is correct
114
- targets = raw_targets.map { |raw_target| HR_Deploy::Target.new(raw_target) }
115
-
116
- if target_name
117
- # Explicit target name given
118
-
119
- target = targets.find { |target| target.name == target_name }
120
- if target
121
- deploy(target, confirm)
122
- else
123
- puts "No such target: #{target_name}"
124
- exit 1
125
- end
126
-
127
- else
128
- # No target is specified, look for the default one
129
- default_targets = targets.select { |target| target.default? }
130
-
131
- if default_targets.length == 0
132
- puts 'No default target specified. Edit config to specify one'
133
- exit 1
134
- elsif default_targets.length > 1
135
- puts 'Multiple default targets specified. Edit config to specify only one'
136
- exit 1
137
- else
138
- # Deploy default target
139
- deploy(default_targets.first, confirm)
140
- end
141
- end
142
- else
143
- # 'ConfigHandler.new.load_targets' will print out the error here
144
- exit 1
145
- end
146
- else
147
- puts "No deployment config is present. Run 'hr_deploy generate' to generate a config example"
148
- exit 1
149
- end
150
- end
151
-
152
- def deploy(target, confirm)
153
- # Some targets need 'curl' to disable/enable New Relic pinger
154
- exit 1 if target.requires_curl? && !tools_available?('curl')
155
-
156
- # Actual deployment
157
- deployer = HR_Deploy::Deployer.new(target, confirm)
158
- deployer.deploy
159
- end
160
-
161
- def inside_app?
162
- File.directory?('.git')
163
- end
164
-
165
- def tools_available?(*tools)
166
-
167
- def command_available?(cmd)
168
- exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
169
- ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
170
- exts.each do |ext|
171
- exe = File.join(path, "#{cmd}#{ext}")
172
- return true if File.executable? exe
173
- end
174
- end
175
- false
176
- end
177
-
178
- all_tools_available = true
179
-
180
- tools.each do |tool|
181
- unless command_available?(tool)
182
- puts "You don't have access to '#{tool}' command"
183
- all_tools_available = false
184
- end
185
- end
186
-
187
- all_tools_available
188
- end
189
-
190
- def help(after_error = false)
191
- help_string = ''
192
-
193
- unless after_error
194
- help_string += "hr_deploy - Deploy Rails apps to Heroku\n\n"
195
- end
196
-
197
-
198
- help_string += <<-eos
199
- Usage: hr_deploy [command]
200
-
201
- The following commands are supported:
202
-
203
- generate Generate a config file which specifies targets to deploy to.
204
- Won't overwrite existing config unless confirmed.
205
-
206
- deploy [target] Deploy to [target]. If no [target] is specified, deploy
207
- to default target specified in config.
208
- This command supports the following options:
209
-
210
- --no-confirm Skip confirmations while deploying.
211
- This does not affect the initial
212
- confirmation that triggers the deploy itself.
213
- Default behavior is to confirm actions.
214
-
215
- version Show gem version.
216
-
217
- help Show help.
218
-
219
- Verbose documentation is at:
220
- https://github.com/enthrops/hr_deploy/blob/master/README.md
221
- eos
222
-
223
- puts help_string
224
- end
225
-
226
- def version
227
- puts HR_Deploy::VERSION
228
- end
229
-
230
- main
5
+ HR_Deploy::Application.new.run
@@ -0,0 +1,64 @@
1
+ require 'hr_deploy/parser'
2
+ require 'hr_deploy/config_handler'
3
+ require 'hr_deploy/deployer'
4
+ require 'hr_deploy/helper'
5
+ require 'hr_deploy/version'
6
+
7
+ module HR_Deploy
8
+
9
+ class Application
10
+
11
+ def run
12
+ parser = HR_Deploy::Parser.new
13
+ command = parser.parse_cmd_args
14
+
15
+ dispatch(command[:action], command[:options])
16
+ end
17
+
18
+ private
19
+
20
+ def dispatch(action, options)
21
+ if action == :help
22
+ HR_Deploy::Helper.new.show_help(options)
23
+ return
24
+ end
25
+
26
+ if action == :version
27
+ show_version
28
+ return
29
+ end
30
+
31
+ if action == :generate
32
+ # Will halt app if not at the top of a git repo
33
+ assert_inside_git_root
34
+ HR_Deploy::ConfigHandler.new.generate_config
35
+ return
36
+ end
37
+
38
+ if action == :deploy
39
+ # Will halt app if not at the top of a git repo
40
+ assert_inside_git_root
41
+ HR_Deploy::Deployer.new.deploy(options[:target_name],
42
+ options[:confirm],
43
+ options[:dry_run],
44
+ options[:db_change])
45
+ return
46
+ end
47
+
48
+ raise "Command was parsed but don't know how to deal with it"
49
+ end
50
+
51
+ def show_version
52
+ puts "hr_deploy #{HR_Deploy::VERSION}"
53
+ end
54
+
55
+ private
56
+
57
+ def assert_inside_git_root
58
+ unless File.directory?('.git')
59
+ puts 'You should be at the top level of a Heroku application to run this command'
60
+ exit 1
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,21 @@
1
+ class String
2
+ def colorize(color_code)
3
+ "\e[#{color_code}m#{self}\e[0m"
4
+ end
5
+
6
+ def green
7
+ colorize(32)
8
+ end
9
+
10
+ def blue
11
+ colorize(34)
12
+ end
13
+
14
+ def red
15
+ colorize(31)
16
+ end
17
+
18
+ def yellow
19
+ colorize(33)
20
+ end
21
+ end
@@ -1,9 +1,12 @@
1
1
  require 'yaml'
2
+ require 'hr_deploy/target'
2
3
 
3
4
  module HR_Deploy
4
5
 
5
6
  class ConfigHandler
6
7
 
8
+ private
9
+
7
10
  CONFIG_FILE_NAME = '.heroku_deploy_config.yaml'
8
11
 
9
12
  EXAMPLE_CONFIG =
@@ -40,6 +43,18 @@ module HR_Deploy
40
43
  # automatically, set this to true. Delete this line if you don't use that.
41
44
  :backup_db: true
42
45
 
46
+ # Whether to enable and disable maintenance mode before and after deploys.
47
+ # Optional, defaults to true. Nice to disable for staging environments.
48
+ :maintenance: true
49
+
50
+ # Whether to precompile and sync your assets to Amazon S3 bucket.
51
+ # Enable if you use the 'asset_sync' gem. This will run 'rake assets:clean'
52
+ # and 'rake assets:precompile' before pushing code to Heroku. Note that
53
+ # this option does not configure 'asset_sync' to work with your project,
54
+ # you have to set that up manually. Delete this line if you don't use that.
55
+ # Optional, defaults to false.
56
+ :s3_asset_sync: true
57
+
43
58
  # Whether this target is default, optional, defaults to false.
44
59
  # If default is set to true, this target will be picked automatically
45
60
  # when you execute 'hr_deploy deploy' without arguments.
@@ -51,29 +66,76 @@ module HR_Deploy
51
66
  :new_relic_disable_pinger_url: 'http://disable'
52
67
  :new_relic_enable_pinger_url: 'http://enable'
53
68
  :backup_db: true
69
+ :maintenance: true
70
+ :s3_asset_sync: true
54
71
  :default: false
55
72
  eos
56
73
 
74
+ public
75
+
76
+ attr_reader :error
77
+ attr_reader :targets
78
+
79
+ def new
80
+ @error = nil
81
+ @raw_targets = nil
82
+ @targets = []
83
+ end
84
+
57
85
  def self.config_exists?
58
86
  File.exists?(CONFIG_FILE_NAME)
59
87
  end
60
88
 
61
- # Check if the YAML file is in the right format
62
- def load_targets
89
+ def generate_config
90
+ generate = true
91
+
92
+ if self.class.config_exists?
93
+ print 'Config file already exists. Overwrite? [Y/n] > '
94
+ answer = $stdin.gets.chomp.downcase
95
+ generate = false unless answer == 'y' || answer == 'yes'
96
+ end
97
+
98
+ if generate
99
+ File.open(CONFIG_FILE_NAME, 'w') { |file| file.write(EXAMPLE_CONFIG) }
100
+ puts "Example config created at #{CONFIG_FILE_NAME}. Edit it to suit your needs"
101
+ else
102
+ puts 'Not overwriting config'
103
+ end
104
+ end
105
+
106
+ def attempt_to_load_targets
107
+ # If any of the checks fail, 'self.error' will be set
108
+ # If no checks fail, 'self.targets' will be set
109
+
110
+ if valid_format?
111
+ if valid_structure?
112
+ self.targets = load_targets
113
+ end
114
+ end
115
+ end
116
+
117
+ private
118
+
119
+ attr_writer :error
120
+ attr_writer :targets
121
+ attr_accessor :raw_targets
122
+
123
+ def valid_format?
63
124
  begin
64
- targets = YAML.load_file(CONFIG_FILE_NAME)
125
+ self.raw_targets = YAML.load_file(CONFIG_FILE_NAME)
65
126
 
66
127
  rescue
67
- puts 'Could not parse config'
68
- puts "Fix it or generate a new one with 'hr_deploy generate'"
69
- return nil
128
+ self.error = "Could not parse config\n" +
129
+ "Fix it or generate a new one with 'hr_deploy generate'"
130
+ false
70
131
 
71
132
  else
133
+ # Check config is in the right format
72
134
 
73
135
  correct_format = true
74
136
 
75
- if targets.instance_of?(Array)
76
- targets.each do |target|
137
+ if raw_targets.instance_of?(Array)
138
+ raw_targets.each do |target|
77
139
  if target.instance_of?(Hash) && target.length == 1
78
140
  options = target.values.first
79
141
  unless options.instance_of?(Hash) || options == nil
@@ -90,28 +152,29 @@ module HR_Deploy
90
152
  end
91
153
 
92
154
  if correct_format
93
- correct_config?(targets) ? targets : nil
155
+ true
94
156
  else
95
- puts 'Incorrect config format'
96
- puts "Fix it or generate a new one with 'hr_deploy generate'"
97
- nil
157
+ self.error = "Incorrect config format\n" +
158
+ "Fix it or generate a new one with 'hr_deploy generate'"
159
+ false
98
160
  end
99
161
  end
100
162
  end
101
163
 
102
- def correct_config?(targets)
164
+ def valid_structure?
103
165
  # At this point we know we've got an array of hashes
104
166
  # Each hash has one key, and another hash as the value of that key
105
167
 
106
- targets.each do |target|
168
+ raw_targets.each do |target|
107
169
 
108
170
  enable_pinger_url_given = false
109
171
  disable_pinger_url_given = false
110
172
 
111
173
  target_name = target.keys.first
174
+
112
175
  # Each target name is a symbol
113
176
  unless target_name.instance_of?(Symbol)
114
- puts "Target name is specified incorrectly: #{target_name}"
177
+ self.error = "Target name is specified incorrectly: #{target_name}"
115
178
  return false
116
179
  end
117
180
 
@@ -124,67 +187,99 @@ module HR_Deploy
124
187
 
125
188
  # Each option name is a symbol
126
189
  unless option.instance_of?(Symbol)
127
- puts "Option is specified incorrectly: #{option}"
190
+ self.error = "Option is specified incorrectly: #{option}"
128
191
  return false
129
192
  end
130
193
 
131
194
  case option
132
195
  when :description
133
196
  unless value.instance_of?(String)
134
- puts 'Target description should be a string'
197
+ self.error = 'Target description should be a string'
135
198
  return false
136
199
  end
137
200
 
138
201
  when :new_relic_disable_pinger_url
139
202
  disable_pinger_url_given = true
140
203
  unless value.instance_of?(String)
141
- puts 'New Relic disable pinger URL should be a string'
204
+ self.error = 'New Relic disable pinger URL should be a string'
142
205
  return false
143
206
  end
144
207
 
145
208
  when :new_relic_enable_pinger_url
146
209
  enable_pinger_url_given = true
147
210
  unless value.instance_of?(String)
148
- puts 'New Relic enable pinger URL should be a string'
211
+ self.error = 'New Relic enable pinger URL should be a string'
149
212
  return false
150
213
  end
151
214
 
152
215
  when :backup_db
153
216
  unless value.instance_of?(TrueClass) || value.instance_of?(FalseClass)
154
- puts 'Backup database option should be a boolean'
217
+ self.error = 'Backup database option should be a boolean'
218
+ return false
219
+ end
220
+
221
+ when :maintenance
222
+ unless value.instance_of?(TrueClass) || value.instance_of?(FalseClass)
223
+ self.error = 'Maintenance mode control option should be a boolean'
224
+ return false
225
+ end
226
+
227
+ when :s3_asset_sync
228
+ unless value.instance_of?(TrueClass) || value.instance_of?(FalseClass)
229
+ self.error = "'asset_sync' gem invoke option should be a boolean"
155
230
  return false
156
231
  end
157
232
 
158
233
  when :default
159
234
  unless value.instance_of?(TrueClass) || value.instance_of?(FalseClass)
160
- puts 'Whether this target is the default one option should be a boolean'
235
+ self.error = 'Whether this target is the default one option should be a boolean'
161
236
  return false
162
237
  end
163
238
 
164
239
  else
165
- puts "Option unknown: #{option}"
240
+ self.error = "Option unknown: #{option}"
166
241
  return false
167
242
  end
168
243
  end
169
244
 
170
245
  if enable_pinger_url_given && (!disable_pinger_url_given)
171
- puts 'URL to enable New Relic pinger is given, but URL to disable it is not'
246
+ self.error = 'URL to enable New Relic pinger is given, but URL to disable it is not'
172
247
  return false
173
248
  end
174
249
 
175
250
  if disable_pinger_url_given && (!enable_pinger_url_given)
176
- puts 'URL to disable New Relic pinger is given, but URL to enable it is not'
251
+ self.error = 'URL to disable New Relic pinger is given, but URL to enable it is not'
177
252
  return false
178
253
  end
179
254
  end
180
255
  end
181
256
 
182
- # Config is correct
257
+ # Config is correct if we made it here
183
258
  true
184
259
  end
185
260
 
186
- def generate_config
187
- File.open(CONFIG_FILE_NAME, 'w') { |file| file.write(EXAMPLE_CONFIG) }
261
+ # 'self.raw_targets' is set and config is valid
262
+ # Map those into 'Target' objects
263
+ def load_targets
264
+ raw_targets.map do |target|
265
+ name = target.keys.first.to_s
266
+ options = target.values.first
267
+
268
+ if options
269
+ # Additional options were given in the config
270
+ HR_Deploy::Target.new(name: name,
271
+ description: options[:description],
272
+ new_relic_disable_pinger_url: options[:new_relic_disable_pinger_url],
273
+ new_relic_enable_pinger_url: options[:new_relic_enable_pinger_url],
274
+ backup_db: options[:backup_db],
275
+ maintenance: options[:maintenance],
276
+ default: options[:default],
277
+ s3_asset_sync: options[:s3_asset_sync])
278
+ else
279
+ # Only target name was given, 'options' hash is nil
280
+ HR_Deploy::Target.new(name: name)
281
+ end
282
+ end
188
283
  end
189
284
  end
190
285
  end