Skip to content

Commit e8f0d0a

Browse files
committed
fix specs for bundler 3
importantly, bundler 3 automatically caches, cleans, and prunes gems by default, so need to always keep track of plugins, even for the regular gemfile run, and include plugins in the list of known gems when cleaning up
1 parent 49ce798 commit e8f0d0a

File tree

11 files changed

+134
-130
lines changed

11 files changed

+134
-130
lines changed

bundler/lib/bundler/definition.rb

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class << self
1414
attr_reader(
1515
:dependencies,
1616
:locked_deps,
17-
:plugins,
17+
:plugin_dependencies,
1818
:locked_gems,
1919
:platforms,
2020
:ruby_version,
@@ -58,7 +58,8 @@ def self.build(gemfile, lockfile, unlock)
5858
# @param ruby_version [Bundler::RubyVersion, nil] Requested Ruby Version
5959
# @param optional_groups [Array(String)] A list of optional groups
6060
# @param lockfile_contents [String, nil] The contents of the lockfile
61-
# @param plugins [Array(Bundler::Dependency)] array of plugin dependencies from Gemfile
61+
# @param plugin_dependencies [Array(Bundler::Dependency)] array of plugin dependencies from Gemfile
62+
# @param plugin_sources [Bundler::SourceList]
6263
def initialize(lockfile,
6364
dependencies,
6465
sources,
@@ -67,7 +68,8 @@ def initialize(lockfile,
6768
optional_groups = [],
6869
gemfiles = [],
6970
lockfile_contents = nil,
70-
plugins = [])
71+
plugin_dependencies = [],
72+
plugin_sources = SourceList.new)
7173
if [true, false].include?(unlock)
7274
@unlocking_bundler = false
7375
@unlocking = unlock
@@ -76,15 +78,16 @@ def initialize(lockfile,
7678
@unlocking = unlock.any? {|_k, v| !Array(v).empty? }
7779
end
7880

79-
@dependencies = dependencies
80-
@plugins = plugins
81-
@sources = sources
82-
@unlock = unlock
83-
@optional_groups = optional_groups
84-
@prefer_local = false
85-
@specs = nil
86-
@ruby_version = ruby_version
87-
@gemfiles = gemfiles
81+
@dependencies = dependencies
82+
@sources = sources
83+
@plugin_dependencies = plugin_dependencies
84+
@plugin_sources = plugin_sources
85+
@unlock = unlock
86+
@optional_groups = optional_groups
87+
@prefer_local = false
88+
@specs = nil
89+
@ruby_version = ruby_version
90+
@gemfiles = gemfiles
8891

8992
@lockfile = lockfile
9093

@@ -240,16 +243,16 @@ def requested_dependencies
240243
dependencies_for(requested_groups)
241244
end
242245

243-
def requested_plugins
244-
plugins_for(requested_groups)
246+
def requested_plugin_dependencies
247+
plugin_dependencies_for(requested_groups)
245248
end
246249

247250
def current_dependencies
248251
filter_relevant(dependencies)
249252
end
250253

251-
def current_plugins
252-
filter_relevant(plugins)
254+
def current_plugin_dependencies
255+
filter_relevant(plugin_dependencies)
253256
end
254257

255258
def current_locked_dependencies
@@ -294,9 +297,9 @@ def dependencies_for(groups)
294297
deps
295298
end
296299

297-
def plugins_for(groups)
300+
def plugin_dependencies_for(groups)
298301
groups.map!(&:to_sym)
299-
plugins = current_plugins # always returns a new array
302+
plugins = current_plugin_dependencies # always returns a new array
300303
plugins.select! do |d|
301304
if RUBY_VERSION >= "3.1"
302305
d.groups.intersect?(groups)
@@ -340,11 +343,14 @@ def resolve
340343
end
341344

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

346352
def groups
347-
(dependencies + plugins).map(&:groups).flatten.uniq
353+
(dependencies + plugin_dependencies).map(&:groups).flatten.uniq
348354
end
349355

350356
def lock(file_or_preserve_unknown_sections = false, preserve_unknown_sections_or_unused = false)
@@ -488,7 +494,7 @@ def validate_platforms!
488494

489495
def validate_plugins!
490496
missing_plugins_list = []
491-
requested_plugins.each do |plugin|
497+
requested_plugin_dependencies.each do |plugin|
492498
missing_plugins_list << plugin unless Plugin.installed?(plugin.name)
493499
end
494500
if missing_plugins_list.size > 1
@@ -516,8 +522,8 @@ def most_specific_locked_platform
516522
end
517523
end
518524

519-
attr_reader :sources
520-
private :sources
525+
attr_reader :sources, :plugin_sources
526+
private :sources, :plugin_sources
521527

522528
def nothing_changed?
523529
return false unless lockfile_exists?

bundler/lib/bundler/dsl.rb

Lines changed: 47 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,12 @@ def self.evaluate(gemfile, lockfile, unlock)
2727
def initialize
2828
@source = nil
2929
@sources = SourceList.new
30+
@plugin_sources = SourceList.new
31+
@current_sources = @sources
3032
@git_sources = {}
3133
@dependencies = []
32-
@plugins = []
34+
@plugin_dependencies = []
35+
@current_dependencies = @dependencies
3336
@groups = []
3437
@install_conditionals = []
3538
@optional_groups = []
@@ -98,12 +101,12 @@ def gem(name, *args)
98101
options["gemfile"] = @gemfile
99102
version = args || [">= 0"]
100103

101-
normalize_options(name, version, true, options)
104+
normalize_options(name, version, options)
102105

103106
dep = Dependency.new(name, version, options)
104107

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

@@ -136,7 +139,7 @@ def gem(name, *args)
136139

137140
# Always prefer the dependency from the Gemfile
138141
if current.gemspec_dev_dep?
139-
@dependencies.delete(current)
142+
@current_dependencies.delete(current)
140143
elsif dep.gemspec_dev_dep?
141144
return
142145
elsif current.source != dep.source
@@ -151,7 +154,7 @@ def gem(name, *args)
151154
end
152155
end
153156

154-
@dependencies << dep
157+
@current_dependencies << dep
155158
end
156159

157160
def source(source, *args, &blk)
@@ -161,20 +164,22 @@ def source(source, *args, &blk)
161164

162165
if options.key?("type")
163166
options["type"] = options["type"].to_s
164-
unless Plugin.source?(options["type"])
167+
unless (source_plugin = Plugin.source_plugin(options["type"]))
165168
raise InvalidOption, "No plugin sources available for #{options["type"]}"
166169
end
167170

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

175+
plugin(source_plugin) unless @plugin_dependencies.any? {|d| d.name == source_plugin }
176+
172177
source_opts = options.merge("uri" => source)
173-
with_source(@sources.add_plugin_source(options["type"], source_opts), &blk)
178+
with_source(add_source(:add_plugin_source, options["type"], source_opts), &blk)
174179
elsif block_given?
175-
with_source(@sources.add_rubygems_source("remotes" => source), &blk)
180+
with_source(add_source(:add_rubygems_source, "remotes" => source), &blk)
176181
else
177-
@sources.add_global_rubygems_remote(source)
182+
add_source(:add_global_rubygems_remote, source)
178183
end
179184
end
180185

@@ -200,8 +205,7 @@ def path(path, options = {}, &blk)
200205

201206
source_options["global"] = true unless block_given?
202207

203-
source = @sources.add_path_source(source_options)
204-
with_source(source, &blk)
208+
with_source(add_source(:add_path_source, source_options), &blk)
205209
end
206210

207211
def git(uri, options = {}, &blk)
@@ -216,20 +220,29 @@ def git(uri, options = {}, &blk)
216220
raise DeprecatedError, msg
217221
end
218222

219-
with_source(@sources.add_git_source(normalize_hash(options).merge("uri" => uri)), &blk)
223+
options = normalize_hash(options).merge("uri" => uri)
224+
with_source(add_source(:add_git_source, options), &blk)
220225
end
221226

222-
def github(repo, options = {})
227+
def github(repo, options = {}, &blk)
223228
raise ArgumentError, "GitHub sources require a block" unless block_given?
224-
github_uri = @git_sources["github"].call(repo)
225-
git_options = normalize_hash(options).merge("uri" => github_uri)
226-
git_source = @sources.add_git_source(git_options)
227-
with_source(git_source) { yield }
229+
github_uri = @git_sources["github"].call(repo)
230+
options = normalize_hash(options).merge("uri" => github_uri)
231+
with_source(add_source(:add_git_source, options), &blk)
228232
end
229233

230234
def to_definition(lockfile, unlock, lockfile_contents: nil)
231235
check_primary_source_safety
232-
Definition.new(lockfile, @dependencies, @sources, unlock, @ruby_version, @optional_groups, @gemfiles, lockfile_contents, @plugins)
236+
Definition.new(lockfile,
237+
@dependencies,
238+
@sources,
239+
unlock,
240+
@ruby_version,
241+
@optional_groups,
242+
@gemfiles,
243+
lockfile_contents,
244+
@plugin_dependencies,
245+
@plugin_sources)
233246
end
234247

235248
def group(*args, &blk)
@@ -272,41 +285,12 @@ def env(name)
272285
end
273286

274287
def plugin(name, *args)
275-
options = args.last.is_a?(Hash) ? args.pop.dup : {}
276-
options["gemfile"] = @gemfile
277-
version = args || [">= 0"]
278-
279-
# We don't care to add sources for plugins in this pass over the gemfile
280-
# since we're not installing plugins here (they should already be
281-
# installed), only keeping track of them so that we can verify they
282-
# are currently installed. This is important because otherwise sources
283-
# unique to the plugin (like a git source) would end up in the lockfile,
284-
# which we don't want.
285-
normalize_options(name, version, false, options)
286-
287-
dep = Dependency.new(name, version, options)
288-
289-
# if there's already a plugin with this name we try to prefer one
290-
if current = @plugins.find {|d| d.name == dep.name }
291-
if current.requirement != dep.requirement
292-
raise GemfileError, "You cannot specify the same plugin twice with different version requirements.\n" \
293-
"You specified: #{current.name} (#{current.requirement}) and #{dep.name} (#{dep.requirement})" \
294-
"#{update_prompt}"
295-
end
296-
297-
if current.source != dep.source
298-
raise GemfileError, "You cannot specify the same plugin twice coming from different sources.\n" \
299-
"You specified that #{dep.name} (#{dep.requirement}) should come from " \
300-
"#{current.source || "an unspecified source"} and #{dep.source}\n"
301-
else
302-
Bundler.ui.warn "Your Gemfile lists the plugin #{current.name} (#{current.requirement}) more than once.\n" \
303-
"You should keep only one of them.\n" \
304-
"Remove any duplicate entries and specify the plugin only once." \
305-
"While it's not a problem now, it could cause errors if you change the version of one of them later."
306-
end
307-
end
308-
309-
@plugins << dep
288+
@current_sources = @plugin_sources
289+
@current_dependencies = @plugin_dependencies
290+
gem(name, *args)
291+
ensure
292+
@current_sources = @sources
293+
@current_dependencies = @dependencies
310294
end
311295

312296
def method_missing(name, *args)
@@ -320,6 +304,11 @@ def check_primary_source_safety
320304

321305
private
322306

307+
def add_source(method, *args)
308+
@plugin_sources.send(method, *args) unless @plugin_sources.equal?(@current_sources)
309+
@current_sources.send(method, *args)
310+
end
311+
323312
def add_git_sources
324313
git_source(:github) do |repo_name|
325314
if repo_name =~ GITHUB_PULL_REQUEST_URL
@@ -382,7 +371,7 @@ def valid_keys
382371
@valid_keys ||= VALID_KEYS
383372
end
384373

385-
def normalize_options(name, version, add_to_sources, opts)
374+
def normalize_options(name, version, opts)
386375
if name.is_a?(Symbol)
387376
raise GemfileError, %(You need to specify gem names as Strings. Use 'gem "#{name}"' instead)
388377
end
@@ -417,9 +406,9 @@ def normalize_options(name, version, add_to_sources, opts)
417406
end
418407

419408
# Save sources passed in a key
420-
if opts.key?("source") && add_to_sources
409+
if opts.key?("source")
421410
source = normalize_source(opts["source"])
422-
opts["source"] = @sources.add_rubygems_source("remotes" => source)
411+
opts["source"] = @current_sources.add_rubygems_source("remotes" => source)
423412
end
424413

425414
git_name = (git_names & opts.keys).last
@@ -438,10 +427,8 @@ def normalize_options(name, version, add_to_sources, opts)
438427
else
439428
options = opts.dup
440429
end
441-
if add_to_sources
442-
source = send(type, param, options) {}
443-
opts["source"] = source
444-
end
430+
source = send(type, param, options) {}
431+
opts["source"] = source
445432
end
446433

447434
opts["source"] ||= @source

0 commit comments

Comments
 (0)