flintlock 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES.md CHANGED
@@ -1,3 +1,31 @@
1
+ # Version 0.2.0
2
+
3
+ - Added the ``package`` subcommand, which packages up a local directory into
4
+ a module archive.
5
+ - Added the ``defaults`` subcommand, which prints out the default configuration
6
+ (from ``bin/defaults``) of the given module.
7
+ - Added the ``new`` subcommand, which generates an empty template module
8
+ - Added the ``version`` subcommand, which prints the ``flintlock`` version
9
+ - Added the ``--halt`` option to the ``deploy`` subcommand. This allows users
10
+ to halt deployment after a particular stage has run (e.g. ``--halt stage``)
11
+ - Added a new optional deployment stage called ``detect``. This script should be
12
+ used to validate whether the module is appropriate for installation on the
13
+ current host.
14
+ - Fixed a bug that prevented scripts that required a TTY from working.
15
+ - Fixed a bug with relative paths passed to the ``deploy`` command
16
+ - Module scripts are now silently skipped if they don't exist or are empty
17
+ (rather than erroring out).
18
+ - Fixed a bug where a ``/tmp`` filesystem mounted ``noexec`` would prevent
19
+ scripts from running.
20
+ - Can now deploy from local tarballs
21
+ - Can now deploy from subversion repositories
22
+ - Keyboard interrupts (``SIGQUIT``) are now caught and a nicer error is printed.
23
+ - External commands (e.g. ``git``, ``svn``) are validated prior to being run.
24
+ - Support a wider array of module URIs (e.g. ``svn+ssh://...``)
25
+ - Archives with root directories are now supported as valid module archives
26
+ (e.g. ``<archive_root>/<root_dir>/bin/*`` vs ``<archive_root>/bin/*`` layouts)
27
+ - Archive detection now uses MIME hinting rather than filename hinting
28
+ - Fixed a few issues with the RPM spec file
1
29
 
2
30
  # Version 0.1.0
3
31
 
data/README.md CHANGED
@@ -44,6 +44,7 @@ files and directories to ``/some/empty/directory``. Let's see what happens:
44
44
  ```console
45
45
  $ flintlock deploy git://github.com/jcmcken/flintlock-redis.git /some/empty/directory
46
46
  run fetching module
47
+ run detecting compatibility
47
48
  info deploying jcmcken/redis (0.0.1) to '/some/empty/directory'
48
49
  create creating deploy directory
49
50
  run installing and configuring dependencies
@@ -92,10 +93,12 @@ is usually acceptable. But all of the important files should really be located t
92
93
 
93
94
  ## Supported Formats
94
95
 
95
- Currently ``flintlock`` can install modules from a number of sources. In addition to
96
- local directories, ``flintlock`` supports the following protocols/formats:
96
+ Currently ``flintlock`` can install modules from a number of sources:
97
97
 
98
- * ``git``
98
+ * From a local directory
99
+ * From a local tarball (``tar`` or ``tar.gz``)
100
+ * Over ``git`` supported protocols (``git://..``)
101
+ * Over ``svn`` supported protocols (``svn://...``)
99
102
  * ``tar`` or ``tar.gz`` over ``http``/``https``
100
103
 
101
104
  Attempting to install any other way will throw an error message similar to the following:
@@ -117,6 +120,7 @@ At its heart, a module has the following minimal layout:
117
120
  sample-app-1
118
121
  |-- bin
119
122
  | |-- defaults
123
+ | |-- detect
120
124
  | |-- modify
121
125
  | |-- prepare
122
126
  | |-- stage
@@ -130,8 +134,8 @@ generate this structure.
130
134
 
131
135
  The top-level directory (in this case, ``sample-app-1``) can be called anything.
132
136
 
133
- The files under ``bin`` are executable scripts (using any language you care to use). All
134
- of these scripts must exist, but they need not do anything. More on these later.
137
+ The files under ``bin`` are executable scripts (using any language you care to
138
+ use). More on these later.
135
139
 
136
140
  The ``metadata.json`` file contains metadata about the module. This metadata looks as follows:
137
141
 
@@ -143,8 +147,9 @@ The ``metadata.json`` file contains metadata about the module. This metadata loo
143
147
  }
144
148
  ```
145
149
 
146
- All three keys (``author``, ``name``, ``version``) are required, but can be any value. These
147
- metadata are merely used to namespace the module.
150
+ All three keys (``author``, ``name``, ``version``) are required, but can be any value
151
+ (as long as they're not the empty string). These metadata are merely used to namespace
152
+ the module.
148
153
 
149
154
  ``flintlock`` developers can choose to include more files in their modules if needed.
150
155
 
@@ -157,6 +162,7 @@ These stages correspond directly to the scripts under ``bin/``.
157
162
 
158
163
  The most important stages, and their purpose, are as follows. (The stages occur in the order listed below)
159
164
 
165
+ * ``detect``: Detect whether the module is compatible with the current host.
160
166
  * ``prepare``: Install or compile any required dependencies. This script takes no arguments.
161
167
  * ``stage``: Stage the application directories and files. This script takes a single argument,
162
168
  which is the directory where your app will be deployed. This directory need not exist, but if
@@ -205,4 +211,7 @@ command line.
205
211
 
206
212
  ### Examples
207
213
 
208
- An example ``flintlock`` module can be found @ http://github.com/jcmcken/flintlock-redis.git.
214
+ Some example ``flintlock`` modules can be found @ the following locations:
215
+
216
+ * http://github.com/jcmcken/flintlock-redis.git
217
+ * http://github.com/jcmcken/flintlock-tomcat.git
@@ -1,3 +1,4 @@
1
1
  require 'rubygems'
2
+ require 'flintlock/util'
2
3
  require 'flintlock/metadata'
3
4
  require 'flintlock/module'
@@ -1,5 +1,6 @@
1
1
  require 'thor'
2
2
  require 'flintlock/module'
3
+ require 'flintlock/version'
3
4
 
4
5
  module Flintlock
5
6
  class Cli < Thor
@@ -7,22 +8,38 @@ module Flintlock
7
8
 
8
9
  desc "deploy MODULE DIRECTORY", "Deploy a flintlock module MODULE to DIRECTORY"
9
10
  method_option :debug, :type => :boolean, :description => "enable debug output", :default => false
11
+ method_option :halt, :type => :string, :banner => 'STAGE',
12
+ :description => "Halt after STAGE", :enum => ['fetch', 'detect', 'prepare', 'stage', 'start', 'modify']
10
13
  def deploy(uri, app_dir)
14
+ app_dir = File.expand_path(app_dir)
11
15
  say_status "run", "fetching module", :magenta
12
16
  mod = get_module(uri, options)
13
- say_status "info", "deploying #{mod.full_name} to '#{app_dir}'", :blue
14
- say_status "create", "creating deploy directory"
15
- mod.create_app_dir(app_dir) rescue abort("deploy directory is not empty")
17
+ return if options[:halt] == 'fetch'
16
18
 
17
19
  begin
20
+ say_status "run", "detecting compatibility", :magenta
21
+ mod.detect
22
+ return if options[:halt] == 'detect'
23
+
24
+ say_status "info", "deploying #{mod.full_name} to '#{app_dir}'", :blue
25
+ say_status "create", "creating deploy directory"
26
+ mod.create_app_dir(app_dir) rescue abort("deploy directory is not empty")
18
27
  say_status "run", "installing and configuring dependencies", :magenta
19
28
  mod.prepare
29
+ return if options[:halt] == 'prepare'
30
+
20
31
  say_status "create", "staging application files"
21
32
  mod.stage(app_dir)
33
+ return if options[:halt] == 'stage'
34
+
22
35
  say_status "run", "launching the application", :magenta
23
36
  mod.start(app_dir)
37
+ return if options[:halt] == 'start'
38
+
24
39
  say_status "run", "altering application runtime environment", :magenta
25
40
  mod.modify(app_dir)
41
+ return if options[:halt] == 'modify'
42
+
26
43
  say_status "info", "complete!", :blue
27
44
  rescue Errno::EACCES => e
28
45
  abort("#{e.message.gsub(/Permission denied/, 'permission denied')}")
@@ -39,24 +56,57 @@ module Flintlock
39
56
  inside("bin") do
40
57
  Module.script_names.each do |script|
41
58
  create_file script
59
+ chmod script, 0755, :verbose => false
42
60
  end
43
61
  end
44
62
  create_file(Metadata.filename, Metadata.empty)
45
63
  end
46
64
  end
47
65
 
66
+ desc "package [DIRECTORY]", "Package up the given module directory"
67
+ method_option :debug, :type => :boolean, :description => "enable debug output", :default => false
68
+ def package(directory = Dir.pwd)
69
+ handle_exception { Module.package(directory, options.dup) }
70
+ end
71
+
72
+ desc "defaults MODULE", "Print the default configuration settings for MODULE"
73
+ def defaults(uri)
74
+ mod = get_module(uri)
75
+ mod.defaults.each do |k,v|
76
+ puts "#{k}=#{v}"
77
+ end
78
+ end
79
+
80
+ desc "version", "Print version information"
81
+ def version
82
+ puts Flintlock::VERSION
83
+ end
84
+
48
85
  private
49
86
 
50
87
  def get_module(uri, options={})
51
- begin
88
+ handle_exception do
52
89
  Flintlock::Module.new(uri, options)
90
+ end
91
+ end
92
+
93
+ def handle_exception(&block)
94
+ begin
95
+ result = block.call
53
96
  rescue InvalidModule => e
54
97
  abort("invalid flintlock module '#{e}'")
55
98
  rescue UnsupportedModuleURI => e
56
99
  abort("don't know how to download '#{e}'!")
57
100
  rescue ModuleDownloadError => e
58
101
  abort("failed to download '#{e}'")
102
+ rescue DependencyError => e
103
+ abort("missing dependency: no such command '#{e}'")
104
+ rescue Interrupt
105
+ abort("interrupted by user")
106
+ rescue PackagingError
107
+ abort("packaging failed!")
59
108
  end
109
+ result
60
110
  end
61
111
 
62
112
  def error(message)
@@ -9,5 +9,10 @@ module Flintlock
9
9
  def unsilence!
10
10
  @logdev, @saved_logdev = @saved_logdev, nil if @saved_logdev
11
11
  end
12
+
13
+ def linewise(output, options={})
14
+ options[:level] ||= :debug
15
+ output.lines.each { |x| send(options[:level], x) }
16
+ end
12
17
  end
13
18
  end
@@ -46,6 +46,10 @@ module Flintlock
46
46
  "#{author}/#{name} (#{version})"
47
47
  end
48
48
 
49
+ def package_name
50
+ "#{author}-#{name}-#{version}"
51
+ end
52
+
49
53
  def self.empty
50
54
  {"author" => "", "version" => "", "name" => ""}.to_json
51
55
  end
@@ -1,6 +1,7 @@
1
1
  require 'flintlock/metadata'
2
2
  require 'flintlock/logger'
3
3
  require 'flintlock/util'
4
+ require 'flintlock/runner'
4
5
  require 'open3'
5
6
  require 'fileutils'
6
7
  require 'logger'
@@ -15,6 +16,7 @@ module Flintlock
15
16
  class UnsupportedModuleURI < RuntimeError; end
16
17
  class ModuleDownloadError < RuntimeError; end
17
18
  class RunFailure < RuntimeError; end
19
+ class PackagingError < RuntimeError; end
18
20
 
19
21
  class Module
20
22
  attr_reader :uri, :metadata
@@ -22,33 +24,34 @@ module Flintlock
22
24
  def initialize(uri = nil, options={})
23
25
  # track temporary files and directories for deletion
24
26
  @tmpfiles = []
25
-
27
+
26
28
  # destroy tmp files on exit
27
29
  at_exit { handle_exit }
28
30
 
29
- @debug = !!options[:debug]
30
31
  @uri = uri || Dir.pwd
31
- @log = load_logger
32
- @root_dir = download_from_uri(@uri)
33
- @metadata = load_metadata
32
+ @log = Util.load_logger(!!options[:debug])
33
+ @runner = Runner.new(options)
34
+
35
+ @root_dir = detect_root_dir(download_from_uri(@uri))
36
+ @metadata = load_metadata(@root_dir)
34
37
 
35
38
  load_scripts!
36
39
  validate
37
40
 
38
41
  @env = load_env(@defaults_script)
39
-
40
42
  end
41
43
 
42
44
  def download_from_uri(uri)
43
- case URI.parse(uri).scheme
44
- when nil # no scheme == local file
45
- uri
45
+ case Util.get_uri_scheme(uri)
46
+ when nil, 'file' # no scheme == local file
47
+ handle_file(uri)
46
48
  when 'git'
47
49
  handle_git_uri(uri)
50
+ when 'svn'
51
+ handle_svn_uri(uri)
48
52
  when 'http', 'https'
49
- raise UnsupportedModuleURI.new(uri) if ! Util.supported_archive?(uri)
50
53
  # over these protocols, we're getting an archive
51
- handle_archive(handle_http_uri(uri))
54
+ handle_file(handle_http_uri(uri))
52
55
  else
53
56
  raise UnsupportedModuleURI, uri
54
57
  end
@@ -59,11 +62,22 @@ module Flintlock
59
62
  end
60
63
 
61
64
  def handle_git_uri(uri)
65
+ Util.depends_on 'git'
66
+
62
67
  root_dir = Dir.mktmpdir
63
68
  @tmpfiles << root_dir
64
- command = Shellwords.join(['git', 'clone', uri, root_dir])
65
- stdout, stderr, status = Open3.capture3(command)
66
- raise ModuleDownloadError, uri if status.exitstatus != 0
69
+ status = @runner.run(['git', 'clone', uri, root_dir])
70
+ raise ModuleDownloadError, uri if status != 0
71
+ root_dir
72
+ end
73
+
74
+ def handle_svn_uri(uri)
75
+ Util.depends_on 'svn'
76
+
77
+ root_dir = Dir.mktmpdir
78
+ @tmpfiles << root_dir
79
+ status = @runner.run(['svn', 'checkout', uri, root_dir])
80
+ raise ModuleDownloadError, uri if status != 0
67
81
  root_dir
68
82
  end
69
83
 
@@ -78,23 +92,32 @@ module Flintlock
78
92
  end
79
93
  end
80
94
  tmpfile
81
- rescue OpenURI::HTTPError
95
+ rescue OpenURI::HTTPError, OpenSSL::SSL::SSLError
82
96
  raise ModuleDownloadError, uri
83
97
  end
84
98
 
85
- def handle_archive(filename)
99
+ def handle_file(filename)
100
+ @log.debug("handling file '#{filename}'")
101
+ Util.depends_on 'tar'
102
+
86
103
  tmpdir = Dir.mktmpdir
87
104
  @tmpfiles << tmpdir
88
- case filename
89
- when /\.tar\.gz$/
105
+
106
+ mime = Util.mime_type(filename)
107
+ @log.debug("mime-type is '#{mime}'")
108
+
109
+ case mime
110
+ when 'application/x-directory'
111
+ return filename
112
+ when 'application/x-gzip'
90
113
  command = ['tar', 'xfz', filename, '-C', tmpdir]
91
- when /\.tar$/
114
+ when 'application/x-tar'
92
115
  command = ['tar', 'xf', filename, '-C', tmpdir]
93
116
  else
94
117
  raise UnsupportedModuleURI, filename
95
118
  end
96
- _, _, status = Open3.capture3(Shellwords.join(command))
97
- raise ModuleDownloadError if status.exitstatus != 0
119
+ status = @runner.run(command)
120
+ raise ModuleDownloadError, filename if status != 0
98
121
  tmpdir
99
122
  end
100
123
 
@@ -102,20 +125,29 @@ module Flintlock
102
125
  @metadata.full_name
103
126
  end
104
127
 
128
+ def package_name
129
+ @metadata.package_name
130
+ end
131
+
132
+ def self.stages
133
+ ['detect', 'prepare', 'stage', 'start', 'modify']
134
+ end
135
+
105
136
  def self.script_names
106
- ['defaults', 'modify', 'prepare', 'stage', 'start', 'stop']
137
+ ['defaults', *Module.stages, 'stop']
107
138
  end
108
139
 
109
140
  def scripts
110
- [@modify_script, @prepare_script, @stage_script, @start_script, @stop_script, @defaults_script]
141
+ [@modify_script, @prepare_script, @stage_script, @start_script, @stop_script, @defaults_script, @detect_script]
111
142
  end
112
143
 
113
- def scripts_exist?
114
- scripts.map { |x| File.file?(x) }.all?
144
+ def valid?
145
+ @metadata.valid?
115
146
  end
116
147
 
117
- def valid?
118
- @metadata.valid? && scripts_exist?
148
+ def detect
149
+ @log.info("running detect stage: #{@detect_script}")
150
+ run_script(@detect_script)
119
151
  end
120
152
 
121
153
  def prepare
@@ -149,7 +181,7 @@ module Flintlock
149
181
 
150
182
  def load_env(defaults_script)
151
183
  # hokey, but seems to work
152
- env_data = %x{set -a && source #{defaults_script} && env}.split.map{ |x| x.split('=', 2) }
184
+ env_data = %x{set -a && source #{defaults_script} && env}.split("\n").map{ |x| x.split('=', 2) }
153
185
  env = Hash[env_data]
154
186
  @log.debug("defaults script is #{defaults_script}")
155
187
  @log.debug("defaults env is #{env.inspect}")
@@ -158,11 +190,34 @@ module Flintlock
158
190
  env
159
191
  end
160
192
 
193
+ def defaults
194
+ Hash[@env.to_a - ENV.to_a]
195
+ end
196
+
161
197
  def create_app_dir(app_dir)
162
198
  FileUtils.mkdir_p(app_dir)
163
199
  raise if ! Util.empty_directory?(app_dir)
164
200
  end
165
201
 
202
+ def self.package(directory, options={})
203
+ Util.depends_on 'tar'
204
+
205
+ mod = Module.new(directory, options)
206
+ archive = mod.package_name + '.tar.gz'
207
+
208
+ if Util.path_split(directory).length > 1
209
+ change_to = File.dirname(directory)
210
+ archive_path = File.basename(directory)
211
+ else
212
+ change_to = '.'
213
+ archive_path = directory
214
+ end
215
+
216
+ status = Runner.new(options).run(['tar', 'cfz', archive, '-C', change_to, archive_path])
217
+ raise PackagingError.new(directory) if status != 0
218
+ archive
219
+ end
220
+
166
221
  private
167
222
 
168
223
  def load_scripts!
@@ -172,36 +227,42 @@ module Flintlock
172
227
  end
173
228
 
174
229
  def validate
230
+ @log.debug('validating module')
175
231
  raise InvalidModule.new(@uri) if ! valid?
176
232
  end
177
233
 
178
- def load_logger
179
- log = Logger.new(STDOUT)
180
- log.silence! if ! @debug
181
- log
182
- end
183
-
184
- def load_metadata
234
+ def load_metadata(root_dir)
235
+ @log.debug('loading module metadata')
185
236
  begin
186
- Metadata.new(File.join(@root_dir, Metadata.filename))
237
+ Metadata.new(File.join(root_dir, Metadata.filename))
187
238
  rescue Errno::ENOENT
188
239
  raise InvalidModule, uri
189
240
  end
190
241
  end
191
242
 
192
- def run(command)
193
- handle_run(*Open3.capture3(@env, command))
243
+ def empty_script?(script)
244
+ File.read(script).strip.empty?
245
+ end
246
+
247
+ def skip_script?(script)
248
+ skip = ! File.file?(script) || empty_script?(script)
249
+ @log.debug("skipping '#{script}'") if skip
250
+ skip
194
251
  end
195
252
 
196
253
  def run_script(script, *args)
197
- run(Shellwords.join([script, *args]))
254
+ return if skip_script?(script)
255
+ command = [*Util.detect_runtime(script), script, *args].compact
256
+ status = @runner.run(command, :env => @env)
257
+ raise RunFailure if status != 0
198
258
  end
199
259
 
200
- def handle_run(stdout, stderr, status)
201
- stdout.lines.each { |x| @log.info(x) }
202
- if status.exitstatus != 0
203
- stderr.lines.each { |x| @log.error(x) }
204
- raise RunFailure
260
+ def detect_root_dir(directory)
261
+ contents = Dir[File.join(directory, '*')]
262
+ if contents.length == 1 && File.directory?(contents[0])
263
+ contents[0]
264
+ else
265
+ directory
205
266
  end
206
267
  end
207
268
  end
@@ -0,0 +1,34 @@
1
+ require 'open3'
2
+
3
+ module Flintlock
4
+ class Runner
5
+ def initialize(options={})
6
+ @log = Util.load_logger(!!options[:debug])
7
+ end
8
+
9
+ def run(command, options={})
10
+ @log.debug("running command: '#{command.inspect}'")
11
+
12
+ options[:capture] = !!options[:capture]
13
+ options[:env] = options.fetch(:env, ENV)
14
+
15
+ rout, wout = IO.pipe
16
+ rerr, werr = IO.pipe
17
+ pid = Process.spawn(options[:env], Shellwords.join(command), :out => wout, :err => werr)
18
+
19
+ Process.wait(pid)
20
+ status = $?.exitstatus
21
+
22
+ # capture stdout/stderr
23
+ wout.close
24
+ werr.close
25
+ stdout = rout.read
26
+ stderr = rerr.read
27
+
28
+ @log.linewise(stdout, :level => :info)
29
+ @log.linewise(stderr)
30
+
31
+ return options[:capture] ? [stdout, stderr, status] : status
32
+ end
33
+ end
34
+ end
@@ -1,27 +1,63 @@
1
+ require 'flintlock/logger'
2
+
1
3
  module Flintlock
4
+ class DependencyError < RuntimeError; end
5
+
2
6
  class Util
3
7
  def self.empty_directory?(directory)
4
8
  Dir[File.join(directory, '*')].empty?
5
9
  end
6
10
 
7
- def self.supported_archives
8
- ['.tar.gz', '.tar']
9
- end
10
-
11
- def self.supported_archive?(filename)
12
- Util.supported_archives.include?(full_extname(filename))
13
- end
14
-
15
11
  def self.full_extname(filename)
16
12
  data = []
17
13
  current_filename = filename.dup
18
14
  while true
19
15
  ext = File.extname(current_filename)
20
- break if ext.empty?
16
+ break if ext.empty? || ext =~ /^\.\d+$/
21
17
  current_filename = current_filename.gsub(/#{ext}$/, '')
22
18
  data << ext
23
19
  end
24
20
  data.reverse.join
25
21
  end
22
+
23
+ def self.which(command)
24
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
25
+ exe = File.join(path, command)
26
+ return exe if File.executable?(exe)
27
+ end
28
+ nil
29
+ end
30
+
31
+ def self.get_uri_scheme(uri)
32
+ scheme = URI.parse(uri).scheme
33
+ return scheme.nil? ? nil : scheme.split('+', 0)[0]
34
+ end
35
+
36
+ def self.load_logger(debug = false)
37
+ log = Logger.new(STDOUT)
38
+ log.silence! if ! debug
39
+ log
40
+ end
41
+
42
+ def self.detect_runtime(script, default="/bin/sh")
43
+ raw = File.open(script, &:readline)[/^\s*#!\s*(.+)/, 1] || default
44
+ raw.split
45
+ rescue EOFError
46
+ [default]
47
+ end
48
+
49
+ def self.path_split(path)
50
+ path.split(File::SEPARATOR).select { |x| ! x.empty? }
51
+ end
52
+
53
+ def self.depends_on(what)
54
+ raise DependencyError.new(what) if which(what).nil?
55
+ end
56
+
57
+ def self.mime_type(filename)
58
+ depends_on 'file'
59
+ stdout, stderr, status = Runner.new.run(['file', '--mime-type', filename], :capture => true)
60
+ stdout.split(':')[1].strip
61
+ end
26
62
  end
27
63
  end
@@ -1,3 +1,3 @@
1
1
  module Flintlock
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
metadata CHANGED
@@ -1,89 +1,135 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: flintlock
3
- version: !ruby/object:Gem::Version
4
- version: 0.1.0
5
- prerelease:
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
6
11
  platform: ruby
7
- authors:
12
+ authors:
8
13
  - Jon McKenzie
9
14
  autorequire:
10
15
  bindir: bin
11
16
  cert_chain: []
12
- date: 2014-05-26 00:00:00.000000000 Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
17
+
18
+ date: 2014-06-03 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
15
22
  name: thor
16
- requirement: !ruby/object:Gem::Requirement
17
- none: false
18
- requirements:
19
- - - ! '>='
20
- - !ruby/object:Gem::Version
21
- version: '0'
22
- type: :runtime
23
23
  prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
25
  none: false
26
- requirements:
27
- - - ! '>='
28
- - !ruby/object:Gem::Version
29
- version: '0'
30
- - !ruby/object:Gem::Dependency
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
31
36
  name: json
32
- requirement: !ruby/object:Gem::Requirement
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
33
39
  none: false
34
- requirements:
35
- - - ! '>='
36
- - !ruby/object:Gem::Version
37
- version: '0'
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
38
47
  type: :runtime
48
+ version_requirements: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ name: rspec
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ type: :development
62
+ version_requirements: *id003
63
+ - !ruby/object:Gem::Dependency
64
+ name: fakefs
39
65
  prerelease: false
40
- version_requirements: !ruby/object:Gem::Requirement
66
+ requirement: &id004 !ruby/object:Gem::Requirement
41
67
  none: false
42
- requirements:
43
- - - ! '>='
44
- - !ruby/object:Gem::Version
45
- version: '0'
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ hash: 3
72
+ segments:
73
+ - 0
74
+ version: "0"
75
+ type: :development
76
+ version_requirements: *id004
46
77
  description: A simple application deployer inspired by Heroku's buildpacks
47
78
  email: jcmcken@gmail.com
48
- executables:
79
+ executables:
49
80
  - flintlock
50
81
  extensions: []
82
+
51
83
  extra_rdoc_files: []
52
- files:
84
+
85
+ files:
53
86
  - lib/flintlock.rb
54
87
  - lib/flintlock/module.rb
55
88
  - lib/flintlock/metadata.rb
56
89
  - lib/flintlock/util.rb
57
90
  - lib/flintlock/cli.rb
58
91
  - lib/flintlock/logger.rb
92
+ - lib/flintlock/runner.rb
59
93
  - lib/flintlock/version.rb
60
94
  - LICENSE
61
95
  - README.md
62
96
  - CHANGES.md
63
97
  - bin/flintlock
98
+ has_rdoc: true
64
99
  homepage: https://github.com/jcmcken/flintlock
65
- licenses:
100
+ licenses:
66
101
  - MIT
67
102
  post_install_message:
68
103
  rdoc_options: []
69
- require_paths:
104
+
105
+ require_paths:
70
106
  - lib
71
- required_ruby_version: !ruby/object:Gem::Requirement
107
+ required_ruby_version: !ruby/object:Gem::Requirement
72
108
  none: false
73
- requirements:
74
- - - ! '>='
75
- - !ruby/object:Gem::Version
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ hash: 53
113
+ segments:
114
+ - 1
115
+ - 9
116
+ - 3
76
117
  version: 1.9.3
77
- required_rubygems_version: !ruby/object:Gem::Requirement
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
119
  none: false
79
- requirements:
80
- - - ! '>='
81
- - !ruby/object:Gem::Version
82
- version: '0'
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ hash: 3
124
+ segments:
125
+ - 0
126
+ version: "0"
83
127
  requirements: []
128
+
84
129
  rubyforge_project:
85
- rubygems_version: 1.8.23
130
+ rubygems_version: 1.3.7
86
131
  signing_key:
87
132
  specification_version: 3
88
133
  summary: A simple application deployer
89
134
  test_files: []
135
+