-
-
Notifications
You must be signed in to change notification settings - Fork 2k
Return Bundler::StubSpec if stub is a Bundler::StubSpec #5630
Conversation
I'd like us to add an integration test as well, since in theory other weird things can happen when |
if Bundler.rubygems.provides?(">= 2.1") | ||
RSpec.describe Bundler::StubSpecification do | ||
let(:with_gem_stub_spec) do | ||
stub = Gem::Specification.stubs.first |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can return nil
on old rubies on CI
@@ -4,6 +4,7 @@ | |||
module Bundler | |||
class StubSpecification < RemoteSpecification | |||
def self.from_stub(stub) | |||
return stub if stub.is_a?(Bundler::StubSpecification) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with this, we should be able to delete the conditional at line 91
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
f771cf5
to
c049b09
Compare
@segiddins removing the conditional on line 91 caused everything to break ( see the red failures on the commits ). I'm not able to investigate the failures in time for 1.15, would you be able to look at that and take this over if we want it for 1.15? (I'm about to be internetless for 2 weeks on vacation). Or, since @indirect is cool with the unit tests, we could continue with this as is, since it passes with the conditional |
👍 |
@bundlerbot r+ |
📌 Commit a542e2b has been approved by |
Return Bundler::StubSpec if stub is a Bundler::StubSpec Supersedes #5593 Fixes #5592 Explanation --- In some cases the `Gem::Specification.stubs` call in [this method](https://github.com/bundler/bundler/blob/master/lib/bundler/rubygems_integration.rb#L773-L778) in the rubygems integration returns a mixed bag of `Bundler::StubSpecification` and `Gem::StubSpecification` objects. We then instantiate `Bundler::StubSpecification` objects and set the `stub` to be both `Gem::StubSpecification` and `Bundler::StubSpecification` objects. This happens after we tell rubygems to use our overrides [here](https://github.com/bundler/bundler/blob/master/lib/bundler/runtime.rb#L21-L24). A `Bundler::StubSpecification` does not define `to_spec` where `Gem::StubSpecification` does. In `Bundler::StubSpecification` we assume the `stub` to be a `Gem::StubSpecification` rather than the `to_spec`-less `Bundler::StubSpecification`. This means that in `_remote_specification`, the call to `to_spec` [here](https://github.com/bundler/bundler/blob/master/lib/bundler/stub_specification.rb#L88) fails. This falls back to `method_missing` [here](https://github.com/bundler/bundler/blob/master/lib/bundler/remote_specification.rb#L96-L98) which, of course, calls `_remote_specification` (and thus an infinite failing loops occurs). ### Why did this happen in such a weird way? We needed to use a combination of `foreman`, `unicorn`, and a call to `Gem::Specification.find_by_name(*args)` to replicate. I suspect this was required because Bundler doesn't call these methods as much. The last call in a doubly nested `bundle exec` resulted in the issue being exasperated. You can however replicate with this: ```ruby gem_stub = Gem::Specification.stubs.first bundler_stub = Bundler::StubSpecification.from_stub(gem_stub) bundler_stub = Bundler::StubSpecification.from_stub(bundler_stub) bundler_stub.to_spec ``` We basically got to a point where we tried calling a method that doesn't exist on a `Bundler::StubSpecification`, so `_remote_specification` was called, but that had a method which didn't exist since we had the weirdness going on described here. It was just a very specific sequence of events that is hard to replicate. Options --- 1. We implement `to_spec` on `Bundler::StubSpecification`, as is done in #5593 2. We assume that `stub` is a `Gem::Specification`. Therefore if we try to create a `Bundler::StubSpecification` with the stub being a `Bundler::StubSpecification`, we simply return that stub we already made instead. Thoughts --- 1. This basically ends up making a linked list of `Bundler::StubSpecifications` where you can follow `stub` all the way up until it's no longer a `Bundler::StubSpecification`. This means that the implementation is an accidental fix as `to_spec` in #5593 actually just calls `stub.to_spec` - which, if the stub is a `Bundler:StubSpecification`, would call that `Bundler::StubSpecification`, following the list up until we find a `Gem::StubSpecification`. 2. This is the right solution IMO. This breaks the weird linked list we made by mistake and just returns the object as we'd expect. Then, when `stub.to_spec` is called in `_remote_specification`, we always know it is a `Gem::StubSpecification` which has it defined. cc @segiddins
☀️ Test successful - status-travis |
Return Bundler::StubSpec if stub is a Bundler::StubSpec Supersedes #5593 Fixes #5592 Explanation --- In some cases the `Gem::Specification.stubs` call in [this method](https://github.com/bundler/bundler/blob/master/lib/bundler/rubygems_integration.rb#L773-L778) in the rubygems integration returns a mixed bag of `Bundler::StubSpecification` and `Gem::StubSpecification` objects. We then instantiate `Bundler::StubSpecification` objects and set the `stub` to be both `Gem::StubSpecification` and `Bundler::StubSpecification` objects. This happens after we tell rubygems to use our overrides [here](https://github.com/bundler/bundler/blob/master/lib/bundler/runtime.rb#L21-L24). A `Bundler::StubSpecification` does not define `to_spec` where `Gem::StubSpecification` does. In `Bundler::StubSpecification` we assume the `stub` to be a `Gem::StubSpecification` rather than the `to_spec`-less `Bundler::StubSpecification`. This means that in `_remote_specification`, the call to `to_spec` [here](https://github.com/bundler/bundler/blob/master/lib/bundler/stub_specification.rb#L88) fails. This falls back to `method_missing` [here](https://github.com/bundler/bundler/blob/master/lib/bundler/remote_specification.rb#L96-L98) which, of course, calls `_remote_specification` (and thus an infinite failing loops occurs). ### Why did this happen in such a weird way? We needed to use a combination of `foreman`, `unicorn`, and a call to `Gem::Specification.find_by_name(*args)` to replicate. I suspect this was required because Bundler doesn't call these methods as much. The last call in a doubly nested `bundle exec` resulted in the issue being exasperated. You can however replicate with this: ```ruby gem_stub = Gem::Specification.stubs.first bundler_stub = Bundler::StubSpecification.from_stub(gem_stub) bundler_stub = Bundler::StubSpecification.from_stub(bundler_stub) bundler_stub.to_spec ``` We basically got to a point where we tried calling a method that doesn't exist on a `Bundler::StubSpecification`, so `_remote_specification` was called, but that had a method which didn't exist since we had the weirdness going on described here. It was just a very specific sequence of events that is hard to replicate. Options --- 1. We implement `to_spec` on `Bundler::StubSpecification`, as is done in #5593 2. We assume that `stub` is a `Gem::Specification`. Therefore if we try to create a `Bundler::StubSpecification` with the stub being a `Bundler::StubSpecification`, we simply return that stub we already made instead. Thoughts --- 1. This basically ends up making a linked list of `Bundler::StubSpecifications` where you can follow `stub` all the way up until it's no longer a `Bundler::StubSpecification`. This means that the implementation is an accidental fix as `to_spec` in #5593 actually just calls `stub.to_spec` - which, if the stub is a `Bundler:StubSpecification`, would call that `Bundler::StubSpecification`, following the list up until we find a `Gem::StubSpecification`. 2. This is the right solution IMO. This breaks the weird linked list we made by mistake and just returns the object as we'd expect. Then, when `stub.to_spec` is called in `_remote_specification`, we always know it is a `Gem::StubSpecification` which has it defined. cc @segiddins (cherry picked from commit 47e7dd0)
Supersedes #5593
Fixes #5592
Explanation
In some cases the
Gem::Specification.stubs
call in this method in the rubygems integration returns a mixed bag ofBundler::StubSpecification
andGem::StubSpecification
objects. We then instantiateBundler::StubSpecification
objects and set thestub
to be bothGem::StubSpecification
andBundler::StubSpecification
objects.This happens after we tell rubygems to use our overrides here.
A
Bundler::StubSpecification
does not defineto_spec
whereGem::StubSpecification
does. InBundler::StubSpecification
we assume thestub
to be aGem::StubSpecification
rather than theto_spec
-lessBundler::StubSpecification
. This means that in_remote_specification
, the call toto_spec
here fails. This falls back tomethod_missing
here which, of course, calls_remote_specification
(and thus an infinite failing loops occurs).Why did this happen in such a weird way?
We needed to use a combination of
foreman
,unicorn
, and a call toGem::Specification.find_by_name(*args)
to replicate.I suspect this was required because Bundler doesn't call these methods as much. The last call in a doubly nested
bundle exec
resulted in the issue being exasperated.You can however replicate with this:
We basically got to a point where we tried calling a method that doesn't exist on a
Bundler::StubSpecification
, so_remote_specification
was called, but that had a method which didn't exist since we had the weirdness going on described here.It was just a very specific sequence of events that is hard to replicate.
Options
to_spec
onBundler::StubSpecification
, as is done in Add to_spec to stub_specification #5593stub
is aGem::Specification
. Therefore if we try to create aBundler::StubSpecification
with the stub being aBundler::StubSpecification
, we simply return that stub we already made instead.Thoughts
Bundler::StubSpecifications
where you can followstub
all the way up until it's no longer aBundler::StubSpecification
. This means that the implementation is an accidental fix asto_spec
in Add to_spec to stub_specification #5593 actually just callsstub.to_spec
- which, if the stub is aBundler:StubSpecification
, would call thatBundler::StubSpecification
, following the list up until we find aGem::StubSpecification
.stub.to_spec
is called in_remote_specification
, we always know it is aGem::StubSpecification
which has it defined.cc @segiddins