deployml 0.3.0 → 0.4.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,29 @@
1
+ ### 0.4.0 / 2010-11-29
2
+
3
+ * Require addressable ~> 2.2.0.
4
+ * Added methods to {DeploYML::Environment} inorder to mirror
5
+ {DeploYML::Project}:
6
+ * `invoke`
7
+ * `setup!`
8
+ * `update!`
9
+ * `install!`
10
+ * `migrate!`
11
+ * `config!`
12
+ * `start!`
13
+ * `stop!`
14
+ * Added {DeploYML::Shell#status} for printing ANSI colored status messages.
15
+ * Added {DeploYML::RemoteShell#uri}.
16
+ * Added {DeploYML::RemoteShell#history}.
17
+ * Added missing documentation.
18
+ * Give the root directory passed to {DeploYML::Project#initialize} the
19
+ default of `Dir.pwd`.
20
+ * If the destination URI has the scheme of `file:`, have
21
+ {DeploYML::Environment#remote_shell} return a {DeploYML::LocalShell}.
22
+ * This should facilitate local deploys.
23
+ * Perform a forced pull in {DeploYML::Environment#update}.
24
+ * Override {DeploYML::Environment#rake} in {DeploYML::Frameworks::Rails}.
25
+ * Escape all arguments of all commands in {DeploYML::RemoteShell#join}.
26
+
1
27
  ### 0.3.0 / 2010-11-21
2
28
 
3
29
  * Initial release:
data/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  * [Source](http://github.com/postmodern/deployml)
4
4
  * [Issues](http://github.com/postmodern/deployml/issues)
5
+ * [Documentation](http://rubydoc.info/gems/deployml/frames)
5
6
  * Postmodern (postmodern.mod3 at gmail.com)
6
7
 
7
8
  ## Description
@@ -63,6 +64,29 @@ Specifying a `server` with options:
63
64
  socket: /var/run/thin.sock
64
65
  rackup: true
65
66
 
67
+ Multiple environments:
68
+
69
+ # config/deploy.yml
70
+ source: git@github.com:user/project.git
71
+ framework: rails3
72
+ orm: datamapper
73
+
74
+ # config/deploy/staging.yml
75
+ dest: ssh://deploy@www.example.com/srv/staging
76
+ server:
77
+ name: thin
78
+ options:
79
+ config: /etc/thin/staging.yml
80
+ socket: /tmp/thin.staging.sock
81
+
82
+ # config/deploy/production.yml
83
+ dest: ssh://deploy@www.example.com/srv/project
84
+ server:
85
+ name: thin
86
+ options:
87
+ config: /etc/thin/example.yml
88
+ socket: /tmp/thin.example.sock
89
+
66
90
  ## Synopsis
67
91
 
68
92
  Cold-Deploy a new project:
@@ -91,7 +115,7 @@ List available tasks:
91
115
 
92
116
  ## Requirements
93
117
 
94
- * [addressable](http://addressable.rubyforge.org/) ~> 2.1.1
118
+ * [addressable](http://addressable.rubyforge.org/) ~> 2.2.0
95
119
  * [rprogram](http://github.com/postmodern/rprogram) ~> 0.2.0
96
120
  * [thor](http://github.com/wycats/thor) ~> 0.14.3
97
121
 
data/gemspec.yml CHANGED
@@ -11,7 +11,7 @@ homepage: http://github.com/postmodern/deployml
11
11
  has_yard: true
12
12
 
13
13
  dependencies:
14
- addressable: ~> 2.1.1
14
+ addressable: ~> 2.2.0
15
15
  rprogram: ~> 0.2.0
16
16
  thor: ~> 0.14.3
17
17
 
data/lib/deployml/cli.rb CHANGED
@@ -4,31 +4,65 @@ require 'thor'
4
4
  require 'pathname'
5
5
 
6
6
  module DeploYML
7
+ #
8
+ # The command-line interface to {DeploYML} using
9
+ # [Thor](http://github.com/wycats/thor#readme).
10
+ #
7
11
  class CLI < Thor
8
12
 
9
13
  namespace 'deploy'
10
14
 
11
15
  desc 'exec', 'Runs a command on the deploy server'
12
- method_option :environment, :type => :string, :default => 'production'
16
+ method_option :environment, :type => :string,
17
+ :default => 'production',
18
+ :aliases => '-E'
19
+
20
+ #
21
+ # Executes a command in the specified environment.
22
+ #
23
+ # @param [String] command
24
+ # The full command to execute.
25
+ #
13
26
  def exec(command)
14
27
  environment.exec(command)
15
28
  end
16
29
 
17
30
  desc 'rake', 'Executes a rake task on the deploy server'
18
- method_option :environment, :type => :string, :default => 'production'
31
+ method_option :environment, :type => :string,
32
+ :default => 'production',
33
+ :aliases => '-E'
19
34
  method_option :args, :type => :array
35
+
36
+ #
37
+ # Invokes a rake task in the specified environment.
38
+ #
39
+ # @param [String] task
40
+ # The name of the rake task.
41
+ #
20
42
  def rake(task)
21
43
  environment.rake(task,*(options[:args]))
22
44
  end
23
45
 
24
46
  desc 'ssh', 'Starts a SSH session with the deploy server'
25
- method_option :environment, :type => :string, :default => 'production'
47
+ method_option :environment, :type => :string,
48
+ :default => 'production',
49
+ :aliases => '-E'
50
+
51
+ #
52
+ # Starts an SSH session with the specified environment.
53
+ #
26
54
  def ssh
27
55
  environment.ssh
28
56
  end
29
57
 
30
58
  desc 'setup', 'Sets up the deployment repository for the project'
31
- method_option :environment, :type => :string, :default => 'production'
59
+ method_option :environment, :type => :string,
60
+ :default => 'production',
61
+ :aliases => '-E'
62
+
63
+ #
64
+ # Sets up the specified environment.
65
+ #
32
66
  def setup
33
67
  status 'Setting up ...'
34
68
 
@@ -38,7 +72,13 @@ module DeploYML
38
72
  end
39
73
 
40
74
  desc 'update', 'Updates the deployment repository of the project'
41
- method_option :environment, :type => :string, :default => 'production'
75
+ method_option :environment, :type => :string,
76
+ :default => 'production',
77
+ :aliases => '-E'
78
+
79
+ #
80
+ # Updates the deployment repository of the specified environment.
81
+ #
42
82
  def update
43
83
  status 'Updating'
44
84
 
@@ -48,7 +88,13 @@ module DeploYML
48
88
  end
49
89
 
50
90
  desc 'install', 'Installs the project on the deploy server'
51
- method_option :environment, :type => :string, :default => 'production'
91
+ method_option :environment, :type => :string,
92
+ :default => 'production',
93
+ :aliases => '-E'
94
+
95
+ #
96
+ # Installs any needed dependencies in the specified environment.
97
+ #
52
98
  def install
53
99
  status 'Installing ...'
54
100
 
@@ -58,7 +104,13 @@ module DeploYML
58
104
  end
59
105
 
60
106
  desc 'migrate', 'Migrates the database for the project'
61
- method_option :environment, :type => :string, :default => 'production'
107
+ method_option :environment, :type => :string,
108
+ :default => 'production',
109
+ :aliases => '-E'
110
+
111
+ #
112
+ # Migrates the database for the specified environment.
113
+ #
62
114
  def migrate
63
115
  status 'Migrating ...'
64
116
 
@@ -68,7 +120,13 @@ module DeploYML
68
120
  end
69
121
 
70
122
  desc 'config', 'Configures the server for the project'
71
- method_option :environment, :type => :string, :default => 'production'
123
+ method_option :environment, :type => :string,
124
+ :default => 'production',
125
+ :aliases => '-E'
126
+
127
+ #
128
+ # Configures the server for the specified environment.
129
+ #
72
130
  def config
73
131
  status 'Configuring ...'
74
132
 
@@ -78,8 +136,13 @@ module DeploYML
78
136
  end
79
137
 
80
138
  desc 'start', 'Starts the server for the project'
81
- method_option :environment, :type => :string, :default => 'production'
139
+ method_option :environment, :type => :string,
140
+ :default => 'production',
141
+ :aliases => '-E'
82
142
 
143
+ #
144
+ # Starts the server in the specified environment.
145
+ #
83
146
  def start
84
147
  status 'Starting ...'
85
148
 
@@ -89,8 +152,13 @@ module DeploYML
89
152
  end
90
153
 
91
154
  desc 'stop', 'Stops the server for the project'
92
- method_option :environment, :type => :string, :default => 'production'
155
+ method_option :environment, :type => :string,
156
+ :default => 'production',
157
+ :aliases => '-E'
93
158
 
159
+ #
160
+ # Stops the server in the specified environment.
161
+ #
94
162
  def stop
95
163
  status 'Stopping ...'
96
164
 
@@ -100,8 +168,13 @@ module DeploYML
100
168
  end
101
169
 
102
170
  desc 'restart', 'Restarts the server for the project'
103
- method_option :environment, :type => :string, :default => 'production'
171
+ method_option :environment, :type => :string,
172
+ :default => 'production',
173
+ :aliases => '-E'
104
174
 
175
+ #
176
+ # Restarts the server in the specified environment.
177
+ #
105
178
  def restart
106
179
  status 'Restarting ...'
107
180
 
@@ -111,8 +184,13 @@ module DeploYML
111
184
  end
112
185
 
113
186
  desc 'deploy', 'Cold-Deploys a new project'
114
- method_option :environment, :type => :string, :default => 'production'
187
+ method_option :environment, :type => :string,
188
+ :default => 'production',
189
+ :aliases => '-E'
115
190
 
191
+ #
192
+ # Cold-deploys into the specified environment.
193
+ #
116
194
  def deploy
117
195
  status 'Deploying ...'
118
196
 
@@ -122,8 +200,13 @@ module DeploYML
122
200
  end
123
201
 
124
202
  desc 'redeploy', 'Redeploys the project'
125
- method_option :environment, :type => :string, :default => 'production'
203
+ method_option :environment, :type => :string,
204
+ :default => 'production',
205
+ :aliases => '-E'
126
206
 
207
+ #
208
+ # Redeploys into the specified environment.
209
+ #
127
210
  def redeploy
128
211
  status 'Redeploying ...'
129
212
 
@@ -184,6 +267,12 @@ module DeploYML
184
267
  project.environment(options[:environment])
185
268
  end
186
269
 
270
+ #
271
+ # Prints a status message.
272
+ #
273
+ # @param [String] message
274
+ # The message to print.
275
+ #
187
276
  def status(message)
188
277
  shell.say_status "[#{options[:environment]}]", message
189
278
  end
@@ -8,6 +8,10 @@ require 'deployml/servers'
8
8
  require 'deployml/frameworks'
9
9
 
10
10
  module DeploYML
11
+ #
12
+ # Contains environment specific configuration loaded by {Project}
13
+ # from YAML files within `config/deploy/`.
14
+ #
11
15
  class Environment < Configuration
12
16
 
13
17
  # Mapping of possible 'server' names to their mixins.
@@ -82,13 +86,18 @@ module DeploYML
82
86
  # @yieldparam [RemoteShell] shell
83
87
  # The remote shell.
84
88
  #
85
- # @return [RemoteShell]
86
- # The remote shell.
89
+ # @return [RemoteShell, LocalShell]
90
+ # The remote shell. If the destination is a local `file://` URI,
91
+ # a local shell will be returned instead.
87
92
  #
88
93
  # @since 0.3.0
89
94
  #
90
95
  def remote_shell(&block)
91
- RemoteShell.new(@dest,&block)
96
+ unless @dest.scheme == 'file'
97
+ RemoteShell.new(@dest,&block)
98
+ else
99
+ LocalShell.new(&block)
100
+ end
92
101
  end
93
102
 
94
103
  #
@@ -162,7 +171,7 @@ module DeploYML
162
171
  #
163
172
  def update(shell)
164
173
  shell.run 'git', 'reset', '--hard', 'HEAD'
165
- shell.run 'git', 'pull'
174
+ shell.run 'git', 'pull', '-f'
166
175
  end
167
176
 
168
177
  #
@@ -231,6 +240,192 @@ module DeploYML
231
240
  def server_restart(shell)
232
241
  end
233
242
 
243
+ #
244
+ # Deploys the project.
245
+ #
246
+ # @param [Array<Symbol>] tasks
247
+ # The tasks to run during the deployment.
248
+ #
249
+ # @return [true]
250
+ # Indicates that the tasks were successfully completed.
251
+ #
252
+ # @since 0.4.0
253
+ #
254
+ def invoke(tasks)
255
+ remote_shell do |shell|
256
+ # setup the deployment repository
257
+ if tasks.include?(:setup)
258
+ shell.status "Cloning #{@source} ..."
259
+ setup(shell)
260
+ shell.status "Cloned."
261
+ end
262
+
263
+ # cd into the deployment repository
264
+ shell.cd @dest.path
265
+
266
+ # update the deployment repository
267
+ if tasks.include?(:update)
268
+ shell.status "Updating #{@dest.path} ..."
269
+ update(shell)
270
+ shell.status "Updated."
271
+ end
272
+
273
+ # framework tasks
274
+ if tasks.include?(:install)
275
+ shell.status "Installing additional dependencies ..."
276
+ install(shell)
277
+ shell.status "Dependencies installed."
278
+ end
279
+
280
+ if tasks.include?(:migrate)
281
+ shell.status "Migrating database ..."
282
+ migrate(shell)
283
+ shell.status "Database migrated."
284
+ end
285
+
286
+ # server tasks
287
+ if tasks.include?(:config)
288
+ shell.status "Configuring server ..."
289
+ server_config(shell)
290
+ shell.status "Server configured."
291
+ elsif tasks.include?(:start)
292
+ shell.status "Starting server ..."
293
+ server_start(shell)
294
+ shell.status "Server started."
295
+ elsif tasks.include?(:stop)
296
+ shell.status "Stopping server ..."
297
+ server_stop(shell)
298
+ shell.status "Server stopped."
299
+ elsif tasks.include?(:restart)
300
+ shell.status "Restarting server ..."
301
+ server_restart(shell)
302
+ shell.status "Server restarted."
303
+ end
304
+ end
305
+
306
+ return true
307
+ end
308
+
309
+ #
310
+ # Sets up the deployment repository for the project.
311
+ #
312
+ # @return [true]
313
+ # Indicates that the tasks were successfully completed.
314
+ #
315
+ # @since 0.4.0
316
+ #
317
+ def setup!
318
+ invoke [:setup]
319
+ end
320
+
321
+ #
322
+ # Updates the deployed repository of the project.
323
+ #
324
+ # @return [true]
325
+ # Indicates that the tasks were successfully completed.
326
+ #
327
+ # @since 0.4.0
328
+ #
329
+ def update!
330
+ invoke [:update]
331
+ end
332
+
333
+ #
334
+ # Installs the project on the destination server.
335
+ #
336
+ # @return [true]
337
+ # Indicates that the tasks were successfully completed.
338
+ #
339
+ # @since 0.4.0
340
+ #
341
+ def install!
342
+ invoke [:install]
343
+ end
344
+
345
+ #
346
+ # Migrates the database used by the project.
347
+ #
348
+ # @return [true]
349
+ # Indicates that the tasks were successfully completed.
350
+ #
351
+ # @since 0.4.0
352
+ #
353
+ def migrate!
354
+ invoke [:migrate]
355
+ end
356
+
357
+ #
358
+ # Configures the Web server to be ran on the destination server.
359
+ #
360
+ # @return [true]
361
+ # Indicates that the tasks were successfully completed.
362
+ #
363
+ # @since 0.4.0
364
+ #
365
+ def config!
366
+ invoke [:config]
367
+ end
368
+
369
+ #
370
+ # Starts the Web server for the project.
371
+ #
372
+ # @return [true]
373
+ # Indicates that the tasks were successfully completed.
374
+ #
375
+ # @since 0.4.0
376
+ #
377
+ def start!
378
+ invoke [:start]
379
+ end
380
+
381
+ #
382
+ # Stops the Web server for the project.
383
+ #
384
+ # @return [true]
385
+ # Indicates that the tasks were successfully completed.
386
+ #
387
+ # @since 0.4.0
388
+ #
389
+ def stop!
390
+ invoke [:stop]
391
+ end
392
+
393
+ #
394
+ # Restarts the Web server for the project.
395
+ #
396
+ # @return [true]
397
+ # Indicates that the tasks were successfully completed.
398
+ #
399
+ # @since 0.4.0
400
+ #
401
+ def restart!
402
+ invoke [:restart]
403
+ end
404
+
405
+ #
406
+ # Deploys a new project.
407
+ #
408
+ # @return [true]
409
+ # Indicates that the tasks were successfully completed.
410
+ #
411
+ # @since 0.4.0
412
+ #
413
+ def deploy!
414
+ invoke [:setup, :install, :migrate, :config, :start]
415
+ end
416
+
417
+ #
418
+ # Redeploys a project.
419
+ #
420
+ # @return [true]
421
+ # Indicates that the tasks were successfully completed.
422
+ #
423
+ # @since 0.4.0
424
+ #
425
+ def redeploy!
426
+ invoke [:update, :install, :migrate, :restart]
427
+ end
428
+
234
429
  protected
235
430
 
236
431
  #
@@ -0,0 +1,20 @@
1
+ module DeploYML
2
+ module Frameworks
3
+ #
4
+ # Provides common methods needed to deploy Rails 2 and 3 projects.
5
+ #
6
+ module Rails
7
+ #
8
+ # Overrides the default `rake` method to add a `RAILS_ENV`
9
+ # environment variable.
10
+ #
11
+ # @see {Environment#rake}
12
+ #
13
+ def rake(task,*args)
14
+ args += ["RAILS_ENV=#{@environment}"]
15
+
16
+ super(task,*args)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,6 +1,19 @@
1
+ require 'deployml/frameworks/rails'
2
+
1
3
  module DeploYML
2
4
  module Frameworks
5
+ #
6
+ # Provides methods for deploying Rails 2 projects.
7
+ #
3
8
  module Rails2
9
+ include Rails
10
+
11
+ #
12
+ # Migrates the database using the `db:migrate` task.
13
+ #
14
+ # @param [LocalShell, RemoteShell] shell
15
+ # The shell to execute commands in.
16
+ #
4
17
  def migrate(shell)
5
18
  shell.run 'rake', 'db:migrate', "RAILS_ENV=#{@environment}"
6
19
  end
@@ -1,10 +1,31 @@
1
+ require 'deployml/frameworks/rails'
2
+
1
3
  module DeploYML
2
4
  module Frameworks
5
+ #
6
+ # Provides methods for deploying Rails 3 projects.
7
+ #
3
8
  module Rails3
9
+ include Rails
10
+
11
+ #
12
+ # Installs any dependencies using `bundle install --deployment`.
13
+ #
14
+ # @param [LocalShell, RemoteShell] shell
15
+ # The shell to execute commands in.
16
+ #
4
17
  def install(shell)
5
18
  shell.run 'bundle', 'install', '--deployment'
6
19
  end
7
20
 
21
+ #
22
+ # Migrates the database using the `db:autoupgrade` if
23
+ # [DataMapper](http://datamapper.org) is being used, or the typical
24
+ # `db:migrate` task.
25
+ #
26
+ # @param [LocalShell, RemoteShell] shell
27
+ # The shell to execute commands in.
28
+ #
8
29
  def migrate(shell)
9
30
  task = case @orm
10
31
  when :datamapper
@@ -1,6 +1,9 @@
1
1
  require 'deployml/shell'
2
2
 
3
3
  module DeploYML
4
+ #
5
+ # Represents a shell running on the local system.
6
+ #
4
7
  class LocalShell
5
8
 
6
9
  include Shell
@@ -39,15 +42,7 @@ module DeploYML
39
42
  # the block has returned.
40
43
  #
41
44
  def cd(path,&block)
42
- if block
43
- cwd = Dir.pwd
44
-
45
- Dir.chdir(path)
46
- block.call()
47
- Dir.chdir(cwd)
48
- else
49
- Dir.chdir(path)
50
- end
45
+ Dir.chdir(path,&block)
51
46
  end
52
47
 
53
48
  end
@@ -2,8 +2,12 @@ require 'rprogram/task'
2
2
 
3
3
  module DeploYML
4
4
  module Options
5
+ #
6
+ # Maps in command-line options for the `mongrel_rails` utility.
7
+ #
5
8
  class Mongrel < RProgram::Task
6
9
 
10
+ # Default options for Mongrel
7
11
  DEFAULTS = {
8
12
  :environment => :production,
9
13
  :address => '127.0.0.1',
@@ -2,6 +2,9 @@ require 'rprogram/task'
2
2
 
3
3
  module DeploYML
4
4
  module Options
5
+ #
6
+ # Maps in command-line options for the `thin` utility.
7
+ #
5
8
  class Thin < RProgram::Task
6
9
 
7
10
  # Default options for Thin
@@ -37,7 +37,7 @@ module DeploYML
37
37
  # The configuration file for the project could not be found
38
38
  # in any of the common directories.
39
39
  #
40
- def initialize(root)
40
+ def initialize(root=Dir.pwd)
41
41
  @root = File.expand_path(root)
42
42
  @config_file = File.join(@root,CONFIG_DIR,CONFIG_FILE)
43
43
  @environments_dir = File.join(@root,CONFIG_DIR,ENVIRONMENTS_DIR)
@@ -117,39 +117,12 @@ module DeploYML
117
117
  # The environment to deploy to.
118
118
  #
119
119
  # @return [true]
120
+ # Indicates that the tasks were successfully completed.
120
121
  #
121
122
  # @since 0.2.0
122
123
  #
123
124
  def invoke(tasks,env=:production)
124
- env = environment(env)
125
-
126
- env.remote_shell do |shell|
127
- # setup the deployment repository
128
- env.setup(shell) if tasks.include?(:setup)
129
-
130
- # cd into the deployment repository
131
- shell.cd env.dest.path
132
-
133
- # update the deployment repository
134
- env.update(shell) if tasks.include?(:update)
135
-
136
- # framework tasks
137
- env.install(shell) if tasks.include?(:install)
138
- env.migrate(shell) if tasks.include?(:migrate)
139
-
140
- # server tasks
141
- if tasks.include?(:config)
142
- env.server_config(shell)
143
- elsif tasks.include?(:start)
144
- env.server_start(shell)
145
- elsif tasks.include?(:stop)
146
- env.server_stop(shell)
147
- elsif tasks.include?(:restart)
148
- env.server_restart(shell)
149
- end
150
- end
151
-
152
- return true
125
+ environment(env).invoke(tasks)
153
126
  end
154
127
 
155
128
  #
@@ -158,8 +131,11 @@ module DeploYML
158
131
  # @param [Symbol, String] env
159
132
  # The environment to deploy to.
160
133
  #
134
+ # @return [true]
135
+ # Indicates that the tasks were successfully completed.
136
+ #
161
137
  def setup!(env=:production)
162
- invoke [:setup], env
138
+ environment(env).setup!
163
139
  end
164
140
 
165
141
  #
@@ -168,8 +144,11 @@ module DeploYML
168
144
  # @param [Symbol, String] env
169
145
  # The environment to deploy to.
170
146
  #
147
+ # @return [true]
148
+ # Indicates that the tasks were successfully completed.
149
+ #
171
150
  def update!(env=:production)
172
- invoke [:update], env
151
+ environment(env).update!
173
152
  end
174
153
 
175
154
  #
@@ -178,8 +157,11 @@ module DeploYML
178
157
  # @param [Symbol, String] env
179
158
  # The environment to deploy to.
180
159
  #
160
+ # @return [true]
161
+ # Indicates that the tasks were successfully completed.
162
+ #
181
163
  def install!(env=:production)
182
- invoke [:install], env
164
+ environment(env).install!
183
165
  end
184
166
 
185
167
  #
@@ -188,8 +170,11 @@ module DeploYML
188
170
  # @param [Symbol, String] env
189
171
  # The environment to deploy to.
190
172
  #
173
+ # @return [true]
174
+ # Indicates that the tasks were successfully completed.
175
+ #
191
176
  def migrate!(env=:production)
192
- invoke [:migrate], env
177
+ environment(env).migrate!
193
178
  end
194
179
 
195
180
  #
@@ -198,8 +183,11 @@ module DeploYML
198
183
  # @param [Symbol, String] env
199
184
  # The environment to deploy to.
200
185
  #
186
+ # @return [true]
187
+ # Indicates that the tasks were successfully completed.
188
+ #
201
189
  def config!(env=:production)
202
- invoke [:config], env
190
+ environment(env).config!
203
191
  end
204
192
 
205
193
  #
@@ -208,8 +196,11 @@ module DeploYML
208
196
  # @param [Symbol, String] env
209
197
  # The environment to deploy to.
210
198
  #
199
+ # @return [true]
200
+ # Indicates that the tasks were successfully completed.
201
+ #
211
202
  def start!(env=:production)
212
- invoke [:start], env
203
+ environment(env).start!
213
204
  end
214
205
 
215
206
  #
@@ -218,8 +209,11 @@ module DeploYML
218
209
  # @param [Symbol, String] env
219
210
  # The environment to deploy to.
220
211
  #
212
+ # @return [true]
213
+ # Indicates that the tasks were successfully completed.
214
+ #
221
215
  def stop!(env=:production)
222
- invoke [:stop], env
216
+ environment(env).stop!
223
217
  end
224
218
 
225
219
  #
@@ -228,8 +222,11 @@ module DeploYML
228
222
  # @param [Symbol, String] env
229
223
  # The environment to deploy to.
230
224
  #
225
+ # @return [true]
226
+ # Indicates that the tasks were successfully completed.
227
+ #
231
228
  def restart!(env=:production)
232
- invoke [:restart], env
229
+ environment(env).restart!
233
230
  end
234
231
 
235
232
  #
@@ -238,10 +235,13 @@ module DeploYML
238
235
  # @param [Symbol, String] env
239
236
  # The environment to deploy to.
240
237
  #
238
+ # @return [true]
239
+ # Indicates that the tasks were successfully completed.
240
+ #
241
241
  # @since 0.2.0
242
242
  #
243
243
  def deploy!(env=:production)
244
- invoke [:setup, :install, :migrate, :config, :start], env
244
+ environment(env).deploy!
245
245
  end
246
246
 
247
247
  #
@@ -250,10 +250,13 @@ module DeploYML
250
250
  # @param [Symbol, String] env
251
251
  # The environment to deploy to.
252
252
  #
253
+ # @return [true]
254
+ # Indicates that the tasks were successfully completed.
255
+ #
253
256
  # @since 0.2.0
254
257
  #
255
258
  def redeploy!(env=:production)
256
- invoke [:update, :install, :migrate, :restart], env
259
+ environment(env).redeploy!
257
260
  end
258
261
 
259
262
  protected
@@ -1,9 +1,22 @@
1
1
  require 'deployml/shell'
2
2
 
3
+ require 'addressable/uri'
4
+ require 'shellwords'
5
+
3
6
  module DeploYML
7
+ #
8
+ # Represents a shell running on a remote server.
9
+ #
4
10
  class RemoteShell
5
11
 
6
12
  include Shell
13
+ include Shellwords
14
+
15
+ # The URI of the remote shell
16
+ attr_reader :uri
17
+
18
+ # The history of the Remote Shell
19
+ attr_reader :history
7
20
 
8
21
  #
9
22
  # Initializes a remote shell session.
@@ -51,7 +64,7 @@ module DeploYML
51
64
  # @param [String] message
52
65
  # The message to echo.
53
66
  #
54
- def each(message)
67
+ def echo(message)
55
68
  run 'echo', message
56
69
  end
57
70
 
@@ -65,12 +78,11 @@ module DeploYML
65
78
  # If a block is given, then the directory will be changed back after
66
79
  # the block has returned.
67
80
  #
68
- def cd(path,&block)
81
+ def cd(path)
69
82
  @history << ['cd', path]
70
83
 
71
- if block
72
- block.call() if block
73
-
84
+ if block_given?
85
+ yield
74
86
  @history << ['cd', '-']
75
87
  end
76
88
  end
@@ -83,7 +95,9 @@ module DeploYML
83
95
  # A single command string.
84
96
  #
85
97
  def join
86
- @history.map { |command| command.join(' ') }.join(' && ')
98
+ @history.map { |command|
99
+ command.map { |word| shellescape(word) }.join(' ')
100
+ }.join(' && ')
87
101
  end
88
102
 
89
103
  #
@@ -2,15 +2,37 @@ require 'deployml/exceptions/invalid_config'
2
2
 
3
3
  module DeploYML
4
4
  module Servers
5
+ #
6
+ # Provides methods for starting, stoping and restarting the
7
+ # [Apache](http://httpd.apache.org/) web server.
8
+ #
5
9
  module Apache
10
+ #
11
+ # Starts Apache using the `apachectl start` command.
12
+ #
13
+ # @param [LocalShell, RemoteShell] shell
14
+ # The shell to execute commands in.
15
+ #
6
16
  def server_start(shell)
7
17
  shell.run 'apachectl', 'start'
8
18
  end
9
19
 
20
+ #
21
+ # Restarts Apache using the `apachectl restart` command.
22
+ #
23
+ # @param [LocalShell, RemoteShell] shell
24
+ # The shell to execute commands in.
25
+ #
10
26
  def server_restart(shell)
11
27
  shell.run 'apachectl', 'restart'
12
28
  end
13
29
 
30
+ #
31
+ # Stops Apache using the `apachectl stop` command.
32
+ #
33
+ # @param [LocalShell, RemoteShell] shell
34
+ # The shell to execute commands in.
35
+ #
14
36
  def server_stop(shell)
15
37
  shell.run 'apachectl', 'stop'
16
38
  end
@@ -3,18 +3,44 @@ require 'deployml/options/mongrel'
3
3
 
4
4
  module DeploYML
5
5
  module Servers
6
+ #
7
+ # Provides methods for configuring, starting, stoping and restarting
8
+ # the [Mongrel](https://github.com/fauna/mongrel) web server.
9
+ #
6
10
  module Mongrel
11
+ #
12
+ # Initializes options used when calling `mongrel`.
13
+ #
7
14
  def initialize_server
8
15
  @mongrel = Options::Mongrel.new(@server_options)
9
16
  @mongrel.environment ||= @name
10
17
  end
11
18
 
19
+ #
20
+ # Executes a command via the `mongrel_rails` command.
21
+ #
22
+ # @param [LocalShell, RemoteShell] shell
23
+ # The shell to execute commands in.
24
+ #
25
+ # @param [Array] args
26
+ # Additional arguments to call `mongrel_rails` with.
27
+ #
12
28
  def mongrel_cluster(shell,*args)
13
29
  options = args + ['-c', @mongrel.config]
14
30
 
15
31
  shell.run 'mongrel_rails', *options
16
32
  end
17
33
 
34
+ #
35
+ # Configures Mongrel by calling `mongrel_rails cluster::configure`.
36
+ #
37
+ # @param [LocalShell, RemoteShell] shell
38
+ # The shell to execute commands in.
39
+ #
40
+ # @raise [MissingOption]
41
+ # No `config` option was listed under the `server` option in the
42
+ # `deploy.yml` configuration file.
43
+ #
18
44
  def server_config(shell)
19
45
  unless @mongrel.config
20
46
  raise(MissingOption,"No 'config' option specified under server options",caller)
@@ -25,14 +51,32 @@ module DeploYML
25
51
  shell.run 'mongrel_rails', 'cluster::configure', *options
26
52
  end
27
53
 
54
+ #
55
+ # Starts Mongrel by calling `mongrel_rails cluster::start`.
56
+ #
57
+ # @param [LocalShell, RemoteShell] shell
58
+ # The shell to execute commands in.
59
+ #
28
60
  def server_start(shell)
29
61
  mongrel_cluster 'cluster::start'
30
62
  end
31
63
 
64
+ #
65
+ # Stops Mongrel by calling `mongrel_rails cluster::stop`.
66
+ #
67
+ # @param [LocalShell, RemoteShell] shell
68
+ # The shell to execute commands in.
69
+ #
32
70
  def server_stop(shell)
33
71
  mongrel_cluster 'cluster::stop'
34
72
  end
35
73
 
74
+ #
75
+ # Restarts Mongrel by calling `mongrel_rails cluster::restart`.
76
+ #
77
+ # @param [LocalShell, RemoteShell] shell
78
+ # The shell to execute commands in.
79
+ #
36
80
  def server_restart(shell)
37
81
  mongrel_cluster 'cluster::restart'
38
82
  end
@@ -3,18 +3,44 @@ require 'deployml/options/thin'
3
3
 
4
4
  module DeploYML
5
5
  module Servers
6
+ #
7
+ # Provides methods for configuring, starting, stoping and restarting
8
+ # the [Thin](http://code.macournoyer.com/thin/) web server.
9
+ #
6
10
  module Thin
11
+ #
12
+ # Initializes options used when calling `thin`.
13
+ #
7
14
  def initialize_server
8
15
  @thin = Options::Thin.new(@server_options)
9
16
  @thin.environment ||= @name
10
17
  end
11
18
 
19
+ #
20
+ # Runs a command via the `thin` command.
21
+ #
22
+ # @param [LocalShell, RemoteShell] shell
23
+ # The shell to execute commands in.
24
+ #
25
+ # @param [Array] args
26
+ # Additional arguments to call `thin` with.
27
+ #
12
28
  def thin(shell,*args)
13
29
  options = args + ['-C', @thin.config, '-s', @thin.servers]
14
30
 
15
31
  shell.run 'thin', *options
16
32
  end
17
33
 
34
+ #
35
+ # Configures Thin by calling `thin config`.
36
+ #
37
+ # @param [LocalShell, RemoteShell] shell
38
+ # The shell to execute commands in.
39
+ #
40
+ # @raise [MissingOption]
41
+ # No `config` option was listed under the `server` option in the
42
+ # `deploy.yml` configuration file.
43
+ #
18
44
  def server_config(shell)
19
45
  unless @thin.config
20
46
  raise(MissingOption,"No 'config' option specified under the server options",caller)
@@ -25,14 +51,32 @@ module DeploYML
25
51
  shell.run 'thin', 'config', *options
26
52
  end
27
53
 
54
+ #
55
+ # Starts Thin by calling `thin start`.
56
+ #
57
+ # @param [LocalShell, RemoteShell] shell
58
+ # The shell to execute commands in.
59
+ #
28
60
  def server_start(shell)
29
61
  thin shell, 'start'
30
62
  end
31
63
 
64
+ #
65
+ # Stops Thin by calling `thin stop`.
66
+ #
67
+ # @param [LocalShell, RemoteShell] shell
68
+ # The shell to execute commands in.
69
+ #
32
70
  def server_stop(shell)
33
71
  thin shell, 'stop'
34
72
  end
35
73
 
74
+ #
75
+ # Restarts Thin by calling `thin restart`.
76
+ #
77
+ # @param [LocalShell, RemoteShell] shell
78
+ # The shell to execute commands in.
79
+ #
36
80
  def server_restart(shell)
37
81
  thin shell, 'restart'
38
82
  end
@@ -1,20 +1,15 @@
1
+ require 'thor/shell/color'
2
+
1
3
  module DeploYML
4
+ #
5
+ # Provides common methods used by both {LocalShell} and {RemoteShell}.
6
+ #
2
7
  module Shell
3
8
 
4
- def initialize(&block)
5
- block.call(self) if block
6
- end
9
+ include Thor::Shell
7
10
 
8
- #
9
- # Place-holder method.
10
- #
11
- # @param [String] program
12
- # The name or path of the program to run.
13
- #
14
- # @param [Array<String>] args
15
- # Additional arguments for the program.
16
- #
17
- def run(program,*args)
11
+ def initialize
12
+ yield self if block_given?
18
13
  end
19
14
 
20
15
  #
@@ -30,8 +25,32 @@ module DeploYML
30
25
  run 'rake', rake_task(task,*args)
31
26
  end
32
27
 
28
+ #
29
+ # Prints a status message.
30
+ #
31
+ # @param [String] message
32
+ # The message to print.
33
+ #
34
+ # @since 0.4.0
35
+ #
36
+ def status(message)
37
+ echo "#{Color::GREEN}>>> #{message}#{Color::CLEAR}"
38
+ end
39
+
33
40
  protected
34
41
 
42
+ #
43
+ # Builds a `rake` task name.
44
+ #
45
+ # @param [String, Symbol] name
46
+ # The name of the `rake` task.
47
+ #
48
+ # @param [Array] args
49
+ # Additional arguments to pass to the `rake` task.
50
+ #
51
+ # @param [String]
52
+ # The `rake` task name to be called.
53
+ #
35
54
  def rake_task(name,*args)
36
55
  name = name.to_s
37
56
 
@@ -1,4 +1,4 @@
1
1
  module DeploYML
2
2
  # deploYML version
3
- VERSION = '0.3.0'
3
+ VERSION = '0.4.0'
4
4
  end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+ require 'deployml/remote_shell'
3
+
4
+ describe RemoteShell do
5
+ let(:uri) { 'ssh://deploy@www.example.com/path' }
6
+
7
+ subject { RemoteShell.new(uri) }
8
+
9
+ it "should parse the given URI" do
10
+ subject.uri.should be_kind_of(Addressable::URI)
11
+
12
+ subject.uri.user.should == 'deploy'
13
+ subject.uri.host.should == 'www.example.com'
14
+ subject.uri.path.should == '/path'
15
+ end
16
+
17
+ it "should convert normal URIs to SSH URIs" do
18
+ subject.ssh_uri.should == 'deploy@www.example.com'
19
+ end
20
+
21
+ it "should enqueue programs to run" do
22
+ subject.run 'echo', 'one'
23
+ subject.run 'echo', 'two'
24
+
25
+ subject.history[0].should == ['echo', 'one']
26
+ subject.history[1].should == ['echo', 'two']
27
+ end
28
+
29
+ it "should enqueue echo commands" do
30
+ subject.echo 'one'
31
+ subject.echo 'two'
32
+
33
+ subject.history[0].should == ['echo', 'one']
34
+ subject.history[1].should == ['echo', 'two']
35
+ end
36
+
37
+ it "should enqueue directory changes" do
38
+ subject.cd '/other'
39
+
40
+ subject.history[0].should == ['cd', '/other']
41
+ end
42
+
43
+ it "should enqueue temporary directory changes" do
44
+ subject.cd '/other' do
45
+ subject.run 'pwd'
46
+ end
47
+
48
+ subject.history[0].should == ['cd', '/other']
49
+ subject.history[1].should == ['pwd']
50
+ subject.history[2].should == ['cd', '-']
51
+ end
52
+
53
+ it "should join all commands together into one command" do
54
+ subject.run 'echo', 'one'
55
+ subject.run 'echo', 'two'
56
+
57
+ subject.join.should == 'echo one && echo two'
58
+ end
59
+
60
+ it "should escape all command arguments" do
61
+ subject.run 'the program'
62
+ subject.run 'echo', '>>> status'
63
+
64
+ subject.join.should == "the\\ program && echo \\>\\>\\>\\ status"
65
+ end
66
+ end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 3
7
+ - 4
8
8
  - 0
9
- version: 0.3.0
9
+ version: 0.4.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Postmodern
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-11-21 00:00:00 -08:00
17
+ date: 2010-11-29 00:00:00 -08:00
18
18
  default_executable: deployml
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -27,9 +27,9 @@ dependencies:
27
27
  - !ruby/object:Gem::Version
28
28
  segments:
29
29
  - 2
30
- - 1
31
- - 1
32
- version: 2.1.1
30
+ - 2
31
+ - 0
32
+ version: 2.2.0
33
33
  type: :runtime
34
34
  version_requirements: *id001
35
35
  - !ruby/object:Gem::Dependency
@@ -139,6 +139,7 @@ files:
139
139
  - lib/deployml/exceptions/unknown_framework.rb
140
140
  - lib/deployml/exceptions/unknown_server.rb
141
141
  - lib/deployml/frameworks.rb
142
+ - lib/deployml/frameworks/rails.rb
142
143
  - lib/deployml/frameworks/rails2.rb
143
144
  - lib/deployml/frameworks/rails3.rb
144
145
  - lib/deployml/local_shell.rb
@@ -167,6 +168,7 @@ files:
167
168
  - spec/helpers/projects/rails/config/deploy/production.yml
168
169
  - spec/helpers/projects/rails/config/deploy/staging.yml
169
170
  - spec/project_spec.rb
171
+ - spec/remote_shell_spec.rb
170
172
  - spec/spec_helper.rb
171
173
  has_rdoc: yard
172
174
  homepage: http://github.com/postmodern/deployml
@@ -201,6 +203,7 @@ signing_key:
201
203
  specification_version: 3
202
204
  summary: A simple deployment solution that works.
203
205
  test_files:
206
+ - spec/remote_shell_spec.rb
204
207
  - spec/project_spec.rb
205
208
  - spec/environment_spec.rb
206
209
  - spec/configuration_spec.rb