Skip to content

Skip CA metadata for unscoped remote queries - #179

Merged
girachawda merged 1 commit into
feature-branch-ca-changes-rubygemsfrom
gc/ca-cli-unscoped-skip-metadata
Aug 20, 2026
Merged

Skip CA metadata for unscoped remote queries#179
girachawda merged 1 commit into
feature-branch-ca-changes-rubygemsfrom
gc/ca-cli-unscoped-skip-metadata

Conversation

@girachawda

@girachawda girachawda commented Aug 19, 2026

Copy link
Copy Markdown

What

Avoid fetching content-addressable platform/Ruby ABI metadata for unscoped remote query commands.

Unscoped commands now keep using the metadata already available from the compact index versions response, while scoped commands still fetch info/<gem> and show decoded platform/Ruby ABI output.

Why

Commands like:

gem list -r
gem search -r
gem info -r

can return every remote gem. If those commands inspect CA platform/Ruby ABI metadata, they can make one info/<gem> request per CA gem name, which is expensive on a large index.

Scoped commands like:

gem list nokogiri -r
gem search nokogiri -r
gem info nokogiri -r

are still bounded by the user's query, so they continue to fetch and display the decoded CA metadata.

Tophat

Instructions
TMPDIR_CA=$(mktemp -d)
GEMHOME_CA=$(mktemp -d)
LOG=$(mktemp)
PORT_CA=$(ruby -rsocket -e 's = TCPServer.new("127.0.0.1", 0); print s.addr[1]; s.close')
MARSHAL_VERSION=$(ruby --disable-gems -Ilib -e 'require "rubygems"; print Gem.marshal_version')
mkdir -p "$TMPDIR_CA/info" "$TMPDIR_CA/quick/Marshal.$MARSHAL_VERSION"

cat > "$TMPDIR_CA/versions" <<'VERSIONS'
created_at: 2026-01-01T00:00:00Z
---
skinny 1-abcdef12 0000
fat 1-x86_64-linux,1-arm64-darwin 0000
mixed 1-x86_64-linux,2-fedcba98 0000
VERSIONS

cat > "$TMPDIR_CA/info/skinny" <<'INFO'
---
1-abcdef12 |checksum:123,ruby:~> 3.3.0,platform:= x86_64-linux
INFO

cat > "$TMPDIR_CA/info/mixed" <<'INFO'
---
2-fedcba98 |checksum:456,ruby:~> 3.4.0,platform:= arm64-darwin
INFO

make_spec() {
  local name=$1 version=$2 platform=$3 address=$4
  local filename
  if [ -n "$address" ]; then
    filename="$name-$version-$address.gemspec.rz"
  elif [ "$platform" = "ruby" ]; then
    filename="$name-$version.gemspec.rz"
  else
    filename="$name-$version-$platform.gemspec.rz"
  fi

  ruby --disable-gems -Ilib -rzlib -e 'require "rubygems"; name, version, platform, address = ARGV; spec = Gem::Specification.new(name, version); spec.platform = platform; spec.content_address = address unless address.empty?; spec.summary = "summary for #{name}"; spec.homepage = "http://example.com/#{name}"; spec.authors = ["Tophat User"]; print Zlib::Deflate.deflate(Marshal.dump(spec))' "$name" "$version" "$platform" "$address" > "$TMPDIR_CA/quick/Marshal.$MARSHAL_VERSION/$filename"
}

make_spec skinny 1 x86_64-linux abcdef12
make_spec fat 1 x86_64-linux ""
make_spec fat 1 arm64-darwin ""
make_spec mixed 1 x86_64-linux ""
make_spec mixed 2 arm64-darwin fedcba98

python3 -m http.server "$PORT_CA" --bind 127.0.0.1 --directory "$TMPDIR_CA" >"$LOG" 2>&1 &
SERVER_PID=$!
trap 'kill "$SERVER_PID" 2>/dev/null || true; rm -rf "$TMPDIR_CA" "$GEMHOME_CA" "$LOG"' EXIT
sleep 0.5

SOURCE_CA="http://127.0.0.1:$PORT_CA"
export GEM_HOME="$GEMHOME_CA" GEM_PATH="$GEMHOME_CA"

ruby --disable-gems -Ilib exe/gem list -r --clear-sources --source "$SOURCE_CA"
ruby --disable-gems -Ilib exe/gem search -r --clear-sources --source "$SOURCE_CA"
ruby --disable-gems -Ilib exe/gem info -r --clear-sources --source "$SOURCE_CA"
ruby --disable-gems -Ilib exe/gem list skinny -r --clear-sources --source "$SOURCE_CA"
ruby --disable-gems -Ilib exe/gem search skinny -r --clear-sources --source "$SOURCE_CA"
ruby --disable-gems -Ilib exe/gem info skinny -r --clear-sources --source "$SOURCE_CA"
ruby --disable-gems -Ilib exe/gem list mixed -r --all --clear-sources --source "$SOURCE_CA"
ruby --disable-gems -Ilib exe/gem search mixed -r --all --clear-sources --source "$SOURCE_CA"
ruby --disable-gems -Ilib exe/gem info mixed -r --all --clear-sources --source "$SOURCE_CA"
grep 'GET /info/' "$LOG" || true
Output:
== UNSCOPED list: skinny + fat + mixed ==
fat (1 arm64-darwin x86_64-linux)
mixed (2 fedcba98, 1 x86_64-linux)
skinny (1 abcdef12)

== UNSCOPED search: skinny + fat + mixed ==
fat (1 arm64-darwin x86_64-linux)
mixed (2 fedcba98, 1 x86_64-linux)
skinny (1 abcdef12)

== UNSCOPED info: skinny + fat + mixed ==
fat (1)
    Platforms: arm64-darwin, x86_64-linux
    Author: Tophat User
    Homepage: http://example.com/fat

    summary for fat

mixed (2, 1)
    Platforms:
        1: x86_64-linux
        2: fedcba98
    Author: Tophat User
    Homepage: http://example.com/mixed

    summary for mixed

skinny (1)
    Platform: abcdef12
    Author: Tophat User
    Homepage: http://example.com/skinny

    summary for skinny

== SCOPED list skinny ==
skinny (1 Platform: x86_64-linux, Ruby ABI: 3.3)

== SCOPED search skinny ==
skinny (1 Platform: x86_64-linux, Ruby ABI: 3.3)

== SCOPED info skinny ==
skinny (1)
    Platforms:
        x86_64-linux Ruby ABI: 3.3
    Author: Tophat User
    Homepage: http://example.com/skinny

    summary for skinny

== SCOPED list mixed --all ==
mixed (2 Platform: arm64-darwin, Ruby ABI: 3.4
       1 Platform: x86_64-linux)

== SCOPED search mixed --all ==
mixed (2 Platform: arm64-darwin, Ruby ABI: 3.4
       1 Platform: x86_64-linux)

== SCOPED info mixed --all ==
mixed (2, 1)
    Platforms:
        1: x86_64-linux
        2: arm64-darwin Ruby ABI: 3.4
    Author: Tophat User
    Homepage: http://example.com/mixed

    summary for mixed

== info/<gem> request summary ==
GET /info/skinny
GET /info/skinny
GET /info/skinny
GET /info/mixed
GET /info/mixed
GET /info/mixed

Assisted-By: devx/3250d709-a0a0-4417-8f2f-83669ac9e685
@girachawda
girachawda force-pushed the gc/ca-cli-unscoped-skip-metadata branch from 85e6e2d to 1c69ac3 Compare August 19, 2026 17:44
end

spec_tuples = decode_content_addressable_tuples(spec_tuples, latest: specs_type == :latest)
if args.empty?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

this matches for the unscoped commands for gem list, search and info

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.

Pull request overview

Avoids expensive CA metadata requests for unscoped remote gem queries while preserving decoded metadata for scoped queries.

Changes:

  • Skips CA tuple decoding when no query arguments are supplied.
  • Adds regression coverage for unscoped list, search, and info commands.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
lib/rubygems/query_utils.rb Restricts CA metadata decoding to scoped queries.
test/rubygems/test_gem_commands_search_command.rb Tests unscoped search behavior.
test/rubygems/test_gem_commands_list_command.rb Tests unscoped remote list behavior.
test/rubygems/test_gem_commands_info_command.rb Tests unscoped remote info behavior.

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

@jenshenny jenshenny 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.

LGTM!

@girachawda
girachawda merged commit 987191a into feature-branch-ca-changes-rubygems Aug 20, 2026
107 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.

3 participants