Skip to content

Move content address to a separate CONTENT ADDRESSES lockfile section - #194

Merged
jenshenny merged 1 commit into
feature-branch-ca-changes-rubygemsfrom
ca-lockfile-content-addresses-section
Sep 2, 2026
Merged

Move content address to a separate CONTENT ADDRESSES lockfile section#194
jenshenny merged 1 commit into
feature-branch-ca-changes-rubygemsfrom
ca-lockfile-content-addresses-section

Conversation

@jenshenny

@jenshenny jenshenny commented Sep 1, 2026

Copy link
Copy Markdown

What

Implements item 4 of hsbt's format review: the content address moves out of the GEM/specs line into a dedicated CONTENT ADDRESSES section, so a lockfile written by Bundler 4.1 degrades gracefully on Bundler 4.0 instead of breaking it.

GEM
  specs:
    nokogiri (1.19.4-x86_64-linux)

CONTENT ADDRESSES
  nokogiri (1.19.4-x86_64-linux) 86e5e59f sha256=<CA build's sha>

CHECKSUMS
  nokogiri (1.19.4-x86_64-linux) sha256=<fat gem's sha>

Why the inline format had to go

nokogiri (1.19.4-86e5e59f) x86_64-linux parses on 4.0 as platform unknown → materializes nokogiri-1.19.4-unknown → fails, then re-resolves and rewrites the lockfile (or hard-fails under frozen). Lockfiles are committed and read by many Bundler versions during co-publication, and the format is permanent once shipped.

Verified against released Bundler 4.0.9

4.0 behavior Receipt
Unknown sections skipped silently (@parse_method = nil) lockfile_parser.rb:137
Plain bundle install doesn't rewrite: lockfiles_equal? ignores unknown sections, so the section survives plain installs and frozen mode; it's only dropped on a genuine 4.0 re-lock definition.rb:1196-1208
A locked spec with a missing or empty CHECKSUMS entry counts as a lockfile change → churn on plain install, hard error under frozen definition.rb:619-620, 861-862, 933-935
CHECKSUMS entries that don't map to a known spec are skipped (return unless spec) parse_checksum

Compat matrix

Client / operation Result
4.0 plain install, lockfile in sync fat gem installed, verified, lockfile untouched (section survives)
4.0 frozen / deployment works; no spurious "lockfile changed"
4.0 re-lock (Gemfile change, bundle update) section dropped, coherent fat-gem lockfile; restored on next 4.1 re-lock
4.1 any CA build installed, verified against the section sha; lockfile is a fixed point (no churn, frozen-safe)
skinny-only publication 4.1 fine; 4.0 fails at artifact fetch — no lockfile format can fix a missing artifact

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The permanent lockfile-format and checksum-identity changes warrant final human cross-version review.

Pull request overview

Moves content addresses into a dedicated lockfile section, preserving Bundler 4.0 compatibility while retaining artifact verification.

Changes:

  • Adds CONTENT ADDRESSES generation and parsing.
  • Keeps standard platform pins in GEM and CHECKSUMS.
  • Keys checksums by artifact full name to distinguish fat and content-addressed builds.
File summaries
File Description
lib/bundler/checksum.rb Separates checksums by artifact full name.
lib/bundler/lazy_specification.rb Supports parsed content-address assignment.
lib/bundler/lockfile_generator.rb Generates the new section.
lib/bundler/lockfile_parser.rb Parses the new section and ordinary pins.
lib/bundler/rubygems_ext.rb Removes content addresses from lock names.
spec/bundler/lockfile_parser_spec.rb Tests parsing and checksum separation.
spec/install/gemfile/content_addressable_spec.rb Tests installation and lockfile behavior.
spec/other/ext_spec.rb Tests ordinary platform lock names.
Review details
  • Files reviewed: 8/8 changed files
  • Comments generated: 1
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread spec/install/gemfile/content_addressable_spec.rb Outdated
A content-addressable gem was locked inline as name (version-address)
platform. Bundler 4.0 parses the token after the version as the platform,
gets unknown, materializes name-version-unknown, and fails: it then either
re-resolves and rewrites the lockfile or fails outright under frozen.
Lockfiles are committed and read by many Bundler versions during
co-publication, so the inline form breaks the exact clients the transition
is supposed to protect.

The spec line is now an ordinary platform pin, and the content address moves
to its own section:

    GEM
      specs:
        nokogiri (1.19.4-x86_64-linux)

    CONTENT ADDRESSES
      nokogiri (1.19.4-x86_64-linux) 86e5e59f sha256=<content-addressable build's sha>

    CHECKSUMS
      nokogiri (1.19.4-x86_64-linux) sha256=<platform build's sha>

Verified against the released Bundler 4.0.9:

- unknown unindented sections set @parse_method = nil and are skipped
  silently (lockfile_parser.rb:137)
- Definition#lockfiles_equal? subtracts unknown sections before comparing
  (definition.rb:1196), so a plain 4.0 bundle install does not rewrite the
  lockfile and the section survives; it is only dropped on a genuine 4.0
  re-lock, and restored on the next 4.1 re-lock
- 4.0 classifies a locked spec with a missing or empty CHECKSUMS entry as a
  lockfile change (definition.rb:619-620): a plain install re-resolves and
  frozen mode hard-fails. The platform lock name line must therefore carry a
  real checksum, and it must be the platform build's, since 4.0 attributes
  it to the artifact it installs for that lock name

That last point drives the checksum rules:

- the checksum store is keyed by full name instead of lock name, because a
  content-addressable build and the platform build of the same name,
  version, and platform share a lock name while being different files.
  Store#register accepts anything responding to full_name and lock_name, so
  the parser registers CHECKSUMS entries under the name tuple parsed from
  the line rather than the (possibly content-addressed) spec object
- the content-addressable build's checksum is serialized next to its
  address in CONTENT ADDRESSES, the only place older Bundler never parses,
  and verifies the downloaded gem via the existing install-time registration
- CHECKSUMS carries the platform build's checksum, which the compact index
  supplies for every row the fetcher sees, so it is captured during
  resolution without downloading the platform gem
- when no platform build exists (skinny-only publication), the CHECKSUMS
  line is omitted entirely rather than written bare, so every CHECKSUMS
  line describes an artifact installable by its lock name

The section registers in SECTIONS_BY_VERSION_INTRODUCED under 4.1.0, and
its line format requires a platform, since content addressing only applies
to platformed gems.

Assisted-By: devx/01a05e2e-d421-7e27-b54e-abee85203da5
@jenshenny
jenshenny force-pushed the ca-lockfile-content-addresses-section branch from c7bc124 to 91c4adc Compare September 2, 2026 02:10
@jenshenny
jenshenny marked this pull request as ready for review September 2, 2026 02:49
@jenshenny
jenshenny merged commit 8a1edc2 into feature-branch-ca-changes-rubygems Sep 2, 2026
106 of 109 checks passed
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.

2 participants