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.
- checksums.yaml +7 -0
- data/.editorconfig +10 -0
- data/.gitignore +3 -0
- data/.rspec +2 -0
- data/CHANGELOG.md +125 -0
- data/DoD.md +5 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +71 -0
- data/Guardfile +14 -0
- data/Hemfile +43 -0
- data/LICENSE +21 -0
- data/README.md +42 -0
- data/Rakefile +23 -0
- data/bin/hem +64 -0
- data/features/deps.feature +43 -0
- data/features/hem/basic.feature +43 -0
- data/features/hem/help.feature +16 -0
- data/features/hem/subcommands.feature +15 -0
- data/features/seed/plant.feature +64 -0
- data/features/step_definitions/env.rb +6 -0
- data/features/step_definitions/seed.rb +11 -0
- data/features/support/env.rb +6 -0
- data/hem.gemspec +47 -0
- data/lib/hem/asset_applicator.rb +33 -0
- data/lib/hem/asset_applicators/files.rb +5 -0
- data/lib/hem/asset_applicators/sqldump.rb +38 -0
- data/lib/hem/cli.rb +252 -0
- data/lib/hem/config/file.rb +22 -0
- data/lib/hem/config.rb +5 -0
- data/lib/hem/error_handlers/debug.rb +12 -0
- data/lib/hem/error_handlers/exit_code_map.rb +17 -0
- data/lib/hem/error_handlers/friendly.rb +58 -0
- data/lib/hem/errors.rb +89 -0
- data/lib/hem/help_formatter.rb +118 -0
- data/lib/hem/helper/file_locator.rb +44 -0
- data/lib/hem/helper/github.rb +10 -0
- data/lib/hem/helper/http_download.rb +41 -0
- data/lib/hem/helper/shell.rb +101 -0
- data/lib/hem/helper/vm_command.rb +30 -0
- data/lib/hem/lib/github/api.rb +48 -0
- data/lib/hem/lib/github/client.rb +52 -0
- data/lib/hem/lib/host_check/deps.rb +39 -0
- data/lib/hem/lib/host_check/git.rb +76 -0
- data/lib/hem/lib/host_check/ruby.rb +53 -0
- data/lib/hem/lib/host_check/vagrant.rb +45 -0
- data/lib/hem/lib/host_check.rb +34 -0
- data/lib/hem/lib/s3/local/file.rb +40 -0
- data/lib/hem/lib/s3/local/iohandler.rb +36 -0
- data/lib/hem/lib/s3/remote/file.rb +57 -0
- data/lib/hem/lib/s3/remote/iohandler.rb +38 -0
- data/lib/hem/lib/s3/sync.rb +134 -0
- data/lib/hem/lib/seed/project.rb +71 -0
- data/lib/hem/lib/seed/replacer.rb +56 -0
- data/lib/hem/lib/seed/seed.rb +111 -0
- data/lib/hem/lib/self_signed_cert_generator.rb +38 -0
- data/lib/hem/lib/vm/command.rb +131 -0
- data/lib/hem/lib/vm/inspector.rb +73 -0
- data/lib/hem/logging.rb +20 -0
- data/lib/hem/metadata.rb +42 -0
- data/lib/hem/null.rb +31 -0
- data/lib/hem/patches/deepstruct.rb +21 -0
- data/lib/hem/patches/rake.rb +101 -0
- data/lib/hem/patches/rubygems.rb +6 -0
- data/lib/hem/patches/slop.rb +69 -0
- data/lib/hem/paths.rb +96 -0
- data/lib/hem/tasks/assets.rb +92 -0
- data/lib/hem/tasks/config.rb +15 -0
- data/lib/hem/tasks/deps.rb +103 -0
- data/lib/hem/tasks/exec.rb +3 -0
- data/lib/hem/tasks/magento.rb +281 -0
- data/lib/hem/tasks/ops.rb +6 -0
- data/lib/hem/tasks/pr.rb +45 -0
- data/lib/hem/tasks/seed.rb +61 -0
- data/lib/hem/tasks/self.rb +45 -0
- data/lib/hem/tasks/shell_init.rb +25 -0
- data/lib/hem/tasks/system/completions.rb +76 -0
- data/lib/hem/tasks/system.rb +18 -0
- data/lib/hem/tasks/tools.rb +17 -0
- data/lib/hem/tasks/vm.rb +140 -0
- data/lib/hem/ui.rb +182 -0
- data/lib/hem/util.rb +76 -0
- data/lib/hem/version.rb +3 -0
- data/lib/hem.rb +72 -0
- data/lib/hobo/tasks/magento.rb +3 -0
- data/spec/hem/asset_applicator_spec.rb +30 -0
- data/spec/hem/cli_spec.rb +166 -0
- data/spec/hem/config/file_spec.rb +55 -0
- data/spec/hem/error_handlers/debug_spec.rb +43 -0
- data/spec/hem/error_handlers/friendly_spec.rb +97 -0
- data/spec/hem/error_spec.rb +0 -0
- data/spec/hem/help_formatter_spec.rb +162 -0
- data/spec/hem/helpers/file_locator_spec.rb +11 -0
- data/spec/hem/helpers/github_spec.rb +31 -0
- data/spec/hem/helpers/shell_spec.rb +22 -0
- data/spec/hem/helpers/vm_command_spec.rb +96 -0
- data/spec/hem/lib/github/api_spec.rb +92 -0
- data/spec/hem/lib/s3/sync_spec.rb +16 -0
- data/spec/hem/lib/seed/project_spec.rb +80 -0
- data/spec/hem/lib/seed/replacer_spec.rb +45 -0
- data/spec/hem/lib/seed/seed_spec.rb +127 -0
- data/spec/hem/logging_spec.rb +27 -0
- data/spec/hem/metadata_spec.rb +55 -0
- data/spec/hem/null_spec.rb +30 -0
- data/spec/hem/patches/rake_spec.rb +230 -0
- data/spec/hem/paths_spec.rb +75 -0
- data/spec/hem/ui_spec.rb +189 -0
- data/spec/hem/util_spec.rb +74 -0
- data/spec/spec_helper.rb +12 -0
- data/ssl/ca-bundle-s3.crt +3554 -0
- data/test_files/vagrant_fail/vagrant +2 -0
- 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
|
data/lib/hem/logging.rb
ADDED
|
@@ -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
|
data/lib/hem/metadata.rb
ADDED
|
@@ -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,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
|