From 76de7a92b90e216c6645e93a3a034bc2f4257b03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Tue, 12 Jul 2022 12:33:12 +0200 Subject: [PATCH] [rubygems/rubygems] Fix misleading error if compact index cannot be copied Previously if `~/.bundle/cache/compact_index/rubygems.org.*/version` were owned by root with read-only access, `bundle install` would fail with a misleading error message. For example: ``` There was an error while trying to write to `/tmp/bundler-compact-index-20220711-1823-npllre/versions`. It is likely that you need to grant write permissions for that path. ``` This happened because the EACCESS error was caught by `SharedHelpers.filesystem_access`, which makes it look like the target directory is at fault instead of the source. We can't simply drop this guard because that causes the opposite problem: the permission error appears to come from the source instead of the target, since `CompactIndexClient::Cache#lines` also wraps read access errors. Instead, bring a minimal implementation of `FileUtils.cp` and nest calls to `SharedHelpers.filesystem_access` properly. https://github.com/rubygems/rubygems/commit/320822c070 Co-authored-by: Stan Hu --- lib/bundler/compact_index_client/updater.rb | 19 ++++++++++++++++--- .../install/gems/compact_index_spec.rb | 19 +++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/lib/bundler/compact_index_client/updater.rb b/lib/bundler/compact_index_client/updater.rb index d9b9cec0d4..5b430dfbe2 100644 --- a/lib/bundler/compact_index_client/updater.rb +++ b/lib/bundler/compact_index_client/updater.rb @@ -31,9 +31,8 @@ module Bundler # first try to fetch any new bytes on the existing file if retrying.nil? && local_path.file? - SharedHelpers.filesystem_access(local_temp_path) do - FileUtils.cp local_path, local_temp_path - end + copy_file local_path, local_temp_path + headers["If-None-Match"] = etag_for(local_temp_path) headers["Range"] = if local_temp_path.size.nonzero? @@ -98,6 +97,20 @@ module Bundler SharedHelpers.digest(:MD5).hexdigest(File.read(path)) end end + + private + + def copy_file(source, dest) + SharedHelpers.filesystem_access(source, :read) do + File.open(source, "r") do |s| + SharedHelpers.filesystem_access(dest, :write) do + File.open(dest, "wb", s.stat.mode) do |f| + IO.copy_stream(s, f) + end + end + end + end + end end end end diff --git a/spec/bundler/install/gems/compact_index_spec.rb b/spec/bundler/install/gems/compact_index_spec.rb index 72ad40e24f..5c25b1092a 100644 --- a/spec/bundler/install/gems/compact_index_spec.rb +++ b/spec/bundler/install/gems/compact_index_spec.rb @@ -163,6 +163,25 @@ The checksum of /versions does not match the checksum provided by the server! So expect(the_bundle).to include_gems "rack 1.0.0" end + it "shows proper path when permission errors happen", :permissions do + gemfile <<-G + source "#{source_uri}" + gem "rack" + G + + versions = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index", + "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions") + FileUtils.mkdir_p(File.dirname(versions)) + FileUtils.touch(versions) + FileUtils.chmod("-r", versions) + + bundle :install, :artifice => "compact_index", :raise_on_error => false + + expect(err).to include( + "There was an error while trying to read from `#{versions}`. It is likely that you need to grant read permissions for that path." + ) + end + it "falls back when the user's home directory does not exist or is not writable" do ENV["HOME"] = tmp("missing_home").to_s