summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Rodríguez <[email protected]>2022-09-09 13:00:21 +0200
committerHiroshi SHIBATA <[email protected]>2025-02-27 13:32:32 +0900
commit3e78a2f58e60c2cb7a6fdd2aacdea03b31ca21eb (patch)
treee9f4f4f08214ba415f37d34d83f51f063ad561b0
parent0d62037fc0626855c36359e4a8a02936b592f9d9 (diff)
[rubygems/rubygems] Improve error message when on read-only filesystems
If we fail to write the lockfile, give a better error. https://github.com/rubygems/rubygems/commit/81a08d6eda
-rw-r--r--lib/bundler/definition.rb85
-rw-r--r--spec/bundler/runtime/setup_spec.rb31
-rw-r--r--spec/bundler/support/hax.rb14
3 files changed, 94 insertions, 36 deletions
diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb
index 5e3dc06cf2..557315af4f 100644
--- a/lib/bundler/definition.rb
+++ b/lib/bundler/definition.rb
@@ -412,39 +412,8 @@ module Bundler
raise ProductionError, "Frozen mode is set, but there's no lockfile" unless lockfile_exists?
- added = []
- deleted = []
- changed = []
-
- added.concat @new_platforms.map {|p| "* platform: #{p}" }
- deleted.concat @removed_platforms.map {|p| "* platform: #{p}" }
-
- added.concat new_deps.map {|d| "* #{pretty_dep(d)}" } if new_deps.any?
- deleted.concat deleted_deps.map {|d| "* #{pretty_dep(d)}" } if deleted_deps.any?
-
- both_sources = Hash.new {|h, k| h[k] = [] }
- current_dependencies.each {|d| both_sources[d.name][0] = d }
- current_locked_dependencies.each {|d| both_sources[d.name][1] = d }
-
- both_sources.each do |name, (dep, lock_dep)|
- next if dep.nil? || lock_dep.nil?
-
- gemfile_source = dep.source || default_source
- lock_source = lock_dep.source || default_source
- next if lock_source.include?(gemfile_source)
-
- gemfile_source_name = dep.source ? gemfile_source.to_gemfile : "no specified source"
- lockfile_source_name = lock_dep.source ? lock_source.to_gemfile : "no specified source"
- changed << "* #{name} from `#{lockfile_source_name}` to `#{gemfile_source_name}`"
- end
-
- reason = resolve_needed? ? change_reason : "some dependencies were deleted from your gemfile"
- msg = String.new
- msg << "#{reason.capitalize.strip}, but the lockfile can't be updated because frozen mode is set"
- msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any?
- msg << "\n\nYou have deleted from the Gemfile:\n" << deleted.join("\n") if deleted.any?
- msg << "\n\nYou have changed in the Gemfile:\n" << changed.join("\n") if changed.any?
- msg << "\n\nRun `bundle install` elsewhere and add the updated #{SharedHelpers.relative_gemfile_path} to version control.\n" unless unlocking?
+ msg = lockfile_changes_summary("frozen mode is set")
+ return unless msg
unless explicit_flag
suggested_command = unless Bundler.settings.locations("frozen").keys.include?(:env)
@@ -454,7 +423,7 @@ module Bundler
"freeze by running `#{suggested_command}`." if suggested_command
end
- raise ProductionError, msg if added.any? || deleted.any? || changed.any? || resolve_needed?
+ raise ProductionError, msg
end
def validate_runtime!
@@ -539,6 +508,46 @@ module Bundler
private
+ def lockfile_changes_summary(update_refused_reason)
+ added = []
+ deleted = []
+ changed = []
+
+ added.concat @new_platforms.map {|p| "* platform: #{p}" }
+ deleted.concat @removed_platforms.map {|p| "* platform: #{p}" }
+
+ added.concat new_deps.map {|d| "* #{pretty_dep(d)}" } if new_deps.any?
+ deleted.concat deleted_deps.map {|d| "* #{pretty_dep(d)}" } if deleted_deps.any?
+
+ both_sources = Hash.new {|h, k| h[k] = [] }
+ current_dependencies.each {|d| both_sources[d.name][0] = d }
+ current_locked_dependencies.each {|d| both_sources[d.name][1] = d }
+
+ both_sources.each do |name, (dep, lock_dep)|
+ next if dep.nil? || lock_dep.nil?
+
+ gemfile_source = dep.source || default_source
+ lock_source = lock_dep.source || default_source
+ next if lock_source.include?(gemfile_source)
+
+ gemfile_source_name = dep.source ? gemfile_source.to_gemfile : "no specified source"
+ lockfile_source_name = lock_dep.source ? lock_source.to_gemfile : "no specified source"
+ changed << "* #{name} from `#{lockfile_source_name}` to `#{gemfile_source_name}`"
+ end
+
+ return unless added.any? || deleted.any? || changed.any? || resolve_needed?
+
+ reason = resolve_needed? ? change_reason : "some dependencies were deleted from your gemfile"
+
+ msg = String.new
+ msg << "#{reason.capitalize.strip}, but the lockfile can't be updated because #{update_refused_reason}"
+ msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any?
+ msg << "\n\nYou have deleted from the Gemfile:\n" << deleted.join("\n") if deleted.any?
+ msg << "\n\nYou have changed in the Gemfile:\n" << changed.join("\n") if changed.any?
+ msg << "\n\nRun `bundle install` elsewhere and add the updated #{SharedHelpers.relative_gemfile_path} to version control.\n" unless unlocking?
+ msg
+ end
+
def install_needed?
resolve_needed? || missing_specs?
end
@@ -599,8 +608,12 @@ module Bundler
return
end
- SharedHelpers.filesystem_access(file) do |p|
- File.open(p, "wb") {|f| f.puts(contents) }
+ begin
+ SharedHelpers.filesystem_access(file) do |p|
+ File.open(p, "wb") {|f| f.puts(contents) }
+ end
+ rescue ReadOnlyFileSystemError
+ raise ProductionError, lockfile_changes_summary("file system is read-only")
end
end
diff --git a/spec/bundler/runtime/setup_spec.rb b/spec/bundler/runtime/setup_spec.rb
index 106f1edc57..5edf464827 100644
--- a/spec/bundler/runtime/setup_spec.rb
+++ b/spec/bundler/runtime/setup_spec.rb
@@ -1656,4 +1656,35 @@ end
expect(err).to be_empty
expect(out).to include("Installing myrack 1.0.0")
end
+
+ context "in a read-only filesystem" do
+ before do
+ gemfile <<-G
+ source "https://gem.repo4"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo4/
+
+ PLATFORMS
+ x86_64-darwin-19
+
+ DEPENDENCIES
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "should fail loudly if the lockfile platforms don't include the current platform" do
+ simulate_platform "x86_64-linux" do
+ ruby <<-RUBY, raise_on_error: false, env: { "BUNDLER_SPEC_READ_ONLY" => "true", "BUNDLER_FORCE_TTY" => "true" }
+ require "bundler/setup"
+ RUBY
+ end
+
+ expect(err).to include("Your lockfile does not include the current platform, but the lockfile can't be updated because file system is read-only")
+ end
+ end
end
diff --git a/spec/bundler/support/hax.rb b/spec/bundler/support/hax.rb
index 715af6cab6..01bad64ce7 100644
--- a/spec/bundler/support/hax.rb
+++ b/spec/bundler/support/hax.rb
@@ -37,4 +37,18 @@ module Gem
if ENV["BUNDLER_SPEC_GEM_SOURCES"]
self.sources = [ENV["BUNDLER_SPEC_GEM_SOURCES"]]
end
+
+ if ENV["BUNDLER_SPEC_READ_ONLY"]
+ module ReadOnly
+ def open(file, mode)
+ if file != IO::NULL && mode == "wb"
+ raise Errno::EROFS
+ else
+ super
+ end
+ end
+ end
+
+ File.singleton_class.prepend ReadOnly
+ end
end