Rails项目GitLab CI/CD缓存Ruby gem时ox库C扩展加载失败求助
我为Rails应用搭建了包含三个阶段的GitLab CI/CD流程,第一阶段负责缓存gem与资源,配置如下:
cache: &default-cache untracked: true key: "assets-compile:vendor_ruby:.yarn-cache:tmp_cache_assets_sprockets:v3" paths: - vendor/ruby/ - .yarn-cache/ - public/assets/ - node_modules/ policy: pull-push
后续测试阶段从vendor/ruby目录拉取缓存的gem,配置如下:
testing: cache: <<: *default-cache policy: pull stage: test image: "ruby:3.0" timeout: one hour script: - apt-get update -qq && apt-get install -y -qq nodejs - export PATH="/usr/local/bundle/bin:$PATH" - bundle config set --local path 'vendor/ruby' - RAILS_ENV=test bundle install -j $(nproc)
流程整体运行正常,但gem ox抛出LoadError:
Failure/Error: require 'ox' LoadError: cannot load such file -- ox/ox # ./vendor/ruby/ruby/3.0.0/gems/zeitwerk-2.6.6/lib/zeitwerk/kernel.rb:38:in `require' # ./vendor/ruby/ruby/3.0.0/gems/zeitwerk-2.6.6/lib/zeitwerk/kernel.rb:38:in `require' # ./vendor/ruby/ruby/3.0.0/gems/ox-2.14.11/lib/ox.rb:79:in `<top (required)>'
推测问题在于缓存阶段未编译C扩展,导致无法加载生成的ox/ox文件。已尝试移除Gemfile中的require: false,但未解决问题,请问该如何处理?
你的推测完全正确,ox是带有C扩展的gem,缓存的vendor/ruby仅包含gem源码,缺少编译后的二进制文件,因此测试阶段拉取缓存后无法加载。以下是几种可行的解决方法:
1. 缓存编译后的扩展文件
修改缓存配置,加入Ruby扩展的编译产物目录。Ruby 3.0的扩展文件通常存放在vendor/ruby/ruby/3.0.0/extensions/路径下,更新缓存的paths字段:
cache: &default-cache untracked: true key: "assets-compile:vendor_ruby:.yarn-cache:tmp_cache_assets_sprockets:v3" paths: - vendor/ruby/ - .yarn-cache/ - public/assets/ - node_modules/ - vendor/ruby/ruby/3.0.0/extensions/ # 新增编译产物路径 policy: pull-push
注意:后续更换Ruby版本时,需同步更新缓存key和路径中的版本号,避免缓存不兼容。
2. 在测试阶段强制重新编译C扩展
在测试阶段的脚本中,添加重新编译ox扩展的命令,或者使用bundle install --force强制重新安装带C扩展的gem:
修改测试阶段的script部分:
script: - apt-get update -qq && apt-get install -y -qq nodejs - export PATH="/usr/local/bundle/bin:$PATH" - bundle config set --local path 'vendor/ruby' - RAILS_ENV=test bundle install -j $(nproc) - bundle exec gem pristine ox # 重新编译ox的C扩展
若使用bundle install --force替换原bundle install,会重新安装所有gem,可能增加执行耗时,按需选择。
3. 拆分缓存策略,分离纯Ruby与带C扩展的gem
如果担心不同环境的二进制扩展不兼容,可将带C扩展的ox排除在缓存外,测试阶段单独安装:
- 先修改缓存配置,排除
vendor/ruby/ruby/3.0.0/gems/ox-*目录 - 测试阶段脚本调整为:
script: - apt-get update -qq && apt-get install -y -qq nodejs - export PATH="/usr/local/bundle/bin:$PATH" - bundle config set --local path 'vendor/ruby' - RAILS_ENV=test bundle install -j $(nproc) --without none # 安装其他gem - RAILS_ENV=test bundle install --force ox # 强制重新安装并编译ox
4. 确保缓存阶段完成C扩展编译
如果缓存阶段由专门的job生成,需确保该job执行完整的bundle install,触发C扩展编译。比如添加一个缓存构建job:
cache-job: cache: *default-cache stage: build image: "ruby:3.0" script: - apt-get update -qq && apt-get install -y -qq nodejs - bundle config set --local path 'vendor/ruby' - RAILS_ENV=test bundle install -j $(nproc)
这样缓存的vendor/ruby就包含了编译好的扩展文件,测试阶段拉取后可直接使用。
内容的提问来源于stack exchange,提问作者vivek tripathi

