buildpacks icon indicating copy to clipboard operation
buildpacks copied to clipboard

Can't install native extension gem which includes pre-build binaries

Open sue445 opened this issue 7 months ago • 6 comments

Describe the bug

Can't install native extension gem which includes pre-build binaries

e.g.

  • https://rubygems.org/gems/ffi v1.17.0+
  • https://rubygems.org/gems/google-protobuf v4.31.0+

Additional context How are you using GCP buildpacks?

  • [ ] pack and the gcr.io/buildpacks/builder
  • [x] Cloud Functions
  • [ ] Cloud Run
  • [ ] Cloud Build
  • [ ] App Engine Standard
  • [ ] App Engine Flex
  • [ ] Firebase App Hosting

Did this used to work? yes

What language is your project primarily written in? Ruby 3.3

Steps To Reproduce Gemfile

source "https://rubygems.org"

gem "functions_framework", "1.6.0"

gem "ffi", "1.17.0"

Steps to reproduce the behavior:

  1. Deploy using gcloud functions deploy command
    • e.g. gcloud functions deploy my-func --gen2 --runtime=ruby33

Expected behavior

Deploy is successful

Actual behavior

Error in buildpack

Cloud Build log

ERROR: (gcloud.beta.functions.deploy) OperationError: code=3, message=Build failed with status: FAILURE and message: Fetching gem metadata from https://rubygems.org/...........
Your bundle is locked to ffi (1.17.0-x86_64-linux) from rubygems repository
https://rubygems.org/ or installed locally, but that version can no longer be
found in that source. That means the author of ffi (1.17.0-x86_64-linux) has
removed it. You'll need to update your bundle to a version other than ffi
(1.17.0-x86_64-linux) that hasn't been removed in order to install.. For more details see the logs at https://console.cloud.google.com/cloud-build/builds;xxxxxxxxxxxxxxxxxx

Why?

Since ffi v1.17.0, ffi gem includes pre-build binaries in the following format.

  • ffi-${GEM_VERSION}-x86_64-linux-gnu.gem
  • ffi-${GEM_VERSION}-x86_64-linux-musl.gem
  • ....

c.f. https://rubygems.org/gems/ffi/versions/1.17.0-x86_64-linux-gnu

So it seems that bundler is trying to download 1.17.0-x86_64-linux.gem (instead of 1.17.0-x86_64-linux-gnu.gem) and is getting an error.

bundle lock --add-platform x86_64-linux is executed before bundle install.

https://github.com/GoogleCloudPlatform/buildpacks/blob/3dea9e987f9424a08ad455610431126147d84c7e/cmd/ruby/bundle/main.go#L123

About gem other than ffi

This issue covers ffi, but I have observed the same problem in other google-protobuf v4.31.0+

https://rubygems.org/gems/google-protobuf/versions/4.31.0-x86-linux-gnu

Since I expect that there will be more such gems in the future, I think it is better to use buildpack instead of each gem.

Workaround 1: Use old version

e.g.

# Gemfile
gem "ffi", "< 1.17.0"

This would be fine for a temporary period.

However, new features are not available or security issues remain by continuing to use older versions

Workaround 2: Pass BUNDLE_FORCE_RUBY_PLATFORM to --set-build-env-vars

e.g.

gcloud functions deploy my-func --gen2 --runtime=ruby33 \
--set-build-env-vars=BUNDLE_FORCE_RUBY_PLATFORM=1

This means building all native extensions in Gemfile. This is very slow!

For example, https://rubygems.org/gems/grpc takes 30+ minutes to build, so Cloud Build will give a timeout error when deploying

Running "bundle install (NOKOGIRI_USE_SYSTEM_LIBRARIES=1 MALLOC_ARENA_MAX=2 LANG=C.utf8)"
--
274 | Fetching gem metadata from https://rubygems.org/

(...)

Fetching grpc 1.70.0
Installing grpc 1.70.0 with native extensions
TIMEOUT
ERROR: context deadline exceeded

Workaround 3. Use force_ruby_platform in Gemfile

By using force_ruby_platform , only certain gems can be built at bundle install time without using the pre-build gem.

e.g.

# Gemfile
gem "ffi", force_ruby_platform: true

However, version of bundler used in buildpack is out of date, so I get an error...

ERROR: (gcloud.beta.functions.deploy) OperationError: code=3, message=Build failed with status: FAILURE and message: [!] There was an error parsing `Gemfile`: You passed :force_ruby_platform as an option for gem 'ffi', but it is invalid. Valid options are: group, groups, git, path, glob, name, branch, ref, tag, require, submodules, platform, platforms, type, source, install_if, gemfile, github, gist, bitbucket. You may be able to resolve this by upgrading Bundler to the newest version.. Bundler cannot continue.

 #  from /workspace/Gemfile:25
 #  -------------------------------------------
 >  gem "ffi", force_ruby_platform: true
 #  -------------------------------------------.

force_ruby_platform is available since bunlder v2.3.18+ https://github.com/rubygems/rubygems/releases/tag/bundler-v2.3.18

But buidpack seems to be using bundler v2.3.15

https://github.com/GoogleCloudPlatform/buildpacks/blob/e3a8f6ac1f2eadb97379e93e84d577545cea9d83/cmd/ruby/rubygems/main.go#L37

sue445 avatar May 20 '25 12:05 sue445

The problem is that the default and locked Bundler version (2.3.15) included with the buildpack doesn't recognize newer Linux platform variants like linux-musl and linux-gnu. Gems targeting *-linux-gnu or *-linux-musl require at least RubyGems 3.3.22 and Bundler 2.3.21 to install properly. One workaround is to compile these gems from source.

As for your second workaround, you can extend the timeout like this:

gcloud functions deploy my-func \
  --gen2 \
  --runtime ruby33 \
  --entry-point my_func \
  --region us-central1 \
  --source . \
  --trigger-http \
  --allow-unauthenticated \
  --timeout 3600 \
  --set-build-env-vars BUNDLE_FORCE_RUBY_PLATFORM=1

It would be nice if buildpacks allowed overriding the Bundler version.

pjmartorell avatar Jul 23 '25 13:07 pjmartorell

@pjmartorell

As for your second workaround, you can extend the timeout like this:

No, --timeout is timeout for function that is already deployed, not timeout for deploying.

https://cloud.google.com/sdk/gcloud/reference/functions/deploy#--timeout

sue445 avatar Jul 23 '25 14:07 sue445

@pjmartorell

As for your second workaround, you can extend the timeout like this:

No, --timeout is timeout for function that is already deployed, not timeout for deploying.

https://cloud.google.com/sdk/gcloud/reference/functions/deploy#--timeout

True, sorry for the confusion 👍🏻

pjmartorell avatar Jul 23 '25 14:07 pjmartorell

We have upgraded the bundler version to 2.3.21 in https://github.com/GoogleCloudPlatform/buildpacks/commit/6e396f2db7a79b1c40e246a71fbdb2115ef4104c.

The buildpacks now fully support the force_ruby_platform option and can correctly handle gems with platform variants like linux-musl and linux-gnu. The prod release of the builder will complete next week.

Thanks for bringing this issue to our attention and providing such a detailed analysis!

srinjoyray avatar Jul 31 '25 09:07 srinjoyray

We have upgraded the bundler version to 2.3.21 in 6e396f2.

The buildpacks now fully support the force_ruby_platform option and can correctly handle gems with platform variants like linux-musl and linux-gnu. The prod release of the builder will complete next week.

Thanks for bringing this issue to our attention and providing such a detailed analysis!

Thanks for upgrading the bundler version! 🙌 I gave it a try using the latest builder (universal_builder_20250819_RC00), but it’s still installing bundler 2.3.15 instead of 2.3.21.

Am I missing something here? Could this be because the updated builder hasn’t been released yet?

See the logs:

===> DETECTING
  | target distro name/version labels not found, reading /etc/os-release file
  | 5 of 7 buildpacks participating
  | google.ruby.runtime      0.0.1
  | google.ruby.rubygems     0.9.0
  | google.ruby.bundle       0.9.0
  | google.config.entrypoint 0.9.0
  | google.utils.label-image 0.0.2
  | ===> RESTORING
  | ===> BUILDING
  | target distro name/version labels not found, reading /etc/os-release file
  | === Ruby - Runtime ([email protected]) ===
  | Setting Nodejs runtime version GOOGLE_NODEJS_VERSION: *
  | ***** CACHE MISS: "ruby"
  | Installing Ruby Runtime v3.4.5.
  | 2025/08/28 11:51:16 [DEBUG] GET https://dl.google.com/runtimes/ubuntu2204/ruby/ruby-3.4.5.tar.gz
  | Removing 'tmp' and 'log' directories in user code
  | === Ruby - Rubygems ([email protected]) ===
  | 2025/08/28 11:51:18 [DEBUG] GET https://rubygems.org/rubygems/rubygems-3.3.15.tgz
  | --------------------------------------------------------------------------------
  | Running "ruby setup.rb -E --no-document --destdir /layers/google.ruby.rubygems/rubygems --prefix /"
  |     Successfully built RubyGem
  |     Name: bundler
  |     Version: 2.3.15
  |     File: bundler-2.3.15.gem
  | Bundler 2.3.15 installed
  | RubyGems 3.3.15 installed
  | Regenerating binstubs
  | Regenerating plugins

 

pjmartorell avatar Aug 28 '25 12:08 pjmartorell

We had to rollback the bundler upgrade change because it caused backward-incompatible issues So native extensions are still not supported 😔. We plan to upgrade bundler with the next Ruby 3.5 runtime release.

srinjoyray avatar Aug 28 '25 12:08 srinjoyray