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 +28 -0
- data/README.md +17 -8
- data/lib/flintlock.rb +1 -0
- data/lib/flintlock/cli.rb +54 -4
- data/lib/flintlock/logger.rb +5 -0
- data/lib/flintlock/metadata.rb +4 -0
- data/lib/flintlock/module.rb +105 -44
- data/lib/flintlock/runner.rb +34 -0
- data/lib/flintlock/util.rb +45 -9
- data/lib/flintlock/version.rb +1 -1
- metadata +91 -45
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
|
96
|
-
local directories, ``flintlock`` supports the following protocols/formats:
|
96
|
+
Currently ``flintlock`` can install modules from a number of sources:
|
97
97
|
|
98
|
-
*
|
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
|
134
|
-
|
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
|
147
|
-
metadata are merely used to namespace
|
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
|
-
|
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
|
data/lib/flintlock.rb
CHANGED
data/lib/flintlock/cli.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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)
|
data/lib/flintlock/logger.rb
CHANGED
data/lib/flintlock/metadata.rb
CHANGED
data/lib/flintlock/module.rb
CHANGED
@@ -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
|
-
@
|
33
|
-
|
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
|
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
|
-
|
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
|
-
|
65
|
-
|
66
|
-
|
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
|
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
|
-
|
89
|
-
|
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
|
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
|
-
|
97
|
-
raise ModuleDownloadError if status
|
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',
|
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
|
114
|
-
|
144
|
+
def valid?
|
145
|
+
@metadata.valid?
|
115
146
|
end
|
116
147
|
|
117
|
-
def
|
118
|
-
@
|
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
|
179
|
-
log
|
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(
|
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
|
193
|
-
|
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
|
-
|
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
|
201
|
-
|
202
|
-
if
|
203
|
-
|
204
|
-
|
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
|
data/lib/flintlock/util.rb
CHANGED
@@ -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
|
data/lib/flintlock/version.rb
CHANGED
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
|
-
|
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
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
25
|
none: false
|
26
|
-
requirements:
|
27
|
-
- -
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
|
30
|
-
|
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
|
-
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
33
39
|
none: false
|
34
|
-
requirements:
|
35
|
-
- -
|
36
|
-
- !ruby/object:Gem::Version
|
37
|
-
|
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
|
-
|
66
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
41
67
|
none: false
|
42
|
-
requirements:
|
43
|
-
- -
|
44
|
-
- !ruby/object:Gem::Version
|
45
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
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
|
+
|