hobo-inviqa 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/Gemfile +4 -0
  2. data/Gemfile.lock +93 -0
  3. data/Guardfile +14 -0
  4. data/Hobofile +34 -0
  5. data/Rakefile +2 -0
  6. data/bin/hobo +23 -0
  7. data/features/deps.feature +43 -0
  8. data/features/hobo/basic.feature +30 -0
  9. data/features/hobo/help.feature +12 -0
  10. data/features/hobo/subcommands.feature +16 -0
  11. data/features/seed/plant.feature +64 -0
  12. data/features/step_definitions/seed.rb +11 -0
  13. data/features/support/env.rb +6 -0
  14. data/features/vm.feature +0 -0
  15. data/hobo.gemspec +37 -0
  16. data/lib/hobo.rb +44 -0
  17. data/lib/hobo/cli.rb +185 -0
  18. data/lib/hobo/config/file.rb +21 -0
  19. data/lib/hobo/error_handlers/debug.rb +9 -0
  20. data/lib/hobo/error_handlers/friendly.rb +52 -0
  21. data/lib/hobo/errors.rb +60 -0
  22. data/lib/hobo/help_formatter.rb +111 -0
  23. data/lib/hobo/helper/file_locator.rb +39 -0
  24. data/lib/hobo/helper/shell.rb +59 -0
  25. data/lib/hobo/lib/seed/project.rb +41 -0
  26. data/lib/hobo/lib/seed/replacer.rb +57 -0
  27. data/lib/hobo/lib/seed/seed.rb +43 -0
  28. data/lib/hobo/metadata.rb +28 -0
  29. data/lib/hobo/patches/rake.rb +56 -0
  30. data/lib/hobo/patches/slop.rb +22 -0
  31. data/lib/hobo/paths.rb +49 -0
  32. data/lib/hobo/tasks/debug.rb +22 -0
  33. data/lib/hobo/tasks/deps.rb +45 -0
  34. data/lib/hobo/tasks/seed.rb +43 -0
  35. data/lib/hobo/tasks/tools.rb +13 -0
  36. data/lib/hobo/tasks/vm.rb +49 -0
  37. data/lib/hobo/ui.rb +96 -0
  38. data/lib/hobo/util.rb +7 -0
  39. data/lib/hobo/version.rb +3 -0
  40. data/spec/hobo/cli_spec.rb +135 -0
  41. data/spec/hobo/config/file_spec.rb +48 -0
  42. data/spec/hobo/error_handlers/debug_spec.rb +10 -0
  43. data/spec/hobo/error_handlers/friendly_spec.rb +81 -0
  44. data/spec/hobo/error_spec.rb +0 -0
  45. data/spec/hobo/help_formatter_spec.rb +131 -0
  46. data/spec/hobo/helpers/file_locator_spec.rb +7 -0
  47. data/spec/hobo/helpers/shell_spec.rb +7 -0
  48. data/spec/hobo/lib/seed/project_spec.rb +83 -0
  49. data/spec/hobo/lib/seed/replacer_spec.rb +47 -0
  50. data/spec/hobo/lib/seed/seed_spec.rb +95 -0
  51. data/spec/hobo/metadata_spec.rb +46 -0
  52. data/spec/hobo/patches/rake_spec.rb +0 -0
  53. data/spec/hobo/paths_spec.rb +77 -0
  54. data/spec/hobo/ui_spec.rb +64 -0
  55. data/spec/spec_helper.rb +6 -0
  56. metadata +355 -0
@@ -0,0 +1,59 @@
1
+ module Hobo
2
+ module Helper
3
+ def bundle_shell *args, &block
4
+ has_bundle = begin
5
+ shell "bundle", "exec", "ruby -v"
6
+ true
7
+ rescue ::Hobo::ExternalCommandError
8
+ false
9
+ end
10
+
11
+ if has_bundle
12
+ args = [ 'bundle', 'exec' ] + args
13
+ end
14
+
15
+ shell *args, &block
16
+ end
17
+
18
+ def shell *args, &block
19
+ opts = (args.size > 1 && args.last.is_a?(Hash)) ? args.pop : {}
20
+ opts = {
21
+ :capture => false,
22
+ :indent => 0,
23
+ :realtime => false
24
+ }.merge! opts
25
+
26
+ indent = " " * opts[:indent]
27
+ ::Open3.popen3 *args do |stdin, out, err, external|
28
+ buffer = ::Tempfile.new 'hobo_run_buf'
29
+ buffer.sync = true
30
+ threads = [external]
31
+
32
+ ## Create a thread to read from each stream
33
+ { :out => out, :err => err }.each do |key, stream|
34
+ threads.push(::Thread.new do
35
+ until (line = stream.gets).nil? do
36
+ line = ::Hobo.ui.color(line, :error) if key == :err
37
+ buffer.write(line)
38
+ line = yield line if block
39
+ puts indent + line if opts[:realtime] && !line.nil?
40
+ end
41
+ end)
42
+ end
43
+
44
+ threads.each do |t|
45
+ t.join
46
+ end
47
+
48
+ buffer.fsync
49
+ buffer.rewind
50
+
51
+ raise ::Hobo::ExternalCommandError.new(args.join(" "), external.value.exitstatus, buffer) if external.value.exitstatus != 0
52
+
53
+ return opts[:capture] ? buffer.read.strip : nil
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ include Hobo::Helper
@@ -0,0 +1,41 @@
1
+ module Hobo
2
+ module Lib
3
+ module Seed
4
+ class Project
5
+ def initialize(opts = {})
6
+ @opts = {
7
+ :replacer => Replacer.new,
8
+ :config_class => Hobo::Config::File,
9
+ :project_config_file => Hobo.project_config_file
10
+ }.merge! opts
11
+ end
12
+
13
+ def setup seed, config
14
+ seed.update
15
+ seed.export config[:project_path]
16
+ config[:seed][:version] = seed.version
17
+
18
+ @opts[:replacer].replace(config[:project_path], config)
19
+
20
+ project_path = config[:project_path]
21
+ config.delete :project_path
22
+ @opts[:config_class].save @opts[:project_config_file], config
23
+
24
+ initialize_git project_path, config[:git_url]
25
+ end
26
+
27
+ private
28
+
29
+ def initialize_git path, git_url
30
+ Dir.chdir path do
31
+ Hobo::Helper.shell 'git', 'init'
32
+ Hobo::Helper.shell 'git', 'remote', 'add', 'origin', git_url
33
+ Hobo::Helper.shell 'git', 'add', '*'
34
+ Hobo::Helper.shell 'git', 'commit', '-m', "'Initial hobo project'"
35
+ Hobo::Helper.shell 'git', 'checkout', '-b', 'develop'
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,57 @@
1
+ require 'find'
2
+
3
+ module Hobo
4
+ module Lib
5
+ module Seed
6
+ class Replacer
7
+ # Matching files/directories to be excluded from replacements
8
+ EXCLUDES = ["\\.git/", "^./bin", "^./lib", "^./spec"]
9
+
10
+ def replace(path, tokens)
11
+ if tokens.instance_of? Hash
12
+ tokens = flat_hash(tokens)
13
+ elsif !tokens.instance_of? Array
14
+ raise "Invalid token list (expected Array or Hash)"
15
+ end
16
+
17
+ return search_replace(path, tokens)
18
+ end
19
+
20
+ private
21
+
22
+ def search_replace(path, tokens, &block)
23
+ files = []
24
+ excludes = Regexp.new(EXCLUDES.join("|"))
25
+ Find.find(path) do |candidate|
26
+ Find.prune if candidate =~ excludes # Skip excluded
27
+ next unless FileTest.file? candidate # Skip unless file
28
+
29
+ content = File.read(candidate)
30
+ next unless content.force_encoding("UTF-8").valid_encoding? # Skip unless file can be valid UTF-8
31
+
32
+ match = false
33
+ tokens.each do |token, replacement|
34
+ token = "{{#{token.join('.')}}}"
35
+ match = content.match(token)
36
+ if match
37
+ content.gsub!(token, replacement)
38
+ files.push(candidate)
39
+ end
40
+ end
41
+
42
+ File.write(candidate, content) if files.include? candidate
43
+ end
44
+ return files.uniq
45
+ end
46
+
47
+ # http://stackoverflow.com/questions/9647997/converting-a-nested-hash-into-a-flat-hash
48
+ def flat_hash(hash, k = [])
49
+ return {k => hash} unless hash.is_a?(Hash)
50
+ hash.inject({}) do |h, v|
51
+ h.merge! flat_hash(v[-1], k + [v[0]])
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,43 @@
1
+ module Hobo
2
+ module Lib
3
+ module Seed
4
+ class Seed
5
+ def initialize(seed_path, url)
6
+ @seed_path = seed_path
7
+ @url = url
8
+ end
9
+
10
+ def export path
11
+ path = File.expand_path(path)
12
+ FileUtils.mkdir_p path
13
+ Dir.chdir @seed_path do
14
+ Hobo::Helper.shell "git archive master | tar -x -C #{path.shellescape}"
15
+ end
16
+ end
17
+
18
+ def update
19
+ FileUtils.mkdir_p @seed_path
20
+ if File.exists? File.join(@seed_path, 'HEAD')
21
+ Dir.chdir @seed_path do
22
+ Hobo::Helper.shell 'git', 'fetch', '--all'
23
+ end
24
+ else
25
+ Hobo::Helper.shell 'git', 'clone', @url, @seed_path, '--mirror'
26
+ end
27
+ end
28
+
29
+ def version
30
+ Dir.chdir @seed_path do
31
+ Hobo::Helper.shell 'git', 'rev-parse', '--short', 'HEAD', :capture => true
32
+ end
33
+ end
34
+
35
+ class << self
36
+ def name_to_url name
37
+ name.match(/\./) ? name : "git@github.com:inviqa/hobo-seed-#{name}"
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,28 @@
1
+ module Hobo
2
+ class Metadata
3
+ class << self
4
+ attr_accessor :metadata, :store, :defaults
5
+
6
+ def store
7
+ @store ||= {}
8
+ end
9
+
10
+ def metadata
11
+ @metadata ||= {}
12
+ end
13
+
14
+ def default type, value
15
+ @defaults ||= {}
16
+ @defaults[type] = value
17
+ store[type] = value if store[type].nil?
18
+ end
19
+
20
+ def add task, type, data = nil
21
+ data = store[type] if data.nil?
22
+ metadata[task] ||= {}
23
+ metadata[task][type] = data
24
+ store[type] = @defaults[type] ? @defaults[type].dup : @defaults[type]
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,56 @@
1
+ module Rake
2
+ class Task
3
+ attr_accessor :opts
4
+ end
5
+
6
+ module DSL
7
+ def replace *args, &block
8
+ Rake::Task[args[0]].clear
9
+ task(*args, &block)
10
+ end
11
+
12
+ def hidden value = true
13
+ Hobo::Metadata.store[:hidden] = value
14
+ end
15
+
16
+ def project_only
17
+ Hobo::Metadata.store[:project_only] = true
18
+ end
19
+
20
+ def task *args, &block
21
+ name = args[0].is_a?(Hash) ? args[0].keys.first.to_s : args[0]
22
+ scoped_name = Rake.application.current_scope.path_with_task_name(name).to_s
23
+
24
+ [:opts, :desc, :long_desc, :hidden, :project_only].each do |meta|
25
+ Hobo::Metadata.add scoped_name, meta
26
+ end
27
+
28
+ task = Rake::Task.define_task(*args, &block)
29
+ end
30
+
31
+ def option *args
32
+ Hobo::Metadata.store[:opts].push args
33
+ end
34
+
35
+ def desc description
36
+ Hobo::Metadata.store[:desc] = description
37
+ end
38
+
39
+ def long_desc description
40
+ Hobo::Metadata.store[:long_desc] = description
41
+ end
42
+
43
+ alias :_old_namespace :namespace
44
+ def namespace name, opts = {}, &block
45
+ scoped_name = Rake.application.current_scope.path_with_task_name(name).to_s
46
+ [:desc, :long_desc, :hidden, :project_only].each do |meta|
47
+ Hobo::Metadata.add scoped_name, meta
48
+ end
49
+
50
+ _old_namespace(name, &block)
51
+ end
52
+ end
53
+ end
54
+
55
+ Hobo::Metadata.default :opts, []
56
+ Hobo::Metadata.default :desc, nil
@@ -0,0 +1,22 @@
1
+ class Slop
2
+ attr_accessor :long_desc, :arg_list, :hidden
3
+ def long_description desc = nil
4
+ @long_desc = desc if desc
5
+ @long_desc
6
+ end
7
+
8
+ def arg_list list = nil
9
+ @arg_list = list if list
10
+ @arg_list
11
+ end
12
+
13
+ def hidden value = nil
14
+ @hidden = value if value
15
+ @hidden
16
+ end
17
+
18
+ def project_only value = nil
19
+ @config[:project_only] = value unless value.nil?
20
+ @config[:project_only]
21
+ end
22
+ end
data/lib/hobo/paths.rb ADDED
@@ -0,0 +1,49 @@
1
+ module Hobo
2
+ class << self
3
+ attr_accessor :project_path
4
+
5
+ def config_path
6
+ File.join(ENV['HOME'], '.hobo')
7
+ end
8
+
9
+ def seed_cache_path
10
+ File.join(config_path, 'seeds')
11
+ end
12
+
13
+ def project_path
14
+ return @project_path unless @project_path.nil?
15
+ dir = Dir.pwd
16
+ while File.split(dir)[1] != File.split(dir)[0]
17
+ match = [
18
+ File.exists?(File.join(dir, 'Hobofile')),
19
+ File.exists?(File.join(dir, 'tools', 'hobo')),
20
+ File.exists?(File.join(dir, 'tools', 'vagrant', 'Vagrantfile'))
21
+ ] - [false]
22
+
23
+ return @project_path = dir if match.length > 0
24
+
25
+ dir = File.split(dir)[0]
26
+ end
27
+ return @project_path = nil
28
+ end
29
+
30
+ def project_bin_path
31
+ return nil if !project_path
32
+ File.join(project_path, 'bin')
33
+ end
34
+
35
+ def hobofile_path
36
+ return nil if !project_path
37
+ File.join(project_path, 'Hobofile')
38
+ end
39
+
40
+ def project_config_file
41
+ return nil if !project_path
42
+ File.join(project_path, 'tools', 'hobo', 'storage.yaml')
43
+ end
44
+
45
+ def user_config_file
46
+ File.join(config_path, 'config.rb')
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,22 @@
1
+ desc "Internal hobo debugging tools"
2
+ hidden true
3
+ namespace 'hobo-debug' do
4
+
5
+ desc "Display project paths"
6
+ project_only
7
+ task "paths" do
8
+ Hobo.ui.info "<%=color('Project path:', :green)%> " + Hobo.project_path
9
+ {
10
+ :gemfile => "*Gemfile",
11
+ :vagrantfile => "*Vagrantfile",
12
+ :cheffile => "*Cheffile",
13
+ :'composer.json' => "composer.json"
14
+ }.each do |k,v|
15
+ path = nil
16
+ locate v do |p|
17
+ path = p
18
+ end
19
+ Hobo.ui.info "<%=color('#{k.to_s}:', :green) %> #{path.nil? ? "none" : path}"
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,45 @@
1
+ require 'bundler'
2
+
3
+ desc "Dependency related tasks"
4
+ hidden true
5
+ namespace :deps do
6
+
7
+ desc "Install Gem dependencies"
8
+ task :gems do
9
+ locate("*Gemfile", missing: "No Gemfile found") do
10
+ Hobo.ui.title "Installing Gem dependencies"
11
+ Bundler.with_clean_env do
12
+ shell "bundle", "install", realtime: true, indent: 2
13
+ end
14
+ Hobo.ui.separator
15
+ end
16
+ end
17
+
18
+ desc "Install composer dependencies"
19
+ task :composer do
20
+ if File.exists? File.join(Hobo.project_path, "composer.json")
21
+ Rake::Task["tools:composer"].invoke
22
+ Hobo.ui.title "Installing composer dependencies"
23
+ shell "php", File.join(Hobo.project_bin_path, 'composer.phar'), "install", "--ansi", realtime: true, indent: 2
24
+ end
25
+ Hobo.ui.separator
26
+ end
27
+
28
+ desc "Install vagrant plugins"
29
+ task :vagrant_plugins do
30
+ Hobo.ui.error "Vagrant plugins can't be installed automatically yet"
31
+ end
32
+
33
+ desc "Install chef dependencies"
34
+ task :chef do
35
+ locate("*Cheffile", missing: "No Cheffile found") do
36
+ Hobo.ui.title "Installing chef dependencies"
37
+ Bundler.with_clean_env do
38
+ bundle_shell "librarian-chef", "install", "--verbose", realtime: true, indent: 2 do |line|
39
+ line =~ /Installing.*</ ? line : nil
40
+ end
41
+ end
42
+ Hobo.ui.separator
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,43 @@
1
+
2
+ desc "Project seed commands"
3
+ namespace :seed do
4
+
5
+ desc "Create a new project from a seed repository"
6
+
7
+ option '-g=', '--git-url=', 'Git repository for project'
8
+ option '-s=', '--seed=', 'Seed name or URL to use'
9
+
10
+ task :plant, [ :name ] do |t, args|
11
+ name = args[:name]
12
+
13
+ Hobo.project_path = File.join(Dir.pwd, name)
14
+
15
+ raise Hobo::UserError.new "Name must match sprint zero guidelines" unless name.match /[a-z0-9\-]+/
16
+ raise Hobo::UserError.new "#{Hobo.project_path} already exists!" if File.exists? Hobo.project_path
17
+
18
+ config = {
19
+ :name => name,
20
+ :project_path => Hobo.project_path,
21
+ :git_url => t.opts[:'git-url'] || Hobo.ui.ask("Repository URL", default: "git@github.com:inviqa/#{name}")
22
+ }
23
+
24
+ seed_name = t.opts[:seed] || Hobo.ui.ask("Project seed", default: "default")
25
+
26
+ config[:seed] = {
27
+ :name => File.basename(seed_name),
28
+ :url => Hobo::Lib::Seed::Seed.name_to_url(seed_name)
29
+ }
30
+
31
+ seed = Hobo::Lib::Seed::Seed.new(
32
+ File.join(Hobo.seed_cache_path, config[:seed][:name]),
33
+ config[:seed][:url]
34
+ )
35
+
36
+ Hobo::Lib::Seed::Project.new().setup(seed, config)
37
+
38
+ Hobo.ui.separator
39
+ Hobo.ui.success "Your new project is available in #{Hobo.project_path}.\n"
40
+ Hobo.ui.success "You will need to review the initial commit and if all is well, push the repository to github using `git push origin --all`."
41
+ Hobo.ui.separator
42
+ end
43
+ end