linecook 1.2.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/{History → History.rdoc} +3 -2
- data/README.rdoc +93 -0
- data/bin/linecook +32 -56
- data/bin/linecook_run +19 -6
- data/bin/linecook_scp +12 -4
- data/doc/vm_setup.rdoc +75 -0
- data/lib/linecook.rb +3 -2
- data/lib/linecook/attributes.rb +33 -8
- data/lib/linecook/command.rb +61 -0
- data/lib/linecook/command_set.rb +85 -0
- data/lib/linecook/command_utils.rb +20 -0
- data/lib/linecook/commands/build.rb +108 -57
- data/lib/linecook/commands/compile.rb +181 -0
- data/lib/linecook/commands/{helper.rb → compile_helper.rb} +123 -94
- data/lib/linecook/commands/run.rb +43 -39
- data/lib/linecook/commands/snapshot.rb +24 -24
- data/lib/linecook/commands/ssh.rb +7 -7
- data/lib/linecook/commands/start.rb +10 -10
- data/lib/linecook/commands/state.rb +7 -7
- data/lib/linecook/commands/stop.rb +3 -3
- data/lib/linecook/commands/{vbox_command.rb → virtual_box_command.rb} +31 -29
- data/lib/linecook/cookbook.rb +149 -131
- data/lib/linecook/executable.rb +28 -0
- data/lib/linecook/package.rb +177 -361
- data/lib/linecook/proxy.rb +4 -10
- data/lib/linecook/recipe.rb +289 -369
- data/lib/linecook/test.rb +114 -98
- data/lib/linecook/utils.rb +31 -41
- data/lib/linecook/version.rb +2 -6
- metadata +120 -68
- data/HowTo/Control Virtual Machines +0 -106
- data/HowTo/Generate Scripts +0 -268
- data/HowTo/Run Scripts +0 -87
- data/HowTo/Setup Virtual Machines +0 -76
- data/README +0 -117
- data/lib/linecook/commands.rb +0 -11
- data/lib/linecook/commands/command.rb +0 -58
- data/lib/linecook/commands/command_error.rb +0 -12
- data/lib/linecook/commands/env.rb +0 -89
- data/lib/linecook/commands/init.rb +0 -86
- data/lib/linecook/commands/package.rb +0 -57
- data/lib/linecook/template.rb +0 -17
- data/lib/linecook/test/command_parser.rb +0 -75
- data/lib/linecook/test/file_test.rb +0 -197
- data/lib/linecook/test/regexp_escape.rb +0 -86
- data/lib/linecook/test/shell_test.rb +0 -177
- data/lib/linecook/test/shim.rb +0 -71
- data/templates/Gemfile +0 -3
- data/templates/Rakefile +0 -146
- data/templates/_gitignore +0 -4
- data/templates/attributes/project_name.rb +0 -3
- data/templates/config/ssh +0 -14
- data/templates/cookbook +0 -10
- data/templates/files/example.txt +0 -1
- data/templates/helpers/project_name/echo.erb +0 -4
- data/templates/packages/abox.yml +0 -2
- data/templates/project_name.gemspec +0 -30
- data/templates/recipes/abox.rb +0 -16
- data/templates/templates/example.erb +0 -1
- data/templates/test/project_name_test.rb +0 -24
- data/templates/test/test_helper.rb +0 -14
@@ -1,8 +1,8 @@
|
|
1
|
-
require 'linecook/commands/
|
1
|
+
require 'linecook/commands/virtual_box_command'
|
2
2
|
|
3
3
|
module Linecook
|
4
4
|
module Commands
|
5
|
-
|
5
|
+
|
6
6
|
# :startdoc::desc take a vm snapshop
|
7
7
|
#
|
8
8
|
# Takes the specified snapshot of one or more VirtualBox virtual machines.
|
@@ -20,14 +20,14 @@ module Linecook
|
|
20
20
|
#
|
21
21
|
# To reset:
|
22
22
|
#
|
23
|
-
#
|
23
|
+
# linecook snapshot --reset CURRENT
|
24
24
|
#
|
25
25
|
# After which there will only be a single 'CURRENT' snapshot, which
|
26
26
|
# corresponds to the original snapshot.
|
27
27
|
#
|
28
|
-
class Snapshot <
|
29
|
-
config :reset, false,
|
30
|
-
|
28
|
+
class Snapshot < VirtualBoxCommand
|
29
|
+
config :reset, false # -r, --reset : reset a snapshot
|
30
|
+
|
31
31
|
def process(snapshot, *hosts)
|
32
32
|
vm_names = resolve_vm_names(hosts)
|
33
33
|
each_vm_name(vm_names) do |vm_name|
|
@@ -38,75 +38,75 @@ module Linecook
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
42
|
def parse_snapshots(vm_name)
|
43
43
|
info = `VBoxManage -q showvminfo #{vm_name}`
|
44
44
|
snapshots = {}
|
45
|
-
|
45
|
+
|
46
46
|
stack = [{}]
|
47
47
|
parent = nil
|
48
|
-
|
48
|
+
|
49
49
|
info.each_line do |line|
|
50
50
|
next unless line =~ /^(\s+)Name\: (.*?) \(/
|
51
51
|
depth = $1.length / 3
|
52
52
|
name = $2
|
53
|
-
|
53
|
+
|
54
54
|
if depth > stack.length
|
55
55
|
stack.push stack.last[parent]
|
56
56
|
elsif depth < stack.length
|
57
57
|
stack.pop
|
58
58
|
end
|
59
|
-
|
59
|
+
|
60
60
|
snapshot = {}
|
61
61
|
snapshots[name] = snapshot
|
62
62
|
stack.last[name] = snapshot
|
63
63
|
parent = name
|
64
64
|
end
|
65
|
-
|
65
|
+
|
66
66
|
snapshots
|
67
67
|
end
|
68
|
-
|
68
|
+
|
69
69
|
def reset_snapshot(vm_name, snapshot)
|
70
70
|
stop(vm_name) if running?(vm_name)
|
71
|
-
|
71
|
+
|
72
72
|
snapshot = snapshot.upcase
|
73
73
|
restore(vm_name, snapshot)
|
74
|
-
|
74
|
+
|
75
75
|
snapshots = parse_snapshots(vm_name)
|
76
76
|
parent = snapshots.keys.select {|key| key =~ /\A#{snapshot}(?:_\d+)\z/ }.first
|
77
77
|
parent ||= snapshot
|
78
|
-
|
78
|
+
|
79
79
|
children = snapshots[parent]
|
80
80
|
children.each do |key, value|
|
81
81
|
inside_out_each(key, value) do |child|
|
82
82
|
sh! "VBoxManage -q snapshot #{vm_name} delete #{child}"
|
83
83
|
end
|
84
84
|
end
|
85
|
-
|
85
|
+
|
86
86
|
unless parent == snapshot
|
87
87
|
sh! "VBoxManage -q snapshot #{vm_name} edit #{parent} --name #{snapshot}"
|
88
88
|
end
|
89
89
|
end
|
90
|
-
|
90
|
+
|
91
91
|
def snapshot(vm_name, snapshot)
|
92
92
|
snapshot = snapshot.upcase
|
93
93
|
snapshots = parse_snapshots(vm_name)
|
94
|
-
|
94
|
+
|
95
95
|
count = snapshots.keys.grep(/\A#{snapshot}(?:_|\z)/).length
|
96
96
|
if count > 0
|
97
97
|
sh! "VBoxManage -q snapshot #{vm_name} edit #{snapshot} --name #{snapshot}_#{count - 1}"
|
98
98
|
end
|
99
|
-
|
100
|
-
sh! "VBoxManage -q snapshot #{vm_name} take #{snapshot}"
|
99
|
+
|
100
|
+
sh! "VBoxManage -q snapshot #{vm_name} take #{snapshot} --pause"
|
101
101
|
end
|
102
|
-
|
102
|
+
|
103
103
|
private
|
104
|
-
|
104
|
+
|
105
105
|
def inside_out_each(key, value, &block) # :nodoc:
|
106
106
|
value.each_pair do |k, v|
|
107
107
|
inside_out_each(k, v, &block)
|
108
108
|
end
|
109
|
-
|
109
|
+
|
110
110
|
yield(key)
|
111
111
|
end
|
112
112
|
end
|
@@ -1,24 +1,24 @@
|
|
1
|
-
require 'linecook/commands/
|
1
|
+
require 'linecook/commands/virtual_box_command'
|
2
2
|
|
3
3
|
module Linecook
|
4
4
|
module Commands
|
5
|
-
|
5
|
+
|
6
6
|
# :startdoc::desc ssh to a vm
|
7
7
|
#
|
8
8
|
# Connects to a virtual machine using ssh, as configured in config/ssh.
|
9
|
-
#
|
10
|
-
class Ssh <
|
9
|
+
#
|
10
|
+
class Ssh < VirtualBoxCommand
|
11
11
|
undef_config :names
|
12
|
-
|
12
|
+
|
13
13
|
def default_host
|
14
14
|
load_hosts(ssh_config_file).collect {|host, vm_name| host }.first
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
def process(host=default_host)
|
18
18
|
if host.to_s.strip.empty?
|
19
19
|
raise CommandError.new("no host specified")
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
ssh = "ssh -F '#{ssh_config_file}' '#{host}' --"
|
23
23
|
|
24
24
|
# Patterned after vagrant/ssh.rb (circa 0.6.6)
|
@@ -1,18 +1,18 @@
|
|
1
|
-
require 'linecook/commands/
|
1
|
+
require 'linecook/commands/virtual_box_command'
|
2
2
|
|
3
3
|
module Linecook
|
4
4
|
module Commands
|
5
|
-
|
5
|
+
|
6
6
|
# :startdoc::desc start a vm
|
7
7
|
#
|
8
8
|
# Starts one or more VirtualBox virtual machines, and resets to a snapshot
|
9
9
|
# if provided. By default all virtual machines configured in config/ssh
|
10
10
|
# will be reset and started in this way.
|
11
|
-
class Start <
|
12
|
-
config :type, 'headless'
|
13
|
-
config :snapshot, ''
|
14
|
-
config :
|
15
|
-
|
11
|
+
class Start < VirtualBoxCommand
|
12
|
+
config :type, 'headless' # vm type (headless|gui)
|
13
|
+
config :snapshot, '' # start snapshot
|
14
|
+
config :use_master_socket, false # -m, --master-socket : use a master socket
|
15
|
+
|
16
16
|
def process(*hosts)
|
17
17
|
vm_names = resolve_vm_names(hosts)
|
18
18
|
each_vm_name(vm_names) do |vm_name|
|
@@ -20,13 +20,13 @@ module Linecook
|
|
20
20
|
stop(vm_name)
|
21
21
|
sleep 0.5
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
unless snapshot.empty?
|
25
25
|
restore(vm_name, snapshot)
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
start(vm_name, type)
|
29
|
-
start_ssh_socket(vm_name) if
|
29
|
+
start_ssh_socket(vm_name) if use_master_socket
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
@@ -1,20 +1,20 @@
|
|
1
|
-
require 'linecook/commands/
|
1
|
+
require 'linecook/commands/virtual_box_command'
|
2
2
|
|
3
3
|
module Linecook
|
4
4
|
module Commands
|
5
|
-
|
6
|
-
# :startdoc::desc print
|
5
|
+
|
6
|
+
# :startdoc::desc print a vm state
|
7
7
|
#
|
8
8
|
# Prints the state of one or more VirtualBox virtual machines. By default
|
9
9
|
# all virtual machines configured in config/ssh will print their state.
|
10
10
|
#
|
11
|
-
class State <
|
12
|
-
config :hosts, false,
|
13
|
-
|
11
|
+
class State < VirtualBoxCommand
|
12
|
+
config :hosts, false # -s, --hosts : print state by host
|
13
|
+
|
14
14
|
def state(vm_name)
|
15
15
|
`VBoxManage showvminfo #{vm_name}` =~ /^State:\s+(.*)$/ ? $1 : 'unknown'
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
def process(*hosts)
|
19
19
|
vm_names = resolve_vm_names(hosts)
|
20
20
|
if hosts
|
@@ -1,14 +1,14 @@
|
|
1
|
-
require 'linecook/commands/
|
1
|
+
require 'linecook/commands/virtual_box_command'
|
2
2
|
|
3
3
|
module Linecook
|
4
4
|
module Commands
|
5
|
-
|
5
|
+
|
6
6
|
# :startdoc::desc stop a vm
|
7
7
|
#
|
8
8
|
# Stops one or more VirtualBox virtual machines using 'poweroff'. By
|
9
9
|
# default all virtual machines configured in config/ssh will be stopped.
|
10
10
|
#
|
11
|
-
class Stop <
|
11
|
+
class Stop < VirtualBoxCommand
|
12
12
|
def process(*hosts)
|
13
13
|
vm_names = resolve_vm_names(hosts)
|
14
14
|
each_vm_name(vm_names) do |vm_name|
|
@@ -1,14 +1,16 @@
|
|
1
|
-
require 'linecook/
|
1
|
+
require 'linecook/command'
|
2
|
+
require 'linecook/command_utils'
|
2
3
|
|
3
4
|
module Linecook
|
4
5
|
module Commands
|
5
|
-
class
|
6
|
-
|
7
|
-
|
8
|
-
config :ssh_config_file, 'config/ssh',
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
class VirtualBoxCommand < Command
|
7
|
+
include CommandUtils
|
8
|
+
|
9
|
+
config :ssh_config_file, 'config/ssh', { # -F FILE : the ssh config file
|
10
|
+
:writer => :ssh_config_file=
|
11
|
+
}
|
12
|
+
config :names, false # -n, --names : use vm names
|
13
|
+
|
12
14
|
# Matches a host declaration in a ssh config file. After the match:
|
13
15
|
#
|
14
16
|
# $1:: The host name
|
@@ -17,7 +19,7 @@ module Linecook
|
|
17
19
|
# (ex: 'Host name # [vm_name]' => 'vm_name')
|
18
20
|
#
|
19
21
|
HOST_REGEXP = /^\s*Host\s+([^\s#]+)(?:\s*#\s*\[(.*?)\])?/
|
20
|
-
|
22
|
+
|
21
23
|
# Returns a hash of (host, vm_name) pairs as declared in a ssh config
|
22
24
|
# file. Basically this means parsing out the name in each config like:
|
23
25
|
#
|
@@ -30,7 +32,7 @@ module Linecook
|
|
30
32
|
#
|
31
33
|
def load_hosts(ssh_config_file)
|
32
34
|
hosts = []
|
33
|
-
|
35
|
+
|
34
36
|
File.open(ssh_config_file) do |io|
|
35
37
|
io.each_line do |line|
|
36
38
|
next unless line =~ HOST_REGEXP
|
@@ -38,90 +40,90 @@ module Linecook
|
|
38
40
|
hosts << [$1, $2 || $1]
|
39
41
|
end
|
40
42
|
end
|
41
|
-
|
43
|
+
|
42
44
|
hosts
|
43
45
|
end
|
44
|
-
|
46
|
+
|
45
47
|
def ssh_config_file=(ssh_config_file)
|
46
48
|
@ssh_config_file = ssh_config_file
|
47
49
|
@host_list = nil
|
48
50
|
@host_map = nil
|
49
51
|
end
|
50
|
-
|
52
|
+
|
51
53
|
def host_list
|
52
54
|
@host_list ||= load_hosts(ssh_config_file)
|
53
55
|
end
|
54
|
-
|
56
|
+
|
55
57
|
def host_map
|
56
58
|
@host_map ||= Hash[*host_list.flatten]
|
57
59
|
end
|
58
|
-
|
60
|
+
|
59
61
|
def resolve_vm_names(hosts)
|
60
62
|
names ? hosts : hosts.collect {|host| host_map[host] || host }
|
61
63
|
end
|
62
|
-
|
64
|
+
|
63
65
|
def each_host(hosts=[])
|
64
66
|
if hosts.empty?
|
65
67
|
hosts = host_list.collect {|host, vm_name| host }
|
66
68
|
hosts.delete('*')
|
67
69
|
end
|
68
|
-
|
70
|
+
|
69
71
|
hosts.uniq.each do |host|
|
70
72
|
yield(host)
|
71
73
|
end
|
72
74
|
end
|
73
|
-
|
75
|
+
|
74
76
|
def each_vm_name(vm_names=[])
|
75
77
|
if vm_names.empty?
|
76
78
|
vm_names = host_list.collect {|host, vm_name| vm_name }
|
77
79
|
vm_names.delete('*')
|
78
80
|
end
|
79
|
-
|
81
|
+
|
80
82
|
vm_names.uniq.each do |vm_name|
|
81
83
|
yield(vm_name)
|
82
84
|
end
|
83
85
|
end
|
84
|
-
|
86
|
+
|
85
87
|
def ssh(host, cmd)
|
86
88
|
sh "ssh -q -F '#{ssh_config_file}' '#{host}' -- #{cmd}"
|
87
89
|
end
|
88
|
-
|
90
|
+
|
89
91
|
def ssh!(host, cmd)
|
90
92
|
unless ssh(host, cmd)
|
91
93
|
raise CommandError, "non-zero exit status: #{$?.exitstatus}"
|
92
94
|
end
|
93
95
|
end
|
94
|
-
|
96
|
+
|
95
97
|
def running?(vm_name)
|
96
98
|
`VBoxManage -q list runningvms`.include?(vm_name)
|
97
99
|
end
|
98
|
-
|
100
|
+
|
99
101
|
def start(vm_name, type='headless')
|
100
102
|
sh! "VBoxManage -q startvm #{vm_name} --type #{type}"
|
101
103
|
end
|
102
|
-
|
104
|
+
|
103
105
|
def stop(vm_name)
|
104
106
|
sh! "VBoxManage -q controlvm #{vm_name} poweroff"
|
105
107
|
end
|
106
|
-
|
108
|
+
|
107
109
|
def restore(vm_name, snapshot)
|
108
110
|
sh! "VBoxManage -q snapshot #{vm_name} restore #{snapshot.upcase}"
|
109
111
|
end
|
110
|
-
|
112
|
+
|
111
113
|
def discardstate(vm_name)
|
112
114
|
sh! "VBoxManage discardstate #{vm_name}"
|
113
115
|
end
|
114
|
-
|
116
|
+
|
115
117
|
def share(vm_name, name, local_dir, remote_dir)
|
116
118
|
share_dir = "#{local_dir}/#{vm_name}"
|
117
119
|
FileUtils.mkdir_p(share_dir) unless File.exists?(share_dir)
|
118
|
-
|
120
|
+
|
119
121
|
ssh vm_name, "sudo umount '#{remote_dir}' > /dev/null 2>&1"
|
120
122
|
sh "VBoxManage sharedfolder remove '#{vm_name}' --name '#{name}' --transient > /dev/null 2>&1"
|
121
123
|
sh! "VBoxManage sharedfolder add '#{vm_name}' --name '#{name}' --hostpath '#{share_dir}' --transient"
|
122
124
|
ssh! vm_name, "sudo mount -t vboxsf -o uid=1000,gid=100 '#{name}' '#{remote_dir}'"
|
123
125
|
end
|
124
|
-
|
126
|
+
|
125
127
|
def start_ssh_socket(vm_name)
|
126
128
|
sh "ssh -MNf -F '#{ssh_config_file}' '#{vm_name}' >/dev/null 2>&1 </dev/null"
|
127
129
|
end
|
data/lib/linecook/cookbook.rb
CHANGED
@@ -1,160 +1,178 @@
|
|
1
|
-
require 'linecook/utils'
|
2
|
-
require 'lazydoc'
|
3
|
-
|
4
1
|
module Linecook
|
5
2
|
class Cookbook
|
6
3
|
class << self
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
config[PATHS_KEY] ||= [project_dir]
|
17
|
-
config[GEMS_KEY] ||= gems
|
18
|
-
|
19
|
-
new(config, project_dir)
|
4
|
+
attr_writer :default_path_map
|
5
|
+
def default_path_map
|
6
|
+
@default_path_map ||= {
|
7
|
+
:attributes => ['attributes'],
|
8
|
+
:files => ['files'],
|
9
|
+
:templates => ['templates'],
|
10
|
+
:recipes => ['recipes']
|
11
|
+
}
|
20
12
|
end
|
21
|
-
|
22
|
-
|
23
|
-
|
13
|
+
|
14
|
+
attr_writer :default_file_name
|
15
|
+
def default_file_name
|
16
|
+
@default_file_name ||= 'cookbook.yml'
|
24
17
|
end
|
25
|
-
|
26
|
-
def
|
18
|
+
|
19
|
+
def gemspecs(latest=true)
|
27
20
|
return [] unless Object.const_defined?(:Gem)
|
28
|
-
|
29
|
-
Gem.source_index
|
30
|
-
|
31
|
-
|
32
|
-
|
21
|
+
|
22
|
+
index = Gem.source_index
|
23
|
+
specs = latest ? index.latest_specs : index.gems.values
|
24
|
+
|
25
|
+
specs.select do |spec|
|
26
|
+
cookbook_file = File.expand_path(default_file_name, spec.full_gem_path)
|
27
|
+
File.exists?(cookbook_file)
|
33
28
|
end
|
34
29
|
end
|
35
30
|
end
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
attr_reader :project_dir
|
50
|
-
attr_reader :config
|
51
|
-
|
52
|
-
def initialize(config={}, project_dir='.')
|
53
|
-
@project_dir = project_dir
|
54
|
-
@config = config
|
55
|
-
end
|
56
|
-
|
57
|
-
def paths
|
58
|
-
Utils.arrayify config[PATHS_KEY]
|
59
|
-
end
|
60
|
-
|
61
|
-
def gems
|
62
|
-
Utils.arrayify config[GEMS_KEY]
|
63
|
-
end
|
64
|
-
|
65
|
-
def rewrites
|
66
|
-
config[REWRITE_KEY]
|
31
|
+
|
32
|
+
attr_reader :registry
|
33
|
+
|
34
|
+
def initialize(*project_dirs)
|
35
|
+
@registry = {}
|
36
|
+
project_dirs.each do |dir|
|
37
|
+
case dir
|
38
|
+
when String then add(dir)
|
39
|
+
when Hash then add('.', dir)
|
40
|
+
else add(*dir)
|
41
|
+
end
|
42
|
+
end
|
67
43
|
end
|
68
|
-
|
69
|
-
|
70
|
-
|
44
|
+
|
45
|
+
# Returns an array of directories comprising the path for type.
|
46
|
+
def path(type)
|
47
|
+
registry[type] || []
|
71
48
|
end
|
72
|
-
|
73
|
-
def
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
gems.collect do |name|
|
78
|
-
spec = specs[name] or raise "no such gem: #{name.inspect}"
|
79
|
-
spec.full_gem_path
|
49
|
+
|
50
|
+
def add(dir, path_map=nil)
|
51
|
+
resolve_path_map(dir, path_map).each_pair do |type, paths|
|
52
|
+
(registry[type] ||= []).concat(paths)
|
80
53
|
end
|
81
54
|
end
|
82
|
-
|
83
|
-
def
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
replacements[key] = replacement
|
55
|
+
|
56
|
+
def rm(dir, path_map=nil)
|
57
|
+
resolve_path_map(dir, path_map).each_pair do |type, paths|
|
58
|
+
if current = registry[type]
|
59
|
+
current = current - paths
|
60
|
+
if current.empty?
|
61
|
+
registry.delete(type)
|
62
|
+
else
|
63
|
+
registry[type] = current
|
64
|
+
end
|
93
65
|
end
|
94
|
-
end if rewrites
|
95
|
-
|
96
|
-
replacements.each do |key, replacement|
|
97
|
-
manifest[replacement] = manifest.delete(key)
|
98
66
|
end
|
99
|
-
|
100
|
-
manifest
|
101
67
|
end
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
manifest[type][name] = full_path
|
68
|
+
|
69
|
+
# Same as find but returns nil if no file can be found.
|
70
|
+
def _find_(type, filename, extnames=nil)
|
71
|
+
if type.nil? || filename.nil?
|
72
|
+
return nil
|
73
|
+
end
|
74
|
+
|
75
|
+
if absolute?(filename)
|
76
|
+
return File.exists?(filename) ? filename : nil
|
77
|
+
end
|
78
|
+
|
79
|
+
path(type).each do |dir|
|
80
|
+
each_full_path(dir, filename, extnames) do |full_path|
|
81
|
+
if File.exists?(full_path) && subpath?(dir, full_path)
|
82
|
+
return full_path
|
118
83
|
end
|
119
84
|
end
|
120
85
|
end
|
121
|
-
|
122
|
-
|
86
|
+
|
87
|
+
nil
|
88
|
+
end
|
89
|
+
|
90
|
+
# Searches for a file by expanding filename vs each directory in the path
|
91
|
+
# for type. The first existing full path is returned. If an array of
|
92
|
+
# extnames is provided, then each extname is tried for each directory,
|
93
|
+
# much as with Kernal.require. Absolute paths that exists are returned
|
94
|
+
# directly. Raises an error if the file cannot be found.
|
95
|
+
def find(type, source_name, extnames=nil)
|
96
|
+
_find_(type, source_name, extnames) or begin
|
97
|
+
case
|
98
|
+
when type.nil?
|
99
|
+
raise "could not find file: #{source_name.inspect} (nil type specified)"
|
100
|
+
when source_name.nil?
|
101
|
+
raise "could not find file: #{source_name.inspect}"
|
102
|
+
when absolute?(source_name)
|
103
|
+
raise "no such file: #{source_name.inspect}"
|
104
|
+
else
|
105
|
+
try_string = try_extnames?(source_name, extnames) ? " (tried #{extnames.join(', ')})" : nil
|
106
|
+
raise "could not find file: #{source_name.inspect}#{try_string}"
|
107
|
+
end
|
108
|
+
end
|
123
109
|
end
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
110
|
+
|
111
|
+
protected
|
112
|
+
|
113
|
+
if Dir.pwd[0] == ?/
|
114
|
+
def absolute?(path)
|
115
|
+
path && path[0] == ?/
|
130
116
|
end
|
131
|
-
|
132
|
-
|
133
|
-
|
117
|
+
else
|
118
|
+
def absolute?(path)
|
119
|
+
path && path =~ /^[A-z]:\//
|
134
120
|
end
|
135
|
-
|
136
|
-
manifest
|
137
121
|
end
|
138
|
-
|
139
|
-
def
|
140
|
-
|
141
|
-
|
142
|
-
|
122
|
+
|
123
|
+
def subpath?(dir, full_path)
|
124
|
+
full_path.index(dir) == 0
|
125
|
+
end
|
126
|
+
|
127
|
+
def try_extnames?(path, extnames)
|
128
|
+
extnames && File.extname(path).empty?
|
129
|
+
end
|
130
|
+
|
131
|
+
def each_full_path(dir, path, extnames=nil)
|
132
|
+
full_path = File.expand_path(path, dir)
|
133
|
+
yield full_path
|
134
|
+
|
135
|
+
if try_extnames?(path, extnames)
|
136
|
+
extnames.each do |extname|
|
137
|
+
full_path = File.expand_path("#{path}#{extname}", dir)
|
138
|
+
yield full_path
|
139
|
+
end
|
140
|
+
end
|
143
141
|
end
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
142
|
+
|
143
|
+
def resolve_path_map(dir, path_map=nil)
|
144
|
+
path_map ||= self.class.default_file_name
|
145
|
+
|
146
|
+
case path_map
|
147
|
+
when Hash
|
148
|
+
expand_path_map(dir, path_map)
|
149
|
+
when String
|
150
|
+
cookbook_file = File.expand_path(path_map, dir)
|
151
|
+
path_map = File.exists?(cookbook_file) ? YAML.load_file(cookbook_file) : nil
|
152
|
+
path_map ||= self.class.default_path_map
|
153
|
+
|
154
|
+
unless path_map.kind_of?(Hash)
|
155
|
+
raise "could not load path map: #{cookbook_file.inspect} (does not load a Hash)"
|
156
|
+
end
|
157
|
+
|
158
|
+
expand_path_map(dir, path_map)
|
159
|
+
else
|
160
|
+
raise "could not resolve path map: #{path_map.inspect} (must be String, Hash, or nil)"
|
161
|
+
end
|
150
162
|
end
|
151
|
-
|
152
|
-
def
|
153
|
-
|
154
|
-
|
155
|
-
|
163
|
+
|
164
|
+
def expand_path_map(dir, path_map)
|
165
|
+
results = Hash.new {|hash, key| hash[key] = [] }
|
166
|
+
|
167
|
+
path_map.each_pair do |type, paths|
|
168
|
+
unless paths.kind_of?(Array)
|
169
|
+
paths = [paths]
|
170
|
+
end
|
171
|
+
paths.each do |path|
|
172
|
+
results[type.to_sym] << File.expand_path(path, dir)
|
173
|
+
end
|
156
174
|
end
|
157
|
-
|
175
|
+
results
|
158
176
|
end
|
159
177
|
end
|
160
178
|
end
|