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 +22 -1
- data/README.md +26 -5
- data/lib/deployml/configuration.rb +203 -48
- data/lib/deployml/environment.rb +127 -43
- data/lib/deployml/frameworks/rails.rb +3 -3
- data/lib/deployml/local_shell.rb +5 -7
- data/lib/deployml/project.rb +3 -3
- data/lib/deployml/remote_shell.rb +16 -20
- data/lib/deployml/servers/mongrel.rb +4 -4
- data/lib/deployml/servers/thin.rb +4 -4
- data/lib/deployml/shell.rb +63 -10
- data/lib/deployml/version.rb +1 -1
- data/spec/configuration_spec.rb +51 -8
- data/spec/remote_shell_spec.rb +14 -3
- metadata +2 -2
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
|
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**
|
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
|
-
|
64
|
-
|
65
|
-
|
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
|
-
|
75
|
-
@dest = nil
|
93
|
+
config = normalize_hash(config)
|
76
94
|
|
77
|
-
@
|
78
|
-
@server_options = {}
|
95
|
+
@bundler = config.fetch(:bundler,false)
|
79
96
|
|
80
|
-
@
|
81
|
-
|
82
|
-
|
97
|
+
@framework = if config[:framework]
|
98
|
+
config[:framework].to_sym
|
99
|
+
end
|
83
100
|
|
84
|
-
@
|
85
|
-
|
101
|
+
@orm = if config[:orm]
|
102
|
+
config[:orm].to_sym
|
103
|
+
end
|
86
104
|
|
87
|
-
|
105
|
+
@server_name, @server_options = parse_server(config[:server])
|
88
106
|
|
89
|
-
@
|
107
|
+
@source = config[:source]
|
108
|
+
@dest = if config[:dest]
|
109
|
+
parse_dest(config[:dest])
|
110
|
+
end
|
90
111
|
|
91
|
-
if config[:
|
92
|
-
|
93
|
-
|
112
|
+
@environment = if config[:environment]
|
113
|
+
config[:environment].to_sym
|
114
|
+
end
|
94
115
|
|
95
|
-
|
96
|
-
@orm = config[:orm].to_sym
|
97
|
-
end
|
116
|
+
@debug = config.fetch(:debug,false)
|
98
117
|
|
99
|
-
|
100
|
-
|
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
|
-
|
108
|
-
|
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[:
|
112
|
-
@
|
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
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
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
|
129
|
-
@
|
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] =
|
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
|
data/lib/deployml/environment.rb
CHANGED
@@ -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(
|
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
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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(
|
114
|
-
shell.
|
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,*
|
132
|
+
def rake(task,*arguments)
|
129
133
|
remote_shell do |shell|
|
130
|
-
shell.cd(
|
131
|
-
shell.rake(task,*
|
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]
|
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(*
|
148
|
-
|
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 [
|
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,
|
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 [
|
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
|
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 [
|
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 [
|
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 [
|
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 [
|
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 [
|
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 [
|
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
|
357
|
+
invoke_task(:setup,shell) if tasks.include?(:setup)
|
273
358
|
|
274
359
|
# cd into the deployment repository
|
275
|
-
shell.cd
|
360
|
+
shell.cd(shell.uri.path)
|
276
361
|
|
277
362
|
# update the deployment repository
|
278
|
-
update
|
363
|
+
invoke_task(:update,shell) if tasks.include?(:update)
|
279
364
|
|
280
365
|
# framework tasks
|
281
|
-
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
|
-
|
371
|
+
invoke_task(:config,shell)
|
288
372
|
elsif tasks.include?(:start)
|
289
|
-
|
373
|
+
invoke_task(:start,shell)
|
290
374
|
elsif tasks.include?(:stop)
|
291
|
-
|
375
|
+
invoke_task(:stop,shell)
|
292
376
|
elsif tasks.include?(:restart)
|
293
|
-
|
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
|
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
|
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,*
|
14
|
-
|
13
|
+
def rake(task,*arguments)
|
14
|
+
arguments += ["RAILS_ENV=#{@environment}"]
|
15
15
|
|
16
|
-
super(task,*
|
16
|
+
super(task,*arguments)
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
data/lib/deployml/local_shell.rb
CHANGED
@@ -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>]
|
15
|
+
# @param [Array<String>] arguments
|
18
16
|
# Additional arguments for the program.
|
19
17
|
#
|
20
|
-
def run(program,*
|
18
|
+
def run(program,*arguments)
|
21
19
|
program = program.to_s
|
22
|
-
|
20
|
+
arguments = arguments.map { |arg| arg.to_s }
|
23
21
|
|
24
|
-
system(program,*
|
22
|
+
system(program,*arguments)
|
25
23
|
end
|
26
24
|
|
27
25
|
#
|
data/lib/deployml/project.rb
CHANGED
@@ -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(
|
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>]
|
41
|
+
# @param [Array<String>] arguments
|
53
42
|
# Additional arguments for the program.
|
54
43
|
#
|
55
|
-
def run(program,*
|
56
|
-
@history << [program, *
|
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]
|
113
|
+
# @param [Array] arguments
|
118
114
|
# Additional arguments to pass to SSH.
|
119
115
|
#
|
120
|
-
def ssh(*
|
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
|
-
|
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]
|
25
|
+
# @param [Array] arguments
|
26
26
|
# Additional arguments to call `mongrel_rails` with.
|
27
27
|
#
|
28
|
-
def mongrel_cluster(shell,*
|
29
|
-
options =
|
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',
|
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]
|
25
|
+
# @param [Array] arguments
|
26
26
|
# Additional arguments to call `thin` with.
|
27
27
|
#
|
28
|
-
def thin(shell,*
|
29
|
-
options =
|
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',
|
51
|
+
options = ['-c', shell.uri.path] + @thin.arguments
|
52
52
|
shell.run 'thin', 'config', *options
|
53
53
|
|
54
54
|
shell.status "Thin configured."
|
data/lib/deployml/shell.rb
CHANGED
@@ -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
|
-
|
8
|
+
class Shell
|
8
9
|
|
9
10
|
include Thor::Shell
|
11
|
+
include Shellwords
|
10
12
|
|
11
|
-
|
12
|
-
|
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>]
|
74
|
+
# @param [Array<String>] arguments
|
22
75
|
# Additional arguments for the Rake task.
|
23
76
|
#
|
24
|
-
def rake(task,*
|
25
|
-
run 'rake', rake_task(task,*
|
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]
|
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,*
|
107
|
+
def rake_task(name,*arguments)
|
55
108
|
name = name.to_s
|
56
109
|
|
57
|
-
unless
|
58
|
-
name += ('[' +
|
110
|
+
unless arguments.empty?
|
111
|
+
name += ('[' + arguments.join(',') + ']')
|
59
112
|
end
|
60
113
|
|
61
114
|
return name
|
data/lib/deployml/version.rb
CHANGED
data/spec/configuration_spec.rb
CHANGED
@@ -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
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
data/spec/remote_shell_spec.rb
CHANGED
@@ -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
|
-
|
18
|
-
|
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.
|
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-
|
13
|
+
date: 2011-04-22 00:00:00 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: addressable
|