diff options
Diffstat (limited to 'lib/bundler/plugin')
| -rw-r--r-- | lib/bundler/plugin/api/source.rb | 47 | ||||
| -rw-r--r-- | lib/bundler/plugin/events.rb | 78 | ||||
| -rw-r--r-- | lib/bundler/plugin/index.rb | 79 | ||||
| -rw-r--r-- | lib/bundler/plugin/installer.rb | 60 | ||||
| -rw-r--r-- | lib/bundler/plugin/installer/git.rb | 4 | ||||
| -rw-r--r-- | lib/bundler/plugin/installer/path.rb | 26 | ||||
| -rw-r--r-- | lib/bundler/plugin/installer/rubygems.rb | 8 | ||||
| -rw-r--r-- | lib/bundler/plugin/source_list.rb | 10 |
8 files changed, 243 insertions, 69 deletions
diff --git a/lib/bundler/plugin/api/source.rb b/lib/bundler/plugin/api/source.rb index d70a16f4bc..798326673a 100644 --- a/lib/bundler/plugin/api/source.rb +++ b/lib/bundler/plugin/api/source.rb @@ -39,7 +39,7 @@ module Bundler # is present to be compatible with `Definition` and is used by # rubygems source. module Source - attr_reader :uri, :options, :name + attr_reader :uri, :options, :name, :checksum_store attr_accessor :dependency_names def initialize(opts) @@ -48,6 +48,7 @@ module Bundler @uri = opts["uri"] @type = opts["type"] @name = opts["name"] || "#{@type} at #{@uri}" + @checksum_store = Checksum::Store.new end # This is used by the default `spec` method to constructs the @@ -66,13 +67,21 @@ module Bundler # to check out same version of gem later. # # There options are passed when the source plugin is created from the - # lock file. + # lockfile. # # @return [Hash] def options_to_lock {} end + # Download the gem specified by the spec at appropriate path. + # + # A source plugin can implement this method to split the download and the + # installation of a gem. + # + # @return [Boolean] Whether the download of the gem succeeded. + def download(spec, opts); end + # Install the gem specified by the spec at appropriate path. # `install_path` provides a sufficient default, if the source can only # satisfy one gem, but is not binding. @@ -95,7 +104,7 @@ module Bundler # # Note: Do not override if you don't know what you are doing. def post_install(spec, disable_exts = false) - opts = { :env_shebang => false, :disable_extensions => disable_exts } + opts = { env_shebang: false, disable_extensions: disable_exts } installer = Bundler::Source::Path::Installer.new(spec, opts) installer.post_install end @@ -106,7 +115,7 @@ module Bundler def install_path @install_path ||= begin - base_name = File.basename(Bundler::URI.parse(uri).normalize.path) + base_name = File.basename(Gem::URI.parse(uri).normalize.path) gem_install_dir.join("#{base_name}-#{uri_hash[0..11]}") end @@ -130,7 +139,7 @@ module Bundler Bundler::Index.build do |index| files.each do |file| next unless spec = Bundler.load_gemspec(file) - Bundler.rubygems.set_installed_by_version(spec) + spec.installed_by_version = Gem::VERSION spec.source = self Bundler.rubygems.validate(spec) @@ -175,7 +184,7 @@ module Bundler # # This is used by `app_cache_path` def app_cache_dirname - base_name = File.basename(Bundler::URI.parse(uri).normalize.path) + base_name = File.basename(Gem::URI.parse(uri).normalize.path) "#{base_name}-#{uri_hash}" end @@ -195,6 +204,7 @@ module Bundler FileUtils.rm_rf(new_cache_path) FileUtils.cp_r(install_path, new_cache_path) + FileUtils.rm_rf(app_cache_path.join(".git")) FileUtils.touch(app_cache_path.join(".bundlecache")) end @@ -244,7 +254,21 @@ module Bundler specs.unmet_dependency_names end + # Used by definition. + # # Note: Do not override if you don't know what you are doing. + def spec_names + specs.spec_names + end + + # Used by definition. + # + # Note: Do not override if you don't know what you are doing. + def add_dependency_names(names) + @dependencies |= Array(names) + end + + # NOTE: Do not override if you don't know what you are doing. def can_lock?(spec) spec.source == self end @@ -269,8 +293,9 @@ module Bundler def to_s "plugin source for #{@type} with uri #{@uri}" end + alias_method :identifier, :to_s - # Note: Do not override if you don't know what you are doing. + # NOTE: Do not override if you don't know what you are doing. def include?(other) other == self end @@ -279,7 +304,7 @@ module Bundler SharedHelpers.digest(:SHA1).hexdigest(uri) end - # Note: Do not override if you don't know what you are doing. + # NOTE: Do not override if you don't know what you are doing. def gem_install_dir Bundler.install_path end @@ -294,12 +319,6 @@ module Bundler end # @private - # Returns true - def bundler_plugin_api_source? - true - end - - # @private # This API on source might not be stable, and for now we expect plugins # to download all specs in `#specs`, so we implement the method for # compatibility purposes and leave it undocumented (and don't support) diff --git a/lib/bundler/plugin/events.rb b/lib/bundler/plugin/events.rb index bc037d1af5..3fbf60307e 100644 --- a/lib/bundler/plugin/events.rb +++ b/lib/bundler/plugin/events.rb @@ -31,6 +31,54 @@ module Bundler end # @!parse + # A hook called before the Gemfile is evaluated + # Includes the Gemfile path and the Lockfile path + # GEM_BEFORE_EVAL = "before-eval" + define :GEM_BEFORE_EVAL, "before-eval" + + # @!parse + # A hook called after the Gemfile is evaluated + # Includes a Bundler::Definition + # GEM_AFTER_EVAL = "after-eval" + define :GEM_AFTER_EVAL, "after-eval" + + # @!parse + # A hook called before any gems install + # Includes an Array of Bundler::Dependency objects + # GEM_BEFORE_INSTALL_ALL = "before-install-all" + define :GEM_BEFORE_INSTALL_ALL, "before-install-all" + + # @!parse + # A hook called before each individual gem is downloaded from a remote source. + # Includes a spec-like object responding to the Gem::Specification API + # (for example, a Bundler spec proxy such as Bundler::EndpointSpecification + # or Bundler::RemoteSpecification). Does not fire when the gem is already + # present at the initial download-cache check. + # GEM_BEFORE_FETCH = "before-fetch" + define :GEM_BEFORE_FETCH, "before-fetch" + + # @!parse + # A hook called after each individual gem is downloaded from a remote source. + # Includes a spec-like object responding to the Gem::Specification API + # (for example, a Bundler spec proxy such as Bundler::EndpointSpecification + # or Bundler::RemoteSpecification). Does not fire when the gem is already + # present at the initial download-cache check. + # GEM_AFTER_FETCH = "after-fetch" + define :GEM_AFTER_FETCH, "after-fetch" + + # @!parse + # A hook called before a git source is fetched or checked out. + # Includes a Bundler::Source::Git reference. + # GIT_BEFORE_FETCH = "before-git-fetch" + define :GIT_BEFORE_FETCH, "before-git-fetch" + + # @!parse + # A hook called after a git source is fetched or checked out. + # Includes a Bundler::Source::Git reference. + # GIT_AFTER_FETCH = "after-git-fetch" + define :GIT_AFTER_FETCH, "after-git-fetch" + + # @!parse # A hook called before each individual gem is installed # Includes a Bundler::ParallelInstaller::SpecInstallation. # No state, error, post_install_message will be present as nothing has installed yet @@ -46,16 +94,34 @@ module Bundler define :GEM_AFTER_INSTALL, "after-install" # @!parse - # A hook called before any gems install - # Includes an Array of Bundler::Dependency objects - # GEM_BEFORE_INSTALL_ALL = "before-install-all" - define :GEM_BEFORE_INSTALL_ALL, "before-install-all" - - # @!parse # A hook called after any gems install # Includes an Array of Bundler::Dependency objects # GEM_AFTER_INSTALL_ALL = "after-install-all" define :GEM_AFTER_INSTALL_ALL, "after-install-all" + + # @!parse + # A hook called before any gems require + # Includes an Array of Bundler::Dependency objects. + # GEM_BEFORE_REQUIRE_ALL = "before-require-all" + define :GEM_BEFORE_REQUIRE_ALL, "before-require-all" + + # @!parse + # A hook called before each individual gem is required + # Includes a Bundler::Dependency. + # GEM_BEFORE_REQUIRE = "before-require" + define :GEM_BEFORE_REQUIRE, "before-require" + + # @!parse + # A hook called after each individual gem is required + # Includes a Bundler::Dependency. + # GEM_AFTER_REQUIRE = "after-require" + define :GEM_AFTER_REQUIRE, "after-require" + + # @!parse + # A hook called after all gems required + # Includes an Array of Bundler::Dependency objects. + # GEM_AFTER_REQUIRE_ALL = "after-require-all" + define :GEM_AFTER_REQUIRE_ALL, "after-require-all" end end end diff --git a/lib/bundler/plugin/index.rb b/lib/bundler/plugin/index.rb index 819bc60588..1dfb061304 100644 --- a/lib/bundler/plugin/index.rb +++ b/lib/bundler/plugin/index.rb @@ -31,9 +31,13 @@ module Bundler begin load_index(global_index_file, true) - rescue GenericSystemCallError + rescue PermissionError # no need to fail when on a read-only FS, for example nil + rescue ArgumentError => e + # ruby 3.4 checks writability in Dir.tmpdir + raise unless e.message&.include?("could not find a temporary directory") + nil end load_index(local_index_file) if SharedHelpers.in_bundle? end @@ -74,7 +78,10 @@ module Bundler def unregister_plugin(name) @commands.delete_if {|_, v| v == name } @sources.delete_if {|_, v| v == name } - @hooks.each {|_, plugin_names| plugin_names.delete(name) } + @hooks.each do |hook, names| + names.delete(name) + @hooks.delete(hook) if names.empty? + end @plugin_paths.delete(name) @load_paths.delete(name) save_index @@ -112,6 +119,12 @@ module Bundler @plugin_paths[name] end + def up_to_date?(spec) + path = installed?(spec.name) + + path == spec.full_gem_path + end + def installed_plugins @plugin_paths.keys end @@ -133,6 +146,14 @@ module Bundler @hooks[event] || [] end + # This plugin is installed inside the .bundle/plugin directory, + # and thus is managed solely by Bundler + def installed_in_plugin_root?(name) + return false unless (path = installed?(name)) + + path.start_with?("#{Plugin.root}/") + end + private # Reads the index file from the directory and initializes the instance @@ -142,8 +163,10 @@ module Bundler # @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 && index_f.exist? && !index_f.size.zero? + valid_file = index_f&.exist? && !index_f.size.zero? break unless valid_file data = index_f.read @@ -153,8 +176,8 @@ module Bundler @commands.merge!(index["commands"]) @hooks.merge!(index["hooks"]) - @load_paths.merge!(index["load_paths"]) - @plugin_paths.merge!(index["plugin_paths"]) + @load_paths.merge!(transform_index_paths(index["load_paths"]) {|p| absolutize_path(p, base) }) + @plugin_paths.merge!(transform_index_paths(index["plugin_paths"]) {|p| absolutize_path(p, base) }) @sources.merge!(index["sources"]) unless global end end @@ -163,12 +186,14 @@ module Bundler # 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, - "sources" => @sources, + "commands" => @commands, + "hooks" => @hooks, + "load_paths" => transform_index_paths(@load_paths) {|p| relativize_path(p, base) }, + "plugin_paths" => transform_index_paths(@plugin_paths) {|p| relativize_path(p, base) }, + "sources" => @sources, } require_relative "../yaml_serializer" @@ -177,6 +202,40 @@ module Bundler 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 transform_index_paths(paths) + return {} unless paths + + paths.transform_values do |value| + if value.is_a?(Array) + value.map {|path| yield path } + else + yield value + end + 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/lib/bundler/plugin/installer.rb b/lib/bundler/plugin/installer.rb index 980b0e29b8..9be8b36843 100644 --- a/lib/bundler/plugin/installer.rb +++ b/lib/bundler/plugin/installer.rb @@ -10,6 +10,7 @@ module Bundler class Installer autoload :Rubygems, File.expand_path("installer/rubygems", __dir__) autoload :Git, File.expand_path("installer/git", __dir__) + autoload :Path, File.expand_path("installer/path", __dir__) def install(names, options) check_sources_consistency!(options) @@ -18,10 +19,10 @@ module Bundler if options[:git] install_git(names, version, options) - elsif options[:local_git] - install_local_git(names, version, options) + elsif options[:path] + install_path(names, version, options[:path]) else - sources = options[:source] || Bundler.rubygems.sources + sources = options[:source] || Gem.sources install_rubygems(names, version, sources) end end @@ -33,7 +34,7 @@ module Bundler # @return [Hash] map of names to their specs they are installed with def install_definition(definition) def definition.lock(*); end - definition.resolve_remotely! + definition.remotely! specs = definition.specs install_from_specs specs @@ -42,23 +43,33 @@ module Bundler private def check_sources_consistency!(options) - if options.key?(:git) && options.key?(:local_git) - raise InvalidOption, "Remote and local plugin git sources can't be both specified" + if (options.keys & [:source, :git, :path]).length > 1 + raise InvalidOption, "Only one of --source, --git, or --path may be specified" + end + + if (options.key?(:branch) || options.key?(:ref)) && !options.key?(:git) + raise InvalidOption, "--#{options.key?(:branch) ? "branch" : "ref"} can only be used with git sources" + end + + if options.key?(:branch) && options.key?(:ref) + raise InvalidOption, "--branch and --ref can't be both specified" end end def install_git(names, version, options) - uri = options.delete(:git) - options["uri"] = uri + source_list = SourceList.new + source = source_list.add_git_source({ "uri" => options[:git], + "branch" => options[:branch], + "ref" => options[:ref] }) - install_all_sources(names, version, options, options[:source]) + install_all_sources(names, version, source_list, source) end - def install_local_git(names, version, options) - uri = options.delete(:local_git) - options["uri"] = uri + def install_path(names, version, path) + source_list = SourceList.new + source = source_list.add_path_source({ "path" => path, "root_path" => SharedHelpers.pwd }) - install_all_sources(names, version, options, options[:source]) + install_all_sources(names, version, source_list, source) end # Installs the plugin from rubygems source and returns the path where the @@ -70,19 +81,23 @@ module Bundler # # @return [Hash] map of names to the specs of plugins installed def install_rubygems(names, version, sources) - install_all_sources(names, version, nil, sources) + source_list = SourceList.new + + Array(sources).each {|remote| source_list.add_global_rubygems_remote(remote) } + + install_all_sources(names, version, source_list) end - def install_all_sources(names, version, git_source_options, rubygems_source) - source_list = SourceList.new + def install_all_sources(names, version, source_list, source = nil) + deps = names.map {|name| Dependency.new(name, version, { "source" => source }) } - source_list.add_git_source(git_source_options) if git_source_options - source_list.global_rubygems_source = rubygems_source if rubygems_source + Bundler.configure_gem_home_and_path(Plugin.root) - deps = names.map {|name| Dependency.new name, version } + Bundler.settings.temporary(deployment: false, frozen: false) do + definition = Definition.new(nil, deps, source_list, true) - definition = Definition.new(nil, deps, source_list, true) - install_definition(definition) + install_definition(definition) + end end # Installs the plugins and deps from the provided specs and returns map of @@ -95,7 +110,8 @@ module Bundler paths = {} specs.each do |spec| - spec.source.install spec + spec.source.download(spec) + spec.source.install(spec) paths[spec.name] = spec end diff --git a/lib/bundler/plugin/installer/git.rb b/lib/bundler/plugin/installer/git.rb index fbb6c5e40e..deec5e99b3 100644 --- a/lib/bundler/plugin/installer/git.rb +++ b/lib/bundler/plugin/installer/git.rb @@ -20,10 +20,6 @@ module Bundler end end - def version_message(spec) - "#{spec.name} #{spec.version}" - end - def root Plugin.root end diff --git a/lib/bundler/plugin/installer/path.rb b/lib/bundler/plugin/installer/path.rb new file mode 100644 index 0000000000..58c4924eb0 --- /dev/null +++ b/lib/bundler/plugin/installer/path.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Bundler + module Plugin + class Installer + class Path < Bundler::Source::Path + def root + SharedHelpers.in_bundle? ? Bundler.root : Plugin.root + end + + def eql?(other) + return unless other.class == self.class + expanded_original_path == other.expanded_original_path && + version == other.version + end + + alias_method :==, :eql? + + def generate_bin(spec, disable_extensions = false) + # Need to find a way without code duplication + # For now, we can ignore this + end + end + end + end +end diff --git a/lib/bundler/plugin/installer/rubygems.rb b/lib/bundler/plugin/installer/rubygems.rb index e144c14b24..cb5db9c30e 100644 --- a/lib/bundler/plugin/installer/rubygems.rb +++ b/lib/bundler/plugin/installer/rubygems.rb @@ -4,16 +4,8 @@ module Bundler module Plugin class Installer class Rubygems < Bundler::Source::Rubygems - def version_message(spec) - "#{spec.name} #{spec.version}" - end - private - def requires_sudo? - false # Will change on implementation of project level plugins - end - def rubygems_dir Plugin.root end diff --git a/lib/bundler/plugin/source_list.rb b/lib/bundler/plugin/source_list.rb index 547661cf2f..d929ade29e 100644 --- a/lib/bundler/plugin/source_list.rb +++ b/lib/bundler/plugin/source_list.rb @@ -9,6 +9,10 @@ module Bundler add_source_to_list Plugin::Installer::Git.new(options), git_sources end + def add_path_source(options = {}) + add_source_to_list Plugin::Installer::Path.new(options), path_sources + end + def add_rubygems_source(options = {}) add_source_to_list Plugin::Installer::Rubygems.new(options), @rubygems_sources end @@ -17,13 +21,9 @@ module Bundler path_sources + git_sources + rubygems_sources + [metadata_source] end - def default_source - git_sources.first || global_rubygems_source - end - private - def rubygems_aggregate_class + def source_class Plugin::Installer::Rubygems end end |
