deployml 0.4.2 → 0.5.0

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