Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix plugin installation from gemfile #6957

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Manifest.txt
Expand Up @@ -162,6 +162,7 @@ bundler/lib/bundler/plugin/api/source.rb
bundler/lib/bundler/plugin/dsl.rb
bundler/lib/bundler/plugin/events.rb
bundler/lib/bundler/plugin/index.rb
bundler/lib/bundler/plugin/index_definition.rb
bundler/lib/bundler/plugin/installer.rb
bundler/lib/bundler/plugin/installer/git.rb
bundler/lib/bundler/plugin/installer/path.rb
Expand Down
4 changes: 2 additions & 2 deletions bundler/lib/bundler/cli/update.rb
Expand Up @@ -15,8 +15,6 @@ def run

Bundler.self_manager.update_bundler_and_restart_with_it_if_needed(update_bundler) if update_bundler

Plugin.gemfile_install(Bundler.default_gemfile) if Bundler.feature_flag.plugins?

sources = Array(options[:source])
groups = Array(options[:group]).map(&:to_sym)

Expand All @@ -33,6 +31,8 @@ def run

conservative = options[:conservative]

Plugin.gemfile_install(Bundler.default_gemfile, unlock: full_update && !conservative) if Bundler.feature_flag.plugins?

if full_update
if conservative
Bundler.definition(conservative: conservative)
Expand Down
84 changes: 68 additions & 16 deletions bundler/lib/bundler/definition.rb
Expand Up @@ -14,6 +14,7 @@ class << self
attr_reader(
:dependencies,
:locked_deps,
:plugin_dependencies,
:locked_gems,
:platforms,
:ruby_version,
Expand Down Expand Up @@ -56,7 +57,19 @@ def self.build(gemfile, lockfile, unlock)
# to be updated or true if all gems should be updated
# @param ruby_version [Bundler::RubyVersion, nil] Requested Ruby Version
# @param optional_groups [Array(String)] A list of optional groups
def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, optional_groups = [], gemfiles = [])
# @param lockfile_contents [String, nil] The contents of the lockfile
# @param plugin_dependencies [Array(Bundler::Dependency)] array of plugin dependencies from Gemfile
# @param plugin_sources [Bundler::SourceList]
def initialize(lockfile,
dependencies,
sources,
unlock,
ruby_version = nil,
optional_groups = [],
gemfiles = [],
lockfile_contents = nil,
plugin_dependencies = [],
plugin_sources = SourceList.new)
if [true, false].include?(unlock)
@unlocking_bundler = false
@unlocking = unlock
Expand All @@ -65,17 +78,18 @@ def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, opti
@unlocking = unlock.any? {|_k, v| !Array(v).empty? }
end

@dependencies = dependencies
@sources = sources
@unlock = unlock
@optional_groups = optional_groups
@prefer_local = false
@specs = nil
@ruby_version = ruby_version
@gemfiles = gemfiles
@dependencies = dependencies
@sources = sources
@plugin_dependencies = plugin_dependencies
@plugin_sources = plugin_sources
@unlock = unlock
@optional_groups = optional_groups
@prefer_local = false
@specs = nil
@ruby_version = ruby_version
@gemfiles = gemfiles

@lockfile = lockfile
@lockfile_contents = String.new

@locked_bundler_version = nil
@resolved_bundler_version = nil
Expand All @@ -84,8 +98,8 @@ def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, opti
@new_platform = nil
@removed_platform = nil

if lockfile_exists?
@lockfile_contents = Bundler.read_file(lockfile)
if lockfile_exists? || lockfile_contents
@lockfile_contents = lockfile_contents || Bundler.read_file(lockfile)
@locked_gems = LockfileParser.new(@lockfile_contents)
@locked_platforms = @locked_gems.platforms
@platforms = @locked_platforms.dup
Expand All @@ -106,6 +120,7 @@ def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, opti
@locked_sources = []
end
else
@lockfile_contents = String.new
@unlock = {}
@platforms = []
@locked_gems = nil
Expand Down Expand Up @@ -228,10 +243,18 @@ def requested_dependencies
dependencies_for(requested_groups)
end

def requested_plugin_dependencies
plugin_dependencies_for(requested_groups)
end

def current_dependencies
filter_relevant(dependencies)
end

def current_plugin_dependencies
filter_relevant(plugin_dependencies)
end

def current_locked_dependencies
filter_relevant(locked_dependencies)
end
Expand Down Expand Up @@ -274,6 +297,19 @@ def dependencies_for(groups)
deps
end

def plugin_dependencies_for(groups)
groups.map!(&:to_sym)
plugins = current_plugin_dependencies # always returns a new array
plugins.select! do |d|
if RUBY_VERSION >= "3.1"
d.groups.intersect?(groups)
else
!(d.groups & groups).empty?
end
end
plugins
end

# Resolve all the dependencies specified in Gemfile. It ensures that
# dependencies that have been already resolved via locked file and are fresh
# are reused when resolving dependencies
Expand Down Expand Up @@ -307,11 +343,14 @@ def resolve
end

def spec_git_paths
sources.git_sources.map {|s| File.realpath(s.path) if File.exist?(s.path) }.compact
# plugin sources from the main Gemfile run we never instructed that they are cached,
# but they were, by the plugin run of the gemfile
plugin_sources.git_sources.each(&:cached!)
(sources.git_sources + plugin_sources.git_sources).uniq.filter_map {|s| File.realpath(s.path) if File.exist?(s.path) }
end

def groups
dependencies.map(&:groups).flatten.uniq
(dependencies + plugin_dependencies).map(&:groups).flatten.uniq
end

def lock(file_or_preserve_unknown_sections = false, preserve_unknown_sections_or_unused = false)
Expand Down Expand Up @@ -417,6 +456,7 @@ def ensure_equivalent_gemfile_and_lockfile(explicit_flag = false)
def validate_runtime!
validate_ruby!
validate_platforms!
validate_plugins!
end

def validate_ruby!
Expand Down Expand Up @@ -452,6 +492,18 @@ def validate_platforms!
"Add the current platform to the lockfile with\n`bundle lock --add-platform #{local_platform}` and try again."
end

def validate_plugins!
missing_plugins_list = []
requested_plugin_dependencies.each do |plugin|
missing_plugins_list << plugin unless Plugin.installed?(plugin.name)
end
if missing_plugins_list.size > 1
raise GemNotFound, "Plugins #{missing_plugins_list.join(", ")} are not installed"
elsif missing_plugins_list.any?
raise GemNotFound, "Plugin #{missing_plugins_list.join(", ")} is not installed"
end
end

def add_platform(platform)
@new_platform ||= !@platforms.include?(platform)
@platforms |= [platform]
Expand All @@ -470,8 +522,8 @@ def most_specific_locked_platform
end
end

attr_reader :sources
private :sources
attr_reader :sources, :plugin_sources
private :sources, :plugin_sources

def nothing_changed?
return false unless lockfile_exists?
Expand Down
64 changes: 44 additions & 20 deletions bundler/lib/bundler/dsl.rb
Expand Up @@ -27,8 +27,12 @@ def self.evaluate(gemfile, lockfile, unlock)
def initialize
@source = nil
@sources = SourceList.new
@plugin_sources = SourceList.new
@current_sources = @sources
@git_sources = {}
@dependencies = []
@plugin_dependencies = []
@current_dependencies = @dependencies
@groups = []
@install_conditionals = []
@optional_groups = []
Expand Down Expand Up @@ -102,7 +106,7 @@ def gem(name, *args)
dep = Dependency.new(name, version, options)

# if there's already a dependency with this name we try to prefer one
if current = @dependencies.find {|d| d.name == dep.name }
if current = @current_dependencies.find {|d| d.name == dep.name }
if current.requirement != dep.requirement
current_requirement_open = current.requirements_list.include?(">= 0")

Expand Down Expand Up @@ -135,7 +139,7 @@ def gem(name, *args)

# Always prefer the dependency from the Gemfile
if current.gemspec_dev_dep?
@dependencies.delete(current)
@current_dependencies.delete(current)
elsif dep.gemspec_dev_dep?
return
elsif current.source != dep.source
Expand All @@ -150,7 +154,7 @@ def gem(name, *args)
end
end

@dependencies << dep
@current_dependencies << dep
end

def source(source, *args, &blk)
Expand All @@ -160,20 +164,22 @@ def source(source, *args, &blk)

if options.key?("type")
options["type"] = options["type"].to_s
unless Plugin.source?(options["type"])
unless (source_plugin = Plugin.source_plugin(options["type"]))
raise InvalidOption, "No plugin sources available for #{options["type"]}"
end

unless block_given?
raise InvalidOption, "You need to pass a block to #source with :type option"
end

plugin(source_plugin) unless @plugin_dependencies.any? {|d| d.name == source_plugin }

source_opts = options.merge("uri" => source)
with_source(@sources.add_plugin_source(options["type"], source_opts), &blk)
with_source(add_source(:add_plugin_source, options["type"], source_opts), &blk)
elsif block_given?
with_source(@sources.add_rubygems_source("remotes" => source), &blk)
with_source(add_source(:add_rubygems_source, "remotes" => source), &blk)
else
@sources.add_global_rubygems_remote(source)
add_source(:add_global_rubygems_remote, source)
end
end

Expand All @@ -199,8 +205,7 @@ def path(path, options = {}, &blk)

source_options["global"] = true unless block_given?

source = @sources.add_path_source(source_options)
with_source(source, &blk)
with_source(add_source(:add_path_source, source_options), &blk)
end

def git(uri, options = {}, &blk)
Expand All @@ -215,20 +220,29 @@ def git(uri, options = {}, &blk)
raise DeprecatedError, msg
end

with_source(@sources.add_git_source(normalize_hash(options).merge("uri" => uri)), &blk)
options = normalize_hash(options).merge("uri" => uri)
with_source(add_source(:add_git_source, options), &blk)
end

def github(repo, options = {})
def github(repo, options = {}, &blk)
raise ArgumentError, "GitHub sources require a block" unless block_given?
github_uri = @git_sources["github"].call(repo)
git_options = normalize_hash(options).merge("uri" => github_uri)
git_source = @sources.add_git_source(git_options)
with_source(git_source) { yield }
github_uri = @git_sources["github"].call(repo)
options = normalize_hash(options).merge("uri" => github_uri)
with_source(add_source(:add_git_source, options), &blk)
end

def to_definition(lockfile, unlock)
def to_definition(lockfile, unlock, lockfile_contents: nil)
check_primary_source_safety
Definition.new(lockfile, @dependencies, @sources, unlock, @ruby_version, @optional_groups, @gemfiles)
Definition.new(lockfile,
@dependencies,
@sources,
unlock,
@ruby_version,
@optional_groups,
@gemfiles,
lockfile_contents,
@plugin_dependencies,
@plugin_sources)
end

def group(*args, &blk)
Expand Down Expand Up @@ -270,8 +284,13 @@ def env(name)
@env = old
end

def plugin(*args)
# Pass on
def plugin(name, *args)
@current_sources = @plugin_sources
@current_dependencies = @plugin_dependencies
gem(name, *args)
ensure
@current_sources = @sources
@current_dependencies = @dependencies
end

def method_missing(name, *args)
Expand All @@ -285,6 +304,11 @@ def check_primary_source_safety

private

def add_source(method, *args)
@plugin_sources.send(method, *args) unless @plugin_sources.equal?(@current_sources)
@current_sources.send(method, *args)
end

def add_git_sources
git_source(:github) do |repo_name|
if repo_name =~ GITHUB_PULL_REQUEST_URL
Expand Down Expand Up @@ -384,7 +408,7 @@ def normalize_options(name, version, opts)
# Save sources passed in a key
if opts.key?("source")
source = normalize_source(opts["source"])
opts["source"] = @sources.add_rubygems_source("remotes" => source)
opts["source"] = @current_sources.add_rubygems_source("remotes" => source)
end

git_name = (git_names & opts.keys).last
Expand Down