flintlock 0.1.0 → 0.2.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/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
+