Skip to content

Don't require source plugins to be installed to parse a lockfile#9620

Open
hsbt wants to merge 3 commits into
masterfrom
claude/reverent-neumann-3593b5
Open

Don't require source plugins to be installed to parse a lockfile#9620
hsbt wants to merge 3 commits into
masterfrom
claude/reverent-neumann-3593b5

Conversation

@hsbt

@hsbt hsbt commented Jun 12, 2026

Copy link
Copy Markdown
Member

What was the end-user or developer problem that led to this PR?

Third-party tools like bundler-audit read lockfiles through Bundler::LockfileParser.new(contents) without evaluating the Gemfile.

When a lockfile contains a PLUGIN SOURCE block whose plugin is not installed locally, Plugin.from_lock raised Bundler::Plugin::UnknownSourceError and aborted the whole parse, so even DEPENDENCIES could not be read. The same raise also prevented bundle install from recovering when a plugin source was removed from the Gemfile but still present in the lockfile.

Fixes #9614

What is your fix for the problem, implemented in this PR?

Plugin.from_lock now falls back to the inert UnloadedSource placeholder when the plugin handling the source type is not installed, so the lockfile can still be parsed and the specs under that source remain readable.

Regular bundle install is unaffected because plugins are installed during the plugin-install-from-gemfile phase before the main lockfile parse, and a Gemfile that still declares the source keeps raising from the DSL as before.

Since all unloaded sources share a single class, UnloadedSource#== and #hash now also compare the source type so that two plugin sources with the same remote but different types are not conflated.

Make sure the following tasks are checked

hsbt and others added 2 commits June 12, 2026 13:37
Unlike real plugin sources, where each type is handled by its own
class, every unloaded source shares this single class, so the
class+uri comparison inherited from API::Source would conflate two
different plugin types pointing at the same remote.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Third-party tools like bundler-audit read lockfiles through
Bundler::LockfileParser without evaluating the Gemfile, and a PLUGIN
SOURCE block whose plugin isn't installed locally aborted the whole
parse with UnknownSourceError, taking even DEPENDENCIES down with it.
Fall back to the inert UnloadedSource placeholder instead, which also
lets bundle install converge away a plugin source that was removed
from the Gemfile.

#9614

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 12, 2026 04:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts Bundler’s plugin-source lockfile handling so that third-party tools (and Bundler itself during recovery scenarios) can parse a lockfile containing PLUGIN SOURCE blocks even when the corresponding source plugin is not installed locally, by falling back to Bundler::Plugin::UnloadedSource instead of raising.

Changes:

  • Update Bundler::Plugin.from_lock to return UnloadedSource when a plugin source type is not registered/installed, avoiding UnknownSourceError during lockfile parsing.
  • Refine UnloadedSource#== / #hash to incorporate the plugin source type, preventing different plugin-source types with the same remote from being treated as the same source.
  • Add specs covering successful parsing of dependencies/specs from lockfiles with missing plugin source handlers.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
spec/bundler/plugin_spec.rb Adds coverage for Plugin.from_lock returning UnloadedSource when the source plugin is missing.
spec/bundler/lockfile_parser_spec.rb Adds coverage ensuring lockfile parsing still succeeds (deps + specs) with an uninstalled plugin source type.
bundler/lib/bundler/plugin/unloaded_source.rb Updates equality/hash semantics to differentiate unloaded sources by plugin source type.
bundler/lib/bundler/plugin.rb Changes plugin source instantiation from lockfiles to fall back to UnloadedSource when the plugin isn’t installed.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +11 to +22
# Unlike real plugin sources, where the handling class encodes the
# source type, all unloaded sources share this class, so the type must
# be compared explicitly.
def ==(other)
super && options["type"] == other.options["type"]
end

alias_method :eql?, :==

def hash
[super, options["type"]].hash
end
@hsbt hsbt mentioned this pull request Jun 12, 2026
4 tasks
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bundler::LockfileParser raises UnknownSourceError reading a lockfile with a PLUGIN SOURCE for an uninstalled plugin

2 participants