Ruby Gemfile.lock 中的 Platform 是什么意思?

xiaoronglv for Workstream · 2021年09月27日 · 最后由 rocLv 回复于 2021年09月28日 · 878 次阅读

我们团队内的程序员用的电脑系统各不相同,有人用 windows,有人用 Mac,有人用 Ubuntu,我注意到 Gemfile.lock 中的 Platform 经常会变来变去。

尤其是创建一个 microservice 时,跑测试时必定会遇到一个问题。

Your bundle only supports platforms ["x86_64-darwin-19"] but your local
platform is x86_64-linux. Add the current platform to the lockfile with 
`bundle lock --add-platform x86_64-linux` and try again.

引起这个问题的原因:

  • 新项目是在 Mac 上创建的。
  • 测试是在 linux 上跑的。

问题

  1. Gemfile.lock 中这段代码是什么意思呢?

  2. 为什么 Bundler 要创建这个声明?

  3. 这个声明是为了解决什么问题?

  4. 是与可执行文件有关系吗?

PLATFORMS
  ruby
  x86_64-darwin-19

猜测

猜测 1: 某些 gem 自带二进制可执行文件,只能在某个平台运行。

猜测 2: Platform 是指定支持的 Ruby 解析器

参考资料

https://www.moncefbelyamani.com/understanding-the-gemfile-lock-file/#platforms

https://bundler.io/man/gemfile.5.html#PLATFORMS

https://github.com/rubygems/rubygems/issues/4269#issuecomment-758564690

把那个用 Windows 的拉出去打一顿。 我在 M1 Mac 和 WSL2 上使用。只有 ruby,没有出现 x86 那一串。

这是 bundler 作者的答复,但是我还是没 get 到他的点。

https://github.com/rubygems/rubygems/issues/4269#issuecomment-758564690

Hi @schneems!

So, I've been sleeping on this issue, and I think what we have now is quite good to be honest. Let me try to explain why we did this, and why I believe it's a good thing.

Context

In previous bundler versions, bundler didn't consider platforms for resolution at all. What bundler would previously do is to resolve dependencies without considering platforms, and then at installation time, pick up platform specific variants with the same version as the resolved version if they exist. That approach had several problems:

  • Resolution correctness. Resolution would be incorrect sometimes, since it can happen that for the same gem and version number, a platform specific variant can have different dependencies than the standard variant. Without considering the specific platform for resolution, we might not get a valid set of dependencies, since new unconsidered constraints can be introduced after resolution. This is rare, but not that rare. For example, nokogiri recently released prerelease versions meeting this condition.
  • Safety. Not only resolution is invalid, but the previous approach meant resolution was not actually fully locked. Say you're using foo-1.2.0 in your application. You have a lockfile specifically locking foo to 1.2.0, you test it in CI and in your staging environment, and it's all good. Then someone pushes foo-1.2.0-x86_64-linux to rubygems.org, a platform specific variant for foo 1.2.0, that, for example, has a critical bug, or that was pushed by a malicious actor. Then when you deploy to production, foo-1.2.0-x86_64-linux will be installed because it matches the running platform more closely. To me this is totally unexpected, if you have a Gemfile.lock file, no third party release should be able to change the set of third party code that you run.

Resolving for the specific running platform and recording the exact resolution in the lockfile fixes the above issues.

Current situation

  • I believe we can improve on it but the current error message is not too bad, and allows for easily fixing the issue.
  • This error will only happen if you are using bundler in frozen mode (with BUNDLE_FROZEN or BUNDLE_DEPLOYMENT). If you're not using frozen mode, then bundler will automatically re-resolve using the running platform if it's not already in the lockfile. If you are using frozen mode, I believe it means that you explicitly want to avoid the second issue I mentioned above, so... an error if that can't be guaranteed seems appropriate.
  • Yes, some tutorials could be broken by this, but only under some circumstances:

    • If the tutorial provides a Gemfile.lock then bundler will respect that. Meaning, if the lockfile was generated with previous bundler versions, bundler will still fallback to how it worked in previous versions. But if you generate a lockfile from scratch, then it will use the more secure and correct mode.
    • If the tutorial does not set frozen mode, then bundler will re-resolve and just work as mentioned above.
    • If the two above are not met, yes, things can break, but just like they could break if there's a new Rails release or whatever dependencies the tutorial uses. I believe that's acceptable, but I'm willing to help updating any popular tutorials that we detect to be affected by this.

Please let me know what you think.

开发环境中,当执行 bundle install 的时候,gemfile.lock 中的 plantforms 会发生变化,是为了锁定当前系统环境的特定的 gem,避免供应链攻击。

你的测试环境是 x86 64 位 linux,那么 plantforms 需要包含 x86_64-linux

bundle lock --add-platform x86_64-linux

如果服务器环境的 bundler 是 2.1.x,那么还需加上

bundle lock --add-platform ruby

简而言之,作者的意思是没有加platform之前,虽然gem 的版本是相同的,但是有可能platform不同,导致拉取的gem实际上不同(因为不同的platform会有针对不同系统的版本)

比如说 Rails 的默认 Gemfile 里面,tzinfo就是针对 Windows 平台的

rocLv 回复

我刚才试了一下这个命令

bundle lock --add-platform jruby
bundle lock --add-platform mingw

结果发现 Gemfile.lock 中的 nokogiri 下载了好几个版本。

如果有些版本需要编译,我的 Mac 电脑下载了 windows 版本,也无法编译啊。

难道 Gem 不同 platform 的版本,都是预先编译好的吗?

(请熟悉编译的同学指导一下)

cc @rocLv

xiaoronglv 回复

你可以用bundle open nokogiri 打开本地文件,然后设置一个断点,

按照正常的理解是不同平台,编译相应的 Gem,如果不确定可以加断点试试。

比如说 windows 平台,引用的是*x86_64*版本的 gem;jruby平台,引用的是-java版。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号