Fix Bundler settings resolution order (#483, #507) - #532
Conversation
Assisted-by: Claude Code:claude-opus-5-5
When BUNDLE_GEMFILE was set both in .bundle/config and in the environment, socket-patch followed the environment. Bundler does the reverse: local app config outranks ENV. A dual-boot project with `gemfile Gemfile.next` committed and BUNDLE_GEMFILE=Gemfile exported got its Gemfile rewired and attested while bundler installed Gemfile.next unpatched. The app config value now wins, unless the environment names a manifest in another directory (that moves bundler's root, so the project's config is never read). Add one app-config reader shared by every Bundler key, and a resolver for bundler's cache dir (cache_path / BUNDLE_CACHE_PATH, default vendor/cache) in the same priority. Refs #507, #483 Assisted-by: Claude Code:claude-opus-5-5
The hosted gem stale-install guard only looked for committed archives in vendor/cache. With `bundle config set --local cache_path vendor/gems` (or BUNDLE_CACHE_PATH), a committed unpatched archive there gave no warning, the same run's VEX attested the gem, and bundle install then installed the unpatched bytes. Both guard flavors now use bundler's configured cache dir, so the stale archive warns, joins the delete-list remedy, and keeps the purl out of the in-run attestation. Fixes #483 Fixes #507 Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
|
BugBot review Generated by Claude Code |
|
Ready for review at head
Generated by Claude Code |
|
Reviewed P1 — Honor Reproduced against Bundler 4.0.15 and the actual CLI: set Validation: 10 Bundler-related core unit tests, 9 manifest-selection tests, and all 25 |
Bundler's load_config returns {} whenever BUNDLE_IGNORE_CONFIG is set,
so a cache_path or gemfile setting in .bundle/config is then ignored.
The stale-install guard still followed the ignored cache_path, skipped
the vendor/cache archive bundler actually installs from, and attested
the purl. Both settings now read the app config through one reader
that honors the switch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MWt5CXPnmqVEUZe4wnCfgX
|
Confirmed the
Left as is: the pre-existing Generated by Claude Code |
|
Follow-up review of I reran the original independent CLI reproduction at this exact head: 1 passed. With a local config pointing at |
|
CI on a757732: I don't think this failure comes from this PR:
I couldn't read the failing envelope ( Generated by Claude Code |
|
BugBot review Generated by Claude Code |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit a757732. Configure here.
LLM Description written by Claude Code:claude-opus-5-5
Fixes #483
Fixes #507
Summary
Gem modes now read Bundler settings in Bundler's own priority, with the local app config over the environment.
BUNDLE_GEMFILEenv var override.bundle/config, but Bundler does the reverse, so hosted mode wiresGemfilewhile bundler installsGemfile.nextunpatched and VEX attests it #507: WhenBUNDLE_GEMFILEis set in both.bundle/configand the environment, the.bundle/configvalue now wins, as it does in Bundler. Before, a dual-boot project (bundle config set --local gemfile Gemfile.next) with an exportedBUNDLE_GEMFILE=Gemfilehad itsGemfilerewired and attested, while Bundler installedGemfile.nextunpatched. That project now getsredirect_gem_bundle_gemfile_unsupported, nothing is redirected, and nothing is attested. Vendored mode uses the same resolver (vendor/gem.rs→bundler_loaded_manifest), so it is fixed too.vendor/cache, so a committed cache at a configuredcache_pathgets no warning, the in-run VEX attests it, and Bundler 2.4 installs the unpatched gem #483: The hosted gem stale-install guard now looks for the committed archive in Bundler's configured cache dir (BUNDLE_CACHE_PATHin the app config, then the env var, elsevendor/cache) instead of alwaysvendor/cache. Both flavors use it: the standalone warning and the folded delete-list remedy. A stale archive there now warns and keeps the same run's VEX from attesting the purl..bundle/configwhenBUNDLE_IGNORE_CONFIGis set, as Bundler does.Root cause
socket-patch read Bundler settings one key at a time, with no resolver that follows
Bundler::Settingspriority (local app config$BUNDLE_APP_CONFIG/config/.bundle/configfirst, thenENV):gemfile:formats/gem/manifest.rs::classifychecked the environment before the app config, which is the reverse of Bundler's order. A unit test even pinned the inverted order.cache_path: never read.scan/hosted.rshard-coded<cwd>/vendor/cachein both guard flavors.Changes
crawlers/ruby_crawler.rs: addedbundle_config_setting(contents, key), one exact-key app-config reader thatconfig_gemfilenow uses. Addedbundler_app_cache_dir[_with_env], which resolvescache_pathin Bundler's priority (relative to the project root, absolute stands alone, read-only use so no containment needed). Both resolvers load the file throughread_app_config, which returns nothing underBUNDLE_IGNORE_CONFIG(Bundler'sload_configreturns{}for any set value).formats/gem/manifest.rs::classify: the app config value now outranks the env. The one exception: an envBUNDLE_GEMFILEnaming a file in another directory still decides alone. That value movesBundler.root, so Bundler reads that root's config and never the project's. The run still refuses, now naming the env var. The inverted unit test is replaced.scan/hosted.rs: both stale-guard flavors use the resolved cache dir, and the warning text now says "its cache dir".redirect_gem_bundle_gemfile_unsupportedandredirect_gem_stale_installrows), docs/ecosystems.md, and CHANGELOG[Unreleased] / Fixed.npm/,pypi/,gem/only dispatch to the binary).Per-issue checklist
BUNDLE_GEMFILEenv var override.bundle/config, but Bundler does the reverse, so hosted mode wiresGemfilewhile bundler installsGemfile.nextunpatched and VEX attests it #507, each test red onmainand green here:formats::gem::manifest::tests::config_wins_over_env_like_bundler_settings: configGemfile.next+ envGemfilegivesUnsupported{AppConfig}and the remedy namesbundle config unset --local gemfile. Both directions of the supported-spelling conflict are covered too.formats::gem::manifest::tests::env_gemfile_in_another_directory_is_never_overridden_by_project_configcrawlers::ruby_crawler::tests::loaded_manifest_app_config_beats_the_environment(on disk)e2e_redirect_gem_build::gem_hosted_bundle_gemfile_config_outranks_env_redirects_nothing(ScanVexDualBoot+BUNDLE_GEMFILE=Gemfileexported to socket-patch). Onmainit fails with exactly the reported envelope:"redirected":1,"rewrittenFiles":["Gemfile"],"vex":{"statements":1}.vendor/cache, so a committed cache at a configuredcache_pathgets no warning, the in-run VEX attests it, and Bundler 2.4 installs the unpatched gem #483, each test red onmainand green here:commands::scan::hosted::tests::gem_stale_probe_follows_the_configured_bundle_cache_path: covers the standalone warning, thestale_purlsVEX exclusion, the folded delete list, and that a leftovervendor/cachearchive is ignored oncecache_pathmoves it.e2e_redirect_gem_stale_install::gem_hosted_stale_archive_at_configured_cache_path_warns_and_is_not_attested, for the app-config,BUNDLE_CACHE_PATH-env andBUNDLE_IGNORE_CONFIGarms. Onmainthe first two fail with the reported symptom: noredirect_gem_stale_install,vex.statements: 1. The ignore-config arm fails the same way on 477aae9.crawlers::ruby_crawler::tests::app_cache_dir_follows_bundler_settings_priorityandbundle_config_setting_matches_the_exact_key(new helpers)Test evidence (Linux, Ruby 3.3.6, Bundler 4.0.17)
cargo clippy --workspace --all-features -- -D warnings: clean.cargo test -p socket-patch-core --all-features --lib: 4720 passed, 4 failed. The 4 failures (copy_tree::relax_loop_must_not_traverse_symlinked_root,vlt_heal::an_unremovable_hidden_lock_keeps_every_store_entry,pypi_poetry::wire_write_failure_…,pypi_requirements::wire_failure_rolls_back_…) fail identically onorigin/main. They are permission tests that root bypasses in this sandbox, and they are unrelated to this change.cargo test -p socket-patch-cli --all-features --lib: 830 passed.--test e2e_redirect_gem_stale_install(25),in_process_gem_apply(11),in_process_gem_multi_platform(7),covgap_commands_scan_hosted(50),hosted_memory_parity(31), corecrawler_ruby_e2e(25): all pass.--test e2e_redirect_gem_build --include-ignored(19/19) and--test e2e_vendor_gem_build --include-ignored(15/15), with real Bundler: all pass on a757732.cargo fmt --all -- --check:mainitself is not rustfmt-clean (460 diffs with the pinned 1.93.1 rustfmt, and CI doesn't gate it), so I formatted only this PR's own hunks.cargo test --workspace --all-features: building every integration-test binary used up the sandbox's disk allowance. CI covers it.Follow-ups
~/.bundle/config(priority below ENV) is still not consulted for any key. That was already true and is unchanged here.BUNDLE_PATHapp-config probe (RubyCrawler::app_config_bundle_path) doesn't honorBUNDLE_IGNORE_CONFIGyet. It only adds a discovery path, it predates this PR, and this PR doesn't touch it.🤖 Generated with Claude Code
https://claude.ai/code/session_01MWt5CXPnmqVEUZe4wnCfgX
Note
Medium Risk
Changes which Gemfile gets wired and where stale-cache warnings fire—incorrect resolution could skip patches or attest unpatched gems, but behavior now matches Bundler and is heavily tested.
Overview
Aligns Ruby gem hosted and vendored flows with Bundler’s settings order so socket-patch does not edit or attest manifests Bundler never loads.
BUNDLE_GEMFILE(#507):.bundle/confignow wins over the environment (matchingBundler::Settings). Dual-boot setups withgemfile Gemfile.nextlocally plusBUNDLE_GEMFILE=Gemfilein the shell no longer get the ignoredGemfileredirected or VEX-attested; they hitredirect_gem_bundle_gemfile_unsupportedinstead. An env gemfile outside the project root still wins alone (Bundler’s root moves).BUNDLE_IGNORE_CONFIGskips reading.bundle/configfor gemfile resolution.Stale-install guard (#483): Hosted
redirect_gem_stale_installprobes the committed.gemin Bundler’scache_path(app configBUNDLE_CACHE_PATH:→ envBUNDLE_CACHE_PATH→ defaultvendor/cache), not a hard-codedvendor/cache. Stale archives at the configured path warn and exclude the purl from same-run--vex; leftover archives under the default path when cache is relocated are ignored.Shared helpers:
bundle_config_setting,bundler_app_cache_dir, and updatedmanifest::classify. Docs (CLI_CONTRACT.md,ecosystems.md, CHANGELOG) and unit/e2e tests cover config-over-env and custom cache paths.Reviewed by Cursor Bugbot for commit a757732. Configure here.