deployml 0.4.2 → 0.5.0

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.
data/ChangeLog.md CHANGED
@@ -1,3 +1,24 @@
1
+ ### 0.5.0 / 2011-04-22
2
+
3
+ * Added support for specifying multiple `dest` URIs.
4
+ * Added support for specifying `before` and `after` commands.
5
+ * Added {DeploYML::Configuration#each_dest}.
6
+ * Added {DeploYML::Configuration#normalize}.
7
+ * Added {DeploYML::Configuration#normalize_array}.
8
+ * Added {DeploYML::Configuration#parse_address}.
9
+ * Added {DeploYML::Configuration#parse_dest}.
10
+ * Added {DeploYML::Configuration#parse_commands}.
11
+ * Added {DeploYML::Environment#invoke_task}.
12
+ * Added {DeploYML::Environment#config}.
13
+ * Added {DeploYML::Environment#start}.
14
+ * Added {DeploYML::Environment#stop}.
15
+ * Added {DeploYML::Environment#restart}.
16
+ * Added {DeploYML::Shell#uri}.
17
+ * Added {DeploYML::Shell#exec}.
18
+ * Converted {DeploYML::Shell} into a Class.
19
+ * Raise an exception in {DeploYML::RemoteShell#ssh_uri} if the Shell URI
20
+ does not have a host component.
21
+
1
22
  ### 0.4.2 / 2011-04-11
2
23
 
3
24
  * Require rprogram ~> 0.2.
@@ -29,7 +50,7 @@
29
50
  * `start!`
30
51
  * `stop!`
31
52
  * Added {DeploYML::Shell#status} for printing ANSI colored status messages.
32
- * Added {DeploYML::RemoteShell#uri}.
53
+ * Added `DeploYML::RemoteShell#uri`.
33
54
  * Added {DeploYML::RemoteShell#history}.
34
55
  * Added missing documentation.
35
56
  * Give the root directory passed to {DeploYML::Project#initialize} the
data/README.md CHANGED
@@ -13,8 +13,9 @@ Git and SSH.
13
13
  ## Features
14
14
 
15
15
  * Requires only **one YAML file** (`config/deploy.yml`) with a minimum of
16
- **two** things (`source` and `dest`).
16
+ **two** settings (`source` and `dest`).
17
17
  * Supports multiple deployment environments (`config/deploy/staging.yml`).
18
+ * Supports multiple deployment destinations.
18
19
  * Supports [Git](http://www.git-scm.com/).
19
20
  * Can deploy Ruby web applications or static sites.
20
21
  * Supports common Web Servers:
@@ -31,7 +32,7 @@ Git and SSH.
31
32
  * Supports any Operating System that supports Ruby and SSH.
32
33
  * Provides a simple command-line interface using Thor.
33
34
 
34
- ## Examples
35
+ ## Configuration Examples
35
36
 
36
37
  Specifying `source` and `dest` URIs as Strings:
37
38
 
@@ -46,6 +47,14 @@ Specifying `dest` URI as a Hash:
46
47
  host: www.example.com
47
48
  path: /var/www/site
48
49
 
50
+ Specify multiple `dest` URIs:
51
+
52
+ source: git@github.com:user/project.git
53
+ dest:
54
+ - deploy@www1.example.com/var/www/site
55
+ - deploy@www2.example.com/var/www/site
56
+ - deploy@www3.example.com/var/www/site
57
+
49
58
  Specifying a `server` option:
50
59
 
51
60
  source: git@github.com:user/project.git
@@ -60,9 +69,9 @@ Specifying a `server` with options:
60
69
  name: thin
61
70
  options:
62
71
  servers: 4
63
- deamonize: true
64
- socket: /var/run/thin.sock
65
- rackup: true
72
+ deamonize: true
73
+ socket: /var/run/thin.sock
74
+ rackup: true
66
75
 
67
76
  Multiple environments:
68
77
 
@@ -87,6 +96,18 @@ Multiple environments:
87
96
  config: /etc/thin/example.yml
88
97
  socket: /tmp/thin.example.sock
89
98
 
99
+ Specifying before/after commands:
100
+
101
+ before:
102
+ restart: rm public/some/file
103
+
104
+ after:
105
+ install:
106
+ - mkdir tmp
107
+ - mkdir tmp/pids
108
+ - mkdir log
109
+ update: rake post_deploy
110
+
90
111
  ## Synopsis
91
112
 
92
113
  Cold-Deploy a new project:
@@ -1,4 +1,5 @@
1
1
  require 'deployml/exceptions/missing_option'
2
+ require 'deployml/exceptions/invalid_config'
2
3
 
3
4
  require 'addressable/uri'
4
5
 
@@ -12,6 +13,18 @@ module DeploYML
12
13
  # Default SCM to use
13
14
  DEFAULT_SCM = :rsync
14
15
 
16
+ # Valid task names
17
+ TASKS = [
18
+ :setup,
19
+ :update,
20
+ :install,
21
+ :migrate,
22
+ :config,
23
+ :start,
24
+ :stop,
25
+ :restart
26
+ ]
27
+
15
28
  # The server run the deployed project under
16
29
  attr_reader :server_name
17
30
 
@@ -39,6 +52,12 @@ module DeploYML
39
52
  # Specifies whether to enable debugging.
40
53
  attr_accessor :debug
41
54
 
55
+ # The arbitrary commands to run before various tasks
56
+ attr_reader :before
57
+
58
+ # The arbitrary commands to run after various tasks
59
+ attr_reader :after
60
+
42
61
  #
43
62
  # Creates a new {Configuration} using the given configuration.
44
63
  #
@@ -48,8 +67,8 @@ module DeploYML
48
67
  # @option config [String] :source
49
68
  # The source URI of the project Git repository.
50
69
  #
51
- # @option config [String, Hash] :dest
52
- # The destination URI to upload the project to.
70
+ # @option config [Array<String, Hash>, String, Hash] :dest
71
+ # The destination URI(s) to upload the project to.
53
72
  #
54
73
  # @option config [Boolean] :bundler
55
74
  # Specifies whether the projects dependencies are controlled by
@@ -71,67 +90,86 @@ module DeploYML
71
90
  # The `server` option Hash did not contain a `name` option.
72
91
  #
73
92
  def initialize(config={})
74
- @source = nil
75
- @dest = nil
93
+ config = normalize_hash(config)
76
94
 
77
- @server_name = nil
78
- @server_options = {}
95
+ @bundler = config.fetch(:bundler,false)
79
96
 
80
- @bundler = false
81
- @framework = nil
82
- @orm = nil
97
+ @framework = if config[:framework]
98
+ config[:framework].to_sym
99
+ end
83
100
 
84
- @environment = nil
85
- @debug = false
101
+ @orm = if config[:orm]
102
+ config[:orm].to_sym
103
+ end
86
104
 
87
- config = normalize_hash(config)
105
+ @server_name, @server_options = parse_server(config[:server])
88
106
 
89
- @bundler = config[:bundler]
107
+ @source = config[:source]
108
+ @dest = if config[:dest]
109
+ parse_dest(config[:dest])
110
+ end
90
111
 
91
- if config[:framework]
92
- @framework = config[:framework].to_sym
93
- end
112
+ @environment = if config[:environment]
113
+ config[:environment].to_sym
114
+ end
94
115
 
95
- if config[:orm]
96
- @orm = config[:orm].to_sym
97
- end
116
+ @debug = config.fetch(:debug,false)
98
117
 
99
- case config[:server]
100
- when Symbol, String
101
- @server_name = config[:server].to_sym
102
- when Hash
103
- unless config[:server].has_key?(:name)
104
- raise(MissingOption,"the 'server' option must contain a 'name' option for which server to use",caller)
105
- end
118
+ @before = {}
119
+ @after = {}
106
120
 
107
- if config[:server].has_key?(:name)
108
- @server_name = config[:server][:name].to_sym
121
+ TASKS.each do |task|
122
+ if (config.has_key?(:before) && config[:before].has_key?(task))
123
+ @before[task] = parse_commands(config[:before][task])
109
124
  end
110
125
 
111
- if config[:server].has_key?(:options)
112
- @server_options.merge!(config[:server][:options])
126
+ if (config.has_key?(:after) && config[:after].has_key?(task))
127
+ @after[task] = parse_commands(config[:after][task])
113
128
  end
114
129
  end
130
+ end
115
131
 
116
- @source = config[:source]
117
- @dest = case config[:dest]
118
- when Hash
119
- Addressable::URI.new(config[:dest])
120
- when String
121
- Addressable::URI.parse(config[:dest])
122
- end
123
-
124
- if config[:environment]
125
- @environment = config[:environment].to_sym
126
- end
132
+ #
133
+ # Iterates over each destination.
134
+ #
135
+ # @yield [dest]
136
+ # The given block will be passed each destination URI.
137
+ #
138
+ # @yieldparam [Addressable::URI] dest
139
+ # A destination URI.
140
+ #
141
+ # @return [Enumerator]
142
+ # If no block is given, an Enumerator object will be returned.
143
+ #
144
+ # @since 0.5.0
145
+ #
146
+ def each_dest(&block)
147
+ return enum_for(:each_dest) unless block_given?
127
148
 
128
- if config[:debug]
129
- @debug = config[:debug]
149
+ if @dest.kind_of?(Array)
150
+ @dest.each(&block)
151
+ elsif @dest
152
+ yield @dest
130
153
  end
131
154
  end
132
155
 
133
156
  protected
134
157
 
158
+ #
159
+ # Normalizes an Array.
160
+ #
161
+ # @param [Array] array
162
+ # The Array to normalize.
163
+ #
164
+ # @return [Array]
165
+ # The normalized Array.
166
+ #
167
+ # @since 0.5.0
168
+ #
169
+ def normalize_array(array)
170
+ array.map { |value| normalize(value) }
171
+ end
172
+
135
173
  #
136
174
  # Converts all the keys of a Hash to Symbols.
137
175
  #
@@ -145,15 +183,132 @@ module DeploYML
145
183
  new_hash = {}
146
184
 
147
185
  hash.each do |key,value|
148
- new_hash[key.to_sym] = if value.kind_of?(Hash)
149
- normalize_hash(value)
150
- else
151
- value
152
- end
186
+ new_hash[key.to_sym] = normalize(value)
153
187
  end
154
188
 
155
189
  return new_hash
156
190
  end
157
191
 
192
+ #
193
+ # Normalizes a value.
194
+ #
195
+ # @param [Hash, Array, Object] value
196
+ # The value to normalize.
197
+ #
198
+ # @return [Hash, Array, Object]
199
+ # The normalized value.
200
+ #
201
+ # @since 0.5.0
202
+ #
203
+ def normalize(value)
204
+ case value
205
+ when Hash
206
+ normalize_hash(value)
207
+ when Array
208
+ normalize_array(value)
209
+ else
210
+ value
211
+ end
212
+ end
213
+
214
+ #
215
+ # Parses the value for the `server` setting.
216
+ #
217
+ # @return [Array<Symbol, Hash>]
218
+ # The name of the server and additional options.
219
+ #
220
+ # @since 0.5.0
221
+ #
222
+ def parse_server(server)
223
+ name = nil
224
+ options = {}
225
+
226
+ case server
227
+ when Symbol, String
228
+ name = server.to_sym
229
+ when Hash
230
+ unless server.has_key?(:name)
231
+ raise(MissingOption,"the 'server' option must contain a 'name' option for which server to use",caller)
232
+ end
233
+
234
+ if server.has_key?(:name)
235
+ name = server[:name].to_sym
236
+ end
237
+
238
+ if server.has_key?(:options)
239
+ options.merge!(server[:options])
240
+ end
241
+ end
242
+
243
+ return [name, options]
244
+ end
245
+
246
+ #
247
+ # Parses an address.
248
+ #
249
+ # @param [Hash, String] address
250
+ # The address to parse.
251
+ #
252
+ # @return [Addressable::URI]
253
+ # The parsed address.
254
+ #
255
+ # @since 0.5.0
256
+ #
257
+ def parse_address(address)
258
+ case address
259
+ when Hash
260
+ Addressable::URI.new(address)
261
+ when String
262
+ Addressable::URI.parse(address)
263
+ else
264
+ raise(InvalidConfig,"invalid address: #{address.inspect}",caller)
265
+ end
266
+ end
267
+
268
+ #
269
+ # Parses the value for the `dest` setting.
270
+ #
271
+ # @param [Array, Hash, String] dest
272
+ # The value of the `dest` setting.
273
+ #
274
+ # @return [Array<Addressable::URI>, Addressable::URI]
275
+ # The parsed `dest` value.
276
+ #
277
+ # @since 0.5.0
278
+ #
279
+ def parse_dest(dest)
280
+ case dest
281
+ when Array
282
+ dest.map { |address| parse_address(address) }
283
+ else
284
+ parse_address(dest)
285
+ end
286
+ end
287
+
288
+ #
289
+ # Parses a command.
290
+ #
291
+ # @param [Array, String] command
292
+ # The command or commands to parse.
293
+ #
294
+ # @return [Array<String>]
295
+ # The individual commands.
296
+ #
297
+ # @raise [InvalidConfig]
298
+ # The command must be either an Array of a String.
299
+ #
300
+ # @since 0.5.0
301
+ #
302
+ def parse_commands(command)
303
+ case command
304
+ when Array
305
+ command.map { |line| line.to_s }
306
+ when String
307
+ command.enum_for(:each_line).map { |line| line.chomp }
308
+ else
309
+ raise(InvalidConfig,"commands must be an Array or a String")
310
+ end
311
+ end
312
+
158
313
  end
159
314
  end
@@ -46,11 +46,11 @@ module DeploYML
46
46
  super(config)
47
47
 
48
48
  unless @source
49
- raise(MissingOption,":source option is missing for the #{@name} environment")
49
+ raise(MissingOption,":source option is missing for the #{@name} environment",caller)
50
50
  end
51
51
 
52
52
  unless @dest
53
- raise(MissingOption,":dest option is missing for the #{@name} environment")
53
+ raise(MissingOption,":dest option is missing for the #{@name} environment",caller)
54
54
  end
55
55
 
56
56
  @environment ||= name.to_sym
@@ -68,13 +68,13 @@ module DeploYML
68
68
  # @yieldparam [LocalShell] shell
69
69
  # The remote shell session.
70
70
  #
71
- # @return [LocalShell]
71
+ # @return [Array<LocalShell>]
72
72
  # The local shell.
73
73
  #
74
74
  # @since 0.3.0
75
75
  #
76
76
  def local_shell(&block)
77
- LocalShell.new(&block)
77
+ each_dest.map { |dest| LocalShell.new(dest,&block) }
78
78
  end
79
79
 
80
80
  #
@@ -83,20 +83,24 @@ module DeploYML
83
83
  # @yield [shell]
84
84
  # If a block is given, it will be passed the new remote shell.
85
85
  #
86
- # @yieldparam [RemoteShell] shell
86
+ # @yieldparam [LocalShell, RemoteShell] shell
87
87
  # The remote shell.
88
88
  #
89
- # @return [RemoteShell, LocalShell]
89
+ # @return [Array<RemoteShell, LocalShell>]
90
90
  # The remote shell. If the destination is a local `file://` URI,
91
91
  # a local shell will be returned instead.
92
92
  #
93
93
  # @since 0.3.0
94
94
  #
95
95
  def remote_shell(&block)
96
- unless @dest.scheme == 'file'
97
- RemoteShell.new(@dest,&block)
98
- else
99
- LocalShell.new(&block)
96
+ each_dest.map do |dest|
97
+ shell = if dest.scheme == 'file'
98
+ LocalShell
99
+ else
100
+ RemoteShell
101
+ end
102
+
103
+ shell.new(dest,&block)
100
104
  end
101
105
  end
102
106
 
@@ -110,8 +114,8 @@ module DeploYML
110
114
  #
111
115
  def exec(command)
112
116
  remote_shell do |shell|
113
- shell.cd(@dest.path)
114
- shell.run(command)
117
+ shell.cd(shell.uri.path)
118
+ shell.exec(command)
115
119
  end
116
120
 
117
121
  return true
@@ -125,10 +129,10 @@ module DeploYML
125
129
  #
126
130
  # @since 0.3.0
127
131
  #
128
- def rake(task,*args)
132
+ def rake(task,*arguments)
129
133
  remote_shell do |shell|
130
- shell.cd(@dest.path)
131
- shell.rake(task,*args)
134
+ shell.cd(shell.uri.path)
135
+ shell.rake(task,*arguments)
132
136
  end
133
137
 
134
138
  return true
@@ -137,22 +141,25 @@ module DeploYML
137
141
  #
138
142
  # Starts an SSH session with the destination server.
139
143
  #
140
- # @param [Array] args
144
+ # @param [Array] arguments
141
145
  # Additional arguments to pass to SSH.
142
146
  #
143
147
  # @return [true]
144
148
  #
145
149
  # @since 0.3.0
146
150
  #
147
- def ssh(*args)
148
- RemoteShell.new(@dest).ssh(*args)
151
+ def ssh(*arguments)
152
+ each_dest do |dest|
153
+ RemoteShell.new(dest).ssh(*arguments)
154
+ end
155
+
149
156
  return true
150
157
  end
151
158
 
152
159
  #
153
160
  # Sets up the deployment repository for the project.
154
161
  #
155
- # @param [RemoteShell] shell
162
+ # @param [Shell] shell
156
163
  # The remote shell to execute commands through.
157
164
  #
158
165
  # @since 0.3.0
@@ -160,21 +167,21 @@ module DeploYML
160
167
  def setup(shell)
161
168
  shell.status "Cloning #{@source} ..."
162
169
 
163
- shell.run 'git', 'clone', '--depth', 1, @source, @dest.path
170
+ shell.run 'git', 'clone', '--depth', 1, @source, shell.uri.path
164
171
 
165
- shell.status "Cloned."
172
+ shell.status "Cloned #{@source}."
166
173
  end
167
174
 
168
175
  #
169
176
  # Updates the deployed repository for the project.
170
177
  #
171
- # @param [RemoteShell] shell
178
+ # @param [Shell] shell
172
179
  # The remote shell to execute commands through.
173
180
  #
174
181
  # @since 0.3.0
175
182
  #
176
183
  def update(shell)
177
- shell.status "Updating #{@dest.path} ..."
184
+ shell.status "Updating ..."
178
185
 
179
186
  shell.run 'git', 'reset', '--hard', 'HEAD'
180
187
  shell.run 'git', 'pull', '-f'
@@ -185,7 +192,7 @@ module DeploYML
185
192
  #
186
193
  # Installs any additional dependencies.
187
194
  #
188
- # @param [RemoteShell] shell
195
+ # @param [Shell] shell
189
196
  # The remote shell to execute commands through.
190
197
  #
191
198
  # @since 0.3.0
@@ -203,7 +210,7 @@ module DeploYML
203
210
  #
204
211
  # Place-holder method.
205
212
  #
206
- # @param [RemoteShell] shell
213
+ # @param [Shell] shell
207
214
  # The remote shell to execute commands through.
208
215
  #
209
216
  # @since 0.3.0
@@ -214,7 +221,7 @@ module DeploYML
214
221
  #
215
222
  # Place-holder method.
216
223
  #
217
- # @param [RemoteShell] shell
224
+ # @param [Shell] shell
218
225
  # The remote shell to execute commands through.
219
226
  #
220
227
  # @since 0.3.0
@@ -225,7 +232,7 @@ module DeploYML
225
232
  #
226
233
  # Place-holder method.
227
234
  #
228
- # @param [RemoteShell] shell
235
+ # @param [Shell] shell
229
236
  # The remote shell to execute commands through.
230
237
  #
231
238
  # @since 0.3.0
@@ -236,7 +243,7 @@ module DeploYML
236
243
  #
237
244
  # Place-holder method.
238
245
  #
239
- # @param [RemoteShell] shell
246
+ # @param [Shell] shell
240
247
  # The remote shell to execute commands through.
241
248
  #
242
249
  # @since 0.3.0
@@ -247,7 +254,7 @@ module DeploYML
247
254
  #
248
255
  # Place-holder method.
249
256
  #
250
- # @param [RemoteShell] shell
257
+ # @param [Shell] shell
251
258
  # The remote shell to execute commands through.
252
259
  #
253
260
  # @since 0.3.0
@@ -255,6 +262,84 @@ module DeploYML
255
262
  def server_restart(shell)
256
263
  end
257
264
 
265
+ #
266
+ # Place-holder method.
267
+ #
268
+ # @param [Shell] shell
269
+ # The remote shell to execute commands through.
270
+ #
271
+ # @since 0.5.0
272
+ #
273
+ def config(shell)
274
+ server_config(shell)
275
+ end
276
+
277
+ #
278
+ # Place-holder method.
279
+ #
280
+ # @param [Shell] shell
281
+ # The remote shell to execute commands through.
282
+ #
283
+ # @since 0.5.0
284
+ #
285
+ def start(shell)
286
+ server_start(shell)
287
+ end
288
+
289
+ #
290
+ # Place-holder method.
291
+ #
292
+ # @param [Shell] shell
293
+ # The remote shell to execute commands through.
294
+ #
295
+ # @since 0.5.0
296
+ #
297
+ def stop(shell)
298
+ server_stop(shell)
299
+ end
300
+
301
+ #
302
+ # Place-holder method.
303
+ #
304
+ # @param [Shell] shell
305
+ # The remote shell to execute commands through.
306
+ #
307
+ # @since 0.5.0
308
+ #
309
+ def restart(shell)
310
+ server_restart(shell)
311
+ end
312
+
313
+ #
314
+ # Invokes a task.
315
+ #
316
+ # @param [Symbol] task
317
+ # The name of the task to run.
318
+ #
319
+ # @param [Shell] shell
320
+ # The shell to run the task in.
321
+ #
322
+ # @raise [RuntimeError]
323
+ # The task name was not known.
324
+ #
325
+ # @since 0.5.0
326
+ #
327
+ def invoke_task(task,shell)
328
+ unless TASKS.include?(task)
329
+ raise("invalid task: #{task}")
330
+ end
331
+
332
+ if @before.has_key?(task)
333
+ @before[task].each { |command| shell.exec(command) }
334
+ end
335
+
336
+ send(task,shell) if respond_to?(task)
337
+
338
+ if @after.has_key?(task)
339
+ @after[task].each { |command| shell.exec(command) }
340
+ end
341
+ end
342
+
258
343
  #
259
344
  # Deploys the project.
260
345
  #
@@ -269,28 +354,27 @@ module DeploYML
269
354
  def invoke(tasks)
270
355
  remote_shell do |shell|
271
356
  # setup the deployment repository
272
- setup(shell) if tasks.include?(:setup)
357
+ invoke_task(:setup,shell) if tasks.include?(:setup)
273
358
 
274
359
  # cd into the deployment repository
275
- shell.cd @dest.path
360
+ shell.cd(shell.uri.path)
276
361
 
277
362
  # update the deployment repository
278
- update(shell) if tasks.include?(:update)
363
+ invoke_task(:update,shell) if tasks.include?(:update)
279
364
 
280
365
  # framework tasks
281
- install(shell) if tasks.include?(:install)
282
-
283
- migrate(shell) if tasks.include?(:migrate)
366
+ invoke_task(:install,shell) if tasks.include?(:install)
367
+ ivoke_task(:migrate,shell) if tasks.include?(:migrate)
284
368
 
285
369
  # server tasks
286
370
  if tasks.include?(:config)
287
- server_config(shell)
371
+ invoke_task(:config,shell)
288
372
  elsif tasks.include?(:start)
289
- server_start(shell)
373
+ invoke_task(:start,shell)
290
374
  elsif tasks.include?(:stop)
291
- server_stop(shell)
375
+ invoke_task(:stop,shell)
292
376
  elsif tasks.include?(:restart)
293
- server_restart(shell)
377
+ invoke_task(:restart,shell)
294
378
  end
295
379
  end
296
380
 
@@ -427,12 +511,12 @@ module DeploYML
427
511
  def load_framework!
428
512
  if @orm
429
513
  unless FRAMEWORKS.has_key?(@framework)
430
- raise(UnknownFramework,"Unknown framework #{@framework}")
514
+ raise(UnknownFramework,"Unknown framework #{@framework}",caller)
431
515
  end
432
516
 
433
517
  extend FRAMEWORKS[@framework]
434
518
 
435
- initialize_framework() if self.respond_to?(:initialize_framework)
519
+ initialize_framework if respond_to?(:initialize_framework)
436
520
  end
437
521
  end
438
522
 
@@ -446,12 +530,12 @@ module DeploYML
446
530
  def load_server!
447
531
  if @server_name
448
532
  unless SERVERS.has_key?(@server_name)
449
- raise(UnknownServer,"Unknown server name #{@server_name}")
533
+ raise(UnknownServer,"Unknown server name #{@server_name}",caller)
450
534
  end
451
535
 
452
536
  extend SERVERS[@server_name]
453
537
 
454
- initialize_server() if self.respond_to?(:initialize_server)
538
+ initialize_server if respond_to?(:initialize_server)
455
539
  end
456
540
  end
457
541
 
@@ -10,10 +10,10 @@ module DeploYML
10
10
  #
11
11
  # @see {Environment#rake}
12
12
  #
13
- def rake(task,*args)
14
- args += ["RAILS_ENV=#{@environment}"]
13
+ def rake(task,*arguments)
14
+ arguments += ["RAILS_ENV=#{@environment}"]
15
15
 
16
- super(task,*args)
16
+ super(task,*arguments)
17
17
  end
18
18
  end
19
19
  end
@@ -4,9 +4,7 @@ module DeploYML
4
4
  #
5
5
  # Represents a shell running on the local system.
6
6
  #
7
- class LocalShell
8
-
9
- include Shell
7
+ class LocalShell < Shell
10
8
 
11
9
  #
12
10
  # Runs a program locally.
@@ -14,14 +12,14 @@ module DeploYML
14
12
  # @param [String] program
15
13
  # The name or path of the program to run.
16
14
  #
17
- # @param [Array<String>] args
15
+ # @param [Array<String>] arguments
18
16
  # Additional arguments for the program.
19
17
  #
20
- def run(program,*args)
18
+ def run(program,*arguments)
21
19
  program = program.to_s
22
- args = args.map { |arg| arg.to_s }
20
+ arguments = arguments.map { |arg| arg.to_s }
23
21
 
24
- system(program,*args)
22
+ system(program,*arguments)
25
23
  end
26
24
 
27
25
  #
@@ -43,7 +43,7 @@ module DeploYML
43
43
  @environments_dir = File.join(@root,CONFIG_DIR,ENVIRONMENTS_DIR)
44
44
 
45
45
  unless (File.file?(@config_file) || File.directory?(@environments_dir))
46
- raise(ConfigNotFound,"could not find '#{CONFIG_FILE}' or '#{ENVIRONMENTS_DIR}' in #{root}")
46
+ raise(ConfigNotFound,"could not find '#{CONFIG_FILE}' or '#{ENVIRONMENTS_DIR}' in #{root}",caller)
47
47
  end
48
48
 
49
49
  load_environments!
@@ -65,7 +65,7 @@ module DeploYML
65
65
  name = name.to_sym
66
66
 
67
67
  unless @environments[name]
68
- raise(UnknownEnvironment,"unknown environment: #{name}")
68
+ raise(UnknownEnvironment,"unknown environment: #{name}",caller)
69
69
  end
70
70
 
71
71
  return @environments[name]
@@ -298,7 +298,7 @@ module DeploYML
298
298
  config = YAML.load_file(path)
299
299
 
300
300
  unless config.kind_of?(Hash)
301
- raise(InvalidConfig,"DeploYML file #{path.dump} does not contain a Hash")
301
+ raise(InvalidConfig,"DeploYML file #{path.dump} does not contain a Hash",caller)
302
302
  end
303
303
 
304
304
  return config
@@ -1,3 +1,4 @@
1
+ require 'deployml/exceptions/invalid_config'
1
2
  require 'deployml/shell'
2
3
 
3
4
  require 'addressable/uri'
@@ -6,12 +7,7 @@ module DeploYML
6
7
  #
7
8
  # Represents a shell running on a remote server.
8
9
  #
9
- class RemoteShell
10
-
11
- include Shell
12
-
13
- # The URI of the remote shell
14
- attr_reader :uri
10
+ class RemoteShell < Shell
15
11
 
16
12
  # The history of the Remote Shell
17
13
  attr_reader :history
@@ -29,16 +25,9 @@ module DeploYML
29
25
  # The remote shell session.
30
26
  #
31
27
  def initialize(uri,&block)
32
- case uri
33
- when Addressable::URI
34
- @uri = uri
35
- else
36
- @uri = Addressable::URI.parse(uri.to_s)
37
- end
38
-
39
28
  @history = []
40
29
 
41
- super(&block)
30
+ super(uri,&block)
42
31
 
43
32
  replay if block
44
33
  end
@@ -49,11 +38,11 @@ module DeploYML
49
38
  # @param [String] program
50
39
  # The name or path of the program to run.
51
40
  #
52
- # @param [Array<String>] args
41
+ # @param [Array<String>] arguments
53
42
  # Additional arguments for the program.
54
43
  #
55
- def run(program,*args)
56
- @history << [program, *args]
44
+ def run(program,*arguments)
45
+ @history << [program, *arguments]
57
46
  end
58
47
 
59
48
  #
@@ -104,7 +93,14 @@ module DeploYML
104
93
  # @return [String]
105
94
  # The SSH compatible URI.
106
95
  #
96
+ # @raise [InvalidConfig]
97
+ # The URI of the shell does not have a host component.
98
+ #
107
99
  def ssh_uri
100
+ unless @uri.host
101
+ raise(InvalidConfig,"URI does not have a host: #{@uri}",caller)
102
+ end
103
+
108
104
  new_uri = @uri.host
109
105
  new_uri = "#{@uri.user}@#{new_uri}" if @uri.user
110
106
 
@@ -114,10 +110,10 @@ module DeploYML
114
110
  #
115
111
  # Starts a SSH session with the destination server.
116
112
  #
117
- # @param [Array] args
113
+ # @param [Array] arguments
118
114
  # Additional arguments to pass to SSH.
119
115
  #
120
- def ssh(*args)
116
+ def ssh(*arguments)
121
117
  options = []
122
118
 
123
119
  # Add the -p option if an alternate destination port is given
@@ -129,7 +125,7 @@ module DeploYML
129
125
  options << ssh_uri
130
126
 
131
127
  # append the additional arguments
132
- args.each { |arg| options << arg.to_s }
128
+ arguments.each { |arg| options << arg.to_s }
133
129
 
134
130
  return system('ssh',*options)
135
131
  end
@@ -22,11 +22,11 @@ module DeploYML
22
22
  # @param [LocalShell, RemoteShell] shell
23
23
  # The shell to execute commands in.
24
24
  #
25
- # @param [Array] args
25
+ # @param [Array] arguments
26
26
  # Additional arguments to call `mongrel_rails` with.
27
27
  #
28
- def mongrel_cluster(shell,*args)
29
- options = args + ['-c', @mongrel.config]
28
+ def mongrel_cluster(shell,*arguments)
29
+ options = arguments + ['-c', @mongrel.config]
30
30
 
31
31
  shell.run 'mongrel_rails', *options
32
32
  end
@@ -48,7 +48,7 @@ module DeploYML
48
48
 
49
49
  shell.status "Configuring Mongrel ..."
50
50
 
51
- options = ['-c', dest.path] + @mongrel.arguments
51
+ options = ['-c', shell.uri.path] + @mongrel.arguments
52
52
  shell.run 'mongrel_rails', 'cluster::configure', *options
53
53
 
54
54
  shell.status "Mongrel configured."
@@ -22,11 +22,11 @@ module DeploYML
22
22
  # @param [LocalShell, RemoteShell] shell
23
23
  # The shell to execute commands in.
24
24
  #
25
- # @param [Array] args
25
+ # @param [Array] arguments
26
26
  # Additional arguments to call `thin` with.
27
27
  #
28
- def thin(shell,*args)
29
- options = args + ['-C', @thin.config, '-s', @thin.servers]
28
+ def thin(shell,*arguments)
29
+ options = arguments + ['-C', @thin.config, '-s', @thin.servers]
30
30
 
31
31
  shell.run 'thin', *options
32
32
  end
@@ -48,7 +48,7 @@ module DeploYML
48
48
 
49
49
  shell.status "Configuring Thin ..."
50
50
 
51
- options = ['-c', dest.path] + @thin.arguments
51
+ options = ['-c', shell.uri.path] + @thin.arguments
52
52
  shell.run 'thin', 'config', *options
53
53
 
54
54
  shell.status "Thin configured."
@@ -1,15 +1,68 @@
1
1
  require 'thor/shell/color'
2
+ require 'shellwords'
2
3
 
3
4
  module DeploYML
4
5
  #
5
6
  # Provides common methods used by both {LocalShell} and {RemoteShell}.
6
7
  #
7
- module Shell
8
+ class Shell
8
9
 
9
10
  include Thor::Shell
11
+ include Shellwords
10
12
 
11
- def initialize
12
- yield self if block_given?
13
+ # The URI of the Shell.
14
+ attr_reader :uri
15
+
16
+ #
17
+ # Initializes a shell session.
18
+ #
19
+ # @param [Addressable::URI, String] uri
20
+ # The URI of the shell.
21
+ #
22
+ # @yield [session]
23
+ # If a block is given, it will be passed the new shell session.
24
+ #
25
+ # @yieldparam [ShellSession] session
26
+ # The shell session.
27
+ #
28
+ def initialize(uri)
29
+ @uri = uri
30
+
31
+ if block_given?
32
+ status "Entered #{@uri}."
33
+ yield self
34
+ status "Leaving #{@uri} ..."
35
+ end
36
+ end
37
+
38
+ #
39
+ # Place holder method.
40
+ #
41
+ # @since 0.5.0
42
+ #
43
+ def run(program,*arguments)
44
+ end
45
+
46
+ #
47
+ # Runs a command in the shell.
48
+ #
49
+ # @param [String] command
50
+ # The command to run.
51
+ #
52
+ # @see #run
53
+ #
54
+ # @since 0.5.0
55
+ #
56
+ def exec(command)
57
+ run(*shellwords(command))
58
+ end
59
+
60
+ #
61
+ # Place holder method.
62
+ #
63
+ # @since 0.5.0
64
+ #
65
+ def echo(message)
13
66
  end
14
67
 
15
68
  #
@@ -18,11 +71,11 @@ module DeploYML
18
71
  # @param [Symbol, String] task
19
72
  # Name of the Rake task to run.
20
73
  #
21
- # @param [Array<String>] args
74
+ # @param [Array<String>] arguments
22
75
  # Additional arguments for the Rake task.
23
76
  #
24
- def rake(task,*args)
25
- run 'rake', rake_task(task,*args)
77
+ def rake(task,*arguments)
78
+ run 'rake', rake_task(task,*arguments)
26
79
  end
27
80
 
28
81
  #
@@ -45,17 +98,17 @@ module DeploYML
45
98
  # @param [String, Symbol] name
46
99
  # The name of the `rake` task.
47
100
  #
48
- # @param [Array] args
101
+ # @param [Array] arguments
49
102
  # Additional arguments to pass to the `rake` task.
50
103
  #
51
104
  # @param [String]
52
105
  # The `rake` task name to be called.
53
106
  #
54
- def rake_task(name,*args)
107
+ def rake_task(name,*arguments)
55
108
  name = name.to_s
56
109
 
57
- unless args.empty?
58
- name += ('[' + args.join(',') + ']')
110
+ unless arguments.empty?
111
+ name += ('[' + arguments.join(',') + ']')
59
112
  end
60
113
 
61
114
  return name
@@ -1,4 +1,4 @@
1
1
  module DeploYML
2
2
  # deploYML version
3
- VERSION = '0.4.2'
3
+ VERSION = '0.5.0'
4
4
  end
@@ -19,11 +19,12 @@ describe Configuration do
19
19
  config = Configuration.new(
20
20
  :dest => 'ssh://user@www.example.com/srv/project'
21
21
  )
22
+ dest = config.dest
22
23
 
23
- config.dest.scheme.should == 'ssh'
24
- config.dest.user.should == 'user'
25
- config.dest.host.should == 'www.example.com'
26
- config.dest.path.should == '/srv/project'
24
+ dest.scheme.should == 'ssh'
25
+ dest.user.should == 'user'
26
+ dest.host.should == 'www.example.com'
27
+ dest.path.should == '/srv/project'
27
28
  end
28
29
 
29
30
  it "should parse 'dest' Hash URIs" do
@@ -33,11 +34,25 @@ describe Configuration do
33
34
  'host' => 'www.example.com',
34
35
  'path' => '/srv/project'
35
36
  })
37
+ dest = config.dest
36
38
 
37
- config.dest.scheme.should == 'ssh'
38
- config.dest.user.should == 'user'
39
- config.dest.host.should == 'www.example.com'
40
- config.dest.path.should == '/srv/project'
39
+ dest.scheme.should == 'ssh'
40
+ dest.user.should == 'user'
41
+ dest.host.should == 'www.example.com'
42
+ dest.path.should == '/srv/project'
43
+ end
44
+
45
+ it "should parse 'dest' Arrays of URIs" do
46
+ config = Configuration.new(:dest => %w[
47
+ ssh://deploy@dev1.example.com/var/www/project1
48
+ ssh://deploy@dev2.example.com/var/www/project1
49
+ ssh://deploy@dev3.example.com/var/www/project1
50
+ ])
51
+ dest = config.dest
52
+
53
+ dest[0].host.should == 'dev1.example.com'
54
+ dest[1].host.should == 'dev2.example.com'
55
+ dest[2].host.should == 'dev3.example.com'
41
56
  end
42
57
 
43
58
  it "should default the 'debug' option to false" do
@@ -70,4 +85,32 @@ describe Configuration do
70
85
  config.server_name.should == :thin
71
86
  config.server_options.should == {:address => '127.0.0.1'}
72
87
  end
88
+
89
+ it "should parse 'before' / 'after' command strings" do
90
+ config = Configuration.new(
91
+ :before => {
92
+ :install => "command1\ncommand2\n",
93
+ :update => "command3\ncommand4\n"
94
+ }
95
+ )
96
+
97
+ config.before[:install][0].should == 'command1'
98
+ config.before[:install][1].should == 'command2'
99
+ config.before[:update][0].should == 'command3'
100
+ config.before[:update][1].should == 'command4'
101
+ end
102
+
103
+ it "should parse 'before' / 'after' commands" do
104
+ config = Configuration.new(
105
+ :before => {
106
+ :install => %w[command1 command2],
107
+ :update => %w[command3 command4]
108
+ }
109
+ )
110
+
111
+ config.before[:install][0].should == 'command1'
112
+ config.before[:install][1].should == 'command2'
113
+ config.before[:update][0].should == 'command3'
114
+ config.before[:update][1].should == 'command4'
115
+ end
73
116
  end
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
  require 'deployml/remote_shell'
3
3
 
4
4
  describe RemoteShell do
5
- let(:uri) { 'ssh://deploy@www.example.com/path' }
5
+ let(:uri) { Addressable::URI.parse('ssh://deploy@www.example.com/path') }
6
6
 
7
7
  subject { RemoteShell.new(uri) }
8
8
 
@@ -14,8 +14,19 @@ describe RemoteShell do
14
14
  subject.uri.path.should == '/path'
15
15
  end
16
16
 
17
- it "should convert normal URIs to SSH URIs" do
18
- subject.ssh_uri.should == 'deploy@www.example.com'
17
+ describe "#ssh_uri" do
18
+ it "should convert normal URIs to SSH URIs" do
19
+ subject.ssh_uri.should == 'deploy@www.example.com'
20
+ end
21
+
22
+ it "must require a URI with a host component" do
23
+ bad_uri = Addressable::URI.parse('deploy@www.example.com:/var/www')
24
+ shell = RemoteShell.new(bad_uri)
25
+
26
+ lambda {
27
+ shell.ssh_uri
28
+ }.should raise_error(InvalidConfig)
29
+ end
19
30
  end
20
31
 
21
32
  it "should enqueue programs to run" do
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: deployml
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.4.2
5
+ version: 0.5.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Postmodern
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-04-11 00:00:00 Z
13
+ date: 2011-04-22 00:00:00 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: addressable