我应该在我的Gemfile中指定确切的版本吗?

Eth*_*han 64 ruby rubygems ruby-on-rails bundler ruby-on-rails-3

我注意到在rubygems.org上有很多宝石建议你用主要版本而不是精确版本来指定它们.例如...

haml-rails宝石 ......

gem "haml-rails", "~> 0.3.4"  # "$ bundle install" will acquire the 
                              # latest version before 1.0.
Run Code Online (Sandbox Code Playgroud)

然而,根据Bundler文档,它听起来像我最好确定这样的确切版本...

gem "haml-rails", "0.3.4"
Run Code Online (Sandbox Code Playgroud)

所以你的haml-rails gem和它的所有依赖都不会向前发展.如果您在几周之后在另一台机器上检查项目并运行,$ bundle install那么您将拥有与您指定的所有内容完全相同的版本.

我已经看到点发布破坏了东西,我认为Bundler的整个想法的一部分是" Bundle.lock"你所有的宝石版本.

但是在rubygems.org上他们使用"〜>"很多,所以也许我错过了什么?

任何澄清对我理解Bundler和gem管理都非常有帮助.

Abe*_*ker 56

这是Gemfile.lock文件的目的 - bundle install使用Gemfile.lock 运行只使用其中列出的依赖项进行安装; 它不会重新解析Gemfile.要更新依赖关系/更新gem版本,您必须显式执行a bundle update,这将更新您的Gemfile.lock文件.

如果没有Gemfile.lock,将代码部署到生产将是一个主要问题,因为正如您所提到的,依赖项和gem版本可能会发生变化.

简而言之,使用悲观版本约束operator(~>)通常应该是安全的,正如rubygems.org所建议的那样.确保在执行操作后重新运行测试bundle update以确保没有任何中断.

有一个很好的文章由耶胡达·卡茨说,对Gemfile.lock的多一点信息.

  • 我认为你所编写的内容的要点是,应该在一个*Gemfile*中保持悲观版本约束,这样一个*可以*轻松升级到与指定的主要版本和次要版本相匹配的最新版本.但是也应该使用*Gemfile.lock*文件,并将其保存在源代码中,以便必须明确地进行升级以影响部署代码的任何环境. (3认同)
  • @ethan RubyGems有一个[doc](http://rubygems.rubyforge.org/rubygems-update/Gem/Version.html)解释它(参见"防止版本灾难"一节).它的要点是它只允许版本号中的最后一个整数增加(例如'〜> 1.0.5'允许更新到版本1.0.9999,但从不到1.1.x).该机制用于允许更新gem,但不会引入可能破坏事物的不兼容性(它假设gems遵循链接轮廓的"Rational Versioning"策略). (2认同)

Epi*_*ene 9

TL; 博士

是的,使用悲观锁定( ~>) 并在所有 gem 上指定一个语义版本到补丁 ( Major.minor.patch)!

讨论

我对这个问题缺乏明确性感到惊讶,甚至“行业专家”前几天告诉我,Gemfile.lock那里可以维护 gem 版本。错误的!

您希望以Gemfile一种可以随时运行bundle update而不会破坏一切的方式来组织您的活动。要实现这一点:

  1. 使用悲观锁定为所有 gem 指定补丁级别版本。这将允许bundle update为您提供修复,但不会破坏更改。

  2. ref为来自 git 的 gems指定一个

这种设置的唯一缺点是,当一个 gem 的新的次要/主要版本出现时,您必须手动提高版本。

警告场景

考虑一下如果您不锁定您的宝石会发生什么。
gem "rails"的 gemfile 中有一个已解锁,并且其中的版本Gemfile.lock4.1.16. 您正在编码并在某些时候执行bundle update. 现在您的 Rails 版本跳转到5.2.0(假设其他一些 gem 不能阻止这一点)并且一切都中断了。
帮自己一个忙,不要让任何宝石这样做!

一个示例 Gemfile

# lock that bundler
if (version = Gem::Version.new(Bundler::VERSION)) < Gem::Version.new('1.16.3')
  abort "Bundler version >= 1.16.3 is required. You are running #{version}"
end

source "http://rubygems.org"

# specify explicit ref for git repos
gem "entity_validator",
  git: "https://github.com/plataformatec/devise",
  ref: "acc45c5a44c45b252ccba65fd169a45af73ff369" # "2018-08-02"

# consider hard-lock on gems you do not want to change one bit
gem "rails", "5.1.5"

# pessimistic lock on your common gems
gem "newrelic_rpm", "~> 4.8.0"
gem "puma", "~> 3.12.0"

group :test do
  gem "simplecov", "~> 0.16.1", require: false
end
Run Code Online (Sandbox Code Playgroud)

一个让步
如果你确信你的测试会发现由 gem 版本更改引入的错误,你可以尝试在次要版本而不是补丁上使用悲观锁定 gems。
这将允许 gem 版本在指定的主要版本内增加,但永远不会增加到下一个。

gem "puma", "~> 3.12"
Run Code Online (Sandbox Code Playgroud)

  • 您写道“您希望以这样的方式组织您的 Gemfile,以便您可以随时运行捆绑包更新,而不必冒破坏所有内容的风险”。不,这不是目标。听起来您可能不明白“捆绑更新”和“捆绑安装”之间的区别。`update` _更新_ `Gemfile.lock` 并更改您正在使用的版本。您希望能够_随时运行“捆绑安装”,而不必冒破坏所有内容的风险。事实上,您正在强制“Gemfile”执行“Gemfile.lock”的预期操作。 (4认同)
  • 那些“行业专家”是正确的:“Gemfile.lock”*确实*实际上维护了 gem 版本。直到(当然)您决定用“bundle update”**覆盖它**(这基本上就像说“bundle overwrite_my_locked_gem_versions”)。 (3认同)
  • 在我看来(这就是我首先写下答案的原因),有一种误解,仅仅因为有一个包含所有确切版本的“Gemfile.lock”文件,开发人员不需要在中指定 gem 版本`Gemfile`(锁定“维护”版本的想法)。那是错误的。开发人员通过在“Gemfile”中指定版本并偶尔运行“bundle update”来“维护”版本。 (3认同)
  • 悲观这个词在这里令人困惑(我理解这只是语义,但仍然如此)。如果您将其锁定到带有 = 的版本,那就太悲观了!但〜&gt;实际上允许您更新到最新的次要版本。 (2认同)

MrD*_*anA 5

我肯定会说使用确切的版本号.您可能总是将其锁定为主要版本,或者从不指定任何版本,并且没关系,但如果您真的想要这种精细的控制级别并且在其他计算机上运行时对您的程序有100%的信心,使用确切的版本号.

我一直在没有指定确切版本号的情况下,当我或其他人做了一个时bundle install,该项目因为它进入了更新版本而破产.部署到生产时,这可能会特别糟糕.

Bundler 确实锁定了你的gem规范,但是如果你告诉它只是使用一个主要的版本,那么它将其锁定.所以只知道"哦版本被锁定在> 0.1"或者其他什么,但不是"哦该版本特别锁定在0.1.2.3".

  • 如果存在`Gemfile.lock`,那么Bundler实际上知道要安装哪个特定版本(这就是为什么`Gemfile.lock`应该与`Gemfile`一起存储在repo中). (8认同)
  • 尽管`Gemfile.lock`存在,但做一个`bundle update <gem>`可能会比你想象的更快地更新方式,这可能是一个危险和棘手的情况. (3认同)