Skip to content

fix(vendor): vendor pure-ruby (?platform=ruby) gems, gate on the purl not the staging dir - #172

Open
Mikola Lysenko (mikolalysenko) wants to merge 1 commit into
mainfrom
fix/gem-platform-ruby-vendorable
Open

fix(vendor): vendor pure-ruby (?platform=ruby) gems, gate on the purl not the staging dir#172
Mikola Lysenko (mikolalysenko) wants to merge 1 commit into
mainfrom
fix/gem-platform-ruby-vendorable

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Problem

Vendoring a pure-ruby gem whose production patch purl is platform-qualified — pkg:gem/activestorage@7.0.2.2?platform=ruby — was refused with platform_gem_unsupported ("installed dir gem does not equal activestorage-7.0.2.2"). The ruby platform is the DEFAULT, portable RubyGems platform, not a native build, so this refusal is wrong.

The gem vendor gate used installed_dir.file_name() != "<name>-<version>" as a proxy for "platform-specific build". That proxy breaks on the registry auto-fetch ladder: a lockfile-resolvable-but-not-installed gem is staged into a private tempdir named literally gem (registry_fetch::fetch_gem), so dir_name is gem and every pure-ruby auto-fetched gem tripped the gate.

What #157 already fixed vs. what remained (verified against current main, real production API + rubygems.org, SOCKET_NO_CONFIG=true):

Fix

Gate on the authoritative signal — the purl's own ?platform= qualifier — instead of the staging dir name:

  • ?platform=ruby (and a bare, unqualified purl) is the portable build and vendors normally.
  • Only a genuine native platform (x86_64-linux, arm64-darwin, java, x64-mingw32, …) is refused, with the same clear platform_gem_unsupported code.
  • Defense in depth: a locally-resolved platform-suffixed install dir (<name>-<version>-<suffix>) is still refused (preserving the downstream invariant that platform installs never reach the copy/lock edit). The auto-fetch gem dir is not such a suffix, so it passes.

Adds a small reusable purl_qualifier(purl, key) to utils::purl (the other ecosystems only ever strip qualifiers) rather than hand-rolling the parse. Change is scoped to the gem vendor path.

Test

  • New hermetic unit tests in the gem vendor module (RED before / GREEN after — both fail on the old gate, the first reproducing the exact installed dir gem does not equal rack-3.2.6 symptom):
    • test_platform_ruby_gem_from_autofetch_staging_dir_vendors — a ?platform=ruby gem from a gem-named auto-fetch staging dir now vendors successfully.
    • test_refuses_native_platform_qualifier — a native ?platform=x86_64-linux purl is still refused even from a clean leaf dir.
    • test_refuses_platform_suffixed_dir (existing) stays green via the defense-in-depth dir check.
    • test_purl_qualifier — unit coverage for the new helper.
  • Real end-to-end (rebuilt binary, SOCKET_NO_CONFIG=true, live prod + rubygems.org):
    • The previously-broken auto-fetch scenario now vendors cleanly 4/4 runs (applied: 1, exit 0), via vendor_fetched_missingvendor_prebuilt_downloaded.
    • Installed-gem scenario still clean 8/8.
    • Delivery proof: a cold, frozen bundle install from the committed .socket/vendor/ tree succeeds (exit 0) and bundle info activestorage resolves 7.0.2.2 from the vendored (patched) path.
  • cargo build + cargo clippy (both crates) clean; the only clippy warnings are pre-existing doc_lazy_continuation in e2e_vendored_production.rs (untouched).

Note (separate, pre-existing, out of scope): with the default --vendor-source auto/service, the patch-service gem-stub-gemspec for activestorage omits s.summary, which RubyGems' validation rejects on bundle install. This is a converter/service-side artifact defect, not in the gem vendor CLI path — it affects the installed and auto-fetch paths equally and was simply never reached before because vendoring failed first. Delivery via the local-build stub (a valid gemspec) works, as shown above. The live-ignored gem_bundler_vendored_known_platform_defect e2e leg will now take its built-in "vendor succeeds" branch; promoting it to a full frozen-install delivery proof is a follow-up gated on that stub-summary fix.

🤖 Generated with Claude Code


Note

Medium Risk
Changes gem vendor gating logic on production patch paths; scope is limited to platform detection with added tests, but incorrect classification could still allow or block vendoring incorrectly.

Overview
Fixes incorrect platform_gem_unsupported refusals for pure-ruby gems whose patch PURL is ?platform=ruby (or unqualified), especially on the registry auto-fetch path where content is staged in a tempdir named gem rather than <name>-<version>.

Adds purl_qualifier in utils/purl to read qualifier values (case-insensitive keys, stops at #subpath). Gem vendor logic now treats ?platform=ruby and bare purls as portable and vendors them; only non-empty, non-ruby platform values (e.g. x86_64-linux) are refused via platform_gem_unsupported.

Defense in depth: platform-suffixed install dirs (<name>-<version>-<suffix>) are still refused via dir_name.starts_with("{leaf}-") instead of dir_name != leaf, so the literal gem staging dir is not mistaken for a native build.

New unit tests cover purl_qualifier, native platform refusal from a clean leaf dir, and the auto-fetch gem staging regression.

Reviewed by Cursor Bugbot for commit ed3688f. Configure here.

… not the staging dir

The gem vendor gate refused any install whose dir name was not
`<name>-<version>`, using that as a proxy for "platform-specific build".
That proxy is wrong for the registry auto-fetch ladder: a lockfile-
resolvable-but-not-installed gem is staged into a private tempdir named
literally `gem` (registry_fetch::fetch_gem), so a pure-ruby gem like
`activestorage` (whose production patch purl is `?platform=ruby`, the
DEFAULT portable platform) was refused with `platform_gem_unsupported`.

#157 (find_packages_for_rollback) already fixed the runtime behavior for
gems the crawler finds installed (qualified `?platform=ruby` now maps to
the installed base dir), so `scan --mode vendored` vendors cleanly when the
gem is installed under `vendor/bundle` or a gem home. But the auto-fetch
path (fresh clone / uninstalled but lock-resolvable) still hit the bug with
the identical error.

Gate on the authoritative signal instead: the purl's own `?platform=`
qualifier. `ruby` (and a bare, unqualified purl) is the portable build and
vendors; only a genuine native platform (`x86_64-linux`, `arm64-darwin`,
`java`, `x64-mingw32`, …) is refused. A defense-in-depth dir check still
refuses a locally-resolved platform-suffixed install dir
(`<name>-<version>-<suffix>`), which the auto-fetch `gem` dir is not.

Adds a reusable `purl_qualifier(purl, key)` to utils::purl (the other
ecosystems only ever strip qualifiers) and hermetic regression tests:
`?platform=ruby` from a `gem`-named staging dir now vendors; a native
`?platform=x86_64-linux` is refused even from a clean leaf dir.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant