File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -465,6 +465,28 @@ def parse(line)
465465 Resolver ::APISet ::GemParser . prepend ( UnfreezeCompactIndexParsedResponse )
466466 end
467467
468+ # RubyGems before 4.0.13 split compact index dependency/requirement entries
469+ # on every colon, which mangles metadata values that contain colons such as
470+ # the `created_at` timestamps the cooldown feature relies on. Split only on
471+ # the first colon so those values survive on older RubyGems.
472+ #
473+ # The module is defined unconditionally so it stays testable on any RubyGems,
474+ # but only prepended when the host RubyGems still has the buggy behavior.
475+ module SplitCompactIndexEntryOnFirstColon
476+ private
477+
478+ def parse_dependency ( string )
479+ dependency = string . split ( ":" , 2 )
480+ dependency [ -1 ] = dependency [ -1 ] . split ( "&" ) if dependency . size > 1
481+ dependency [ 0 ] = -dependency [ 0 ]
482+ dependency
483+ end
484+ end
485+
486+ unless Gem . rubygems_version >= Gem ::Version . new ( "4.0.13" )
487+ Resolver ::APISet ::GemParser . prepend ( SplitCompactIndexEntryOnFirstColon )
488+ end
489+
468490 if Gem . rubygems_version < Gem ::Version . new ( "3.6.0" )
469491 class Package ; end
470492 require "rubygems/package/tar_reader"
Original file line number Diff line number Diff line change 1+ # frozen_string_literal: true
2+
3+ require "bundler/rubygems_ext"
4+
5+ RSpec . describe Gem ::SplitCompactIndexEntryOnFirstColon do
6+ # Reproduces the RubyGems < 4.0.13 `Gem::Resolver::APISet::GemParser` that
7+ # split each compact index entry on every colon, corrupting metadata values
8+ # that themselves contain colons.
9+ let ( :legacy_parser_class ) do
10+ Class . new do
11+ def parse_dependency ( string )
12+ dependency = string . split ( ":" )
13+ dependency [ -1 ] = dependency [ -1 ] . split ( "&" ) if dependency . size > 1
14+ dependency [ 0 ] = -dependency [ 0 ]
15+ dependency
16+ end
17+ end
18+ end
19+
20+ before { legacy_parser_class . prepend ( described_class ) }
21+
22+ it "preserves colon-bearing metadata values such as created_at timestamps" do
23+ parser = legacy_parser_class . new
24+
25+ expect ( parser . send ( :parse_dependency , "created_at:2026-05-12T10:00:00Z" ) ) . to eq ( [ "created_at" , [ "2026-05-12T10:00:00Z" ] ] )
26+ end
27+
28+ it "still parses ordinary name:requirement entries" do
29+ parser = legacy_parser_class . new
30+
31+ expect ( parser . send ( :parse_dependency , "myrack:>= 1.0" ) ) . to eq ( [ "myrack" , [ ">= 1.0" ] ] )
32+ end
33+
34+ it "keeps parse_dependency private" do
35+ parser = legacy_parser_class . new
36+
37+ expect { parser . parse_dependency ( "created_at:x" ) } . to raise_error ( NoMethodError , /private method/ )
38+ end
39+ end
Original file line number Diff line number Diff line change @@ -142,6 +142,7 @@ module WindowsTagGroup
142142 "spec/bundler/ci_detector_spec.rb" ,
143143 ] ,
144144 windows_d : [
145+ "spec/bundler/rubygems_ext_spec.rb" ,
145146 "spec/bundler/resolver/cooldown_spec.rb" ,
146147 "spec/install/cooldown_spec.rb" ,
147148 "spec/commands/outdated_spec.rb" ,
You can’t perform that action at this time.
0 commit comments