Skip to content

Read Gemfile.lock sections and DEPENDENCIES entries through formats::gem in hosted and vendored modes #780

Description

[agent] Filed by the scheduled architecture audit routine (ecosystems and formats). Register: discussion #560 register.

Kind: refactor. Source: review Part 5.4 ("Gem: … its own section model … two DEPENDENCIES-name parsers"), register E19 (gem half).

Problem

formats/gem says it is the one read model for Gemfile.lock, but hosted and vendored mode each re-scan the lock with their own section model and their own DEPENDENCIES-entry grammar. Verified on 045d7ec:

Section model DEPENDENCIES entry name
shared reader formats::gem::parse (inventory, VEX, lock_lists_direct_dependency, upstream restore) Section for every GEM/PATH/GIT/PLUGIN SOURCE header, with remotes and spec lines indent 2, split([' ', '(', '!']) (mod.rs#L286-L298)
hosted formats::gem::hosted::converge_gem_lock_source its own GemLockSection (hosted.rs#L26-L35), GEM headers only, built by a second header walk gem_lock_dependency_name: trim_start, split(" ("), trim_end_matches('!')
vendored vendor/gem.rs section_span(lines, header) / section_end: the first line equal to header, plus find_our_path_section and find_path_section (#L1926-L1942, #L2299-L2317) dep_entry_name: at_indent(2) + find([' ', '(', '!']), and spec_entry_name beside it

So there are three section models and three DEPENDENCIES-name rules (the review counted two of the latter). The name rules agree on every Bundler-written entry I tried. The section models have already drifted: vendored section_span(&lines, "GEM") sees only the first GEM section, while the shared reader and hosted see all of them. Bundler 2 writes one GEM section per source, so a gem resolved from any source but the first can't be vendored. That bug is filed separately as #779, since it can be fixed before this refactor.

Symptoms and impact

Size: formats/gem is 1,149 lines, vendor/gem.rs 7,576 (2,461 production).

Proposed change

Make formats::gem the single source of line spans, and have both writers splice through it:

  1. Extend GemfileLock with what the writers need: each Section's end line (exclusive, the next column-0 header), and the DEPENDENCIES entries as (line_no, name, pinned) using one name rule. Expose gem_sections(), path_sections(), dependencies() and section(header).
  2. Hosted: build converge_gem_lock_source's section and DEPENDENCIES lookups from parse(lk). Delete GemLockSection, its header walk and gem_lock_dependency_name.
  3. Vendored: replace section_span, section_end, dep_entry_name, spec_entry_name, find_our_path_section and find_path_section with the parsed model. Delete them. edit_lock looks the spec up across every GEM section, which fixes Vendored gem refuses a gem whose spec is not in the lock's first GEM section #779 if it is still open.

Land it as two PRs, hosted first (smaller), then vendored. Neither changes output bytes.

Size and scope

Est. +120 / −250 production lines across formats/gem/mod.rs, formats/gem/hosted.rs and vendor/gem.rs. Out of scope: the Gemfile (Ruby source) models in formats/gem/manifest.rs and rewrite_gem's CHECKSUMS regexes (review Part 3, #340), and the hosted ↔ vendored takeover logic (#775, PR #776).

Acceptance criteria

  • GemLockSection, gem_lock_dependency_name, section_span, section_end, dep_entry_name and spec_entry_name no longer exist. grep -n '"DEPENDENCIES"' crates/socket-patch-core/src finds the header only in formats/gem/mod.rs and tests.
  • Byte-identical output: e2e_redirect_gem_build, e2e_vendor_gem_build, the hosted gem goldens and the vendor/gem.rs unit tests stay green unchanged.
  • New formats::gem tests: section end lines with CRLF and trailing blank lines, a two-GEM-section lock, and DEPENDENCIES entries rack, rack!, rack (~> 3.1), rack (= 3.2.6)! naming rack with the right pinned flag.
  • A hosted test and a vendored test on a lock whose target gem is in the second GEM section.

Dependencies

Open PRs #768 and #776 edit vendor/gem.rs and formats/gem/mod.rs, so start after they merge. Blocks nothing; it makes #779's fix a deletion.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent:triagedarch-auditFiled by a scheduled architecture audit routine (see the architecture review discussion)pm:bundlerBundler (RubyGems)priority:p1refactorStructural change: duplicated code or logic, missing abstraction, layering, dead code

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions