# frozen_string_literal: true module Bundler class BundlerError < StandardError def self.status_code(code) define_method(:status_code) { code } if match = BundlerError.all_errors.find {|_k, v| v == code } error, _ = match raise ArgumentError, "Trying to register #{self} for status code #{code} but #{error} is already registered" end BundlerError.all_errors[self] = code end def self.all_errors @all_errors ||= {} end end class GemfileError < BundlerError; status_code(4); end class InstallError < BundlerError; status_code(5); end # Internal error, should be rescued class SolveFailure < BundlerError; status_code(6); end class GemNotFound < BundlerError; status_code(7); end class InstallHookError < BundlerError; status_code(8); end class GemfileNotFound < BundlerError; status_code(10); end class GitError < BundlerError; status_code(11); end class DeprecatedError < BundlerError; status_code(12); end class PathError < BundlerError; status_code(13); end class GemspecError < BundlerError; status_code(14); end class InvalidOption < BundlerError; status_code(15); end class ProductionError < BundlerError; status_code(16); end class HTTPError < BundlerError status_code(17) def filter_uri(uri) URICredentialsFilter.credential_filtered_uri(uri) end end class RubyVersionMismatch < BundlerError; status_code(18); end class SecurityError < BundlerError; status_code(19); end class LockfileError < BundlerError; status_code(20); end class CyclicDependencyError < BundlerError; status_code(21); end class GemfileLockNotFound < BundlerError; status_code(22); end class PluginError < BundlerError; status_code(29); end class ThreadCreationError < BundlerError; status_code(33); end class APIResponseMismatchError < BundlerError; status_code(34); end class APIResponseInvalidDependenciesError < BundlerError; status_code(35); end class GemfileEvalError < GemfileError; end class MarshalError < StandardError; end class ChecksumMismatchError < SecurityError def initialize(lock_name, existing, checksum) @lock_name = lock_name @existing = existing @checksum = checksum end def message <<~MESSAGE Bundler found mismatched checksums. This is a potential security risk. #{@lock_name} #{@existing.to_lock} from #{@existing.sources.join("\n and ")} #{@lock_name} #{@checksum.to_lock} from #{@checksum.sources.join("\n and ")} #{mismatch_resolution_instructions} To ignore checksum security warnings, disable checksum validation with `bundle config set --local disable_checksum_validation true` MESSAGE end def mismatch_resolution_instructions removable, remote = [@existing, @checksum].partition(&:removable?) case removable.size when 0 msg = +"Mismatched checksums each have an authoritative source:\n" msg << " 1. #{@existing.sources.reject(&:removable?).map(&:to_s).join(" and ")}\n" msg << " 2. #{@checksum.sources.reject(&:removable?).map(&:to_s).join(" and ")}\n" msg << "You may need to alter your Gemfile sources to resolve this issue.\n" when 1 msg = +"If you trust #{remote.first.sources.first}, to resolve this issue you can:\n" msg << removable.first.removal_instructions when 2 msg = +"To resolve this issue you can either:\n" msg << @checksum.removal_instructions msg << "or if you are sure that the new checksum from #{@checksum.sources.first} is correct:\n" msg << @existing.removal_instructions end end status_code(37) end class PermissionError < BundlerError def initialize(path, permission_type = :write) @path = path @permission_type = permission_type end def action case @permission_type when :read then "read from" when :write then "write to" when :executable, :exec then "execute" else @permission_type.to_s end end def permission_type case @permission_type when :create "executable permissions for all parent directories and write permissions for `#{parent_folder}`" else "#{@permission_type} permissions for that path" end end def parent_folder File.dirname(@path) end def message "There was an error while trying to #{action} `#{@path}`. " \ "It is likely that you need to grant #{permission_type}." end status_code(23) end class GemRequireError < BundlerError attr_reader :orig_exception def initialize(orig_exception, msg) full_message = msg + "\nGem Load Error is: #{orig_exception.message}\n"\ "Backtrace for gem load error is:\n"\ "#{orig_exception.backtrace.join("\n")}\n"\ "Bundler Error Backtrace:\n" super(full_message) @orig_exception = orig_exception end status_code(24) end class YamlSyntaxError < BundlerError attr_reader :orig_exception def initialize(orig_exception, msg) super(msg) @orig_exception = orig_exception end status_code(25) end class TemporaryResourceError < PermissionError def message "There was an error while trying to #{action} `#{@path}`. " \ "Some resource was temporarily unavailable. It's suggested that you try" \ "the operation again." end status_code(26) end class VirtualProtocolError < BundlerError def message "There was an error relating to virtualization and file access. " \ "It is likely that you need to grant access to or mount some file system correctly." end status_code(27) end class OperationNotSupportedError < PermissionError def message "Attempting to #{action} `#{@path}` is unsupported by your OS." end status_code(28) end class NoSpaceOnDeviceError < PermissionError def message "There was an error while trying to #{action} `#{@path}`. " \ "There was insufficient space remaining on the device." end status_code(31) end class GenericSystemCallError < BundlerError attr_reader :underlying_error def initialize(underlying_error, message) @underlying_error = underlying_error super("#{message}\nThe underlying system error is #{@underlying_error.class}: #{@underlying_error}") end status_code(32) end class DirectoryRemovalError < BundlerError def initialize(orig_exception, msg) full_message = "#{msg}.\n" \ "The underlying error was #{orig_exception.class}: #{orig_exception.message}, with backtrace:\n" \ " #{orig_exception.backtrace.join("\n ")}\n\n" \ "Bundler Error Backtrace:" super(full_message) end status_code(36) end class InsecureInstallPathError < BundlerError def initialize(path) @path = path end def message "The installation path is insecure. Bundler cannot continue.\n" \ "#{@path} is world-writable (without sticky bit).\n" \ "Bundler cannot safely replace gems in world-writeable directories due to potential vulnerabilities.\n" \ "Please change the permissions of this directory or choose a different install path." end status_code(38) end end