我对捆绑器及其生成的文件有点陌生。我有一个来自 GitHub 的 git repo 的副本,该副本由许多人贡献,所以我惊讶地发现 bundler 创建了一个在 repo 中不存在且不在 .gitignore
列表中的文件。
由于我已经分叉了它,我知道将它添加到 repo 不会破坏主 repo 的任何内容,但是如果我执行 pull request,它会导致问题吗?
是否应将 Gemfile.lock
包含在存储库中?
2022 年更新来自 TrinitronX
快进到 2021 年,现在 Bundler 文档 [web 存档] 现在说要在 gem 中提交 Gemfile.lock...¯_(ツ)_/¯ 我想这对开发人员来说是有意义的,并且在开始时易于使用项目。但是,现在 CI 作业需要确保删除任何杂散的 Gemfile.lock 文件以针对其他版本进行测试。
旧版答案 ~2010
假设您没有编写 rubygem,Gemfile.lock 应该在您的存储库中。它用作所有所需 gem 及其依赖项的快照。这样,bundler 就不必在每次部署时重新计算所有 gem 依赖项等。
来自牛仔编码的评论如下:
如果您正在处理 gem,那么不要签入您的 Gemfile.lock。如果您正在开发 Rails 应用程序,请检查您的 Gemfile.lock。
这是一个很好的 article 解释什么是锁定文件。
真正的问题发生在您正在开发需要具有可配置数据库适配器的开源 Rails 应用程序时。我正在开发 Fat Free CRM 的 Rails 3 分支。我的首选是 postgres,但我们希望默认数据库是 mysql2。
在这种情况下,Gemfile.lock
仍然需要使用默认的一组 gem 签入,但我需要忽略我在我的机器上对其所做的更改。为此,我运行:
git update-index --assume-unchanged Gemfile.lock
并扭转:
git update-index --no-assume-unchanged Gemfile.lock
在 Gemfile
中包含类似以下代码的内容也很有用。这会根据您的 database.yml 加载适当的数据库适配器 gem。
# Loads the database adapter gem based on config/database.yml (Default: mysql2)
# -----------------------------------------------------------------------------
db_gems = {"mysql2" => ["mysql2", ">= 0.2.6"],
"postgresql" => ["pg", ">= 0.9.0"],
"sqlite3" => ["sqlite3"]}
adapter = if File.exists?(db_config = File.join(File.dirname(__FILE__),"config","database.yml"))
db = YAML.load_file(db_config)
# Fetch the first configured adapter from config/database.yml
(db["production"] || db["development"] || db["test"])["adapter"]
else
"mysql2"
end
gem *db_gems[adapter]
# -----------------------------------------------------------------------------
我不能说这是否是一个既定的最佳实践,但它对我很有效。
我和我的同事有不同的Gemfile.lock,因为我们使用不同的平台,windows和mac,而我们的服务器是linux。
我们决定删除 repo 中的 Gemfile.lock 并在 git repo 中创建 Gemfile.lock.server,就像 database.yml 一样。然后在将它部署到服务器之前,我们使用 cap deploy 钩子将 Gemfile.lock.server 复制到服务器上的 Gemfile.lock
同意 r-dub,将其保存在源代码管理中,但对我来说,真正的好处是:
在相同的环境中协作(忽略windohs和linux/mac的东西)。在 Gemfile.lock 之前,下一个安装项目的家伙可能会看到各种令人困惑的错误,责备自己,但他只是那个幸运的家伙获得了下一个版本的超级 gem,打破了现有的依赖关系。
更糟糕的是,这发生在服务器上,除非受到纪律处分并安装准确的版本,否则会获得未经测试的版本。 Gemfile.lock 明确说明了这一点,它会明确告诉您您的版本不同。
注意:记住将东西分组,如 :development 和 :test
Bundler 文档也解决了这个问题:
原件:http://gembundler.com/v1.3/rationale.html
编辑:http://web.archive.org/web/20160309170442/http://bundler.io/v1.3/rationale.html
请参阅名为“将代码检查到版本控制中”的部分:
在开发应用程序一段时间后,将应用程序连同 Gemfile 和 Gemfile.lock 快照一起签入。现在,您的存储库记录了您上次使用的所有 gem 的确切版本,您确定应用程序可以正常工作。请记住,虽然您的 Gemfile 仅列出了三个 gem(具有不同程度的版本严格性),但您的应用程序依赖于几十个 gem,一旦您考虑到您所依赖的 gem 的所有隐式要求。这很重要:Gemfile.lock 使您的应用程序成为您自己的代码和它最后一次运行的第三方代码的单个包,您可以确定一切正常。在 Gemfile 中指定您依赖的第三方代码的确切版本不会提供相同的保证,因为 gems 通常会为其依赖项声明一系列版本。下次您在同一台机器上运行 bundle install 时,bundler 将看到它已经拥有您需要的所有依赖项,并跳过安装过程。不要签入 .bundle 目录或其中的任何文件。这些文件特定于每台特定的机器,并用于在运行 bundle install 命令之间保留安装选项。如果你运行了 bundle pack,你的 bundle 需要的 gems(虽然不是 git gems)会被下载到 vendor/cache 中。如果您需要的所有 gem 都存在于该文件夹中并签入到您的源代码管理中,则 Bundler 可以在不连接到 Internet(或 RubyGems 服务器)的情况下运行。由于源代码控制存储库的大小增加,这是一个可选步骤,不建议这样做。
2021 年的简单答案:Gemfile.lock 也应该在 Rubygems 的版本控制中。接受的答案现在是 11 岁。
这里有一些推理(从评论中挑选):
@josevalim https://github.com/heartcombo/devise/pull/3147#issuecomment-52193788
Gemfile.lock 应该保留在存储库中,因为贡献者和开发人员应该能够分叉项目并使用保证工作的版本运行它。
@rafaelfranca https://github.com/rails/rails/pull/18951#issuecomment-74888396
我认为即使对于插件也忽略锁定文件不是一个好主意。这意味着“git clone;bundle;rake test”序列不能保证通过,因为您的数十个依赖项中的一个已升级并导致您的代码中断。此外,正如@chancancode 所说,它使平分变得更加困难。
Rails 在 git 中也有 Gemfile.lock:
https://github.com/rails/rails/commit/0ad6d27643057f2eccfe8351409a75a6d1bbb9d0
没有 Gemfile.lock 意味着:
新贡献者无法运行测试,因为奇怪的事情失败了,所以他们不会贡献或获得失败的 PR ......糟糕的第一次体验。
如果您丢失了本地 Gemfile.lock,您将无法回到旧项目并修复错误而无需更新/重写项目
->始终检查 Gemfile.lock,如果您想更加彻底,请让 travis 删除它https://grosser.it/2015/08/14/check-in-your-gemfile-lock/
聚会有点晚了,但答案仍然花了我时间和外国阅读来理解这个问题。所以我想总结一下我对 Gemfile.lock 的发现。
当您构建 Rails 应用程序时,您会在本地机器上使用某些版本的 gems。如果你想避免生产模式和其他分支中的错误,你必须在任何地方使用那个 Gemfile.lock 文件,并告诉 bundler bundle
以在每次更改时重建 gem。
如果您的生产机器上的 Gemfile.lock
已更改并且 Git 不允许您git pull
,您应该编写 git reset --hard
以避免该文件更改并再次编写 git pull
。
这里的其他答案是正确的:是的,您的 Ruby 应用程序(不是您的 Ruby gem)应该在 repo 中包含 Gemfile.lock
。要详细了解为什么应该这样做,请继续阅读:
我错误地认为每个环境(开发、测试、登台、生产......)都做了一个bundle install
来构建自己的 Gemfile.lock。我的假设是基于 Gemfile.lock 不包含任何分组数据的事实,例如 :test、:prod 等。这个假设是错误,因为我在一个痛苦的本地问题中发现。
经过仔细调查,我很困惑为什么我的 Jenkins 构建显示成功获取特定的 gem(ffaker
,FWIW),但是当应用程序加载并需要 ffaker 时,它说找不到文件。怎么回事?
更多的调查和实验显示了这两个文件的作用:
首先它使用 Gemfile.lock 去获取所有的 gem,即使是那些不会在这个特定环境中使用的。然后它使用 Gemfile 来选择在这个环境中实际使用的那些获取的 gem。
因此,即使它基于 Gemfile.lock 在第一步中获取了 gem,它也没有包含在我的 :test 环境中,基于 Gemfile 中的组。
修复(在我的情况下)是将 gem 'ffaker'
从 :development 组移动到主组,因此所有 env 都可以使用它。 (或者,仅将其添加到 :development、:test 中,视情况而定)
Gemfile.lock
提交到 gem 中...¯\_(ツ)_/¯ 我想这对开发人员和易用性很有意义开始一个项目。但是,现在 CI 作业需要确保删除任何杂散的Gemfile.lock
文件以针对其他版本进行测试。