Skip to content

Commit e3236c9

Browse files
committed
Merge pull request #9585 from ruby/split-compact-index-entry-on-first-colon
Split compact index entries on the first colon on older RubyGems (cherry picked from commit 6d11166)
1 parent e5f4e12 commit e3236c9

3 files changed

Lines changed: 62 additions & 0 deletions

File tree

bundler/lib/bundler/rubygems_ext.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff 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"
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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

bundler/spec/support/windows_tag_group.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff 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",

0 commit comments

Comments
 (0)