Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 35 additions & 3 deletions bundler/lib/bundler/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -265,14 +265,46 @@ def message
class InvalidArgumentError < BundlerError; status_code(40); end

class IncorrectLockfileDependencies < BundlerError
attr_reader :spec
attr_reader :spec, :actual_dependencies, :lockfile_dependencies

def initialize(spec)
def initialize(spec, actual_dependencies = nil, lockfile_dependencies = nil)
@spec = spec
@actual_dependencies = actual_dependencies
@lockfile_dependencies = lockfile_dependencies
end

def message
"Bundler found incorrect dependencies in the lockfile for #{spec.full_name}"
msg = "Bundler found incorrect dependencies in the lockfile for #{spec.full_name}\n"

if @actual_dependencies && @lockfile_dependencies
msg << "\n"
msg << "The gemspec for #{spec.full_name} specifies the following dependencies:\n"
if @actual_dependencies.empty?
msg << " (none)\n"
else
@actual_dependencies.sort_by(&:to_s).each do |dep|
msg << " #{dep}\n"
end
end

msg << "\n"
msg << "However, the lockfile has the following dependencies recorded:\n"
if @lockfile_dependencies.empty?
msg << " (none)\n"
else
@lockfile_dependencies.sort_by(&:to_s).each do |dep|
msg << " #{dep}\n"
end
end

msg << "\n"
msg << "This discrepancy may be caused by manually editing the lockfile.\n"
msg << "Please run `bundle install` to regenerate the lockfile with correct dependencies."
else
msg << "\nPlease run `bundle install` to regenerate the lockfile."
end

msg
end

status_code(41)
Expand Down
2 changes: 1 addition & 1 deletion bundler/lib/bundler/lazy_specification.rb
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ def validate_dependencies(spec)
spec.dependencies = dependencies
else
if !source.is_a?(Source::Path) && spec.runtime_dependencies.sort != dependencies.sort
raise IncorrectLockfileDependencies.new(self)
raise IncorrectLockfileDependencies.new(self, spec.runtime_dependencies, dependencies)
end
end
end
Expand Down
104 changes: 104 additions & 0 deletions bundler/spec/bundler/errors_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# frozen_string_literal: true

RSpec.describe Bundler::IncorrectLockfileDependencies do
describe "#message" do
let(:spec) do
double("LazySpecification", full_name: "rubocop-1.82.0")
end

context "without dependency details" do
subject { described_class.new(spec) }

it "provides a basic error message" do
expect(subject.message).to include("Bundler found incorrect dependencies in the lockfile for rubocop-1.82.0")
expect(subject.message).to include("Please run `bundle install` to regenerate the lockfile.")
end
end

context "with dependency details" do
let(:actual_dependencies) do
[
Gem::Dependency.new("json", [">= 2.3", "< 4.0"]),
Gem::Dependency.new("parallel", ["~> 1.10"]),
Gem::Dependency.new("parser", [">= 3.3.0.2"]),
]
end

let(:lockfile_dependencies) do
[
Gem::Dependency.new("json", [">= 2.3", "< 3.0"]),
Gem::Dependency.new("parallel", ["~> 1.10"]),
Gem::Dependency.new("parser", [">= 3.2.0.0"]),
]
end

subject { described_class.new(spec, actual_dependencies, lockfile_dependencies) }

it "provides a detailed error message showing the discrepancy" do
message = subject.message

expect(message).to include("Bundler found incorrect dependencies in the lockfile for rubocop-1.82.0")
expect(message).to include("The gemspec for rubocop-1.82.0 specifies the following dependencies:")
expect(message).to include("json (>= 2.3, < 4.0)")
expect(message).to include("parallel (~> 1.10)")
expect(message).to include("parser (>= 3.3.0.2)")
expect(message).to include("However, the lockfile has the following dependencies recorded:")
expect(message).to include("json (>= 2.3, < 3.0)")
expect(message).to include("parser (>= 3.2.0.0)")
expect(message).to include("This discrepancy may be caused by manually editing the lockfile.")
expect(message).to include("Please run `bundle install` to regenerate the lockfile with correct dependencies.")
end
end

context "when gemspec has dependencies but lockfile has none" do
let(:actual_dependencies) do
[
Gem::Dependency.new("myrack-test", ["~> 1.0"]),
]
end

let(:lockfile_dependencies) { [] }

subject { described_class.new(spec, actual_dependencies, lockfile_dependencies) }

it "shows that lockfile has no dependencies" do
message = subject.message

expect(message).to include("The gemspec for rubocop-1.82.0 specifies the following dependencies:")
expect(message).to include("myrack-test (~> 1.0)")
expect(message).to include("However, the lockfile has the following dependencies recorded:")
expect(message).to include("(none)")
end
end

context "when gemspec has no dependencies but lockfile has some" do
let(:actual_dependencies) { [] }

let(:lockfile_dependencies) do
[
Gem::Dependency.new("unexpected", ["~> 1.0"]),
]
end

subject { described_class.new(spec, actual_dependencies, lockfile_dependencies) }

it "shows that gemspec has no dependencies" do
message = subject.message

expect(message).to include("The gemspec for rubocop-1.82.0 specifies the following dependencies:")
expect(message).to include("(none)")
expect(message).to include("However, the lockfile has the following dependencies recorded:")
expect(message).to include("unexpected (~> 1.0)")
end
end
end

describe "#status_code" do
let(:spec) { double("LazySpecification", full_name: "test-1.0.0") }
subject { described_class.new(spec) }

it "returns 41" do
expect(subject.status_code).to eq(41)
end
end
end
6 changes: 5 additions & 1 deletion bundler/spec/commands/install_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1658,7 +1658,11 @@ def run
bundle "install", raise_on_error: false

expect(exitstatus).to eq(41)
expect(err).to eq("Bundler found incorrect dependencies in the lockfile for myrack_middleware-1.0")
expect(err).to include("Bundler found incorrect dependencies in the lockfile for myrack_middleware-1.0")
expect(err).to include("The gemspec for myrack_middleware-1.0 specifies the following dependencies:")
expect(err).to include("myrack (= 0.9.1)")
expect(err).to include("However, the lockfile has the following dependencies recorded:")
expect(err).to include("(none)")
end

it "updates the lockfile when not frozen" do
Expand Down
38 changes: 38 additions & 0 deletions bundler/spec/install/failure_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,42 @@
end
end
end

context "when lockfile dependencies don't match the gemspec" do
before do
build_repo4 do
build_gem "myrack", "1.0.0" do |s|
s.add_dependency "myrack-test", "~> 1.0"
end

build_gem "myrack-test", "1.0.0"
end

gemfile <<-G
source "https://gem.repo4"
gem "myrack"
G

# First install to generate lockfile
bundle :install

# Manually edit lockfile to have incorrect dependencies
lockfile_content = File.read(bundled_app_lock)
# Remove the myrack-test dependency from myrack
lockfile_content.gsub!(/^ myrack \(1\.0\.0\)\n myrack-test \(~> 1\.0\)\n/, " myrack (1.0.0)\n")
File.write(bundled_app_lock, lockfile_content)
end

it "reports the mismatch with detailed information" do
bundle :install, raise_on_error: false, env: { "BUNDLE_FROZEN" => "true" }

expect(err).to include("Bundler found incorrect dependencies in the lockfile for myrack-1.0.0")
expect(err).to include("The gemspec for myrack-1.0.0 specifies the following dependencies:")
expect(err).to include("myrack-test (~> 1.0)")
expect(err).to include("However, the lockfile has the following dependencies recorded:")
expect(err).to include("(none)")
expect(err).to include("This discrepancy may be caused by manually editing the lockfile.")
expect(err).to include("Please run `bundle install` to regenerate the lockfile with correct dependencies.")
end
end
end
6 changes: 5 additions & 1 deletion bundler/spec/lock/lockfile_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1608,7 +1608,11 @@
gem "myrack_middleware"
G

expect(err).to eq("Bundler found incorrect dependencies in the lockfile for myrack_middleware-1.0")
expect(err).to include("Bundler found incorrect dependencies in the lockfile for myrack_middleware-1.0")
expect(err).to include("The gemspec for myrack_middleware-1.0 specifies the following dependencies:")
expect(err).to include("myrack (= 0.9.1)")
expect(err).to include("However, the lockfile has the following dependencies recorded:")
expect(err).to include("(none)")
expect(the_bundle).not_to include_gems "myrack_middleware 1.0"
end

Expand Down