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

我注意到在 rubygems.org 上有很多 gems 建议你用主版本而不是精确版本来指定它们。比如说..。

哈米尔轨道宝石。

gem "haml-rails", "~> 0.3.4"  # "$ bundle install" will acquire the
# latest version before 1.0.

然而,基于 Bundler 医生,它听起来像它会更好地确定确切的版本像这样..。

gem "haml-rails", "0.3.4"

这就是你的 Haml-ails gem,它的所有依赖关系都不会向前漂移。如果几周后在另一台机器上签出该项目并运行 $ bundle install,那么所有指定的版本都将完全相同。

我见过点释放打破东西,我认为部分的整个想法的 Bundler 是“ Bundle.lock”所有你的宝石版本。

但是在 rubygems.org 上他们经常使用“ ~ >”,所以也许我遗漏了什么?

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

53679 次浏览

I would definitely say use the exact version numbers. You can probably always just lock it down to a major version, or never specify any version, and be okay, but if you really want that fine grained level of control and to have 100% confidence in your program when being run on other machines, use the exact version numbers.

I've been in situations where the exact version number wasn't specified, and when I or someone else did a bundle install, the project broke because it went to a newer version. This can be especially bad when deploying to production.

Bundler does lock in your gem specifications, but if you're telling it to just use a major release, then it locks that in. So is just knows "Oh the version is locked in at > 0.1" or whatever, but not "Oh the version is locked in specifically at 0.1.2.3".

This is the purpose of the Gemfile.lock file - running bundle install with a Gemfile.lock present only installs using the dependencies listed in there; it doesn't re-resolve the Gemfile. To update dependencies / update gem versions, you then have to explicitly do a bundle update, which will update your Gemfile.lock file.

If there wasn't a Gemfile.lock, deploying code to production would be a major issue because, as you mention, the dependencies and gem versions could change.

In short, you should be generally safe using the pessimistic version constraint operator (~>) as rubygems.org advises. Just be sure to re-run your tests after you do a bundle update to make sure nothing breaks.

There's a nice article by Yehuda Katz that has a little more info on Gemfile.lock.

TL;DR

Yes, use pessimistic locking (~>) and specify a semantic version down to patch (Major.minor.patch) on all your gems!

Discussion

I am surprised by the lack of clarity on this issue, even "industry experts" told me the other day that Gemfile.lock is there to maintain gem versions. Wrong!

You want to organize your Gemfile in such a manner that you can run bundle update any time without risking breaking everything. To achive this:

  1. Specify a patch-level version for all your gems with pessimistic locking. This will allow bundle update to give you fixes, but not breaking changes.

  2. Specify a ref for gems from git

The only downside to this setup is that when a sweet new minor/major version for a gem comes out, you have to bump the version up manually.

Warning scenario

Consider what happens if you do not lock your gems.
You have an unlocked gem "rails" in your gemfile and the version in Gemfile.lock is 4.1.16. You are coding along and at some point you do a bundle update. Now your Rails version jumps to 5.2.0 (provided some other gem does not prevent this) and everything breaks.
Do yourself a favor and do not allow this for any gem!

An example 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

A concession
If you are confident your tests will catch bugs introduced by gem version changes, you can try pessimistic-locking gems at minor version, not patch.
This will allow the gem version to increase within the specified major version, but never into the next one.

gem "puma", "~> 3.12"