hobo-inviqa 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -0
- data/Gemfile.lock +93 -0
- data/Guardfile +14 -0
- data/Hobofile +34 -0
- data/Rakefile +2 -0
- data/bin/hobo +23 -0
- data/features/deps.feature +43 -0
- data/features/hobo/basic.feature +30 -0
- data/features/hobo/help.feature +12 -0
- data/features/hobo/subcommands.feature +16 -0
- data/features/seed/plant.feature +64 -0
- data/features/step_definitions/seed.rb +11 -0
- data/features/support/env.rb +6 -0
- data/features/vm.feature +0 -0
- data/hobo.gemspec +37 -0
- data/lib/hobo.rb +44 -0
- data/lib/hobo/cli.rb +185 -0
- data/lib/hobo/config/file.rb +21 -0
- data/lib/hobo/error_handlers/debug.rb +9 -0
- data/lib/hobo/error_handlers/friendly.rb +52 -0
- data/lib/hobo/errors.rb +60 -0
- data/lib/hobo/help_formatter.rb +111 -0
- data/lib/hobo/helper/file_locator.rb +39 -0
- data/lib/hobo/helper/shell.rb +59 -0
- data/lib/hobo/lib/seed/project.rb +41 -0
- data/lib/hobo/lib/seed/replacer.rb +57 -0
- data/lib/hobo/lib/seed/seed.rb +43 -0
- data/lib/hobo/metadata.rb +28 -0
- data/lib/hobo/patches/rake.rb +56 -0
- data/lib/hobo/patches/slop.rb +22 -0
- data/lib/hobo/paths.rb +49 -0
- data/lib/hobo/tasks/debug.rb +22 -0
- data/lib/hobo/tasks/deps.rb +45 -0
- data/lib/hobo/tasks/seed.rb +43 -0
- data/lib/hobo/tasks/tools.rb +13 -0
- data/lib/hobo/tasks/vm.rb +49 -0
- data/lib/hobo/ui.rb +96 -0
- data/lib/hobo/util.rb +7 -0
- data/lib/hobo/version.rb +3 -0
- data/spec/hobo/cli_spec.rb +135 -0
- data/spec/hobo/config/file_spec.rb +48 -0
- data/spec/hobo/error_handlers/debug_spec.rb +10 -0
- data/spec/hobo/error_handlers/friendly_spec.rb +81 -0
- data/spec/hobo/error_spec.rb +0 -0
- data/spec/hobo/help_formatter_spec.rb +131 -0
- data/spec/hobo/helpers/file_locator_spec.rb +7 -0
- data/spec/hobo/helpers/shell_spec.rb +7 -0
- data/spec/hobo/lib/seed/project_spec.rb +83 -0
- data/spec/hobo/lib/seed/replacer_spec.rb +47 -0
- data/spec/hobo/lib/seed/seed_spec.rb +95 -0
- data/spec/hobo/metadata_spec.rb +46 -0
- data/spec/hobo/patches/rake_spec.rb +0 -0
- data/spec/hobo/paths_spec.rb +77 -0
- data/spec/hobo/ui_spec.rb +64 -0
- data/spec/spec_helper.rb +6 -0
- metadata +355 -0
data/lib/hobo/cli.rb
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
module Hobo
|
2
|
+
|
3
|
+
class Halt < Error
|
4
|
+
end
|
5
|
+
|
6
|
+
class Cli
|
7
|
+
attr_accessor :slop, :help_formatter
|
8
|
+
|
9
|
+
def initialize opts = {}
|
10
|
+
@opts = opts
|
11
|
+
@slop = opts[:slop] || Slop.new
|
12
|
+
@help_formatter = opts[:help] || Hobo::HelpFormatter.new(@slop)
|
13
|
+
@help_opts = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def start argv = ARGV
|
17
|
+
load_hobofile
|
18
|
+
|
19
|
+
tasks = structure_tasks Hobo::Metadata.metadata.keys
|
20
|
+
args = fix_args_with_equals argv
|
21
|
+
define_global_opts @slop
|
22
|
+
|
23
|
+
begin
|
24
|
+
# Parse out global args first
|
25
|
+
@slop.parse! args
|
26
|
+
opts = @slop.to_hash
|
27
|
+
@help_opts[:all] = opts[:all]
|
28
|
+
Hobo.ui.interactive = !(opts[:'non-interactive'] == true)
|
29
|
+
|
30
|
+
@slop.add_callback :empty do
|
31
|
+
show_help
|
32
|
+
end
|
33
|
+
|
34
|
+
# Necessary to make command level help work
|
35
|
+
args.push "--help" if @slop.help?
|
36
|
+
|
37
|
+
@help_formatter.command_map = define_tasks(tasks, @slop)
|
38
|
+
|
39
|
+
remaining = @slop.parse! args
|
40
|
+
raise Hobo::InvalidCommandOrOpt.new remaining.join(" "), self if remaining.size > 0
|
41
|
+
|
42
|
+
show_help if @slop.help?
|
43
|
+
rescue Halt
|
44
|
+
# NOP
|
45
|
+
end
|
46
|
+
|
47
|
+
return 0
|
48
|
+
end
|
49
|
+
|
50
|
+
def show_help(opts = {})
|
51
|
+
Hobo.ui.info @help_formatter.help(@help_opts.merge(opts))
|
52
|
+
halt
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def load_hobofile
|
58
|
+
if Hobo.in_project? && File.exists?(Hobo.hobofile_path)
|
59
|
+
load Hobo.hobofile_path
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def define_global_opts slop
|
64
|
+
slop.on '--debug', 'Enable debugging'
|
65
|
+
slop.on '-a', '--all', 'Show hidden commands'
|
66
|
+
slop.on '-h', '--help', 'Display help'
|
67
|
+
slop.on '--non-interactive', 'Run non-interactively. Defaults will be automaticall used where possible.'
|
68
|
+
|
69
|
+
slop.on '-v', '--version', 'Print version information' do
|
70
|
+
Hobo.ui.info "Hobo version #{Hobo::VERSION}"
|
71
|
+
halt
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def halt
|
76
|
+
raise Halt.new
|
77
|
+
end
|
78
|
+
|
79
|
+
def define_tasks structured_list, scope, stack = [], map = {}
|
80
|
+
structured_list.each do |k, v|
|
81
|
+
name = (stack + [k]).join(':')
|
82
|
+
new_stack = stack + [k]
|
83
|
+
map[name] = if v.size == 0
|
84
|
+
define_command(name, scope, new_stack)
|
85
|
+
else
|
86
|
+
define_namespace(name, scope, new_stack, v, map)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
return map
|
90
|
+
end
|
91
|
+
|
92
|
+
# Map rake namespace to a Slop command
|
93
|
+
def define_namespace name, scope, stack, subtasks, map
|
94
|
+
metadata = Hobo::Metadata.metadata[name]
|
95
|
+
hobo = self
|
96
|
+
new_scope = nil
|
97
|
+
|
98
|
+
scope.instance_eval do
|
99
|
+
new_scope = command stack.last do
|
100
|
+
|
101
|
+
description metadata[:desc]
|
102
|
+
long_description metadata[:long_desc]
|
103
|
+
hidden metadata[:hidden]
|
104
|
+
project_only metadata[:project_only]
|
105
|
+
|
106
|
+
# NOP; run runs help anyway
|
107
|
+
on '-h', '--help', 'Display help' do end
|
108
|
+
|
109
|
+
run do |opts, args|
|
110
|
+
hobo.show_help(target: name)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
define_tasks subtasks, new_scope, stack, map
|
116
|
+
return new_scope
|
117
|
+
end
|
118
|
+
|
119
|
+
# Map rake task to a Slop command
|
120
|
+
def define_command name, scope, stack
|
121
|
+
metadata = Hobo::Metadata.metadata[name]
|
122
|
+
hobo = self
|
123
|
+
new_scope = nil
|
124
|
+
|
125
|
+
scope.instance_eval do
|
126
|
+
new_scope = command stack.last do
|
127
|
+
task = Rake::Task[name]
|
128
|
+
|
129
|
+
description metadata[:desc]
|
130
|
+
long_description metadata[:long_desc]
|
131
|
+
arg_list task.arg_names
|
132
|
+
hidden metadata[:hidden]
|
133
|
+
project_only metadata[:project_only]
|
134
|
+
|
135
|
+
metadata[:opts].each do |opt|
|
136
|
+
on *opt
|
137
|
+
end if metadata[:opts]
|
138
|
+
|
139
|
+
on '-h', '--help', 'Display help' do
|
140
|
+
hobo.show_help(target: name)
|
141
|
+
end
|
142
|
+
|
143
|
+
run do |opts, args|
|
144
|
+
raise ::Hobo::ProjectOnlyError.new if opts.project_only && !Hobo.in_project?
|
145
|
+
task.opts = opts.to_hash
|
146
|
+
raise ::Hobo::MissingArgumentsError.new(name, args, hobo) if args && task.arg_names.length > args.length
|
147
|
+
task.invoke *args
|
148
|
+
args.pop(task.arg_names.size)
|
149
|
+
task.opts = nil
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
return new_scope
|
155
|
+
end
|
156
|
+
|
157
|
+
# Expand flat task list in to hierarchy (non-recursive)
|
158
|
+
def structure_tasks list
|
159
|
+
out = {}
|
160
|
+
list.each do |name|
|
161
|
+
ref = out
|
162
|
+
name = name.split(":")
|
163
|
+
name.each do |n|
|
164
|
+
ref[n] ||= {}
|
165
|
+
ref = ref[n]
|
166
|
+
end
|
167
|
+
end
|
168
|
+
out
|
169
|
+
end
|
170
|
+
|
171
|
+
# Slop badly handles assignment args passed as --arg=val
|
172
|
+
# This hack fixes that by making them --arg val
|
173
|
+
def fix_args_with_equals args
|
174
|
+
items = []
|
175
|
+
args.each do |item|
|
176
|
+
if item.match /^-.*\=/
|
177
|
+
item.split('=').each { |i| items.push i }
|
178
|
+
else
|
179
|
+
items.push item
|
180
|
+
end
|
181
|
+
end
|
182
|
+
return items
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Hobo
|
4
|
+
module Config
|
5
|
+
class File
|
6
|
+
def self.save(file, config)
|
7
|
+
dir = ::File.dirname file
|
8
|
+
FileUtils.mkdir_p dir unless ::File.exists? dir
|
9
|
+
::File.open(file, 'w+') do |f|
|
10
|
+
f.puts config.to_yaml
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.load(file)
|
15
|
+
config = ::File.exists?(file) ? YAML.load_file(file) : {}
|
16
|
+
raise "Invalid hobo configuration (#{file})" unless config
|
17
|
+
return config
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'tmpdir' # Needed for Dir.tmpdir
|
2
|
+
|
3
|
+
module Hobo
|
4
|
+
module ErrorHandlers
|
5
|
+
class Friendly
|
6
|
+
def handle error
|
7
|
+
log_file = File.join(Dir.tmpdir, 'hobo_error.log')
|
8
|
+
|
9
|
+
# Not possible to match Interrupt class unless we use class name as string for some reason!
|
10
|
+
case error.class.to_s
|
11
|
+
when "Interrupt"
|
12
|
+
Hobo.ui.warning "\n\nCaught Interrupt. Aborting\n"
|
13
|
+
return 1
|
14
|
+
when "Hobo::ExternalCommandError"
|
15
|
+
FileUtils.cp error.output.path, log_file
|
16
|
+
Hobo.ui.error <<-ERROR
|
17
|
+
|
18
|
+
The following external command appears to have failed (exit status #{error.exit_code}):
|
19
|
+
#{error.command}
|
20
|
+
|
21
|
+
The output of the command has been logged to #{log_file}
|
22
|
+
ERROR
|
23
|
+
return 3
|
24
|
+
when "Hobo::InvalidCommandOrOpt"
|
25
|
+
Hobo.ui.error "\n#{error.message}"
|
26
|
+
Hobo.ui.info error.cli.help_formatter.help if error.cli
|
27
|
+
return 4
|
28
|
+
when "Hobo::MissingArgumentsError"
|
29
|
+
Hobo.ui.error "\n#{error.message}"
|
30
|
+
Hobo.ui.info error.cli.help_formatter.help(target: error.command) if error.cli
|
31
|
+
return 5
|
32
|
+
when "Hobo::UserError"
|
33
|
+
Hobo.ui.error "\n#{error.message}\n"
|
34
|
+
return 6
|
35
|
+
when "Hobo::ProjectOnlyError"
|
36
|
+
Hobo.ui.error "\nHobo requires you to be in a project directory for this command!\n"
|
37
|
+
return 7
|
38
|
+
else
|
39
|
+
File.write(log_file, "(#{error.class}) #{error.message}\n\n#{error.backtrace.join("\n")}")
|
40
|
+
Hobo.ui.error <<-ERROR
|
41
|
+
|
42
|
+
An unexpected error has occured:
|
43
|
+
#{error.message}
|
44
|
+
|
45
|
+
The backtrace has been logged to #{log_file}
|
46
|
+
ERROR
|
47
|
+
return 128
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/hobo/errors.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
module Hobo
|
2
|
+
class Error < StandardError
|
3
|
+
end
|
4
|
+
|
5
|
+
class RubyVersionError < Error
|
6
|
+
def initialize
|
7
|
+
super("Ruby 1.9+ is required to run hobo")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class MissingDependencies < Error
|
12
|
+
def initialize deps
|
13
|
+
deps.map! { |dep| " - #{dep}"}
|
14
|
+
super("Hobo requires the following commands to be available on your path:\n\n" + deps.join("\n"))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class InvalidCommandOrOpt < Error
|
19
|
+
attr_accessor :command, :cli
|
20
|
+
def initialize command, cli = nil
|
21
|
+
@command = command
|
22
|
+
@cli = cli
|
23
|
+
super("Invalid command or option specified: '#{command}'")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class MissingArgumentsError < Error
|
28
|
+
attr_accessor :command, :cli
|
29
|
+
def initialize command, args, cli = nil
|
30
|
+
@command = command
|
31
|
+
@args = args
|
32
|
+
@cli = cli
|
33
|
+
super("Not enough arguments for #{command}")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class ExternalCommandError < Error
|
38
|
+
attr_accessor :command, :exit_code, :output
|
39
|
+
|
40
|
+
def initialize command, exit_code, output
|
41
|
+
@command = command
|
42
|
+
@exit_code = exit_code
|
43
|
+
@output = output
|
44
|
+
super("'#{command}' returned exit code #{exit_code}")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class UserError < Error
|
49
|
+
end
|
50
|
+
|
51
|
+
class ProjectOnlyError < Error
|
52
|
+
end
|
53
|
+
|
54
|
+
class NonInteractiveError < Error
|
55
|
+
def initialize question
|
56
|
+
@question = question
|
57
|
+
super("A task requested input from user but hobo is in non-interactive mode")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module Hobo
|
2
|
+
class HelpFormatter
|
3
|
+
attr_accessor :command_map
|
4
|
+
ALIGN_PAD = 4
|
5
|
+
|
6
|
+
def initialize global
|
7
|
+
@global = global
|
8
|
+
@command_map = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def help opts = {}
|
12
|
+
target = opts[:target] || nil
|
13
|
+
output = [""]
|
14
|
+
|
15
|
+
command = @command_map[target]
|
16
|
+
|
17
|
+
if !command
|
18
|
+
command_opts = []
|
19
|
+
commands = commands_for(@global, opts)
|
20
|
+
command = @global
|
21
|
+
else
|
22
|
+
command_opts = options_for(command)
|
23
|
+
commands = commands_for(command, opts)
|
24
|
+
end
|
25
|
+
|
26
|
+
global_opts = options_for(@global)
|
27
|
+
|
28
|
+
align_to = longest([global_opts, command_opts, commands]) + ALIGN_PAD
|
29
|
+
|
30
|
+
if command != @global
|
31
|
+
description = [ command.long_description, command.description ].compact.first
|
32
|
+
output.push Hobo.ui.color("#{description}\n", :description) if description
|
33
|
+
end
|
34
|
+
|
35
|
+
output.push section("Usage", [usage(command, target)])
|
36
|
+
output.push section("Global options", global_opts, align_to)
|
37
|
+
output.push section("Command options", command_opts, align_to)
|
38
|
+
output.push section("Commands", commands, align_to)
|
39
|
+
|
40
|
+
return output.compact.join("\n")
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def longest inputs
|
46
|
+
inputs.map do |input|
|
47
|
+
next unless input.is_a? Array
|
48
|
+
if input.size == 0
|
49
|
+
0
|
50
|
+
else
|
51
|
+
input.map(&:first).map(&:size).max
|
52
|
+
end
|
53
|
+
end.compact.max
|
54
|
+
end
|
55
|
+
|
56
|
+
def options_for source
|
57
|
+
heads = source.options.reject(&:tail?)
|
58
|
+
tails = (source.options - heads)
|
59
|
+
|
60
|
+
(heads + tails).map do |opt|
|
61
|
+
next if source != @global && opt.short == 'h'
|
62
|
+
line = padded(opt.short ? "-#{opt.short}," : "", 4)
|
63
|
+
|
64
|
+
if opt.long
|
65
|
+
value = opt.config[:argument] ? "#{opt.long.upcase}" : ""
|
66
|
+
value = "[#{value}]" if opt.accepts_optional_argument?
|
67
|
+
value = "=#{value}" unless value.empty?
|
68
|
+
|
69
|
+
line += "--#{opt.long}#{value}"
|
70
|
+
end
|
71
|
+
|
72
|
+
[Hobo.ui.color(line, :opt), opt.description]
|
73
|
+
end.compact
|
74
|
+
end
|
75
|
+
|
76
|
+
def commands_for source, opts = {}
|
77
|
+
source.commands.map do |name, command|
|
78
|
+
next if command.hidden && !opts[:all]
|
79
|
+
next unless command.hidden == false || command.description || opts[:all]
|
80
|
+
[Hobo.ui.color(name, :command), command.description]
|
81
|
+
end.compact
|
82
|
+
end
|
83
|
+
|
84
|
+
def usage source, command = nil
|
85
|
+
banner = source.banner
|
86
|
+
if banner.nil?
|
87
|
+
arg_list = (source.arg_list || []).map do |arg|
|
88
|
+
"<#{arg}>"
|
89
|
+
end
|
90
|
+
|
91
|
+
banner = "#{File.basename($0, '.*')}"
|
92
|
+
banner << " [command]" if source.commands.any? && command.nil?
|
93
|
+
banner << " #{command.split(':').join(' ')}" if command
|
94
|
+
banner << " #{arg_list.join(' ')}" if arg_list.size > 0
|
95
|
+
banner << " [options]"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def section title, contents, align_to = false
|
100
|
+
return nil if contents.empty?
|
101
|
+
output = Hobo.ui.color("#{title}:\n", :help_title)
|
102
|
+
output += contents.map do |line|
|
103
|
+
line.is_a?(String) ? " #{line}" : " #{padded(line[0], align_to)}#{line[1]}"
|
104
|
+
end.join("\n") + "\n"
|
105
|
+
end
|
106
|
+
|
107
|
+
def padded str, target
|
108
|
+
str + (' ' * (target - str.size))
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
3
|
+
module Hobo
|
4
|
+
module Helper
|
5
|
+
def locate(pattern, opts = {}, &block)
|
6
|
+
match = nil
|
7
|
+
|
8
|
+
Dir.chdir Hobo.project_path do
|
9
|
+
match = locate_git(pattern, false, &block)
|
10
|
+
match = locate_git(pattern, true, &block) if !match
|
11
|
+
end
|
12
|
+
|
13
|
+
return true if match
|
14
|
+
|
15
|
+
Hobo.ui.warning opts[:missing] if opts[:missing]
|
16
|
+
return false
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def locate_git pattern, others, &block
|
22
|
+
args = [ 'git', 'ls-files', pattern ]
|
23
|
+
args.push '-o' if others
|
24
|
+
output = Hobo::Helper.shell *args, :capture => true
|
25
|
+
path = output.split("\n")[0]
|
26
|
+
|
27
|
+
unless path.nil?
|
28
|
+
path.strip!
|
29
|
+
Dir.chdir File.dirname(path) do
|
30
|
+
yield path
|
31
|
+
end
|
32
|
+
|
33
|
+
return true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
include Hobo::Helper
|