hobo-inviqa 0.0.7 → 0.0.8
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.
- checksums.yaml +15 -0
- data/.editorconfig +10 -0
- data/Gemfile.lock +19 -4
- data/Guardfile +2 -2
- data/Hobofile +5 -1
- data/README.md +8 -32
- data/bin/hobo +12 -18
- data/hobo.gemspec +3 -0
- data/lib/hobo.rb +8 -1
- data/lib/hobo/cli.rb +14 -3
- data/lib/hobo/error_handlers/debug.rb +5 -2
- data/lib/hobo/error_handlers/exit_code_map.rb +16 -0
- data/lib/hobo/error_handlers/friendly.rb +8 -8
- data/lib/hobo/errors.rb +11 -1
- data/lib/hobo/helper/http_download.rb +41 -0
- data/lib/hobo/helper/shell.rb +3 -2
- data/lib/hobo/helper/vm_command.rb +235 -14
- data/lib/hobo/lib/host_check.rb +20 -6
- data/lib/hobo/lib/host_check/deps.rb +22 -4
- data/lib/hobo/lib/host_check/git.rb +41 -17
- data/lib/hobo/lib/host_check/ruby.rb +30 -20
- data/lib/hobo/lib/host_check/vagrant.rb +37 -6
- data/lib/hobo/lib/s3sync.rb +22 -44
- data/lib/hobo/lib/seed/project.rb +10 -6
- data/lib/hobo/patches/slop.rb +21 -2
- data/lib/hobo/tasks/assets.rb +12 -15
- data/lib/hobo/tasks/config.rb +15 -0
- data/lib/hobo/tasks/deps.rb +37 -6
- data/lib/hobo/tasks/system.rb +15 -0
- data/lib/hobo/tasks/system/completions.rb +76 -0
- data/lib/hobo/tasks/tools.rb +10 -6
- data/lib/hobo/tasks/vm.rb +64 -11
- data/lib/hobo/ui.rb +27 -10
- data/lib/hobo/util.rb +36 -2
- data/lib/hobo/version.rb +2 -2
- data/spec/hobo/asset_applicator_spec.rb +2 -2
- data/spec/hobo/cli_spec.rb +40 -24
- data/spec/hobo/config/file_spec.rb +1 -3
- data/spec/hobo/error_handlers/debug_spec.rb +39 -5
- data/spec/hobo/error_handlers/friendly_spec.rb +38 -21
- data/spec/hobo/help_formatter_spec.rb +3 -3
- data/spec/hobo/helpers/file_locator_spec.rb +2 -2
- data/spec/hobo/helpers/shell_spec.rb +2 -2
- data/spec/hobo/helpers/vm_command_spec.rb +54 -21
- data/spec/hobo/lib/s3sync_spec.rb +6 -3
- data/spec/hobo/lib/seed/project_spec.rb +2 -3
- data/spec/hobo/lib/seed/replacer_spec.rb +1 -2
- data/spec/hobo/lib/seed/seed_spec.rb +2 -3
- data/spec/hobo/logging_spec.rb +2 -2
- data/spec/hobo/metadata_spec.rb +2 -2
- data/spec/hobo/null_spec.rb +2 -2
- data/spec/hobo/paths_spec.rb +1 -2
- data/spec/hobo/ui_spec.rb +104 -20
- data/spec/hobo/util_spec.rb +75 -0
- data/spec/spec_helper.rb +1 -0
- metadata +55 -46
- data/lib/hobo/tasks/host.rb +0 -19
data/lib/hobo/errors.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module Hobo
|
2
2
|
class Error < StandardError
|
3
|
+
attr_reader :exit_code
|
3
4
|
end
|
4
5
|
|
5
6
|
class RubyVersionError < Error
|
@@ -64,4 +65,13 @@ module Hobo
|
|
64
65
|
super("A tool that hobo depends on could not be detected (#{dep})")
|
65
66
|
end
|
66
67
|
end
|
67
|
-
|
68
|
+
|
69
|
+
class HostCheckError < Error
|
70
|
+
attr_accessor :summary, :advice
|
71
|
+
def initialize summary, advice
|
72
|
+
@summary = summary
|
73
|
+
@advice = advice
|
74
|
+
super("Host check failed: #{summary}")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Hobo
|
5
|
+
module Helper
|
6
|
+
def http_download url, target_file, opts = {}
|
7
|
+
opts = { :progress => Hobo.method(:progress) }.merge(opts)
|
8
|
+
uri = URI.parse(url)
|
9
|
+
size = 0
|
10
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
11
|
+
http.use_ssl = uri.scheme == 'https'
|
12
|
+
|
13
|
+
# TODO: May want to verify SSL validity...
|
14
|
+
# http://notetoself.vrensk.com/2008/09/verified-https-in-ruby/#comment-22252
|
15
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
16
|
+
|
17
|
+
http.start do
|
18
|
+
begin
|
19
|
+
file = open(target_file, 'wb+')
|
20
|
+
http.request_get(uri.path) do |response|
|
21
|
+
size = response.content_length
|
22
|
+
response.read_body do |chunk|
|
23
|
+
file.write(chunk)
|
24
|
+
opts[:progress].call(
|
25
|
+
target_file,
|
26
|
+
chunk.length,
|
27
|
+
size,
|
28
|
+
:update
|
29
|
+
) if opts[:progress]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
ensure
|
33
|
+
opts[:progress].call(target_file, 0, size, :finsh) if opts[:progress]
|
34
|
+
file.close
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
include Hobo::Helper
|
data/lib/hobo/helper/shell.rb
CHANGED
@@ -39,7 +39,8 @@ module Hobo
|
|
39
39
|
:capture => false,
|
40
40
|
:indent => 0,
|
41
41
|
:realtime => false,
|
42
|
-
:env => {}
|
42
|
+
:env => {},
|
43
|
+
:ignore_errors => false
|
43
44
|
}.merge! opts
|
44
45
|
|
45
46
|
Hobo::Logging.logger.debug("helper.shell: Invoking '#{args.join(" ")}' with #{opts.to_s}")
|
@@ -71,7 +72,7 @@ module Hobo
|
|
71
72
|
buffer.fsync
|
72
73
|
buffer.rewind
|
73
74
|
|
74
|
-
raise ::Hobo::ExternalCommandError.new(args.join(" "), external.value.exitstatus, buffer) if external.value.exitstatus != 0
|
75
|
+
raise ::Hobo::ExternalCommandError.new(args.join(" "), external.value.exitstatus, buffer) if external.value.exitstatus != 0 && !opts[:ignore_errors]
|
75
76
|
|
76
77
|
return opts[:capture] ? buffer.read.strip : nil
|
77
78
|
end
|
@@ -1,20 +1,69 @@
|
|
1
|
-
require '
|
1
|
+
require 'tempfile'
|
2
|
+
require 'net/ssh/simple'
|
2
3
|
|
3
4
|
module Hobo
|
4
5
|
module Helper
|
6
|
+
attr_accessor :vm_project_mount_path
|
7
|
+
|
8
|
+
# Really expensive method to auto-detect where root project mount exists in VM.
|
9
|
+
# It assumes that the project root is directly mounted.
|
10
|
+
# We cache it in configuration once we find it so it only has to happen once.
|
11
|
+
def vm_project_mount_path
|
12
|
+
configured_path = maybe(Hobo.project_config.vm.project_mount_path)
|
13
|
+
return configured_path if configured_path
|
14
|
+
return @vm_project_mount_path if @vm_project_mount_path
|
15
|
+
|
16
|
+
tmp = Tempfile.new('vm_command_locator', Hobo.project_path)
|
17
|
+
tmp.write(Hobo.project_path)
|
18
|
+
|
19
|
+
locator_file = File.basename(tmp.path)
|
20
|
+
|
21
|
+
pattern = OS.windows? ? 'vboxsf' : Hobo.project_path.shellescape
|
22
|
+
|
23
|
+
# TODO genericise the command escaping from lib/hobo/patches/slop.rb to avoid nested shell escaping hell
|
24
|
+
sed = 's/.* on \(.*\) type.*/\\\1\\\/%%/g'.gsub('%%', locator_file)
|
25
|
+
locator_results = vm_shell(
|
26
|
+
"mount | grep #{pattern} | sed -e\"#{sed}\" | xargs md5sum",
|
27
|
+
:capture => true,
|
28
|
+
:pwd => '/',
|
29
|
+
:psuedo_tty => false,
|
30
|
+
:ignore_errors => true
|
31
|
+
)
|
32
|
+
|
33
|
+
tmp.close
|
34
|
+
|
35
|
+
match = locator_results.match(/^([a-z0-9]{32})\s+(.*)$/)
|
36
|
+
|
37
|
+
raise Exception.new("Unable to locate project mount point in VM") if !match
|
38
|
+
|
39
|
+
@vm_project_mount_path = File.dirname(match[2])
|
40
|
+
|
41
|
+
# Stash it in config
|
42
|
+
Hobo.project_config[:vm] ||= {}
|
43
|
+
Hobo.project_config[:vm][:project_mount_path] = @vm_project_mount_path
|
44
|
+
Hobo::Config::File.save(Hobo.project_config_file, Hobo.project_config)
|
45
|
+
|
46
|
+
return @vm_project_mount_path
|
47
|
+
end
|
48
|
+
|
5
49
|
def vm_shell command, opts = {}
|
6
|
-
shell VmCommand.new(command).to_s, opts
|
50
|
+
shell VmCommand.new(command, opts).to_s, opts
|
7
51
|
end
|
8
52
|
|
9
53
|
def vm_mysql opts = {}
|
10
54
|
opts = {
|
11
55
|
:auto_echo => true,
|
12
56
|
:db => "",
|
13
|
-
:user => maybe(Hobo.project_config.mysql.username) || "
|
14
|
-
:pass => maybe(Hobo.project_config.mysql.password) || "
|
57
|
+
:user => maybe(Hobo.project_config.mysql.username) || "",
|
58
|
+
:pass => maybe(Hobo.project_config.mysql.password) || "",
|
59
|
+
:mysql => 'mysql'
|
15
60
|
}.merge(opts)
|
16
61
|
|
17
|
-
|
62
|
+
opts[:user] = "-u#{opts[:user].shellescape}" unless opts[:user].empty?
|
63
|
+
opts[:pass] = "-p#{opts[:pass].shellescape}" unless opts[:pass].empty?
|
64
|
+
opts[:db] = opts[:db].shellescape unless opts[:db].empty?
|
65
|
+
|
66
|
+
VmCommand.new "#{opts[:mysql]} #{opts[:user]} #{opts[:pass]} #{opts[:db]}".strip, opts
|
18
67
|
end
|
19
68
|
|
20
69
|
def vm_command command = nil, opts = {}
|
@@ -23,17 +72,89 @@ module Hobo
|
|
23
72
|
|
24
73
|
private
|
25
74
|
|
75
|
+
class VmInspector
|
76
|
+
attr_accessor :ssh_config, :project_mount_path
|
77
|
+
|
78
|
+
def project_mount_path
|
79
|
+
#configured_path = maybe(Hobo.project_config.vm.project_mount_path)
|
80
|
+
#return configured_path if configured_path
|
81
|
+
#return @project_mount_path if @project_mount_path
|
82
|
+
|
83
|
+
tmp = Tempfile.new('vm_command_locator', Hobo.project_path)
|
84
|
+
|
85
|
+
begin
|
86
|
+
tmp.write(Hobo.project_path)
|
87
|
+
|
88
|
+
locator_file = File.basename(tmp.path)
|
89
|
+
|
90
|
+
pattern = OS.windows? ? 'vboxsf' : Hobo.project_path.shellescape
|
91
|
+
|
92
|
+
sed = 's/.* on \(.*\) type.*/\1\/%%/g'.gsub('%%', locator_file)
|
93
|
+
locator_results = VmCommand.new(
|
94
|
+
"mount | grep #{pattern} | sed -e\"#{sed}\" | xargs md5sum",
|
95
|
+
:capture => true,
|
96
|
+
:pwd => '/'
|
97
|
+
).run
|
98
|
+
ensure
|
99
|
+
tmp.unlink
|
100
|
+
end
|
101
|
+
|
102
|
+
match = locator_results.match(/^([a-z0-9]{32})\s+(.*)$/)
|
103
|
+
|
104
|
+
raise Exception.new("Unable to locate project mount point in VM") if !match
|
105
|
+
|
106
|
+
@vm_project_mount_path = File.dirname(match[2])
|
107
|
+
|
108
|
+
# Stash it in config
|
109
|
+
Hobo.project_config[:vm] ||= {}
|
110
|
+
Hobo.project_config[:vm][:project_mount_path] = @vm_project_mount_path
|
111
|
+
Hobo::Config::File.save(Hobo.project_config_file, Hobo.project_config)
|
112
|
+
|
113
|
+
return @vm_project_mount_path
|
114
|
+
end
|
115
|
+
|
116
|
+
def ssh_config
|
117
|
+
return @ssh_config if @ssh_config
|
118
|
+
config = nil
|
119
|
+
locate "*Vagrantfile" do
|
120
|
+
config = bundle_shell "vagrant ssh-config", :capture => true
|
121
|
+
end
|
122
|
+
|
123
|
+
raise Exception.new "Could not retrieve VM ssh configuration" unless config
|
124
|
+
|
125
|
+
patterns = {
|
126
|
+
:ssh_user => /^\s*User (.*)$/,
|
127
|
+
:ssh_identity => /^\s*IdentityFile (.*)$/,
|
128
|
+
:ssh_host => /^\s*HostName (.*)$/,
|
129
|
+
:ssh_port => /^\s*Port (\d+)/
|
130
|
+
}
|
131
|
+
|
132
|
+
output = {}
|
133
|
+
|
134
|
+
patterns.each do |k, pattern|
|
135
|
+
match = config.match(pattern)
|
136
|
+
output[k] = match[1] if match
|
137
|
+
end
|
138
|
+
|
139
|
+
return @ssh_config = output
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
26
143
|
class VmCommand
|
144
|
+
class << self
|
145
|
+
attr_accessor :vm_inspector
|
146
|
+
@@vm_inspector = VmInspector.new
|
147
|
+
end
|
148
|
+
|
27
149
|
attr_accessor :opts, :command
|
28
150
|
|
29
151
|
def initialize command, opts = {}
|
30
152
|
@command = command
|
31
153
|
@opts = {
|
32
154
|
:auto_echo => false,
|
33
|
-
:psuedo_tty =>
|
34
|
-
:
|
35
|
-
:
|
36
|
-
:ssh_host => maybe(Hobo.project_config.hostname) || ""
|
155
|
+
:psuedo_tty => false,
|
156
|
+
:pwd => opts[:pwd] || @@vm_inspector.project_mount_path,
|
157
|
+
:append => ''
|
37
158
|
}.merge(opts)
|
38
159
|
end
|
39
160
|
|
@@ -44,12 +165,84 @@ module Hobo
|
|
44
165
|
return self
|
45
166
|
end
|
46
167
|
|
168
|
+
def < pipe
|
169
|
+
pipe = "echo '#{pipe.shellescape}'" if opts[:auto_echo]
|
170
|
+
@pipe_in_vm = pipe
|
171
|
+
@opts[:psuedo_tty] = false
|
172
|
+
return self
|
173
|
+
end
|
174
|
+
|
175
|
+
# TODO Refactor in to ssh helper with similar opts to shell helper
|
176
|
+
# TODO Migrate all vm_shell functionality this direction
|
177
|
+
def run
|
178
|
+
return if @command.nil?
|
179
|
+
opts = @@vm_inspector.ssh_config.merge(@opts)
|
180
|
+
|
181
|
+
Net::SSH::Simple.sync do
|
182
|
+
ssh_opts = {
|
183
|
+
:user => opts[:ssh_user],
|
184
|
+
:port => opts[:ssh_port],
|
185
|
+
:forward_agent => true,
|
186
|
+
:global_known_hosts_file => "/dev/null",
|
187
|
+
:paranoid => false,
|
188
|
+
:user_known_hosts_file => "/dev/null"
|
189
|
+
}
|
190
|
+
|
191
|
+
ssh_opts[:keys] = [opts[:ssh_identity]] if opts[:ssh_identity]
|
192
|
+
|
193
|
+
tmp = Tempfile.new "vm_command_exec"
|
194
|
+
|
195
|
+
begin
|
196
|
+
filename = File.basename(tmp.path)
|
197
|
+
remote_file = "/tmp/#{filename}"
|
198
|
+
tmp.write "#{@command}#{opts[:append]}"
|
199
|
+
tmp.close
|
200
|
+
|
201
|
+
scp_put opts[:ssh_host], tmp.path, remote_file, ssh_opts
|
202
|
+
result = ssh opts[:ssh_host], "cd #{opts[:pwd]}; exec /bin/bash #{remote_file}", ssh_opts
|
203
|
+
ssh opts[:ssh_host], "rm #{remote_file}", ssh_opts
|
204
|
+
|
205
|
+
# Throw exception if exit code not 0
|
206
|
+
|
207
|
+
return opts[:capture] ? result.stdout : result.success
|
208
|
+
ensure
|
209
|
+
tmp.unlink
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# TODO Speed up Vagrant SSH connections
|
215
|
+
# May need to be disabled for windows (mm_send_fd: UsePrivilegeSeparation=yes not supported)
|
216
|
+
# https://gist.github.com/jedi4ever/5657094
|
217
|
+
|
47
218
|
def to_s
|
48
|
-
|
49
|
-
|
50
|
-
|
219
|
+
opts = @@vm_inspector.ssh_config.merge(@opts)
|
220
|
+
|
221
|
+
psuedo_tty = opts[:psuedo_tty] ? "-t" : ""
|
222
|
+
|
223
|
+
ssh_command = [
|
224
|
+
"ssh",
|
225
|
+
"-o 'UserKnownHostsFile /dev/null'",
|
226
|
+
"-o 'StrictHostKeyChecking no'",
|
227
|
+
"-o 'ForwardAgent yes'",
|
228
|
+
"-o 'LogLevel FATAL'",
|
229
|
+
"-p #{opts[:ssh_port]}",
|
230
|
+
"-i #{opts[:ssh_identity].shellescape}",
|
231
|
+
psuedo_tty,
|
232
|
+
"#{opts[:ssh_user].shellescape}@#{opts[:ssh_host].shellescape}"
|
233
|
+
].join(" ")
|
234
|
+
|
235
|
+
pwd_set_command = " -- \"cd #{@opts[:pwd].shellescape}; exec /bin/bash"
|
236
|
+
|
237
|
+
vm_command = [
|
238
|
+
@pipe_in_vm,
|
51
239
|
@command
|
52
|
-
].compact.join("
|
240
|
+
].compact.join(" | ")
|
241
|
+
|
242
|
+
command = [
|
243
|
+
ssh_command + pwd_set_command,
|
244
|
+
vm_command.empty? ? nil : vm_command.shellescape
|
245
|
+
].compact.join(" -c ") + "#{opts[:append].shellescape}\""
|
53
246
|
|
54
247
|
[
|
55
248
|
@pipe,
|
@@ -60,8 +253,36 @@ module Hobo
|
|
60
253
|
def to_str
|
61
254
|
to_s
|
62
255
|
end
|
256
|
+
|
257
|
+
private
|
258
|
+
|
259
|
+
def vagrant_config
|
260
|
+
return @@vagrant_config if @@vagrant_config
|
261
|
+
config = nil
|
262
|
+
locate "*Vagrantfile" do
|
263
|
+
config = bundle_shell "vagrant ssh-config", :capture => true
|
264
|
+
end
|
265
|
+
|
266
|
+
raise Exception.new "Could not retrieve VM ssh configuration" unless config
|
267
|
+
|
268
|
+
patterns = {
|
269
|
+
:ssh_user => /^\s+User (.*)$/,
|
270
|
+
:ssh_identity => /^\s+IdentityFile (.*)$/,
|
271
|
+
:ssh_host => /^\s+HostName (.*)$/,
|
272
|
+
:ssh_port => /^\s+Port (\d+)/
|
273
|
+
}
|
274
|
+
|
275
|
+
output = {}
|
276
|
+
|
277
|
+
patterns.each do |k, pattern|
|
278
|
+
match = config.match(pattern)
|
279
|
+
output[k] = match[1]
|
280
|
+
end
|
281
|
+
|
282
|
+
return @@vagrant_config = output
|
283
|
+
end
|
63
284
|
end
|
64
285
|
end
|
65
286
|
end
|
66
287
|
|
67
|
-
include Hobo::Helper
|
288
|
+
include Hobo::Helper
|
data/lib/hobo/lib/host_check.rb
CHANGED
@@ -4,20 +4,34 @@ module Hobo
|
|
4
4
|
class << self
|
5
5
|
include Hobo::Lib::HostCheck
|
6
6
|
|
7
|
-
def check
|
7
|
+
def check opts = {}
|
8
|
+
opts = {
|
9
|
+
:filter => nil,
|
10
|
+
:raise => false
|
11
|
+
}.merge(opts)
|
12
|
+
|
13
|
+
results = {}
|
8
14
|
methods = Hobo::Lib::HostCheck.public_instance_methods(false)
|
9
15
|
methods.each do |method|
|
16
|
+
next if opts[:filter] && !method.match(opts[:filter])
|
17
|
+
|
10
18
|
name = method.to_s.gsub('_', ' ')
|
11
19
|
name[0] = name[0].upcase
|
12
|
-
|
20
|
+
if opts[:raise]
|
13
21
|
self.send method
|
14
|
-
|
15
|
-
|
16
|
-
|
22
|
+
else
|
23
|
+
begin
|
24
|
+
self.send method
|
25
|
+
results[name] = :ok
|
26
|
+
rescue Hobo::Error => error
|
27
|
+
results[name] = error
|
28
|
+
end
|
17
29
|
end
|
18
30
|
end
|
31
|
+
|
32
|
+
return results
|
19
33
|
end
|
20
34
|
end
|
21
35
|
end
|
22
36
|
end
|
23
|
-
end
|
37
|
+
end
|
@@ -2,20 +2,38 @@ module Hobo
|
|
2
2
|
module Lib
|
3
3
|
module HostCheck
|
4
4
|
def ssh_present
|
5
|
+
advice = "The SSH command could not be located on your system.\n\n"
|
6
|
+
|
7
|
+
if OS.windows?
|
8
|
+
advice += "To make SSH available you must re-install git using the installer from http://git-scm.com/downloads ensuring you select the 'Use git and unix tools everywhere' option."
|
9
|
+
else
|
10
|
+
advice += "Please install openssh using your package manager."
|
11
|
+
end
|
12
|
+
|
5
13
|
begin
|
6
14
|
shell "ssh -V"
|
7
15
|
rescue Errno::ENOENT
|
8
|
-
raise Hobo::
|
16
|
+
raise Hobo::HostCheckError.new("SSH is missing", advice)
|
9
17
|
end
|
10
18
|
end
|
11
19
|
|
12
20
|
def php_present
|
21
|
+
advice = <<-EOF
|
22
|
+
The PHP command could not be located on your system.
|
23
|
+
|
24
|
+
This is an optional command that can speed up composer dependency installs.
|
25
|
+
|
26
|
+
Please install it from your package manager ensuring that the following command does not produce any errors:
|
27
|
+
|
28
|
+
php -r "readfile('https://getcomposer.org/installer');" | php
|
29
|
+
EOF
|
30
|
+
|
13
31
|
begin
|
14
|
-
shell "php
|
32
|
+
shell "php --version"
|
15
33
|
rescue Errno::ENOENT
|
16
|
-
raise Hobo::
|
34
|
+
raise Hobo::HostCheckError.new("PHP is missing", advice)
|
17
35
|
end
|
18
36
|
end
|
19
37
|
end
|
20
38
|
end
|
21
|
-
end
|
39
|
+
end
|