chef 12.4.3 → 12.5.0.alpha.1
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 +4 -4
- data/Rakefile +1 -2
- data/lib/chef.rb +1 -1
- data/lib/chef/application/solo.rb +1 -1
- data/lib/chef/application/windows_service_manager.rb +17 -12
- data/lib/chef/chef_class.rb +7 -0
- data/lib/chef/chef_fs/config.rb +22 -24
- data/lib/chef/chef_fs/file_pattern.rb +4 -15
- data/lib/chef/chef_fs/file_system/cookbook_dir.rb +1 -0
- data/lib/chef/chef_fs/knife.rb +35 -7
- data/lib/chef/chef_fs/path_utils.rb +65 -34
- data/lib/chef/constants.rb +27 -0
- data/lib/chef/delayed_evaluator.rb +21 -0
- data/lib/chef/dsl/recipe.rb +20 -2
- data/lib/chef/event_dispatch/base.rb +40 -16
- data/lib/chef/event_dispatch/dsl.rb +64 -0
- data/lib/chef/exceptions.rb +6 -1
- data/lib/chef/formatters/doc.rb +3 -1
- data/lib/chef/guard_interpreter/resource_guard_interpreter.rb +3 -1
- data/lib/chef/http/http_request.rb +1 -1
- data/lib/chef/knife/bootstrap/templates/chef-full.erb +1 -1
- data/lib/chef/knife/ssl_check.rb +3 -2
- data/lib/chef/knife/user_edit.rb +1 -2
- data/lib/chef/mixin/params_validate.rb +362 -135
- data/lib/chef/node.rb +19 -0
- data/lib/chef/platform/handler_map.rb +0 -5
- data/lib/chef/platform/rebooter.rb +1 -1
- data/lib/chef/property.rb +539 -0
- data/lib/chef/provider.rb +129 -12
- data/lib/chef/provider/deploy.rb +3 -5
- data/lib/chef/provider/lwrp_base.rb +1 -75
- data/lib/chef/provider/package.rb +1 -1
- data/lib/chef/provider/powershell_script.rb +32 -19
- data/lib/chef/provider/registry_key.rb +5 -5
- data/lib/chef/provider/service/macosx.rb +5 -1
- data/lib/chef/recipe.rb +1 -8
- data/lib/chef/resource.rb +499 -84
- data/lib/chef/resource/file/verification.rb +7 -1
- data/lib/chef/resource/lwrp_base.rb +1 -7
- data/lib/chef/run_context.rb +404 -83
- data/lib/chef/version.rb +1 -1
- data/lib/chef/win32/registry.rb +10 -2
- data/lib/chef/workstation_config_loader.rb +3 -158
- data/spec/data/run_context/cookbooks/include/recipes/default.rb +24 -0
- data/spec/data/run_context/cookbooks/include/recipes/includee.rb +3 -0
- data/spec/functional/rebooter_spec.rb +1 -1
- data/spec/functional/resource/{powershell_spec.rb → powershell_script_spec.rb} +3 -3
- data/spec/functional/win32/registry_helper_spec.rb +12 -0
- data/spec/functional/win32/service_manager_spec.rb +2 -2
- data/spec/integration/knife/chef_repo_path_spec.rb +13 -11
- data/spec/integration/recipes/recipe_dsl_spec.rb +0 -15
- data/spec/integration/recipes/resource_action_spec.rb +343 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/shared/functional/win32_service.rb +2 -1
- data/spec/unit/application/solo_spec.rb +4 -3
- data/spec/unit/chef_class_spec.rb +23 -0
- data/spec/unit/chef_fs/path_util_spec.rb +108 -0
- data/spec/unit/event_dispatch/dsl_spec.rb +87 -0
- data/spec/unit/json_compat_spec.rb +4 -3
- data/spec/unit/knife/ssl_check_spec.rb +4 -0
- data/spec/unit/mixin/params_validate_spec.rb +4 -2
- data/spec/unit/node_spec.rb +7 -0
- data/spec/unit/property/state_spec.rb +506 -0
- data/spec/unit/property/validation_spec.rb +658 -0
- data/spec/unit/property_spec.rb +968 -0
- data/spec/unit/provider/{powershell_spec.rb → powershell_script_spec.rb} +0 -0
- data/spec/unit/provider/registry_key_spec.rb +12 -0
- data/spec/unit/provider/service/macosx_spec.rb +4 -4
- data/spec/unit/provider_spec.rb +1 -3
- data/spec/unit/recipe_spec.rb +0 -4
- data/spec/unit/registry_helper_spec.rb +15 -1
- data/spec/unit/resource/file/verification_spec.rb +33 -5
- data/spec/unit/resource/{powershell_spec.rb → powershell_script_spec.rb} +0 -0
- data/spec/unit/resource_spec.rb +2 -2
- data/spec/unit/run_context/child_run_context_spec.rb +133 -0
- data/spec/unit/run_context_spec.rb +7 -0
- metadata +25 -25
- data/spec/unit/workstation_config_loader_spec.rb +0 -283
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b878f5f1ff6e11e17f28431bf13983c1eb79b7a8
|
4
|
+
data.tar.gz: 1a4961de80b9f4f9c76b0c76747a14af12818519
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f8d3e8cb4a2f4cd826942d7bf6f7b250964a1197677b30f8a3f64711d568e84241a22ad0effa4e0fe721556a58bf51cf84f418a7ef2d986943a913020f9ee13f
|
7
|
+
data.tar.gz: 54ab263002c8de538b3167e25b17bbc37808d705c33817b9634d2ad7ba032c8ea9d0b02e84514d70d4178d11ab350f58a4f7a01c04c81e044e1d54ffb4f278ba
|
data/Rakefile
CHANGED
@@ -131,7 +131,7 @@ end
|
|
131
131
|
desc "Build it, tag it and ship it"
|
132
132
|
task :ship => [:clobber_package, :gem] do
|
133
133
|
sh("git tag #{VERSION}")
|
134
|
-
sh("git push
|
134
|
+
sh("git push chef --tags")
|
135
135
|
Dir[File.expand_path("../pkg/*.gem", __FILE__)].reverse.each do |built_gem|
|
136
136
|
sh("gem push #{built_gem}")
|
137
137
|
end
|
@@ -168,4 +168,3 @@ begin
|
|
168
168
|
rescue LoadError
|
169
169
|
puts "yard is not available. (sudo) gem install yard to generate yard documentation."
|
170
170
|
end
|
171
|
-
|
data/lib/chef.rb
CHANGED
@@ -214,7 +214,7 @@ class Chef::Application::Solo < Chef::Application
|
|
214
214
|
FileUtils.mkdir_p(recipes_path)
|
215
215
|
tarball_path = File.join(recipes_path, 'recipes.tgz')
|
216
216
|
fetch_recipe_tarball(Chef::Config[:recipe_url], tarball_path)
|
217
|
-
|
217
|
+
Mixlib::ShellOut.new("tar zxvf #{tarball_path} -C #{recipes_path}").run_command
|
218
218
|
end
|
219
219
|
|
220
220
|
# json_attribs shuld be fetched after recipe_url tarball is unpacked.
|
@@ -78,7 +78,7 @@ class Chef
|
|
78
78
|
|
79
79
|
raise ArgumentError, "Service definition is not provided" if service_options.nil?
|
80
80
|
|
81
|
-
required_options = [:service_name, :service_display_name, :
|
81
|
+
required_options = [:service_name, :service_display_name, :service_description, :service_file_path]
|
82
82
|
|
83
83
|
required_options.each do |req_option|
|
84
84
|
if !service_options.has_key?(req_option)
|
@@ -92,6 +92,7 @@ class Chef
|
|
92
92
|
@service_file_path = service_options[:service_file_path]
|
93
93
|
@service_start_name = service_options[:run_as_user]
|
94
94
|
@password = service_options[:run_as_password]
|
95
|
+
@delayed_start = service_options[:delayed_start]
|
95
96
|
end
|
96
97
|
|
97
98
|
def run(params = ARGV)
|
@@ -113,17 +114,21 @@ class Chef
|
|
113
114
|
cmd = "\"#{ruby}\" \"#{@service_file_path}\" #{opts}".gsub(File::SEPARATOR, File::ALT_SEPARATOR)
|
114
115
|
|
115
116
|
::Win32::Service.new(
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
117
|
+
:service_name => @service_name,
|
118
|
+
:display_name => @service_display_name,
|
119
|
+
:description => @service_description,
|
120
|
+
# Prior to 0.8.5, win32-service creates interactive services by default,
|
121
|
+
# and we don't want that, so we need to override the service type.
|
122
|
+
:service_type => ::Win32::Service::SERVICE_WIN32_OWN_PROCESS,
|
123
|
+
:start_type => ::Win32::Service::SERVICE_AUTO_START,
|
124
|
+
:binary_path_name => cmd,
|
125
|
+
:service_start_name => @service_start_name,
|
126
|
+
:password => @password,
|
127
|
+
)
|
128
|
+
::Win32::Service.configure(
|
129
|
+
:service_name => @service_name,
|
130
|
+
:delayed_start => @delayed_start
|
131
|
+
) unless @delayed_start.nil?
|
127
132
|
puts "Service '#{@service_name}' has successfully been installed."
|
128
133
|
end
|
129
134
|
when 'status'
|
data/lib/chef/chef_class.rb
CHANGED
@@ -52,7 +52,14 @@ class Chef
|
|
52
52
|
#
|
53
53
|
attr_reader :run_context
|
54
54
|
|
55
|
+
# Register an event handler with user specified block
|
55
56
|
#
|
57
|
+
# @return[Chef::EventDispatch::Base] handler object
|
58
|
+
def event_handler(&block)
|
59
|
+
dsl = Chef::EventDispatch::DSL.new('Chef client DSL')
|
60
|
+
dsl.instance_eval(&block)
|
61
|
+
end
|
62
|
+
|
56
63
|
# Get the array of providers associated with a resource_name for the current node
|
57
64
|
#
|
58
65
|
# @param resource_name [Symbol] name of the resource as a symbol
|
data/lib/chef/chef_fs/config.rb
CHANGED
@@ -111,7 +111,7 @@ class Chef
|
|
111
111
|
#
|
112
112
|
def initialize(chef_config = Chef::Config, cwd = Dir.pwd, options = {}, ui = nil)
|
113
113
|
@chef_config = chef_config
|
114
|
-
@cwd = cwd
|
114
|
+
@cwd = File.expand_path(cwd)
|
115
115
|
@cookbook_version = options[:cookbook_version]
|
116
116
|
|
117
117
|
if @chef_config[:repo_mode] == 'everything' && is_hosted? && !ui.nil?
|
@@ -166,34 +166,37 @@ class Chef
|
|
166
166
|
# server_path('/home/jkeiser/chef_repo/cookbooks/blah') == '/cookbooks/blah'
|
167
167
|
# server_path('/home/*/chef_repo/cookbooks/blah') == nil
|
168
168
|
#
|
169
|
-
# If there are multiple
|
170
|
-
# have separate paths), and cwd+the
|
171
|
-
#
|
172
|
-
#
|
169
|
+
# If there are multiple different, manually specified paths to object locations
|
170
|
+
# (cookbooks, roles, data bags, etc. can all have separate paths), and cwd+the
|
171
|
+
# path reaches into one of them, we will return a path relative to the first
|
172
|
+
# one to match it. Otherwise we expect the path provided to be to the chef
|
173
|
+
# repo path itself. Paths that are not available on the server are not supported.
|
173
174
|
#
|
174
175
|
# Globs are allowed as well, but globs outside server paths are NOT
|
175
176
|
# (presently) supported. See above examples. TODO support that.
|
176
177
|
#
|
177
178
|
# If the path does not reach into ANY specified directory, nil is returned.
|
178
179
|
def server_path(file_path)
|
179
|
-
|
180
|
-
absolute_pwd = Chef::ChefFS::PathUtils.realest_path(File.expand_path(file_path, pwd))
|
180
|
+
target_path = Chef::ChefFS::PathUtils.realest_path(file_path, @cwd)
|
181
181
|
|
182
182
|
# Check all object paths (cookbooks_dir, data_bags_dir, etc.)
|
183
|
+
# These are either manually specified by the user or autogenerated relative
|
184
|
+
# to chef_repo_path.
|
183
185
|
object_paths.each_pair do |name, paths|
|
184
186
|
paths.each do |path|
|
185
|
-
|
186
|
-
if PathUtils.
|
187
|
-
|
188
|
-
return relative_path == '.' ? "/#{name}" : "/#{name}/#{relative_path}"
|
187
|
+
object_abs_path = Chef::ChefFS::PathUtils.realest_path(path, @cwd)
|
188
|
+
if relative_path = PathUtils.descendant_path(target_path, object_abs_path)
|
189
|
+
return Chef::ChefFS::PathUtils.join("/#{name}", relative_path)
|
189
190
|
end
|
190
191
|
end
|
191
192
|
end
|
192
193
|
|
193
194
|
# Check chef_repo_path
|
194
195
|
Array(@chef_config[:chef_repo_path]).flatten.each do |chef_repo_path|
|
195
|
-
|
196
|
-
if
|
196
|
+
# We're using realest_path here but we really don't need to - we can just expand the
|
197
|
+
# path and use realpath because a repo_path if provided *must* exist.
|
198
|
+
realest_chef_repo_path = Chef::ChefFS::PathUtils.realest_path(chef_repo_path, @cwd)
|
199
|
+
if Chef::ChefFS::PathUtils.os_path_eq?(target_path, realest_chef_repo_path)
|
197
200
|
return '/'
|
198
201
|
end
|
199
202
|
end
|
@@ -201,15 +204,10 @@ class Chef
|
|
201
204
|
nil
|
202
205
|
end
|
203
206
|
|
204
|
-
# The current directory, relative to server root
|
207
|
+
# The current directory, relative to server root. This is a case-sensitive server path.
|
208
|
+
# It only exists if the current directory is a child of one of the recognized object_paths below.
|
205
209
|
def base_path
|
206
|
-
@base_path ||=
|
207
|
-
if @chef_config[:chef_repo_path]
|
208
|
-
server_path(File.expand_path(@cwd))
|
209
|
-
else
|
210
|
-
nil
|
211
|
-
end
|
212
|
-
end
|
210
|
+
@base_path ||= server_path(@cwd)
|
213
211
|
end
|
214
212
|
|
215
213
|
# Print the given server path, relative to the current directory
|
@@ -217,10 +215,10 @@ class Chef
|
|
217
215
|
server_path = entry.path
|
218
216
|
if base_path && server_path[0,base_path.length] == base_path
|
219
217
|
if server_path == base_path
|
220
|
-
return
|
221
|
-
elsif server_path[base_path.length,1] ==
|
218
|
+
return '.'
|
219
|
+
elsif server_path[base_path.length,1] == '/'
|
222
220
|
return server_path[base_path.length + 1, server_path.length - base_path.length - 1]
|
223
|
-
elsif base_path ==
|
221
|
+
elsif base_path == '/' && server_path[0,1] == '/'
|
224
222
|
return server_path[1, server_path.length - 1]
|
225
223
|
end
|
226
224
|
end
|
@@ -72,7 +72,7 @@ class Chef
|
|
72
72
|
def could_match_children?(path)
|
73
73
|
return false if path == '' # Empty string is not a path
|
74
74
|
|
75
|
-
argument_is_absolute =
|
75
|
+
argument_is_absolute = Chef::ChefFS::PathUtils::is_absolute?(path)
|
76
76
|
return false if is_absolute != argument_is_absolute
|
77
77
|
path = path[1,path.length-1] if argument_is_absolute
|
78
78
|
|
@@ -111,7 +111,7 @@ class Chef
|
|
111
111
|
#
|
112
112
|
# This method assumes +could_match_children?(path)+ is +true+.
|
113
113
|
def exact_child_name_under(path)
|
114
|
-
path = path[1,path.length-1] if
|
114
|
+
path = path[1,path.length-1] if Chef::ChefFS::PathUtils::is_absolute?(path)
|
115
115
|
dirs_in_path = Chef::ChefFS::PathUtils::split(path).length
|
116
116
|
return nil if exact_parts.length <= dirs_in_path
|
117
117
|
return exact_parts[dirs_in_path]
|
@@ -149,7 +149,7 @@ class Chef
|
|
149
149
|
# abc/*/def.match?('abc/foo/def') == true
|
150
150
|
# abc/*/def.match?('abc/foo') == false
|
151
151
|
def match?(path)
|
152
|
-
argument_is_absolute =
|
152
|
+
argument_is_absolute = Chef::ChefFS::PathUtils::is_absolute?(path)
|
153
153
|
return false if is_absolute != argument_is_absolute
|
154
154
|
path = path[1,path.length-1] if argument_is_absolute
|
155
155
|
!!regexp.match(path)
|
@@ -160,17 +160,6 @@ class Chef
|
|
160
160
|
pattern
|
161
161
|
end
|
162
162
|
|
163
|
-
# Given a relative file pattern and a directory, makes a new file pattern
|
164
|
-
# starting with the directory.
|
165
|
-
#
|
166
|
-
# FilePattern.relative_to('/usr/local', 'bin/*grok') == FilePattern.new('/usr/local/bin/*grok')
|
167
|
-
#
|
168
|
-
# BUG: this does not support patterns starting with <tt>..</tt>
|
169
|
-
def self.relative_to(dir, pattern)
|
170
|
-
return FilePattern.new(pattern) if pattern =~ /^#{Chef::ChefFS::PathUtils::regexp_path_separator}/
|
171
|
-
FilePattern.new(Chef::ChefFS::PathUtils::join(dir, pattern))
|
172
|
-
end
|
173
|
-
|
174
163
|
private
|
175
164
|
|
176
165
|
def regexp
|
@@ -195,7 +184,7 @@ class Chef
|
|
195
184
|
|
196
185
|
def calculate
|
197
186
|
if !@regexp
|
198
|
-
@is_absolute =
|
187
|
+
@is_absolute = Chef::ChefFS::PathUtils::is_absolute?(@pattern)
|
199
188
|
|
200
189
|
full_regexp_parts = []
|
201
190
|
normalized_parts = []
|
data/lib/chef/chef_fs/knife.rb
CHANGED
@@ -17,6 +17,7 @@
|
|
17
17
|
#
|
18
18
|
|
19
19
|
require 'chef/knife'
|
20
|
+
require 'pathname'
|
20
21
|
|
21
22
|
class Chef
|
22
23
|
module ChefFS
|
@@ -63,7 +64,7 @@ class Chef
|
|
63
64
|
# --chef-repo-path forcibly overrides all other paths
|
64
65
|
if config[:chef_repo_path]
|
65
66
|
Chef::Config[:chef_repo_path] = config[:chef_repo_path]
|
66
|
-
|
67
|
+
Chef::ChefFS::Config::INFLECTIONS.each_value do |variable_name|
|
67
68
|
Chef::Config.delete("#{variable_name}_path".to_sym)
|
68
69
|
end
|
69
70
|
end
|
@@ -98,14 +99,41 @@ class Chef
|
|
98
99
|
end
|
99
100
|
|
100
101
|
def pattern_arg_from(arg)
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
#
|
105
|
-
|
102
|
+
inferred_path = nil
|
103
|
+
if Chef::ChefFS::PathUtils.is_absolute?(arg)
|
104
|
+
# We should be able to use this as-is - but the user might have incorrectly provided
|
105
|
+
# us with a path that is based off of the OS root path instead of the Chef-FS root.
|
106
|
+
# Do a quick and dirty sanity check.
|
107
|
+
if possible_server_path = @chef_fs_config.server_path(arg)
|
108
|
+
ui.warn("The absolute path provided is suspicious: #{arg}")
|
109
|
+
ui.warn("If you wish to refer to a file location, please provide a path that is rooted at the chef-repo.")
|
110
|
+
ui.warn("Consider writing '#{possible_server_path}' instead of '#{arg}'")
|
111
|
+
end
|
112
|
+
# Use the original path because we can't be sure.
|
113
|
+
inferred_path = arg
|
114
|
+
elsif arg[0,1] == '~'
|
115
|
+
# Let's be nice and fix it if possible - but warn the user.
|
116
|
+
ui.warn("A path relative to a user home directory has been provided: #{arg}")
|
117
|
+
ui.warn("Paths provided need to be rooted at the chef-repo being considered or be relative paths.")
|
118
|
+
inferred_path = @chef_fs_config.server_path(arg)
|
119
|
+
ui.warn("Using '#{inferred_path}' as the path instead of '#{arg}'.")
|
120
|
+
elsif Pathname.new(arg).absolute?
|
121
|
+
# It is definitely a system absolute path (such as C:\ or \\foo\bar) but it cannot be
|
122
|
+
# interpreted as a Chef-FS absolute path. Again attempt to be nice but warn the user.
|
123
|
+
ui.warn("An absolute file system path that isn't a server path was provided: #{arg}")
|
124
|
+
ui.warn("Paths provided need to be rooted at the chef-repo being considered or be relative paths.")
|
125
|
+
inferred_path = @chef_fs_config.server_path(arg)
|
126
|
+
ui.warn("Using '#{inferred_path}' as the path instead of '#{arg}'.")
|
127
|
+
elsif @chef_fs_config.base_path.nil?
|
128
|
+
# These are all relative paths. We can't resolve and root paths unless we are in the
|
129
|
+
# chef repo.
|
130
|
+
ui.error("Attempt to use relative path '#{arg}' when current directory is outside the repository path.")
|
131
|
+
ui.error("Current working directory is '#{@chef_fs_config.cwd}'.")
|
106
132
|
exit(1)
|
133
|
+
else
|
134
|
+
inferred_path = Chef::ChefFS::PathUtils::join(@chef_fs_config.base_path, arg)
|
107
135
|
end
|
108
|
-
Chef::ChefFS::FilePattern.
|
136
|
+
Chef::ChefFS::FilePattern.new(inferred_path)
|
109
137
|
end
|
110
138
|
|
111
139
|
def format_path(entry)
|
@@ -23,31 +23,31 @@ class Chef
|
|
23
23
|
module ChefFS
|
24
24
|
class PathUtils
|
25
25
|
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
26
|
+
# A Chef-FS path is a path in a chef-repository that can be used to address
|
27
|
+
# both files on a local file-system as well as objects on a chef server.
|
28
|
+
# These paths are stricter than file-system paths allowed on various OSes.
|
29
|
+
# Absolute Chef-FS paths begin with "/" (on windows, "\" is acceptable as well).
|
30
|
+
# "/" is used as the path element separator (on windows, "\" is acceptable as well).
|
31
|
+
# No directory/path element may contain a literal "\" character. Any such characters
|
32
|
+
# encountered are either dealt with as separators (on windows) or as escape
|
33
|
+
# characters (on POSIX systems). Relative Chef-FS paths may use ".." or "." but
|
34
|
+
# may never use these to back-out of the root of a Chef-FS path. Any such extraneous
|
35
|
+
# ".."s are ignored.
|
36
|
+
# Chef-FS paths are case sensitive (since the paths on the server are).
|
37
|
+
# On OSes with case insensitive paths, you may be unable to locally deal with two
|
38
|
+
# objects whose server paths only differ by case. OTOH, the case of path segments
|
39
|
+
# that are outside the Chef-FS root (such as when looking at a file-system absolute
|
40
|
+
# path to discover the Chef-FS root path) are handled in accordance to the rules
|
41
|
+
# of the local file-system and OS.
|
42
42
|
|
43
43
|
def self.join(*parts)
|
44
44
|
return "" if parts.length == 0
|
45
45
|
# Determine if it started with a slash
|
46
46
|
absolute = parts[0].length == 0 || parts[0].length > 0 && parts[0] =~ /^#{regexp_path_separator}/
|
47
47
|
# Remove leading and trailing slashes from each part so that the join will work (and the slash at the end will go away)
|
48
|
-
parts = parts.map { |part| part.gsub(
|
48
|
+
parts = parts.map { |part| part.gsub(/^#{regexp_path_separator}+|#{regexp_path_separator}+$/, '') }
|
49
49
|
# Don't join empty bits
|
50
|
-
result = parts.select { |part| part !=
|
50
|
+
result = parts.select { |part| part != '' }.join('/')
|
51
51
|
# Put the / back on
|
52
52
|
absolute ? "/#{result}" : result
|
53
53
|
end
|
@@ -60,36 +60,67 @@ class Chef
|
|
60
60
|
Chef::ChefFS::windows? ? '[\/\\\\]' : '/'
|
61
61
|
end
|
62
62
|
|
63
|
+
# Given a server path, determines if it is absolute.
|
64
|
+
def self.is_absolute?(path)
|
65
|
+
!!(path =~ /^#{regexp_path_separator}/)
|
66
|
+
end
|
63
67
|
# Given a path which may only be partly real (i.e. /x/y/z when only /x exists,
|
64
68
|
# or /x/y/*/blah when /x/y/z/blah exists), call File.realpath on the biggest
|
65
|
-
# part that actually exists.
|
69
|
+
# part that actually exists. The paths operated on here are not Chef-FS paths.
|
70
|
+
# These are OS paths that may contain symlinks but may not also fully exist.
|
66
71
|
#
|
67
72
|
# If /x is a symlink to /blarghle, and has no subdirectories, then:
|
68
73
|
# PathUtils.realest_path('/x/y/z') == '/blarghle/y/z'
|
69
74
|
# PathUtils.realest_path('/x/*/z') == '/blarghle/*/z'
|
70
75
|
# PathUtils.realest_path('/*/y/z') == '/*/y/z'
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
76
|
+
#
|
77
|
+
# TODO: Move this to wherever util/path_helper is these days.
|
78
|
+
def self.realest_path(path, cwd = Dir.pwd)
|
79
|
+
path = File.expand_path(path, cwd)
|
80
|
+
parent_path = File.dirname(path)
|
81
|
+
suffix = []
|
82
|
+
|
83
|
+
# File.dirname happens to return the path as its own dirname if you're
|
84
|
+
# at the root (such as at \\foo\bar, C:\ or /)
|
85
|
+
until parent_path == path do
|
86
|
+
# This can occur if a path such as "C:" is given. Ruby gives the parent as "C:."
|
87
|
+
# for reasons only it knows.
|
88
|
+
raise ArgumentError "Invalid path segment #{path}" if parent_path.length > path.length
|
89
|
+
begin
|
90
|
+
path = File.realpath(path)
|
91
|
+
break
|
92
|
+
rescue Errno::ENOENT
|
93
|
+
suffix << File.basename(path)
|
94
|
+
path = parent_path
|
95
|
+
parent_path = File.dirname(path)
|
81
96
|
end
|
82
97
|
end
|
98
|
+
File.join(path, *suffix.reverse)
|
83
99
|
end
|
84
100
|
|
85
|
-
|
86
|
-
|
87
|
-
|
101
|
+
# Compares two path fragments according to the case-sentitivity of the host platform.
|
102
|
+
def self.os_path_eq?(left, right)
|
103
|
+
Chef::ChefFS::windows? ? left.casecmp(right) == 0 : left == right
|
88
104
|
end
|
89
105
|
|
90
|
-
|
91
|
-
|
106
|
+
# Given two general OS-dependent file paths, determines the relative path of the
|
107
|
+
# child with respect to the ancestor. Both child and ancestor must exist and be
|
108
|
+
# fully resolved - this is strictly a lexical comparison. No trailing slashes
|
109
|
+
# and other shenanigans are allowed.
|
110
|
+
#
|
111
|
+
# TODO: Move this to util/path_helper.
|
112
|
+
def self.descendant_path(path, ancestor)
|
113
|
+
candidate_fragment = path[0, ancestor.length]
|
114
|
+
return nil unless PathUtils.os_path_eq?(candidate_fragment, ancestor)
|
115
|
+
if ancestor.length == path.length
|
116
|
+
''
|
117
|
+
elsif path[ancestor.length,1] =~ /#{PathUtils.regexp_path_separator}/
|
118
|
+
path[ancestor.length+1..-1]
|
119
|
+
else
|
120
|
+
nil
|
121
|
+
end
|
92
122
|
end
|
123
|
+
|
93
124
|
end
|
94
125
|
end
|
95
126
|
end
|