Skip to content

Commit a2945ca

Browse files
committed
Vendor compact index into rubygems.org
Vendor the compact index API into rubygems.org so we don't need to have a dependency on releasing it to update the compact index API. It doesn't make sense to have this when rubygems.org is the consumer and is where the spec is published.
1 parent edcc71a commit a2945ca

11 files changed

Lines changed: 487 additions & 5 deletions

File tree

.rubocop.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ Minitest/MultipleAssertions:
160160
Style/ClassAndModuleChildren:
161161
EnforcedStyle: compact
162162
Exclude:
163+
- lib/compact_index/*.rb
163164
- lib/gemcutter/middleware/hostess.rb
164165
- lib/gemcutter/middleware/redirector.rb
165166

Gemfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ gem "opensearch-ruby", "~> 3.4"
4949
gem "searchkick", "~> 6.1"
5050
gem "faraday_middleware-aws-sigv4", "~> 1.0"
5151
gem "xml-simple", "~> 1.1"
52-
gem "compact_index", "~> 0.15.0"
5352
gem "rack-attack", "~> 6.8"
5453
gem "rqrcode", "~> 3.2"
5554
gem "rotp", "~> 6.2"

Gemfile.lock

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,6 @@ GEM
200200
bcrypt (>= 3.1.1)
201201
email_validator (~> 2.0)
202202
railties (>= 5.0)
203-
compact_index (0.15.0)
204203
concurrent-ruby (1.3.6)
205204
connection_pool (3.0.2)
206205
cose (1.3.1)
@@ -941,7 +940,6 @@ DEPENDENCIES
941940
capybara (~> 3.40)
942941
chartkick (~> 5.2)
943942
clearance (~> 2.11)
944-
compact_index (~> 0.15.0)
945943
csv (~> 3.3)
946944
dalli (~> 3.2)
947945
datadog (~> 2.30)
@@ -1104,7 +1102,6 @@ CHECKSUMS
11041102
choice (0.2.0) sha256=a19617f7dfd4921b38a85d0616446620de685a113ec6d1ecc85bdb67bf38c974
11051103
chunky_png (1.4.0) sha256=89d5b31b55c0cf4da3cf89a2b4ebc3178d8abe8cbaf116a1dba95668502fdcfe
11061104
clearance (2.11.0) sha256=c198711033c095cf127ea076f6afd4b52723548992b5d40bc1d2e3a28e9b0c79
1107-
compact_index (0.15.0) sha256=5c6c404afca8928a7d9f4dde9524f6e1610db17e675330803055db282da84a8b
11081105
concurrent-ruby (1.3.6) sha256=6b56837e1e7e5292f9864f34b69c5a2cbc75c0cf5338f1ce9903d10fa762d5ab
11091106
connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
11101107
cose (1.3.1) sha256=d5d4dbcd6b035d513edc4e1ab9bc10e9ce13b4011c96e3d1b8fe5e6413fd6de5

config/application.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class Application < Rails::Application
3333
# Please, add to the `ignore` list any other `lib` subdirectories that do
3434
# not contain `.rb` files, or that should not be reloaded or eager loaded.
3535
# Common ones are `templates`, `generators`, or `middleware`, for example.
36-
config.autoload_lib(ignore: %w[assets tasks cops shoryuken])
36+
config.autoload_lib(ignore: %w[assets tasks cops shoryuken compact_index])
3737

3838
# Settings in config/environments/* take precedence over those specified here.
3939
# Application configuration can go into files in config/initializers

lib/compact_index.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# frozen_string_literal: true
2+
3+
# Vendored from https://github.com/rubygems/compact_index v0.15.0
4+
5+
require_relative "compact_index/gem"
6+
require_relative "compact_index/gem_version"
7+
require_relative "compact_index/dependency"
8+
9+
require_relative "compact_index/versions_file"
10+
11+
module CompactIndex
12+
def self.names(gem_names)
13+
gem_names.join("\n").prepend("---\n") << "\n"
14+
end
15+
16+
def self.versions(versions_file, gems = nil, args = {})
17+
versions_file.contents(gems, args)
18+
end
19+
20+
def self.info(versions)
21+
versions.inject(+"---\n") do |output, version|
22+
output << version.to_line << "\n"
23+
end
24+
end
25+
end

lib/compact_index/dependency.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# frozen_string_literal: true
2+
3+
module CompactIndex
4+
Dependency = Struct.new(:gem, :version, :platform, :checksum) do
5+
def version_and_platform
6+
if platform.nil? || platform == "ruby"
7+
version
8+
else
9+
"#{version}-#{platform}"
10+
end
11+
end
12+
end
13+
end

lib/compact_index/gem.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# frozen_string_literal: true
2+
3+
module CompactIndex
4+
Gem = Struct.new(:name, :versions) do
5+
def <=>(other)
6+
name <=> other.name
7+
end
8+
end
9+
end

lib/compact_index/gem_version.rb

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# frozen_string_literal: true
2+
3+
module CompactIndex
4+
GemVersion = Struct.new(:number, :platform, :checksum, :info_checksum,
5+
:dependencies, :ruby_version, :rubygems_version) do
6+
def number_and_platform
7+
if platform.nil? || platform == "ruby"
8+
number
9+
else
10+
"#{number}-#{platform}"
11+
end
12+
end
13+
14+
def <=>(other)
15+
number_comp = number <=> other.number
16+
17+
if number_comp.zero?
18+
[number, platform].compact <=> [other.number, other.platform].compact
19+
else
20+
number_comp
21+
end
22+
end
23+
24+
def to_line
25+
line = "#{number_and_platform} #{deps_line}|checksum:#{checksum}"
26+
line << ",ruby:#{ruby_version_line}" if ruby_version && ruby_version != ">= 0"
27+
line << ",rubygems:#{rubygems_version_line}" if rubygems_version && rubygems_version != ">= 0"
28+
line
29+
end
30+
31+
private
32+
33+
def ruby_version_line
34+
join_multiple(ruby_version)
35+
end
36+
37+
def rubygems_version_line
38+
join_multiple(rubygems_version)
39+
end
40+
41+
def deps_line
42+
return "" if dependencies.nil?
43+
44+
dependencies.map do |d|
45+
[d[:gem], join_multiple(d.version_and_platform)].join(":")
46+
end.join(",")
47+
end
48+
49+
def join_multiple(requirements)
50+
requirements = requirements.split(", ")
51+
requirements.sort!
52+
requirements.join("&")
53+
end
54+
end
55+
end

lib/compact_index/versions_file.rb

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# frozen_string_literal: true
2+
3+
require "time"
4+
require "date"
5+
require "digest"
6+
7+
module CompactIndex
8+
class VersionsFile
9+
def initialize(file = nil)
10+
@path = file || "/versions.list"
11+
end
12+
13+
def contents(gems = nil, args = {})
14+
gems = calculate_info_checksums(gems) if args.delete(:calculate_info_checksums) { false }
15+
16+
raise ArgumentError, "Unknown options: #{args.keys.join(', ')}" unless args.empty?
17+
18+
File.read(@path).tap do |out|
19+
out << gem_lines(gems) if gems
20+
end
21+
end
22+
23+
def updated_at
24+
created_at_header(@path) || Time.at(0).utc.to_datetime
25+
end
26+
27+
def create(gems, timestamp = Time.now.iso8601)
28+
gems.sort!
29+
30+
File.open(@path, "w") do |io|
31+
io.write "created_at: #{timestamp}\n---\n"
32+
io.write gem_lines(gems)
33+
end
34+
end
35+
36+
private
37+
38+
def gem_lines(gems)
39+
gems.reduce(+"") do |lines, gem|
40+
version_numbers = gem.versions.map(&:number_and_platform).join(",")
41+
lines << gem.name <<
42+
" " << version_numbers <<
43+
" #{gem.versions.last.info_checksum}\n"
44+
end
45+
end
46+
47+
def calculate_info_checksums(gems)
48+
gems.each do |gem|
49+
info_checksum = Digest::MD5.hexdigest(CompactIndex.info(gem[:versions]))
50+
gem[:versions].last[:info_checksum] = info_checksum
51+
end
52+
end
53+
54+
def created_at_header(path)
55+
return unless File.exist? path
56+
57+
File.open(path) do |file|
58+
file.each_line do |line|
59+
line.match(/created_at: (.*)\n|---\n/) do |match|
60+
return match[1] && DateTime.parse(match[1])
61+
end
62+
end
63+
end
64+
65+
nil
66+
end
67+
end
68+
end

test/unit/compact_index_test.rb

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# frozen_string_literal: true
2+
3+
require "test_helper"
4+
require "compact_index"
5+
6+
class CompactIndexTest < ActiveSupport::TestCase
7+
class NamesTest < CompactIndexTest
8+
test "returns the gem list for one gem name" do
9+
assert_equal "---\ngem\n", CompactIndex.names(["gem"])
10+
end
11+
12+
test "returns the gem list for multiple gem names" do
13+
assert_equal "---\ngem-1\ngem_2\n", CompactIndex.names(%w[gem-1 gem_2])
14+
end
15+
end
16+
17+
class VersionsTest < CompactIndexTest
18+
test "delegates to VersionsFile#contents" do
19+
file = Tempfile.new("versions-endpoint")
20+
versions_file = CompactIndex::VersionsFile.new(file.path)
21+
gems = [CompactIndex::Gem.new("test", [build_version])]
22+
23+
assert_equal versions_file.contents(gems), CompactIndex.versions(versions_file, gems)
24+
ensure
25+
file&.unlink
26+
end
27+
end
28+
29+
class InfoTest < CompactIndexTest
30+
test "without dependencies" do
31+
param = [build_version(number: "1.0.1")]
32+
33+
assert_equal "---\n1.0.1 |checksum:sum+test_gem+1.0.1\n", CompactIndex.info(param)
34+
end
35+
36+
test "multiple versions" do
37+
param = [
38+
build_version(number: "1.0.1", checksum: "abc1"),
39+
build_version(number: "1.0.2", checksum: "abc2")
40+
]
41+
42+
assert_equal "---\n1.0.1 |checksum:abc1\n1.0.2 |checksum:abc2\n", CompactIndex.info(param)
43+
end
44+
45+
test "one dependency" do
46+
deps = [CompactIndex::Dependency.new("foo", "=1.0.1", "ruby", "abc123")]
47+
param = [build_version(number: "1.0.1", dependencies: deps)]
48+
49+
assert_equal "---\n1.0.1 foo:=1.0.1|checksum:sum+test_gem+1.0.1\n", CompactIndex.info(param)
50+
end
51+
52+
test "multiple dependencies" do
53+
deps = [
54+
CompactIndex::Dependency.new("foo1", "=1.0.1", "ruby", "abc123"),
55+
CompactIndex::Dependency.new("foo2", "<2.0", "ruby", "abc123")
56+
]
57+
param = [build_version(number: "1.0.1", dependencies: deps)]
58+
59+
assert_equal "---\n1.0.1 foo1:=1.0.1,foo2:<2.0|checksum:sum+test_gem+1.0.1\n", CompactIndex.info(param)
60+
end
61+
62+
test "dependency with multiple versions" do
63+
deps = [CompactIndex::Dependency.new("foo", "<2.0, >1.0", "ruby", "abc123")]
64+
param = [build_version(number: "1.0.1", dependencies: deps)]
65+
66+
assert_equal "---\n1.0.1 foo:<2.0&>1.0|checksum:sum+test_gem+1.0.1\n", CompactIndex.info(param)
67+
end
68+
69+
test "sorts the requirements" do
70+
deps = [CompactIndex::Dependency.new("foo", ">1.0, <2.0", "ruby", "abc123")]
71+
param = [build_version(number: "1.0.1", dependencies: deps)]
72+
73+
assert_equal "---\n1.0.1 foo:<2.0&>1.0|checksum:sum+test_gem+1.0.1\n", CompactIndex.info(param)
74+
end
75+
76+
test "dependencies have platform" do
77+
deps = [
78+
CompactIndex::Dependency.new("a", "=1.1", "jruby", "abc123"),
79+
CompactIndex::Dependency.new("b", "= 1.2", "darwin-13", "abc123")
80+
]
81+
param = [build_version(number: "1.0.1", dependencies: deps)]
82+
83+
assert_equal "---\n1.0.1 a:=1.1-jruby,b:= 1.2-darwin-13|checksum:sum+test_gem+1.0.1\n", CompactIndex.info(param)
84+
end
85+
86+
test "show ruby required version" do
87+
param = [build_version(number: "1.0.1", ruby_version: ">1.8")]
88+
89+
assert_equal "---\n1.0.1 |checksum:sum+test_gem+1.0.1,ruby:>1.8\n", CompactIndex.info(param)
90+
end
91+
92+
test "show ruby required version with multiple requirements" do
93+
param = [build_version(number: "1.0.1", ruby_version: "< 2.5, >=2.2")]
94+
95+
assert_equal "---\n1.0.1 |checksum:sum+test_gem+1.0.1,ruby:< 2.5&>=2.2\n", CompactIndex.info(param)
96+
end
97+
98+
test "show rubygems required version" do
99+
param = [build_version(number: "1.0.1", rubygems_version: "=2.0")]
100+
101+
assert_equal "---\n1.0.1 |checksum:sum+test_gem+1.0.1,rubygems:=2.0\n", CompactIndex.info(param)
102+
end
103+
104+
test "show rubygems required version with multiple requirements" do
105+
param = [build_version(number: "1.0.1", rubygems_version: ">2.0, <3.1")]
106+
107+
assert_equal "---\n1.0.1 |checksum:sum+test_gem+1.0.1,rubygems:<3.1&>2.0\n", CompactIndex.info(param)
108+
end
109+
110+
test "show both rubygems and ruby required versions" do
111+
param = [build_version(number: "1.0.1", ruby_version: ">1.9", rubygems_version: ">2.0")]
112+
113+
assert_equal "---\n1.0.1 |checksum:sum+test_gem+1.0.1,ruby:>1.9,rubygems:>2.0\n", CompactIndex.info(param)
114+
end
115+
116+
test "adds platform next to version number" do
117+
param = [build_version(number: "1.0.1", platform: "jruby")]
118+
119+
assert_equal "---\n1.0.1-jruby |checksum:sum+test_gem+1.0.1\n", CompactIndex.info(param)
120+
end
121+
end
122+
123+
private
124+
125+
def build_version(**args)
126+
name = args.fetch(:name, "test_gem")
127+
number = args.fetch(:number, "1.0")
128+
CompactIndex::GemVersion.new(
129+
number,
130+
args[:platform],
131+
args.fetch(:checksum, "sum+#{name}+#{number}"),
132+
args.fetch(:info_checksum, "info+#{name}+#{number}"),
133+
args[:dependencies],
134+
args[:ruby_version],
135+
args[:rubygems_version]
136+
)
137+
end
138+
end

0 commit comments

Comments
 (0)