Skip to content

Install content-addressable gemspecs under specifications/<ruby_abi>/ - #196

Merged
jenshenny merged 1 commit into
feature-branch-ca-changes-rubygemsfrom
ca-abi-scoped-specs-v2
Sep 3, 2026
Merged

Install content-addressable gemspecs under specifications/<ruby_abi>/#196
jenshenny merged 1 commit into
feature-branch-ca-changes-rubygemsfrom
ca-abi-scoped-specs-v2

Conversation

@jenshenny

@jenshenny jenshenny commented Sep 3, 2026

Copy link
Copy Markdown

Content-addressable gemspecs install under specifications/<ruby_abi>/ instead of the flat specifications/ directory. Implements item 2 of hsbt's format review. Supersedes #195 with a deliberately smaller scope.

GEM_HOME/
  specifications/
    rake-13.3.1.gemspec                  ← ordinary gems, unchanged
    default/                             ← default gems, unchanged
    3.4/
      nokogiri-1.19.4-86e5e59f.gemspec   ← CA gems, invisible to older RubyGems
  gems/
    nokogiri-1.19.4-86e5e59f/            ← payload location unchanged

Why

A CA gemspec in the flat directory poisons a shared GEM_HOME for older RubyGems: the stub line carries the content address where the platform used to be, so an older StubLine parses it as platform unknown and derives a gem_dir that doesn't exist — gem list shows the gem while gem "name" raises MissingSpecError.

Older RubyGems globs specifications/*.gemspec non-recursively, so a subdirectory is invisible by construction — nothing to parse, nothing to misinterpret. specifications/default/ and #192's plugins/<ruby_abi>/ are the existing precedents.

Install

Installer#spec_file delegates to SpecificationRecord.specification_dir_for(spec, @gemhome), which returns the ABI-scoped path for CA specs and the flat path for everything else. No branching in the installer itself — the record is the single source of layout truth.

write_spec creates the ABI dir lazily via the existing ensure_writable_dir (so Gem::FilePermissionError and Bundler's override apply unchanged). It then writes the gemspec, and only after the write succeeds, clamps the directory to --dir-mode — matching the gem_dir/build_info_dir two-step. The chmod runs in an ensure block, so a failed write can't leave the directory more permissive than requested. For sequential installs into a read-only (0555) ABI dir, writability is restored to 0755 before the write and re-clamped after.

Installer#installed_specs and RequestSet#specs_in glob through dirs_from, so already-installed CA gems are recognized during install planning.

Read

SpecificationRecord.dirs_from expands each gem path into two search dirs: the flat specifications/ and specifications/<Gem.ruby_abi>/. Only the running Ruby's ABI is searched, so a CA build for another ABI sharing the GEM_HOME is never an activation candidate — this also fixes the measured case where two CA builds of one name-version had identical sort_obj values and Dependency#to_spec could activate a ~> 3.4.0 build on a different Ruby.

map_stubs strips one extra path level for ABI-scoped dirs when deriving base_dir/gems_dir, so stubs receive the correct base_dir without any path inference on the stub side. Specification#base_dir strips 3 levels for specs loaded from an ABI-scoped dir (parallel to default_gem?), and #spec_dir delegates to specification_dir_for.

Bundler's EndpointSpecification#local_specification_path delegates to specification_dir_for so post-install spec reload finds CA gemspecs in the ABI dir. RubygemsIntegration#spec_cache_dirs uses dirs_from so the fetcher's local cache lookup includes ABI-scoped dirs. Both fall back to flat-only on older system RubyGems via shims in rubygems_ext.rb.

Uninstall

Uninstall and pristine work with zero uninstaller changes. Uninstaller uses spec.spec_file (which delegates to specification_dir_for) to locate and delete the gemspec, and spec.gem_dir / spec.base_dir for the payload. Since the path model already resolves correctly for ABI-scoped specs, the uninstaller removes the ABI-scoped gemspec + payload + cache without any awareness of the subdirectory layout.

Out of scope (follow-ups)

  • gem doctor awareness of ABI dirs (conservative union-of-ABIs installed set)
  • bundle clean (currently: kept CA gems fully protected via spec.spec_file; stale CA gemspecs are conservatively never deleted)
  • gem contents --spec-dir
  • Legacy flat CA gemspecs written by earlier 4.1.0.dev builds: no migration, pre-release breakage
  • Same-ABI sort_obj tie between two rebuilds (different hashes, same name-version-ABI) — survives this change, open question whether sort_obj should tie-break on content_address

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.

🟡 Changes recommended

Read-only directory reuse and in-memory cross-ABI activation remain incorrect.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Moves content-addressable gemspecs into Ruby ABI-scoped directories while preserving ordinary gem layouts.

Changes:

  • Adds ABI-aware gemspec discovery, installation, and path handling.
  • Updates uninstall and RequestSet behavior.
  • Expands RubyGems and Bundler coverage.
File summaries
File Description
lib/rubygems/specification_record.rb Defines ABI-scoped layout and discovery.
lib/rubygems/specification.rb Resolves ABI-scoped specification paths.
lib/rubygems/installer.rb Writes and discovers ABI-scoped gemspecs.
lib/rubygems/request_set.rb Finds installed ABI-scoped specs.
test/rubygems/test_gem_specification.rb Tests discovery and path semantics.
test/rubygems/test_gem_installer.rb Tests installation and directory modes.
test/rubygems/test_gem_uninstaller.rb Tests ABI-scoped uninstall cleanup.
test/rubygems/test_gem_commands_update_command.rb Updates command installation expectations.
spec/install/gemfile/content_addressable_spec.rb Verifies Bundler’s scoped layout.
Review details
  • Files reviewed: 9/9 changed files
  • Comments generated: 2
  • 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 lib/rubygems/specification_record.rb
Comment thread lib/rubygems/installer.rb Outdated

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.

🟡 Changes recommended

Directory-mode handling is incomplete, and one new test fails on Ruby 3.4 CI.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (1)

lib/rubygems/installer.rb:432

  • The new ABI-scoped specifications directory never gets the requested dir_mode: ensure_writable_dir creates it as 0755, and only gem_dir is chmoded later. Thus --dir-mode 0555 succeeds in the new test only because the shared ABI directory remains writable, contrary to the documented write-before-clamp behavior. Please apply the requested mode after writing while preserving a workable strategy for subsequent installs, and assert the ABI directory's mode.
    spec_file = self.spec_file
    spec_dir = File.dirname spec_file
    ensure_writable_dir spec_dir unless File.directory? spec_dir

    Gem.write_binary(spec_file, spec.to_ruby_for_cache)
  • Files reviewed: 15/15 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread test/rubygems/test_gem_installer.rb Outdated

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.

🟡 Changes recommended

Bundler local-spec resolution and shared-directory permission handling have unresolved correctness and concurrency issues.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (1)

lib/rubygems/installer.rb:442

  • If writing the gemspec fails after the existing read-only ABI directory was temporarily changed to 0755 (for example, due to ENOSPC), this final chmod is skipped and the shared directory remains more permissive than the requested mode. Restore dir_mode in an ensure block so failed installs cannot leave permissions altered.
    Gem.write_binary(spec_file, spec.to_ruby_for_cache)
    File.chmod(dir_mode, spec_dir) if dir_mode && content_addressed
  • Files reviewed: 15/15 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread lib/rubygems/installer.rb
Comment thread lib/rubygems/installer.rb
@jenshenny
jenshenny force-pushed the ca-abi-scoped-specs-v2 branch 6 times, most recently from bf8f633 to 7d45f8e Compare September 3, 2026 16:39
@jenshenny
jenshenny requested a balanced review from Copilot September 3, 2026 16:40

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.

🟢 Approval recommended

The implementation is cohesive and well covered; only a minor test-assertion formatting issue remains.

Review details
  • Files reviewed: 18/18 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread test/rubygems/test_gem_specification.rb
@jenshenny
jenshenny force-pushed the ca-abi-scoped-specs-v2 branch 10 times, most recently from 4a19f2f to af7b1b5 Compare September 3, 2026 18:16
@jenshenny
jenshenny force-pushed the ca-abi-scoped-specs-v2 branch 4 times, most recently from fa060b5 to b6c4df8 Compare September 3, 2026 19:02
@jenshenny
jenshenny requested a balanced review from Copilot September 3, 2026 19:03

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.

🟡 Changes recommended

The temporary permission change exposes private ABI directories to additional users during installation.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 18/18 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread lib/rubygems/installer.rb
Comment thread test/rubygems/test_gem_specification.rb
@jenshenny
jenshenny marked this pull request as ready for review September 3, 2026 19:11
@jenshenny
jenshenny force-pushed the ca-abi-scoped-specs-v2 branch from b6c4df8 to 9245c64 Compare September 3, 2026 19:12
Assisted-By: devx/9c2464e2-74cd-4d36-a6c0-50aef8c2ad01
@jenshenny
jenshenny force-pushed the ca-abi-scoped-specs-v2 branch from 9245c64 to 590aea0 Compare September 3, 2026 19:13
@jenshenny
jenshenny merged commit fee510c into feature-branch-ca-changes-rubygems Sep 3, 2026
22 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