diff --git a/bundler/lib/bundler/plugin/index.rb b/bundler/lib/bundler/plugin/index.rb index 0682d37772b2..99f3a875ee57 100644 --- a/bundler/lib/bundler/plugin/index.rb +++ b/bundler/lib/bundler/plugin/index.rb @@ -157,6 +157,8 @@ def installed_in_plugin_root?(name) # @param [Pathname] index file path # @param [Boolean] is the index file global index def load_index(index_file, global = false) + base = base_for_index(global) + SharedHelpers.filesystem_access(index_file, :read) do |index_f| valid_file = index_f&.exist? && !index_f.size.zero? break unless valid_file @@ -168,8 +170,8 @@ def load_index(index_file, global = false) @commands.merge!(index["commands"]) @hooks.merge!(index["hooks"]) - @load_paths.merge!(index["load_paths"]) - @plugin_paths.merge!(index["plugin_paths"]) + @load_paths.merge!(absolutize_load_paths(index["load_paths"], base)) + @plugin_paths.merge!(absolutize_paths(index["plugin_paths"], base)) @sources.merge!(index["sources"]) unless global end end @@ -178,11 +180,13 @@ def load_index(index_file, global = false) # instance variables in YAML format. (The instance variables are supposed # to be only String key value pairs) def save_index + base = base_for_index(false) + index = { "commands" => @commands, "hooks" => @hooks, - "load_paths" => @load_paths, - "plugin_paths" => @plugin_paths, + "load_paths" => relativize_load_paths(@load_paths, base), + "plugin_paths" => relativize_paths(@plugin_paths, base), "sources" => @sources, } @@ -192,6 +196,60 @@ def save_index File.open(index_f, "w") {|f| f.puts YAMLSerializer.dump(index) } end end + + def base_for_index(global) + global ? Plugin.global_root : Plugin.root + end + + def relativize_paths(paths, base) + return paths unless paths + + paths.transform_values do |path| + relativize_path(path, base) + end + end + + def relativize_load_paths(paths, base) + return paths unless paths + + paths.transform_values do |path_list| + Array(path_list).map {|path| relativize_path(path, base) } + end + end + + def absolutize_paths(paths, base) + return {} unless paths + + paths.transform_values do |path| + absolutize_path(path, base) + end + end + + def absolutize_load_paths(paths, base) + return {} unless paths + + paths.transform_values do |path_list| + Array(path_list).map {|path| absolutize_path(path, base) } + end + end + + def relativize_path(path, base) + pathname = Pathname.new(path) + return path unless pathname.absolute? + + base_path = Pathname.new(base) + if pathname == base_path || pathname.to_s.start_with?(base_path.to_s + File::SEPARATOR) + pathname.relative_path_from(base_path).to_s + else + path + end + end + + def absolutize_path(path, base) + pathname = Pathname.new(path) + pathname = Pathname.new(base).join(pathname) unless pathname.absolute? + pathname.to_s + end end end end diff --git a/bundler/spec/bundler/plugin/index_spec.rb b/bundler/spec/bundler/plugin/index_spec.rb index 565fc9b088e3..2ef4323dd344 100644 --- a/bundler/spec/bundler/plugin/index_spec.rb +++ b/bundler/spec/bundler/plugin/index_spec.rb @@ -193,4 +193,48 @@ include_examples "it cleans up" end end + + describe "relative plugin paths" do + let(:plugin_name) { "relative-plugin" } + + before do + Bundler::Plugin.reset! + allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) + + plugin_root = Bundler::Plugin.root + FileUtils.mkdir_p(plugin_root) + + path = plugin_root.join(plugin_name) + FileUtils.mkdir_p(path.join("lib")) + + index.register_plugin(plugin_name, path.to_s, [path.join("lib").to_s], [], [], []) + end + + it "stores plugin paths relative to the plugin root" do + require "yaml" + data = YAML.load_file(index.index_file) + + expect(data["plugin_paths"][plugin_name]).to eq(plugin_name) + expect(data["load_paths"][plugin_name]).to eq([File.join(plugin_name, "lib")]) + end + + it "expands relative paths when the plugin root changes" do + old_index_file = index.index_file + + new_root = tmp.join("moved_plugin_root") + FileUtils.mkdir_p(new_root) + FileUtils.cp(old_index_file, new_root.join("index")) + + Bundler::Plugin.reset! + Bundler::Plugin.instance_variable_set(:@root, nil) + allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) + allow(Bundler::Plugin).to receive(:root).and_return(new_root) + allow(Bundler::Plugin).to receive(:local_root).and_return(new_root) + + new_index = Index.new + + expect(new_index.plugin_path(plugin_name)).to eq(new_root.join(plugin_name)) + expect(new_index.load_paths(plugin_name)).to eq([new_root.join(plugin_name, "lib").to_s]) + end + end end