2021-12-21 00:50:44 -05:00
# frozen_string_literal: true
module Bundler
#
# This class handles installing and switching to the version of bundler needed
# by an application.
#
class SelfManager
def restart_with_locked_bundler_if_needed
return unless needs_switching? && installed?
2022-01-18 23:28:23 -05:00
restart_with ( lockfile_version )
2021-12-21 00:50:44 -05:00
end
def install_locked_bundler_and_restart_with_it_if_needed
return unless needs_switching?
2022-01-18 23:28:23 -05:00
Bundler . ui . info \
" Bundler #{ current_version } is running, but your lockfile was generated with #{ lockfile_version } . " \
" Installing Bundler #{ lockfile_version } and restarting using that version. "
install_and_restart_with ( lockfile_version )
end
def update_bundler_and_restart_with_it_if_needed ( target )
return unless autoswitching_applies?
spec = resolve_update_version_from ( target )
return unless spec
version = spec . version
Bundler . ui . info " Updating bundler to #{ version } . "
install ( spec )
restart_with ( version )
2021-12-21 00:50:44 -05:00
end
private
2022-01-18 23:28:23 -05:00
def install_and_restart_with ( version )
requirement = Gem :: Requirement . new ( version )
spec = find_latest_matching_spec ( requirement )
2021-12-26 19:41:55 -05:00
2022-01-18 23:28:23 -05:00
if spec . nil?
Bundler . ui . warn " Your lockfile is locked to a version of bundler ( #{ lockfile_version } ) that doesn't exist at https://rubygems.org/. Going on using #{ current_version } "
return
end
2021-12-21 00:50:44 -05:00
2022-01-18 23:28:23 -05:00
install ( spec )
2021-12-21 00:50:44 -05:00
rescue StandardError = > e
Bundler . ui . trace e
Bundler . ui . warn " There was an error installing the locked bundler version ( #{ lockfile_version } ), rerun with the `--verbose` flag for more details. Going on using bundler #{ current_version } . "
else
2022-01-18 23:28:23 -05:00
restart_with ( version )
2021-12-21 00:50:44 -05:00
end
2022-01-18 23:28:23 -05:00
def install ( spec )
spec . source . install ( spec )
2021-12-26 19:41:55 -05:00
end
2022-01-18 23:28:23 -05:00
def restart_with ( version )
2021-12-21 00:50:44 -05:00
configured_gem_home = ENV [ " GEM_HOME " ]
configured_gem_path = ENV [ " GEM_PATH " ]
2021-12-22 19:21:36 -05:00
cmd = [ $PROGRAM_NAME , * ARGV ]
cmd . unshift ( Gem . ruby ) unless File . executable? ( $PROGRAM_NAME )
2021-12-21 00:50:44 -05:00
Bundler . with_original_env do
Kernel . exec (
2022-01-18 23:28:23 -05:00
{ " GEM_HOME " = > configured_gem_home , " GEM_PATH " = > configured_gem_path , " BUNDLER_VERSION " = > version . to_s } ,
2021-12-22 19:21:36 -05:00
* cmd
2021-12-21 00:50:44 -05:00
)
end
end
def needs_switching?
2022-01-18 23:28:23 -05:00
autoswitching_applies? &&
released? ( lockfile_version ) &&
! running? ( lockfile_version ) &&
! updating?
end
def autoswitching_applies?
2021-12-21 00:50:44 -05:00
ENV [ " BUNDLER_VERSION " ] . nil? &&
Bundler . rubygems . supports_bundler_trampolining? &&
SharedHelpers . in_bundle? &&
2022-01-18 23:28:23 -05:00
lockfile_version
end
def resolve_update_version_from ( target )
requirement = Gem :: Requirement . new ( target )
update_candidate = find_latest_matching_spec ( requirement )
if update_candidate . nil?
raise InvalidOption , " The `bundle update --bundler` target version ( #{ target } ) does not exist "
end
resolved_version = update_candidate . version
needs_update = requirement . specific? ? ! running? ( resolved_version ) : running_older_than? ( resolved_version )
return unless needs_update
update_candidate
end
def local_specs
@local_specs || = Bundler :: Source :: Rubygems . new ( " allow_local " = > true ) . specs . select { | spec | spec . name == " bundler " }
end
def remote_specs
@remote_specs || = begin
source = Bundler :: Source :: Rubygems . new ( " remotes " = > " https://rubygems.org " )
source . remote!
source . add_dependency_names ( " bundler " )
source . specs
end
end
def find_latest_matching_spec ( requirement )
local_result = find_latest_matching_spec_from_collection ( local_specs , requirement )
return local_result if local_result && requirement . specific?
remote_result = find_latest_matching_spec_from_collection ( remote_specs , requirement )
return remote_result if local_result . nil?
[ local_result , remote_result ] . max
end
def find_latest_matching_spec_from_collection ( specs , requirement )
specs . sort . reverse_each . find { | spec | requirement . satisfied_by? ( spec . version ) }
end
def running? ( version )
version == current_version
end
def running_older_than? ( version )
current_version < version
end
def released? ( version )
! version . to_s . end_with? ( " .dev " )
2021-12-26 08:42:02 -05:00
end
def updating?
" update " . start_with? ( ARGV . first || " " ) && ARGV [ 1 .. - 1 ] . any? { | a | a . start_with? ( " --bundler " ) }
2021-12-21 00:50:44 -05:00
end
def installed?
Bundler . configure
2022-01-18 23:28:23 -05:00
Bundler . rubygems . find_bundler ( lockfile_version . to_s )
2021-12-21 00:50:44 -05:00
end
def current_version
2022-01-18 23:28:23 -05:00
@current_version || = Gem :: Version . new ( Bundler :: VERSION )
2021-12-21 00:50:44 -05:00
end
def lockfile_version
2022-01-18 23:28:23 -05:00
return @lockfile_version if defined? ( @lockfile_version )
parsed_version = Bundler :: LockfileParser . bundled_with
@lockfile_version = parsed_version ? Gem :: Version . new ( parsed_version ) : nil
2021-12-21 00:50:44 -05:00
end
end
end