Skip to content

Commit 5013be8

Browse files
authored
Merge pull request #9299 from ruby/fix-gh-3340
Implement relative path handling for plugin paths
2 parents 57601ca + 823b6dd commit 5013be8

2 files changed

Lines changed: 121 additions & 4 deletions

File tree

bundler/lib/bundler/plugin/index.rb

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ def installed_in_plugin_root?(name)
163163
# @param [Pathname] index file path
164164
# @param [Boolean] is the index file global index
165165
def load_index(index_file, global = false)
166+
base = base_for_index(global)
167+
166168
SharedHelpers.filesystem_access(index_file, :read) do |index_f|
167169
valid_file = index_f&.exist? && !index_f.size.zero?
168170
break unless valid_file
@@ -174,8 +176,8 @@ def load_index(index_file, global = false)
174176

175177
@commands.merge!(index["commands"])
176178
@hooks.merge!(index["hooks"])
177-
@load_paths.merge!(index["load_paths"])
178-
@plugin_paths.merge!(index["plugin_paths"])
179+
@load_paths.merge!(transform_index_paths(index["load_paths"]) {|p| absolutize_path(p, base) })
180+
@plugin_paths.merge!(transform_index_paths(index["plugin_paths"]) {|p| absolutize_path(p, base) })
179181
@sources.merge!(index["sources"]) unless global
180182
end
181183
end
@@ -184,11 +186,13 @@ def load_index(index_file, global = false)
184186
# instance variables in YAML format. (The instance variables are supposed
185187
# to be only String key value pairs)
186188
def save_index
189+
base = base_for_index(false)
190+
187191
index = {
188192
"commands" => @commands,
189193
"hooks" => @hooks,
190-
"load_paths" => @load_paths,
191-
"plugin_paths" => @plugin_paths,
194+
"load_paths" => transform_index_paths(@load_paths) {|p| relativize_path(p, base) },
195+
"plugin_paths" => transform_index_paths(@plugin_paths) {|p| relativize_path(p, base) },
192196
"sources" => @sources,
193197
}
194198

@@ -198,6 +202,40 @@ def save_index
198202
File.open(index_f, "w") {|f| f.puts YAMLSerializer.dump(index) }
199203
end
200204
end
205+
206+
def base_for_index(global)
207+
global ? Plugin.global_root : Plugin.root
208+
end
209+
210+
def transform_index_paths(paths)
211+
return {} unless paths
212+
213+
paths.transform_values do |value|
214+
if value.is_a?(Array)
215+
value.map {|path| yield path }
216+
else
217+
yield value
218+
end
219+
end
220+
end
221+
222+
def relativize_path(path, base)
223+
pathname = Pathname.new(path)
224+
return path unless pathname.absolute?
225+
226+
base_path = Pathname.new(base)
227+
if pathname == base_path || pathname.to_s.start_with?(base_path.to_s + File::SEPARATOR)
228+
pathname.relative_path_from(base_path).to_s
229+
else
230+
path
231+
end
232+
end
233+
234+
def absolutize_path(path, base)
235+
pathname = Pathname.new(path)
236+
pathname = Pathname.new(base).join(pathname) unless pathname.absolute?
237+
pathname.to_s
238+
end
201239
end
202240
end
203241
end

spec/bundler/plugin/index_spec.rb

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,4 +193,83 @@
193193
include_examples "it cleans up"
194194
end
195195
end
196+
197+
describe "relative plugin paths" do
198+
let(:plugin_name) { "relative-plugin" }
199+
200+
before do
201+
Bundler::Plugin.reset!
202+
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
203+
204+
plugin_root = Bundler::Plugin.root
205+
FileUtils.mkdir_p(plugin_root)
206+
207+
path = plugin_root.join(plugin_name)
208+
FileUtils.mkdir_p(path.join("lib"))
209+
210+
index.register_plugin(plugin_name, path.to_s, [path.join("lib").to_s], [], [], [])
211+
end
212+
213+
it "stores plugin paths relative to the plugin root" do
214+
require "yaml"
215+
data = YAML.load_file(index.index_file)
216+
217+
expect(data["plugin_paths"][plugin_name]).to eq(plugin_name)
218+
expect(data["load_paths"][plugin_name]).to eq([File.join(plugin_name, "lib")])
219+
end
220+
221+
it "expands relative paths to absolute on load" do
222+
require "bundler/yaml_serializer"
223+
224+
plugin_root = Bundler::Plugin.root
225+
226+
relative_index = {
227+
"commands" => {},
228+
"hooks" => {},
229+
"load_paths" => { plugin_name => [File.join(plugin_name, "lib")] },
230+
"plugin_paths" => { plugin_name => plugin_name },
231+
"sources" => {},
232+
}
233+
234+
File.open(index.index_file, "w") {|f| f.puts Bundler::YAMLSerializer.dump(relative_index) }
235+
236+
new_index = Index.new
237+
expect(new_index.plugin_path(plugin_name)).to eq(plugin_root.join(plugin_name))
238+
expect(new_index.load_paths(plugin_name)).to eq([plugin_root.join(plugin_name, "lib").to_s])
239+
end
240+
241+
it "keeps paths outside the plugin root as absolute" do
242+
outside_path = tmp.join("outside", "external-plugin")
243+
FileUtils.mkdir_p(outside_path.join("lib"))
244+
245+
index.register_plugin("external-plugin", outside_path.to_s, [outside_path.join("lib").to_s], [], [], [])
246+
247+
require "yaml"
248+
data = YAML.load_file(index.index_file)
249+
250+
expect(data["plugin_paths"]["external-plugin"]).to eq(outside_path.to_s)
251+
expect(data["load_paths"]["external-plugin"]).to eq([outside_path.join("lib").to_s])
252+
end
253+
254+
it "reads legacy index files with absolute paths" do
255+
require "bundler/yaml_serializer"
256+
257+
plugin_root = Bundler::Plugin.root
258+
absolute_path = plugin_root.join(plugin_name).to_s
259+
260+
legacy_index = {
261+
"commands" => {},
262+
"hooks" => {},
263+
"load_paths" => { plugin_name => [File.join(absolute_path, "lib")] },
264+
"plugin_paths" => { plugin_name => absolute_path },
265+
"sources" => {},
266+
}
267+
268+
File.open(index.index_file, "w") {|f| f.puts Bundler::YAMLSerializer.dump(legacy_index) }
269+
270+
new_index = Index.new
271+
expect(new_index.plugin_path(plugin_name)).to eq(Pathname.new(absolute_path))
272+
expect(new_index.load_paths(plugin_name)).to eq([File.join(absolute_path, "lib")])
273+
end
274+
end
196275
end

0 commit comments

Comments
 (0)