为什么捆绑器使用多个 gem 位置?

Why does bundler use more than one gem location?

这发生在 Puppet's 捆绑包中。

Gemfile specifies

gem "puppet", :path => File.dirname(__FILE__), :require => false

但是我在$GEM_HOME中安装的gem之一毕竟出现在$:中。

$ bundle exec ruby -e 'puts $:'
...
/home/puppy/puppet-git-clone/lib
...
/usr/lib/ruby/vendor_ruby
...
/home/puppy/gems/gems/puppet-3.7.5/lib
...

这本身不是问题,但显然 Ruby 将加载 Puppet 3.7.5 而不是 3.7.3 我从 git 存储库中检出。

$ bundle exec irb
irb(main):001:0> require 'puppet'
=> true
irb(main):002:0> Facter.value(:puppetversion)
=> "3.7.5"

为什么 Puppet 没有从 git 树加载,我该如何进一步调试它?

更新

木偶 .gemspec 可能涉及。 clever 指定版本。我现在担心 Rubygems 实际上会加载已安装的 3.7.5 gem 以便 Puppet.version 会如实报告错误值,从而摆脱捆绑程序。这可能是正在发生的事情吗?

更新 2

按照评论中的建议,我尝试在 Gemfile.

中静态设置路径和版本
gem "puppet", "3.4.2", :path => "/home/puppy/puppet-git-clone", :require => false

至于结果,嗯——至少bundler的观点是一致的;-)

Could not find gem 'puppet (= 3.4.2) ruby' in source at /home/ffrank/git/puppet.
Source contains 'puppet' at: 3.7.3
Run `bundle install` to install missing gems.

前提是您在尝试 bundle exec 之前删除了 Gemfile.lock 并删除了 gem 的所有其他版本...虽然没有明确定义相同的问题,但这是一个Bundler 的已知问题检查一下:

它应该在这个合并的合并请求中得到修复:

这将使您的 "preferred source" 受到青睐和使用。

(链接用作答案,因为这指的是现有活动,而不是我可以放在答案中的解决方案。不是 link-唯一的答案。)

试试这个:

source "file://home/puppy/puppet-git-clone"

gem "puppet", "3.4.2", :path => "/home/puppy/puppet-git-clone"

为什么需要 require false?

按照你在我之前的回答中所说的,试试这个:

gemspec 添加到靠近顶部的 Gemfile

如果您这样做,后续的 gem ... 调用将覆盖最终设置版本的 gemspec

在此处添加:

source ENV['GEM_SOURCE'] || "https://rubygems.org"
gemspec # and let me know if this fixes it

正如我最近所说,我在自己的项目中发现了与您相同的问题。我已经通过解决方法解决了它。这个想法本身就是我在这里给出的,我并不是说它是最有效或无错误的方式。这种方法的变体对我有用。

它涉及劫持 Gemfile,也许 gemspec,具体取决于现实中的罪犯 - 这部分仍然未知。我最近的回答,我给出的第二种方法已经可以解决这个问题。如果没有,您可能需要一个解决方法。 Bundler 面临许多僵局。


作为解决方法,请插入策展人。

我建议,在 Gemfile 的末尾插入一个这样的处理器,自己管理 Bundler::Dsl。我们可以完全专注于您要解决的 gem,但它可以针对所有 gem 解决。

例如...这主要是一个概念,它可能 运行,但它可能有一个错误。你需要加强它。这将删除除您期望的版本之外的所有内容:

PUPPET_VERSION = 'version desired'
until(current = self.dependencies.find { |d| d.name == 'puppet' }) == 1
    current.each { |gem|
        if !gem.version == PUPPET_VERSION
            self.dependencies.delete(current)
        end
    }
end

我不确定您真正想要哪个版本。提到了三个版本,3.7.33.7.5 ...只需插入您想要的那个即可。任何其他版本将从 Bundler 正在使用的依赖项中清除。

快速修复是将 -Ilib 添加到您的 ruby 命令:

$ bundle exec ruby -e "require 'puppet'; puts Facter.value(:puppetversion)"
3.7.5

$ bundle exec ruby -Ilib -e "require 'puppet'; puts Facter.value(:puppetversion)"
3.7.3

如果我们比较加载路径,您可以看到添加 -Ilib 导致 3.7.5 不存在于第二个加载路径中:

$ diff <(bundle exec ruby -e 'puts $:') <(bundle exec ruby -Ilib -e 'puts $:') | grep 'puppet-'
< /Library/Ruby/Gems/2.0.0/gems/puppet-3.7.5/lib

这似乎应该是默认行为,因此捆绑程序中可能存在错误。

我过去也遇到过同样的问题,我想你可以在没有 'require: false' 参数的情况下指定 gem,它会查找 Gemfile 特定版本。

gem "puppet", "3.4.2", :path => "/home/puppy/puppet-git-clone"

如果您使用 'gem' 指定 'require' 选项,使用 bundle exec irb 指定 运行 irb 命令,它总是加载 gem 的最新版本已使用 require: false 选项声明。