Add --no-build-extension and --no-install-plugin options to gem install#9473
Open
Add --no-build-extension and --no-install-plugin options to gem install#9473
--no-build-extension and --no-install-plugin options to gem install#9473Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds opt-out controls to RubyGems and Bundler to reduce supply-chain risk during installation by allowing users to skip native extension builds and RubyGems plugin installation/loading when explicitly requested.
Changes:
- Add
--[no-]build-extensionand--[no-]install-pluginoptions to RubyGems install/update option parsing and propagate them through dependency installation. - Update
Gem::Installer(and Bundler’s RubyGems installer wrapper) to conditionally skip extension builds and plugin installation/loading, with warnings and recovery guidance. - Add test coverage in RubyGems (Minitest) and Bundler (RSpec) for
no_build_extension.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| test/rubygems/test_gem_installer.rb | Adds RubyGems installer tests for skipping extension builds and plugin installation/loading. |
| spec/install/gems/no_build_extension_spec.rb | Adds Bundler spec verifying no_build_extension prevents extension build artifacts. |
| lib/rubygems/request_set.rb | Skips rebuilding extensions for already-installed specs when :build_extension == false. |
| lib/rubygems/installer.rb | Gates extension builds, plugin generation, and plugin loading; adds warning helpers; avoids loading missing plugin wrappers. |
| lib/rubygems/install_update_options.rb | Defines the new CLI flags for install/update commands. |
| lib/rubygems/dependency_installer.rb | Stores and forwards new options into the request set install flow. |
| bundler/lib/bundler/source/rubygems.rb | Passes Bundler settings (no_build_extension, no_install_plugin) into RubyGems installer options. |
| bundler/lib/bundler/source/path/installer.rb | Skips building extensions for path installs when no_build_extension is set. |
| bundler/lib/bundler/settings.rb | Registers no_build_extension and no_install_plugin as boolean settings. |
| bundler/lib/bundler/rubygems_gem_installer.rb | Mirrors RubyGems installer gating behavior for Bundler’s installer wrapper. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+285
to
+289
| if options[:build_extension] == false | ||
| warn_skipped_extensions | ||
| else | ||
| build_extensions | ||
| end |
Comment on lines
533
to
+538
| wrappers: true, | ||
| env_shebang: true, | ||
| build_args: options[:build_args], | ||
| bundler_extension_cache_path: extension_cache_path(spec) | ||
| bundler_extension_cache_path: extension_cache_path(spec), | ||
| build_extension: Bundler.settings[:no_build_extension] ? false : nil, | ||
| install_plugin: Bundler.settings[:no_install_plugin] ? false : nil |
Comment on lines
183
to
186
| sorted_requests.each do |req| | ||
| if req.installed? && @always_install.none? {|spec| spec == req.spec.spec } | ||
| req.spec.spec.build_extensions | ||
| req.spec.spec.build_extensions unless options[:build_extension] == false | ||
| yield req, nil if block_given? |
Comment on lines
293
to
+298
| generate_bin | ||
| generate_plugins | ||
| if options[:install_plugin] == false | ||
| warn_skipped_plugins | ||
| else | ||
| generate_plugins | ||
| end |
Comment on lines
+42
to
+46
| if options[:install_plugin] == false | ||
| warn_skipped_plugins | ||
| else | ||
| generate_plugins | ||
| end |
…nstall These options allow users to opt out of building native extensions and installing plugins during gem installation, providing an equivalent to npm's --ignore-scripts for mitigating arbitrary code execution vectors. Both options default to true to maintain backward compatibility. Users can disable them per-command or globally via gemrc configuration. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When plugins are not installed (e.g. via --no-install-plugin), the plugin files do not exist on disk. Without this check, load_plugin would attempt to load non-existent files and produce spurious LoadError warnings. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extend the --no-build-extension and --no-install-plugin support to Bundler's installation paths. RubyGemsGemInstaller#install now respects these options, and the settings are propagated from Bundler::Settings through Source::RubyGems to the installer. Path::Installer also respects no_build_extension for git/path sources. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…lugins When extensions or plugins are skipped via --no-build-extension or --no-install-plugin, warn the user and point them to the appropriate gem pristine command to re-enable later. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Without this change, reinstalling or upgrading a gem with --no-install-plugin would still execute a pre-existing plugin wrapper left by a previous install via load_plugin. This defeats the opt-out. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…config The quality spec requires all Bundler settings to be documented in the bundle-config man page. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a gem upgrades from a version with plugins to one without, generate_plugins normally removes the old wrapper files. Skipping generate_plugins entirely with --no-install-plugin prevented this cleanup, leaving stale wrappers that would still be loaded. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
| Whether Bundler should include a checksums section in new lockfiles, to protect from compromised gem sources\. Defaults to true\. | ||
| .TP | ||
| \fBno_build_extension\fR (\fBBUNDLE_NO_BUILD_EXTENSION\fR) | ||
| Whether Bundler should skip building native extensions during installation\. When set, gems are installed without compiling their C extensions\. To build extensions later, run \fBgem pristine <gem> \-\-extensions\fR\. |
Collaborator
There was a problem hiding this comment.
I think the gem pristine <gem> command is not going to work if a user has set BUNDLE_PATH (either in env or config). Wouldn't running bundle install again build the extensions ?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Background
Installing a gem can execute arbitrary code through two mechanisms: native extension builds (via
extconf.rb,Rakefile, etc.) and plugin evaluation (viarubygems_plugin.rb). This is a known supply chain attack vector, analogous to npm's postinstall scripts.This PR adds
--no-build-extensionand--no-install-pluginoptions togem install, allowing users to opt out of these behaviors. Bundler also gains correspondingno_build_extensionandno_install_pluginconfiguration settings.When extensions or plugins are skipped, a warning message is displayed with recovery instructions pointing to
gem pristine --extensionsorgem pristine --only-plugins, so users can re-enable them later without reinstalling the gem.These options only take effect when explicitly specified by the user. The default behavior remains unchanged: extensions are built and plugins are installed as before.
Making
--no-build-extensionthe default would be a significant behavioral change that requires careful investigation into how many gems in the ecosystem rely on native extensions being built at install time, how tools like bundler and CI pipelines depend on this behavior, and what the migration path would look like for gem authors.That analysis is outside the scope of this PR.
Make sure the following tasks are checked