hobo-inviqa 0.0.4 → 0.0.6

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.
Files changed (55) hide show
  1. data/Gemfile.lock +27 -6
  2. data/README.md +1 -10
  3. data/bin/hobo +20 -0
  4. data/features/hobo/help.feature +5 -1
  5. data/hobo.gemspec +4 -0
  6. data/lib/hobo.rb +17 -14
  7. data/lib/hobo/asset_applicator.rb +16 -0
  8. data/lib/hobo/cli.rb +40 -20
  9. data/lib/hobo/config.rb +5 -0
  10. data/lib/hobo/config/file.rb +3 -1
  11. data/lib/hobo/errors.rb +8 -1
  12. data/lib/hobo/help_formatter.rb +8 -1
  13. data/lib/hobo/helper/file_locator.rb +8 -5
  14. data/lib/hobo/helper/shell.rb +12 -4
  15. data/lib/hobo/helper/vm_command.rb +67 -0
  16. data/lib/hobo/lib/host_check.rb +23 -0
  17. data/lib/hobo/lib/host_check/deps.rb +21 -0
  18. data/lib/hobo/lib/host_check/git.rb +52 -0
  19. data/lib/hobo/lib/host_check/ruby.rb +42 -0
  20. data/lib/hobo/lib/host_check/vagrant.rb +15 -0
  21. data/lib/hobo/lib/s3sync.rb +233 -0
  22. data/lib/hobo/lib/seed/project.rb +12 -0
  23. data/lib/hobo/lib/seed/seed.rb +19 -0
  24. data/lib/hobo/logging.rb +22 -0
  25. data/lib/hobo/metadata.rb +7 -1
  26. data/lib/hobo/null.rb +31 -0
  27. data/lib/hobo/patches/deepstruct.rb +23 -0
  28. data/lib/hobo/patches/rake.rb +14 -1
  29. data/lib/hobo/patches/slop.rb +11 -1
  30. data/lib/hobo/paths.rb +8 -3
  31. data/lib/hobo/tasks/assets.rb +96 -0
  32. data/lib/hobo/tasks/console.rb +18 -0
  33. data/lib/hobo/tasks/debug.rb +2 -2
  34. data/lib/hobo/tasks/deps.rb +1 -1
  35. data/lib/hobo/tasks/host.rb +17 -0
  36. data/lib/hobo/tasks/vm.rb +2 -2
  37. data/lib/hobo/ui.rb +21 -16
  38. data/lib/hobo/version.rb +1 -1
  39. data/spec/hobo/asset_applicator_spec.rb +31 -0
  40. data/spec/hobo/cli_spec.rb +115 -97
  41. data/spec/hobo/config/file_spec.rb +13 -3
  42. data/spec/hobo/help_formatter_spec.rb +50 -18
  43. data/spec/hobo/helpers/file_locator_spec.rb +5 -1
  44. data/spec/hobo/helpers/shell_spec.rb +15 -1
  45. data/spec/hobo/helpers/vm_command_spec.rb +85 -0
  46. data/spec/hobo/lib/s3sync_spec.rb +13 -0
  47. data/spec/hobo/lib/seed/project_spec.rb +7 -8
  48. data/spec/hobo/lib/seed/seed_spec.rb +3 -4
  49. data/spec/hobo/logging_spec.rb +28 -0
  50. data/spec/hobo/metadata_spec.rb +10 -0
  51. data/spec/hobo/null_spec.rb +31 -0
  52. data/spec/hobo/paths_spec.rb +6 -6
  53. data/spec/hobo/ui_spec.rb +4 -0
  54. metadata +93 -6
  55. data/features/vm.feature +0 -0
@@ -1,5 +1,9 @@
1
+ require 'open3'
2
+ require 'tempfile'
3
+
1
4
  module Hobo
2
5
  module Helper
6
+
3
7
  def bundle_shell *args, &block
4
8
  has_bundle = begin
5
9
  shell "bundle", "exec", "ruby -v"
@@ -20,11 +24,14 @@ module Hobo
20
24
  opts = {
21
25
  :capture => false,
22
26
  :indent => 0,
23
- :realtime => false
27
+ :realtime => false,
28
+ :env => {}
24
29
  }.merge! opts
25
30
 
31
+ Hobo::Logging.logger.debug("helper.shell: Invoking '#{args.join(" ")}' with #{opts.to_s}")
32
+
26
33
  indent = " " * opts[:indent]
27
- ::Open3.popen3 *args do |stdin, out, err, external|
34
+ ::Open3.popen3 opts[:env], *args do |stdin, out, err, external|
28
35
  buffer = ::Tempfile.new 'hobo_run_buf'
29
36
  buffer.sync = true
30
37
  threads = [external]
@@ -33,8 +40,9 @@ module Hobo
33
40
  { :out => out, :err => err }.each do |key, stream|
34
41
  threads.push(::Thread.new do
35
42
  until (line = stream.gets).nil? do
36
- line = ::Hobo.ui.color(line, :error) if key == :err
37
- buffer.write(line)
43
+ line = ::Hobo.ui.color(line.strip, :error) if key == :err
44
+ buffer.write("#{line.strip}\n")
45
+ Hobo::Logging.logger.debug("helper.shell: #{line.strip}")
38
46
  line = yield line if block
39
47
  puts indent + line if opts[:realtime] && !line.nil?
40
48
  end
@@ -0,0 +1,67 @@
1
+ require 'hobo/helper/shell'
2
+
3
+ module Hobo
4
+ module Helper
5
+ def vm_shell command
6
+ shell VmCommand.new(command).to_s
7
+ end
8
+
9
+ def vm_mysql opts = {}
10
+ opts = {
11
+ :auto_echo => true,
12
+ :db => "",
13
+ :user => maybe(Hobo.project_config.mysql.username) || "root",
14
+ :pass => maybe(Hobo.project_config.mysql.password) || "root"
15
+ }.merge(opts)
16
+
17
+ VmCommand.new "mysql -u#{opts[:user].shellescape} -p#{opts[:pass].shellescape} #{opts[:db].shellescape}", opts
18
+ end
19
+
20
+ def vm_command command = nil
21
+ VmCommand.new command
22
+ end
23
+
24
+ private
25
+
26
+ class VmCommand
27
+ attr_accessor :opts, :command
28
+
29
+ def initialize command, opts = {}
30
+ @command = command
31
+ @opts = {
32
+ :auto_echo => false,
33
+ :psuedo_tty => true,
34
+ :ssh_identity => "#{ENV['HOME'].shellescape}/.vagrant.d/insecure_private_key",
35
+ :ssh_user => "vagrant",
36
+ :ssh_host => maybe(Hobo.project_config.hostname) || ""
37
+ }.merge(opts)
38
+ end
39
+
40
+ def << pipe
41
+ pipe = "echo #{pipe.shellescape}" if opts[:auto_echo]
42
+ @pipe = pipe
43
+ @opts[:psuedo_tty] = false
44
+ return self
45
+ end
46
+
47
+ def to_s
48
+ psuedo_tty = @opts[:psuedo_tty] ? "-t" : ""
49
+ command = [
50
+ "ssh -i #{opts[:ssh_identity]} #{psuedo_tty} #{opts[:ssh_user].shellescape}@#{opts[:ssh_host].shellescape}",
51
+ @command
52
+ ].compact.join(" -- ")
53
+
54
+ [
55
+ @pipe,
56
+ command
57
+ ].compact.join(" | ")
58
+ end
59
+
60
+ def to_str
61
+ to_s
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ include Hobo::Helper
@@ -0,0 +1,23 @@
1
+ module Hobo
2
+ module Lib
3
+ module HostCheck
4
+ class << self
5
+ include Hobo::Lib::HostCheck
6
+
7
+ def check silent = true
8
+ methods = Hobo::Lib::HostCheck.public_instance_methods(false)
9
+ methods.each do |method|
10
+ name = method.to_s.gsub('_', ' ')
11
+ name[0] = name[0].upcase
12
+ begin
13
+ self.send method
14
+ Hobo.ui.success "#{name}: OK" unless silent
15
+ rescue
16
+ Hobo.ui.error "#{name}: FAILED" unless silent
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ module Hobo
2
+ module Lib
3
+ module HostCheck
4
+ def ssh_present
5
+ begin
6
+ shell "ssh -V"
7
+ rescue Errno::ENOENT
8
+ raise Hobo::MissingDependency.new("ssh")
9
+ end
10
+ end
11
+
12
+ def php_present
13
+ begin
14
+ shell "php -v"
15
+ rescue Errno::ENOENT
16
+ raise Hobo::MissingDependency.new("php")
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,52 @@
1
+ module Hobo
2
+ module Lib
3
+ module HostCheck
4
+ def git_present
5
+ begin
6
+ shell "git --version"
7
+ rescue Errno::ENOENT
8
+ raise Hobo::MissingDependency.new("ssh")
9
+ end
10
+ end
11
+
12
+ def git_config_name_set
13
+ begin
14
+ shell "git config user.name"
15
+ rescue Hobo::ExternalCommandError
16
+ Hobo.ui.error "You must provide git with your full name"
17
+ name = Hobo.ui.ask "Full name"
18
+ shell "git config --global user.name #{name.shellescape}"
19
+ end
20
+ end
21
+
22
+ def git_config_email_set
23
+ begin
24
+ shell "git config user.email"
25
+ rescue Hobo::ExternalCommandError
26
+ email = Hobo.ui.ask "Email address"
27
+ shell "git config --global user.email #{email.shellescape}"
28
+ end
29
+ end
30
+
31
+ def git_autocrlf_disabled
32
+ return true
33
+ begin
34
+ value = shell "git config core.autocrlf", :capture => true
35
+ if value != "false"
36
+ Hobo.ui.error "You're using git with autocrlf!"
37
+ Hobo.ui.error "This setting can cause problems executing scripts within VMs."
38
+ Hobo.ui.error "If you've had it enabled for a while, you'll need to check out all of your repositories again if you change it."
39
+ disable = Hobo.ui.ask "Would you like to disable this setting?", :default => true
40
+ if disable
41
+ shell "git config --global core.autocrlf false"
42
+ Hobo.ui.success "Disabled autocrlf\nYou can re-enable it by executing `git config --global core.autocrlf true"
43
+ end
44
+
45
+ end
46
+ rescue Hobo::ExternalCommandError
47
+ # NOP
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,42 @@
1
+ module Hobo
2
+ module Lib
3
+ module HostCheck
4
+ def not_using_system_ruby
5
+ return if Gem.win_platform?
6
+ which = shell "which ruby", :capture => true
7
+ unless which =~ /\.rbenc|\.rvm/
8
+ Hobo.ui.error "You're using a system ruby install! rbenv is HIGHLY recommended"
9
+ Hobo.ui.error "You can install it with the following command:\n"
10
+ Hobo.ui.error " curl https://raw.github.com/fesplugas/rbenv-installer/master/bin/rbenv-installer | bash\n"
11
+ Hobo.ui.error "Once installed, run the following:\n"
12
+ Hobo.ui.error " rbenv install 1.9.3-p448 && rbenv global 1.9.3-448 && gem install hobo-inviqa"
13
+ raise "System ruby in use"
14
+ end
15
+ end
16
+
17
+ def system_paths_for_ruby
18
+ return if Gem.win_platform?
19
+ paths = ENV['PATH'].split(':')
20
+ system_path_found = false
21
+ ruby_path_found = false
22
+ paths.each do |path|
23
+ system_before_ruby = system_path_found && !ruby_path_found
24
+ ruby_after_system = path =~ /\.rbenv|\.rvm/ && system_path_found
25
+ raise "Bad system paths" if system_before_ruby or ruby_after_system
26
+
27
+ ruby_path_found = true if path =~ /\.rbenv|\.rvm/
28
+ system_path_found = true if path =~ /\/usr\/bin|\/usr\/local\/bin/
29
+ end
30
+ end
31
+
32
+ def ruby_include_paths
33
+ paths = $:
34
+ bad_paths = paths.reject do |path|
35
+ path.match /\.rbenv|\.rvm/
36
+ end.compact.length > 0
37
+
38
+ raise "Bad gem paths" if bad_paths
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,15 @@
1
+ require 'semantic'
2
+
3
+ module Hobo
4
+ module Lib
5
+ module HostCheck
6
+ def vagrant_version
7
+ version = shell "vagrant --version", :capture => true
8
+ version.gsub!(/^Vagrant /, '')
9
+ version = ::Semantic::Version.new version
10
+ minimum_version = ::Semantic::Version.new "1.3.5"
11
+ raise "Vagrant too old" if version < minimum_version
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,233 @@
1
+ require 'aws-sdk'
2
+ require 'fileutils'
3
+ require 'ruby-progressbar'
4
+
5
+ module Hobo
6
+ module Lib
7
+ class S3Sync
8
+ include Hobo::Logging
9
+
10
+ def initialize key_id, secret
11
+ opts = {
12
+ :access_key_id => key_id,
13
+ :secret_access_key => secret,
14
+ :verify_response_body_content_length => false
15
+ }
16
+
17
+ logger.debug("s3sync: Options #{opts}")
18
+
19
+ @s3 = AWS::S3.new opts
20
+ end
21
+
22
+ def delta source, dest
23
+ to_add = (source.sort - dest.sort).map(&:first)
24
+ to_remove = (dest.sort - source.sort).map(&:first)
25
+ to_remove = to_remove - to_add
26
+
27
+ {
28
+ :add => to_add,
29
+ :remove => to_remove
30
+ }
31
+ end
32
+
33
+ def io_handler uri
34
+ parsed = URI.parse(uri)
35
+ parsed.scheme == 's3' ?
36
+ Remote.new(@s3, parsed.host, parsed.path) :
37
+ Local.new(uri)
38
+ end
39
+
40
+ def sync source, dest, opts = {}
41
+ opts = { :progress => method(:progress) }.merge(opts)
42
+
43
+ source_io = io_handler(source)
44
+ destination_io = io_handler(dest)
45
+
46
+ logger.debug("s3sync: Synchronzing (#{source_io.class.name} -> #{destination_io.class.name}")
47
+
48
+ raise "S3 -> S3 synchronisation not supported" if source_io.is_a? Remote and destination_io.is_a? Remote
49
+
50
+ source_listing = source_io.ls
51
+ destination_listing = destination_io.ls
52
+ logger.debug("s3sync: Source listing - #{source_listing}")
53
+ logger.debug("s3sync: Destination listing - #{destination_listing}")
54
+
55
+ delta = delta(source_listing, destination_listing)
56
+ logger.debug("s3sync: Delta #{delta}")
57
+
58
+ delta[:add].each do |file|
59
+ logger.debug("s3sync: Synchronizing #{file}")
60
+ source_file = source_io.open(file, "r")
61
+ destination_file = destination_io.open(file, "wb+")
62
+
63
+ source_file.buffer
64
+
65
+ written = 0
66
+ size = source_file.size
67
+ destination_file.write({ :size => source_file.size }) do |buffer, bytes|
68
+ chunk = source_file.read(bytes)
69
+ buffer.write(chunk)
70
+ written += chunk.length
71
+ opts[:progress].call(file, written, size, :update)
72
+ end
73
+
74
+ destination_file.close
75
+ source_file.close
76
+
77
+ opts[:progress].call(file, written, size, :finish)
78
+ end
79
+
80
+ delta[:remove].each do |file|
81
+ logger.debug("s3sync: Removing #{file}")
82
+ destination_io.rm(file)
83
+ end
84
+
85
+ return delta
86
+ end
87
+
88
+ def progress file, written, total, type
89
+ @progress ||= {}
90
+ @progress[file] ||= ProgressBar.create(
91
+ :title => file,
92
+ :total => total,
93
+ :format => "%t [%B] %p%% %e",
94
+ )
95
+
96
+ case type
97
+ when :update
98
+ @progress[file].progress = written
99
+ when :finished
100
+ @progress[file].finish
101
+ end
102
+ end
103
+
104
+
105
+ class Local
106
+ include Hobo::Logging
107
+
108
+ def initialize path
109
+ @path = path
110
+ end
111
+
112
+ def ls
113
+ logger.debug("s3sync: Listing local directory: #{@path}")
114
+ out = {}
115
+ dir = "#{@path.chomp('/')}/"
116
+ files = Dir.glob("#{dir}**/*")
117
+ files.each do |file|
118
+ out[file.gsub(/^#{dir}/, '')] = Digest::MD5.file(file).hexdigest
119
+ end
120
+ return out
121
+ end
122
+
123
+ def open file, mode
124
+ file_path = File.join(@path, file)
125
+ FileUtils.mkdir_p File.dirname(file_path)
126
+ LocalFile.new File.open(file_path, mode)
127
+ end
128
+
129
+ def rm file
130
+ File.unlink file
131
+ end
132
+ end
133
+
134
+ class Remote
135
+ include Hobo::Logging
136
+
137
+ def initialize s3, bucket, prefix
138
+ @s3 = s3
139
+ @bucket = bucket
140
+ @prefix = prefix ? "#{prefix.gsub(/^\//, '').chomp('/')}/" : ""
141
+ end
142
+
143
+ def ls
144
+ out = {}
145
+ logger.debug("s3sync: Listing remote bucket: #{@bucket} w/ prefix #{@prefix}")
146
+ @s3.buckets[@bucket].objects.with_prefix(@prefix).each do |file|
147
+ filename = file.key.gsub(/^#{@prefix}/, '')
148
+ next if filename == ""
149
+ out[filename] = file.etag.gsub('"', '')
150
+ end
151
+ return out
152
+ end
153
+
154
+ def open file, mode
155
+ s3_key = File.join(@prefix, file)
156
+ RemoteFile.new @s3.buckets[@bucket].objects[s3_key], @prefix
157
+ end
158
+
159
+ def rm file
160
+ s3_key = File.join(@prefix, file)
161
+ @s3.buckets[@bucket].objects[s3_key].delete
162
+ end
163
+ end
164
+
165
+ class LocalFile
166
+ def initialize file
167
+ @file = file
168
+ end
169
+
170
+ def buffer
171
+ # NOP
172
+ end
173
+
174
+ def read bytes
175
+ @file.read bytes
176
+ end
177
+
178
+ def write opts = {}
179
+ opts = { :chunk_size => 4096 }.merge(opts)
180
+ while @file.size < opts[:size] do
181
+ yield @file, opts[:chunk_size]
182
+ end
183
+ end
184
+
185
+ def size
186
+ @file.size
187
+ end
188
+
189
+ def close
190
+ @file.close
191
+ end
192
+ end
193
+
194
+ class RemoteFile
195
+ def initialize object, prefix
196
+ @object = object
197
+ @prefix = prefix
198
+ @r_buffer, @w_buffer = IO.pipe
199
+ @buffer_thread = nil
200
+ end
201
+
202
+ def buffer
203
+ @buffer_thread = Thread.new do
204
+ @object.read do |chunk|
205
+ @w_buffer.write chunk
206
+ end
207
+ end
208
+ end
209
+
210
+ def read bytes
211
+ @r_buffer.readpartial(bytes)
212
+ end
213
+
214
+ def write opts = {}
215
+ s3_opts = { :single_request => true, :content_length => opts[:size] }
216
+ @object.write s3_opts do |buffer, bytes|
217
+ yield buffer, bytes
218
+ end
219
+ end
220
+
221
+ def size
222
+ @object.content_length
223
+ end
224
+
225
+ def close
226
+ @r_buffer.close
227
+ @w_buffer.close
228
+ @buffer_thread.exit if @buffer_thread
229
+ end
230
+ end
231
+ end
232
+ end
233
+ end