hem 1.0.1.beta1

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 (111) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +10 -0
  3. data/.gitignore +3 -0
  4. data/.rspec +2 -0
  5. data/CHANGELOG.md +125 -0
  6. data/DoD.md +5 -0
  7. data/Gemfile +4 -0
  8. data/Gemfile.lock +71 -0
  9. data/Guardfile +14 -0
  10. data/Hemfile +43 -0
  11. data/LICENSE +21 -0
  12. data/README.md +42 -0
  13. data/Rakefile +23 -0
  14. data/bin/hem +64 -0
  15. data/features/deps.feature +43 -0
  16. data/features/hem/basic.feature +43 -0
  17. data/features/hem/help.feature +16 -0
  18. data/features/hem/subcommands.feature +15 -0
  19. data/features/seed/plant.feature +64 -0
  20. data/features/step_definitions/env.rb +6 -0
  21. data/features/step_definitions/seed.rb +11 -0
  22. data/features/support/env.rb +6 -0
  23. data/hem.gemspec +47 -0
  24. data/lib/hem/asset_applicator.rb +33 -0
  25. data/lib/hem/asset_applicators/files.rb +5 -0
  26. data/lib/hem/asset_applicators/sqldump.rb +38 -0
  27. data/lib/hem/cli.rb +252 -0
  28. data/lib/hem/config/file.rb +22 -0
  29. data/lib/hem/config.rb +5 -0
  30. data/lib/hem/error_handlers/debug.rb +12 -0
  31. data/lib/hem/error_handlers/exit_code_map.rb +17 -0
  32. data/lib/hem/error_handlers/friendly.rb +58 -0
  33. data/lib/hem/errors.rb +89 -0
  34. data/lib/hem/help_formatter.rb +118 -0
  35. data/lib/hem/helper/file_locator.rb +44 -0
  36. data/lib/hem/helper/github.rb +10 -0
  37. data/lib/hem/helper/http_download.rb +41 -0
  38. data/lib/hem/helper/shell.rb +101 -0
  39. data/lib/hem/helper/vm_command.rb +30 -0
  40. data/lib/hem/lib/github/api.rb +48 -0
  41. data/lib/hem/lib/github/client.rb +52 -0
  42. data/lib/hem/lib/host_check/deps.rb +39 -0
  43. data/lib/hem/lib/host_check/git.rb +76 -0
  44. data/lib/hem/lib/host_check/ruby.rb +53 -0
  45. data/lib/hem/lib/host_check/vagrant.rb +45 -0
  46. data/lib/hem/lib/host_check.rb +34 -0
  47. data/lib/hem/lib/s3/local/file.rb +40 -0
  48. data/lib/hem/lib/s3/local/iohandler.rb +36 -0
  49. data/lib/hem/lib/s3/remote/file.rb +57 -0
  50. data/lib/hem/lib/s3/remote/iohandler.rb +38 -0
  51. data/lib/hem/lib/s3/sync.rb +134 -0
  52. data/lib/hem/lib/seed/project.rb +71 -0
  53. data/lib/hem/lib/seed/replacer.rb +56 -0
  54. data/lib/hem/lib/seed/seed.rb +111 -0
  55. data/lib/hem/lib/self_signed_cert_generator.rb +38 -0
  56. data/lib/hem/lib/vm/command.rb +131 -0
  57. data/lib/hem/lib/vm/inspector.rb +73 -0
  58. data/lib/hem/logging.rb +20 -0
  59. data/lib/hem/metadata.rb +42 -0
  60. data/lib/hem/null.rb +31 -0
  61. data/lib/hem/patches/deepstruct.rb +21 -0
  62. data/lib/hem/patches/rake.rb +101 -0
  63. data/lib/hem/patches/rubygems.rb +6 -0
  64. data/lib/hem/patches/slop.rb +69 -0
  65. data/lib/hem/paths.rb +96 -0
  66. data/lib/hem/tasks/assets.rb +92 -0
  67. data/lib/hem/tasks/config.rb +15 -0
  68. data/lib/hem/tasks/deps.rb +103 -0
  69. data/lib/hem/tasks/exec.rb +3 -0
  70. data/lib/hem/tasks/magento.rb +281 -0
  71. data/lib/hem/tasks/ops.rb +6 -0
  72. data/lib/hem/tasks/pr.rb +45 -0
  73. data/lib/hem/tasks/seed.rb +61 -0
  74. data/lib/hem/tasks/self.rb +45 -0
  75. data/lib/hem/tasks/shell_init.rb +25 -0
  76. data/lib/hem/tasks/system/completions.rb +76 -0
  77. data/lib/hem/tasks/system.rb +18 -0
  78. data/lib/hem/tasks/tools.rb +17 -0
  79. data/lib/hem/tasks/vm.rb +140 -0
  80. data/lib/hem/ui.rb +182 -0
  81. data/lib/hem/util.rb +76 -0
  82. data/lib/hem/version.rb +3 -0
  83. data/lib/hem.rb +72 -0
  84. data/lib/hobo/tasks/magento.rb +3 -0
  85. data/spec/hem/asset_applicator_spec.rb +30 -0
  86. data/spec/hem/cli_spec.rb +166 -0
  87. data/spec/hem/config/file_spec.rb +55 -0
  88. data/spec/hem/error_handlers/debug_spec.rb +43 -0
  89. data/spec/hem/error_handlers/friendly_spec.rb +97 -0
  90. data/spec/hem/error_spec.rb +0 -0
  91. data/spec/hem/help_formatter_spec.rb +162 -0
  92. data/spec/hem/helpers/file_locator_spec.rb +11 -0
  93. data/spec/hem/helpers/github_spec.rb +31 -0
  94. data/spec/hem/helpers/shell_spec.rb +22 -0
  95. data/spec/hem/helpers/vm_command_spec.rb +96 -0
  96. data/spec/hem/lib/github/api_spec.rb +92 -0
  97. data/spec/hem/lib/s3/sync_spec.rb +16 -0
  98. data/spec/hem/lib/seed/project_spec.rb +80 -0
  99. data/spec/hem/lib/seed/replacer_spec.rb +45 -0
  100. data/spec/hem/lib/seed/seed_spec.rb +127 -0
  101. data/spec/hem/logging_spec.rb +27 -0
  102. data/spec/hem/metadata_spec.rb +55 -0
  103. data/spec/hem/null_spec.rb +30 -0
  104. data/spec/hem/patches/rake_spec.rb +230 -0
  105. data/spec/hem/paths_spec.rb +75 -0
  106. data/spec/hem/ui_spec.rb +189 -0
  107. data/spec/hem/util_spec.rb +74 -0
  108. data/spec/spec_helper.rb +12 -0
  109. data/ssl/ca-bundle-s3.crt +3554 -0
  110. data/test_files/vagrant_fail/vagrant +2 -0
  111. metadata +339 -0
@@ -0,0 +1,131 @@
1
+ module Hem
2
+ module Lib
3
+ module Vm
4
+ class Command
5
+ class << self
6
+ attr_accessor :vm_inspector
7
+ @@vm_inspector = Inspector.new
8
+ end
9
+
10
+ attr_accessor :opts, :command
11
+
12
+ def initialize command, opts = {}
13
+ @command = command
14
+ @opts = {
15
+ :auto_echo => false,
16
+ :psuedo_tty => false,
17
+ :pwd => opts[:pwd] || @@vm_inspector.project_mount_path,
18
+ :append => ''
19
+ }.merge(opts)
20
+ end
21
+
22
+ def pipe cmd, pipe_opts = {}
23
+ pipe_opts = pipe_opts.merge({ :on => :vm })
24
+ cmd = "echo #{cmd.shellescape.gsub(/(\\+)/, '\\\\\1')}" if @opts[:auto_echo]
25
+
26
+ case pipe_opts[:on]
27
+ when :vm
28
+ @pipe_in_vm = cmd
29
+ when :host
30
+ @pipe = cmd
31
+ else
32
+ raise "Unknown pipe source: #{pipe_opts[:on]}"
33
+ end
34
+ @opts[:psuedo_tty] = false
35
+ return self
36
+ end
37
+
38
+ def << cmd
39
+ pipe cmd, :on => :host
40
+ end
41
+
42
+ def < cmd
43
+ pipe cmd, :on => :vm
44
+ end
45
+
46
+ # TODO Refactor in to ssh helper with similar opts to shell helper
47
+ # TODO Migrate all vm_shell functionality this direction
48
+ def run
49
+ return if @command.nil?
50
+ require 'net/ssh/simple'
51
+ opts = @@vm_inspector.ssh_config.merge(@opts)
52
+
53
+ Net::SSH::Simple.sync do
54
+ ssh_opts = {
55
+ :user => opts[:ssh_user],
56
+ :port => opts[:ssh_port],
57
+ :forward_agent => true,
58
+ :global_known_hosts_file => "/dev/null",
59
+ :paranoid => false,
60
+ :user_known_hosts_file => "/dev/null"
61
+ }
62
+
63
+ ssh_opts[:keys] = [opts[:ssh_identity]] if opts[:ssh_identity]
64
+
65
+ tmp = Tempfile.new "vm_command_exec"
66
+
67
+ begin
68
+ filename = File.basename(tmp.path)
69
+ remote_file = "/tmp/#{filename}"
70
+ tmp.write "#{@command}#{opts[:append]}"
71
+ tmp.close
72
+
73
+ scp_put opts[:ssh_host], tmp.path, remote_file, ssh_opts
74
+ result = ssh opts[:ssh_host], "cd #{opts[:pwd]}; exec /bin/bash #{remote_file}", ssh_opts
75
+ ssh opts[:ssh_host], "rm #{remote_file}", ssh_opts
76
+
77
+ # Throw exception if exit code not 0
78
+
79
+ return opts[:capture] ? result.stdout : result.success
80
+ ensure
81
+ tmp.unlink
82
+ end
83
+ end
84
+ end
85
+
86
+ # TODO Speed up Vagrant SSH connections
87
+ # May need to be disabled for windows (mm_send_fd: UsePrivilegeSeparation=yes not supported)
88
+ # https://gist.github.com/jedi4ever/5657094
89
+
90
+ def to_s
91
+ opts = @@vm_inspector.ssh_config.merge(@opts)
92
+
93
+ psuedo_tty = opts[:psuedo_tty] ? "-t" : ""
94
+
95
+ ssh_command = [
96
+ "ssh",
97
+ "-o 'UserKnownHostsFile /dev/null'",
98
+ "-o 'StrictHostKeyChecking no'",
99
+ "-o 'ForwardAgent yes'",
100
+ "-o 'LogLevel FATAL'",
101
+ "-p #{opts[:ssh_port]}",
102
+ "-i #{opts[:ssh_identity].shellescape}",
103
+ psuedo_tty,
104
+ "#{opts[:ssh_user].shellescape}@#{opts[:ssh_host].shellescape}"
105
+ ].join(" ")
106
+
107
+ pwd_set_command = " -- \"cd #{@opts[:pwd].shellescape}; exec /bin/bash"
108
+
109
+ vm_command = [
110
+ @pipe_in_vm,
111
+ @command
112
+ ].compact.join(" | ")
113
+
114
+ command = [
115
+ ssh_command + pwd_set_command,
116
+ vm_command.empty? ? nil : vm_command.shellescape
117
+ ].compact.join(" -c ") + "#{opts[:append].shellescape}\""
118
+
119
+ [
120
+ @pipe,
121
+ command
122
+ ].compact.join(" | ")
123
+ end
124
+
125
+ def to_str
126
+ to_s
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,73 @@
1
+ module Hem
2
+ module Lib
3
+ module Vm
4
+ class Inspector
5
+ attr_accessor :ssh_config, :project_mount_path, :project_config
6
+
7
+ def project_mount_path
8
+ configured_path = maybe(Hem.project_config.vm.project_mount_path)
9
+ return configured_path if configured_path
10
+ return @project_mount_path if @project_mount_path
11
+
12
+ tmp = Tempfile.new('vm_command_locator', Hem.project_path)
13
+
14
+ begin
15
+ tmp.write(Hem.project_path)
16
+
17
+ locator_file = File.basename(tmp.path)
18
+
19
+ pattern = Hem.windows? ? 'vboxsf' : Hem.project_path.shellescape
20
+
21
+ sed = 's/.* on \(.*\) type.*/\1\/%%/g'.gsub('%%', locator_file)
22
+ locator_results = Command.new(
23
+ "mount | grep #{pattern} | sed -e\"#{sed}\" | xargs md5sum",
24
+ :capture => true,
25
+ :pwd => '/'
26
+ ).run
27
+ ensure
28
+ tmp.unlink
29
+ end
30
+
31
+ match = locator_results.match(/^([a-z0-9]{32})\s+(.*)$/)
32
+
33
+ raise Exception.new("Unable to locate project mount point in VM") if !match
34
+
35
+ @vm_project_mount_path = File.dirname(match[2])
36
+
37
+ # Stash it in config
38
+ Hem.project_config[:vm] ||= {}
39
+ Hem.project_config[:vm][:project_mount_path] = @vm_project_mount_path
40
+ Hem::Config::File.save(Hem.project_config_file, Hem.project_config)
41
+
42
+ return @vm_project_mount_path
43
+ end
44
+
45
+ def ssh_config
46
+ return @ssh_config if @ssh_config
47
+ config = nil
48
+ locate "*Vagrantfile" do
49
+ config = shell "vagrant ssh-config", :capture => true
50
+ end
51
+
52
+ raise Exception.new "Could not retrieve VM ssh configuration" unless config
53
+
54
+ patterns = {
55
+ :ssh_user => /^\s*User (.*)$/,
56
+ :ssh_identity => /^\s*IdentityFile (.*)$/,
57
+ :ssh_host => /^\s*HostName (.*)$/,
58
+ :ssh_port => /^\s*Port (\d+)/
59
+ }
60
+
61
+ output = {}
62
+
63
+ patterns.each do |k, pattern|
64
+ match = config.match(pattern)
65
+ output[k] = match[1] if match
66
+ end
67
+
68
+ return @ssh_config = output
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,20 @@
1
+ module Hem
2
+ class << self
3
+ attr_accessor :logger
4
+ end
5
+
6
+ module Logging
7
+ def logger
8
+ Hem::Logging.logger
9
+ end
10
+
11
+ def self.logger
12
+ unless Hem.logger
13
+ Hem.logger = Logger.new(STDOUT)
14
+ Hem.logger.level = Logger::WARN
15
+ end
16
+
17
+ return Hem.logger
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,42 @@
1
+ module Hem
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
+ end
25
+
26
+ def to_store task
27
+ reset_store
28
+ return unless metadata[task]
29
+ metadata[task].each do |k,v|
30
+ store[k] = v
31
+ end
32
+ end
33
+
34
+ def reset_store
35
+ @store = {}
36
+ @defaults.each do |k, v|
37
+ @store[k] = v.nil? ? nil : v.dup
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
data/lib/hem/null.rb ADDED
@@ -0,0 +1,31 @@
1
+ module Hem
2
+ class Null
3
+ def method_missing(method, *args, &block)
4
+ self
5
+ end
6
+
7
+ def nil?
8
+ true
9
+ end
10
+
11
+ def to_a
12
+ []
13
+ end
14
+
15
+ def to_s
16
+ ""
17
+ end
18
+
19
+ def to_f
20
+ 0.0
21
+ end
22
+
23
+ def to_i
24
+ 0
25
+ end
26
+ end
27
+ end
28
+
29
+ def maybe val
30
+ val.nil? ? nil : val
31
+ end
@@ -0,0 +1,21 @@
1
+ # Copied from the DeepStruct gem
2
+ # Modified to return Null on unknown key
3
+ module DeepStruct
4
+ class HashWrapper < DeepWrapper
5
+ def method_missing(method, *args, &block)
6
+ return @value.send(method, *args, &block) if @value.respond_to?(method)
7
+ method = method.to_s
8
+ if method.chomp!('?')
9
+ key = method.to_sym
10
+ self.has_key?(key) && !!self[key]
11
+ elsif method.chomp!('=')
12
+ raise ArgumentError, "wrong number of arguments (#{arg_count} for 1)", caller(1) if args.length != 1
13
+ self[method] = args[0]
14
+ elsif args.length == 0 && self.has_key?(method)
15
+ self[method]
16
+ else
17
+ Hem::Null.new
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,101 @@
1
+ module Rake
2
+ class Task
3
+ attr_accessor :opts
4
+ def opts
5
+ @opts = @opts || {}
6
+ end
7
+ end
8
+
9
+ module DSL
10
+ def before(task_name, new_tasks = nil, &new_task)
11
+ task_name = task_name.to_s
12
+ new_tasks = [new_tasks].flatten.compact
13
+ old_task = Rake.application.instance_variable_get('@tasks').delete(task_name)
14
+
15
+ Hem::Metadata.to_store task_name
16
+ task task_name => old_task.prerequisites do
17
+ new_task.call unless new_task.nil?
18
+ new_tasks.each do |t|
19
+ Rake::Task[t].invoke
20
+ end
21
+ old_task.invoke
22
+ end
23
+ end
24
+
25
+ def after(task_name, new_tasks = nil, &new_task)
26
+ task_name = task_name.to_s
27
+ new_tasks = [new_tasks].flatten.compact
28
+ old_task = Rake.application.instance_variable_get('@tasks').delete(task_name)
29
+
30
+ Hem::Metadata.to_store task_name
31
+ task task_name => old_task.prerequisites do
32
+ old_task.invoke
33
+ new_tasks.each do |t|
34
+ Rake::Task[t].invoke
35
+ end
36
+ new_task.call unless new_task.nil?
37
+ end
38
+ end
39
+
40
+ def replace *args, &block
41
+ old = (args[0].is_a? Hash) ? args[0].keys[0] : args[0]
42
+ Hem::Logging.logger.debug("rake.dsl: Replacing #{old} with block")
43
+ Rake::Task[old].clear
44
+ task(*args, &block)
45
+ end
46
+
47
+ def invoke task, *args, &block
48
+ Rake::Task[task].invoke(*args, &block)
49
+ end
50
+
51
+ def hidden value = true
52
+ Hem::Metadata.store[:hidden] = value
53
+ end
54
+
55
+ def project_only
56
+ Hem::Metadata.store[:project_only] = true
57
+ end
58
+
59
+ def task *args, &block
60
+ name = args[0].is_a?(Hash) ? args[0].keys.first.to_s : args[0]
61
+ scoped_name = Rake.application.current_scope.path_with_task_name(name).to_s
62
+
63
+ [:opts, :desc, :long_desc, :hidden, :project_only].each do |meta|
64
+ Hem::Metadata.add scoped_name, meta
65
+ end
66
+
67
+ Hem::Metadata.reset_store
68
+
69
+ Hem::Logging.logger.debug("Added metadata to #{scoped_name} -- #{Hem::Metadata.metadata[scoped_name]}")
70
+
71
+ task = Rake::Task.define_task(*args, &block)
72
+ end
73
+
74
+ def option *args
75
+ Hem::Metadata.store[:opts].push args
76
+ end
77
+
78
+ def desc description
79
+ Hem::Metadata.store[:desc] = description
80
+ end
81
+
82
+ def long_desc description
83
+ Hem::Metadata.store[:long_desc] = description
84
+ end
85
+
86
+ alias :_old_namespace :namespace
87
+ def namespace name, opts = {}, &block
88
+ scoped_name = Rake.application.current_scope.path_with_task_name(name).to_s
89
+ [:desc, :long_desc, :hidden, :project_only].each do |meta|
90
+ Hem::Metadata.add scoped_name, meta
91
+ end
92
+
93
+ Hem::Metadata.reset_store
94
+
95
+ _old_namespace(name, &block)
96
+ end
97
+ end
98
+ end
99
+
100
+ Hem::Metadata.default :opts, []
101
+ Hem::Metadata.default :desc, nil
@@ -0,0 +1,6 @@
1
+ # No sensible way to silence gem warnings
2
+ class Gem::Specification
3
+ def self.warn m
4
+ #NOP
5
+ end
6
+ end
@@ -0,0 +1,69 @@
1
+ class Slop
2
+ attr_accessor :long_desc, :arg_list, :hidden, :desc, :unparsed
3
+
4
+ # Slop has a description method but it uses @config which is inherited
5
+ # This is not desired behaviour
6
+ def description desc = nil
7
+ @desc = desc if desc
8
+ @desc
9
+ end
10
+
11
+ def long_description desc = nil
12
+ @long_desc = desc if desc
13
+ @long_desc
14
+ end
15
+
16
+ def arg_list list = nil
17
+ @arg_list = list if list
18
+ @arg_list
19
+ end
20
+
21
+ def hidden value = nil
22
+ @hidden = value if value
23
+ @hidden
24
+ end
25
+
26
+ def project_only value = nil
27
+ @config[:project_only] = value unless value.nil?
28
+ @config[:project_only]
29
+ end
30
+
31
+ alias :old_parse! :parse!
32
+ def parse!(items = ARGV, &block)
33
+ if @unparsed.nil?
34
+ split_index = items.index('--')
35
+
36
+ unparsed = []
37
+ unless split_index.nil?
38
+ unparsed = items.slice(split_index + 1, items.length)
39
+ items = items.slice(0, split_index)
40
+ end
41
+
42
+ @unparsed = unparsed.map do |c|
43
+ "\'#{c.gsub("'", '\\\'').gsub('(', '\\(').gsub(')', '\\)')}\'"
44
+ end.join(' ')
45
+ end
46
+
47
+ old_parse!(items, &block)
48
+ end
49
+
50
+ class Option
51
+ DEFAULT_OPTIONS[:key_delimiter] = '='
52
+
53
+ alias :old_value= :value=
54
+ def value=(new_value)
55
+ if config[:as].to_s.downcase == 'hash'
56
+ @value ||= {}
57
+
58
+ if new_value.respond_to?(:split)
59
+ new_array_hash = new_value.split(config[:delimiter], config[:limit]).map do |v|
60
+ v.split(config[:key_delimiter], 2)
61
+ end
62
+ @value.merge!(Hash[new_array_hash])
63
+ end
64
+ else
65
+ self.old_value = new_value
66
+ end
67
+ end
68
+ end
69
+ end
data/lib/hem/paths.rb ADDED
@@ -0,0 +1,96 @@
1
+ module Hem
2
+ class << self
3
+ attr_accessor :project_path
4
+
5
+ def config_path
6
+ [ File.join(ENV['HOME'], '.hem'), File.join(ENV['HOME'], '.hobo') ].each do |path|
7
+ return path if File.exists? File.join(path, 'config.yaml')
8
+ end
9
+ end
10
+
11
+ def seed_cache_path
12
+ File.join(config_path, 'seeds')
13
+ end
14
+
15
+ def detect_project_type
16
+ return unless @project_type.nil?
17
+
18
+ searches = [
19
+ { type: "Hem", indicators: [ "Hemfile", "tools/hem"] },
20
+ { type: "Hobo", indicators: [ "Hobofile", "tools/hobo"] },
21
+ { type: "Hem", indicators: ["tools/vagrant/Vagrantfile"] }
22
+ ]
23
+
24
+ searches.each do |search|
25
+ next if @project_path
26
+ path = project_path_compat search
27
+ if path
28
+ @project_type = search[:type]
29
+ @project_path = path
30
+ break
31
+ end
32
+ end
33
+
34
+ @project_type ||= 'Hem'
35
+ end
36
+
37
+ def project_dsl_type
38
+ detect_project_type
39
+ @project_type
40
+ end
41
+
42
+ def project_dsl_file
43
+ "#{project_dsl_type}file"
44
+ end
45
+
46
+ def project_path
47
+ detect_project_type
48
+ @project_path
49
+ end
50
+
51
+ def project_path_compat search
52
+ dir = Dir.pwd.split('/').reverse
53
+ min_length = Gem.win_platform? ? 1 : 0
54
+ Hem::Logging.logger.debug("paths.project: Searching backwards from #{Dir.pwd}")
55
+
56
+ while dir.length > min_length
57
+ test_dir = dir.reverse.join('/')
58
+ Hem::Logging.logger.debug("paths.project: Testing #{test_dir}")
59
+
60
+ results = search[:indicators].map do |s|
61
+ File.exists?(File.join(test_dir, s))
62
+ end
63
+
64
+ match = results - [false]
65
+
66
+ return test_dir if match.length > 0
67
+
68
+ dir.shift
69
+ end
70
+ return nil
71
+ end
72
+
73
+ def project_bin_path
74
+ return nil if !project_path
75
+ File.join(project_path, 'bin')
76
+ end
77
+
78
+ def hemfile_path
79
+ return nil if !project_path
80
+ File.join(project_path, project_dsl_file)
81
+ end
82
+
83
+ def project_config_file
84
+ return nil if !project_path
85
+ File.join(project_path, 'tools', project_dsl_type.downcase, 'config.yaml')
86
+ end
87
+
88
+ def user_config_file
89
+ File.join(config_path, 'config.yaml')
90
+ end
91
+
92
+ def user_hemfile_path
93
+ File.join(config_path, project_dsl_file)
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,92 @@
1
+ desc "Project asset commands"
2
+ project_only
3
+ namespace :assets do
4
+ def render_delta delta, type
5
+ if delta[:add] and delta[:add].length > 0
6
+ x = type == 'download' ? 'download from S3' : 'upload to S3'
7
+ Hem.ui.section " Files to #{x}:" do
8
+ delta[:add].each do |f|
9
+ Hem.ui.success " #{f}"
10
+ end
11
+ end
12
+ end
13
+
14
+ if delta[:remove] and delta[:remove].length > 0
15
+ x = type == 'download' ? 'locally' : 'from S3'
16
+ puts Hem.ui.color " Files to delete #{x}:", :error
17
+ delta[:remove].each do |f|
18
+ puts Hem.ui.color " #{f}", :error
19
+ end
20
+ Hem.ui.separator
21
+ end
22
+ end
23
+
24
+ def do_sync src, dst, env, type
25
+ unless Hem.project_config.asset_bucket.nil?
26
+ sync = Hem::Lib::S3::Sync.new(Hem.aws_credentials)
27
+ changes = sync.sync(src, dst, :dry => true)
28
+
29
+ if (changes[:add] + changes[:remove]).length > 0
30
+ answer = if type == 'download' && changes[:remove].length == 0
31
+ 'y'
32
+ else
33
+ render_delta changes, type
34
+ Hem.ui.ask "Proceed? (Y/N)", :default => 'Y'
35
+ end
36
+
37
+ if answer.downcase == 'y'
38
+ sync.sync(src, dst)
39
+ else
40
+ raise Hem::Error.new "Asset sync aborted"
41
+ end
42
+ else
43
+ Hem.ui.warning " No changes required"
44
+ end
45
+ else
46
+ Hem.ui.warning " No asset bucket configured. Skipping..."
47
+ end
48
+ end
49
+
50
+ desc "Download project assets"
51
+ option "-e=", "--env=", "Environment"
52
+ task :download do |task, args|
53
+ Hem.ui.section "Synchronizing assets (download)" do
54
+ env = task.opts[:env] || args[:env] || 'development'
55
+ src = "s3://#{Hem.project_config.asset_bucket}/#{env}/"
56
+ dst = "#{Hem.project_path}/tools/assets/#{env}"
57
+ do_sync src, dst, env, "download"
58
+ end
59
+ end
60
+
61
+ desc "Upload project assets"
62
+ option "-e=", "--env=", "Environment"
63
+ task :upload do |task, args|
64
+ Hem.ui.section "Synchronizing assets (upload)" do
65
+ env = task.opts[:env] || args[:env] || 'development'
66
+ dst = "s3://#{Hem.project_config.asset_bucket}/#{env}/"
67
+ src = "#{Hem.project_path}/tools/assets/#{env}"
68
+ Hem.ui.warning "Please note that asset uploads can be destructive and will affect the whole team!"
69
+ Hem.ui.warning "Only upload if you're sure your assets are free from errors and will not impact other team members"
70
+ do_sync src, dst, env, "upload"
71
+ end
72
+ end
73
+
74
+ desc "Apply project assets"
75
+ option "-e=", "--env=", "Environment"
76
+ project_only
77
+ task :apply do |task, args|
78
+ env = task.opts[:env] || args[:env] || 'development'
79
+ path = "tools/assets/#{env}"
80
+
81
+ next unless File.exists? path
82
+
83
+ Dir.new(path).each do |file|
84
+ file = File.join(path, file)
85
+ next unless File.file? file
86
+ Hem.asset_applicators.each do |matcher, proc|
87
+ proc.call(file) if matcher.match(file)
88
+ end
89
+ end
90
+
91
+ end
92
+ end
@@ -0,0 +1,15 @@
1
+ desc "Configure hem"
2
+ task :config do
3
+ config = Hem.user_config
4
+
5
+ # Not required at present
6
+ # config.full_name = Hem.ui.ask("Full name", :default => config.full_name).to_s
7
+ # config.email = Hem.ui.ask("Email", :default => config.email).to_s
8
+
9
+ config[:aws] ||= {}
10
+ config.aws.access_key_id = Hem.ui.ask("AWS access key ID", :default => config.aws.access_key_id).to_s
11
+ config.aws.secret_access_key = Hem.ui.ask("AWS secret access key", :default => config.aws.secret_access_key).to_s
12
+
13
+ Hem::Config::File.save(Hem.user_config_file, config)
14
+ File.chmod(0600, Hem.user_config_file)
15
+ end