gemirro 1.6.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +1 -1
- data/.gitignore +2 -0
- data/.rubocop.yml +3 -1
- data/Gemfile +2 -1
- data/Gemfile.lock +73 -61
- data/MANIFEST +0 -3
- data/bin/gemirro +1 -8
- data/gemirro.gemspec +4 -6
- data/lib/gemirro/cli/index.rb +48 -27
- data/lib/gemirro/cli/init.rb +48 -29
- data/lib/gemirro/cli/list.rb +37 -17
- data/lib/gemirro/cli/server.rb +173 -114
- data/lib/gemirro/cli/update.rb +42 -17
- data/lib/gemirro/cli.rb +64 -21
- data/lib/gemirro/configuration.rb +26 -20
- data/lib/gemirro/gem_version.rb +1 -0
- data/lib/gemirro/gem_version_collection.rb +4 -4
- data/lib/gemirro/gems_fetcher.rb +9 -9
- data/lib/gemirro/http.rb +115 -41
- data/lib/gemirro/indexer.rb +33 -232
- data/lib/gemirro/mirror_file.rb +3 -10
- data/lib/gemirro/server.rb +12 -43
- data/lib/gemirro/source.rb +2 -15
- data/lib/gemirro/utils.rb +22 -12
- data/lib/gemirro/version.rb +1 -1
- data/lib/gemirro/versions_fetcher.rb +7 -12
- data/lib/gemirro/versions_file.rb +26 -24
- data/lib/gemirro.rb +3 -7
- data/spec/gemirro/cli_spec.rb +49 -15
- data/spec/gemirro/configuration_spec.rb +9 -2
- data/spec/gemirro/gems_fetcher_spec.rb +2 -2
- data/spec/gemirro/http_spec.rb +58 -54
- data/spec/gemirro/indexer_spec.rb +5 -5
- data/spec/gemirro/server_spec.rb +30 -52
- data/spec/gemirro/source_spec.rb +2 -10
- data/spec/gemirro/versions_fetcher_spec.rb +3 -4
- data/spec/gemirro/versions_file_spec.rb +8 -32
- data/template/config.rb +1 -1
- metadata +29 -69
- data/template/public/latest_specs.4.8 +0 -0
- data/template/public/prerelease_specs.4.8 +0 -0
- data/template/public/specs.4.8 +0 -0
data/lib/gemirro/server.rb
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'sinatra/base'
|
|
4
|
-
require '
|
|
4
|
+
require 'puma'
|
|
5
5
|
require 'uri'
|
|
6
6
|
require 'addressable/uri'
|
|
7
|
+
require 'base64'
|
|
7
8
|
|
|
8
9
|
module Gemirro
|
|
9
10
|
##
|
|
@@ -32,6 +33,7 @@ module Gemirro
|
|
|
32
33
|
config.server.port = '2000' if config.server.port.nil?
|
|
33
34
|
|
|
34
35
|
set :static, true
|
|
36
|
+
set :server, 'puma'
|
|
35
37
|
|
|
36
38
|
set :views, Gemirro::Configuration.views_directory
|
|
37
39
|
set :port, config.server.port
|
|
@@ -78,39 +80,6 @@ module Gemirro
|
|
|
78
80
|
erb(:index, {}, gems: Utils.gems_collection)
|
|
79
81
|
end
|
|
80
82
|
|
|
81
|
-
##
|
|
82
|
-
# Return gem dependencies as marshaled binary
|
|
83
|
-
#
|
|
84
|
-
# @return [nil]
|
|
85
|
-
#
|
|
86
|
-
get '/api/v1/dependencies' do
|
|
87
|
-
content_type 'application/octet-stream'
|
|
88
|
-
if params[:gems].to_s != '' && params[:gems].to_s.split(',').any?
|
|
89
|
-
Marshal.dump(dependencies_loader(params[:gems].to_s.split(',')))
|
|
90
|
-
else
|
|
91
|
-
200
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
##
|
|
96
|
-
# Return gem dependencies as json
|
|
97
|
-
#
|
|
98
|
-
# @return [nil]
|
|
99
|
-
#
|
|
100
|
-
get '/api/v1/dependencies.json' do
|
|
101
|
-
content_type 'application/json'
|
|
102
|
-
|
|
103
|
-
return '[]' unless params[:gems]
|
|
104
|
-
|
|
105
|
-
gem_names = params[:gems].to_s
|
|
106
|
-
.split(',')
|
|
107
|
-
.map(&:strip)
|
|
108
|
-
.reject(&:empty?)
|
|
109
|
-
return '[]' if gem_names.empty?
|
|
110
|
-
|
|
111
|
-
JSON.dump(dependencies_loader(gem_names))
|
|
112
|
-
end
|
|
113
|
-
|
|
114
83
|
##
|
|
115
84
|
# compact_index, Return list of available gem names
|
|
116
85
|
#
|
|
@@ -120,10 +89,10 @@ module Gemirro
|
|
|
120
89
|
content_type 'text/plain'
|
|
121
90
|
|
|
122
91
|
content_path = Dir.glob(File.join(Gemirro.configuration.destination, 'names.*.*.list')).last
|
|
123
|
-
_, etag, repr_digest, _ = content_path.split('.'
|
|
92
|
+
_, etag, repr_digest, _ = File.basename(content_path).split('.')
|
|
124
93
|
|
|
125
|
-
headers 'etag' => etag
|
|
126
|
-
headers 'repr-digest' => %(sha-256
|
|
94
|
+
headers 'etag' => %("#{etag}")
|
|
95
|
+
headers 'repr-digest' => %(sha-256=#{Base64.strict_encode64([repr_digest].pack('H*'))})
|
|
127
96
|
send_file content_path
|
|
128
97
|
end
|
|
129
98
|
|
|
@@ -136,10 +105,10 @@ module Gemirro
|
|
|
136
105
|
content_type 'text/plain'
|
|
137
106
|
|
|
138
107
|
content_path = Dir.glob(File.join(Utils.configuration.destination, 'versions.*.*.list')).last
|
|
139
|
-
_, etag, repr_digest, _ = content_path.split('.'
|
|
108
|
+
_, etag, repr_digest, _ = File.basename(content_path).split('.')
|
|
140
109
|
|
|
141
|
-
headers 'etag' => etag
|
|
142
|
-
headers 'repr-digest' => %(sha-256
|
|
110
|
+
headers 'etag' => %("#{etag}")
|
|
111
|
+
headers 'repr-digest' => %(sha-256=#{Base64.strict_encode64([repr_digest].pack('H*'))})
|
|
143
112
|
send_file content_path
|
|
144
113
|
end
|
|
145
114
|
|
|
@@ -155,10 +124,10 @@ module Gemirro
|
|
|
155
124
|
content_type 'text/plain'
|
|
156
125
|
|
|
157
126
|
content_path = Dir.glob(File.join(Utils.configuration.destination, 'info', "#{params[:gemname]}.*.*.list")).last
|
|
158
|
-
_, etag, repr_digest, _ = content_path.split('.'
|
|
127
|
+
_, etag, repr_digest, _ = File.basename(content_path).split('.')
|
|
159
128
|
|
|
160
|
-
headers 'etag' => etag
|
|
161
|
-
headers 'repr-digest' => %(sha-256
|
|
129
|
+
headers 'etag' => %("#{etag}")
|
|
130
|
+
headers 'repr-digest' => %(sha-256=#{Base64.strict_encode64([repr_digest].pack('H*'))})
|
|
162
131
|
send_file content_path
|
|
163
132
|
end
|
|
164
133
|
|
data/lib/gemirro/source.rb
CHANGED
|
@@ -33,23 +33,10 @@ module Gemirro
|
|
|
33
33
|
#
|
|
34
34
|
def fetch_versions
|
|
35
35
|
Utils.logger.info(
|
|
36
|
-
"Fetching
|
|
36
|
+
"Fetching versions on #{@name} (#{@host})"
|
|
37
37
|
)
|
|
38
38
|
|
|
39
|
-
Http.get("#{host}
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
##
|
|
43
|
-
# Fetches a list of all the available Gems and their versions.
|
|
44
|
-
#
|
|
45
|
-
# @return [String]
|
|
46
|
-
#
|
|
47
|
-
def fetch_prerelease_versions
|
|
48
|
-
Utils.logger.info(
|
|
49
|
-
"Fetching #{Configuration.prerelease_versions_file} " \
|
|
50
|
-
"on #{@name} (#{@host})"
|
|
51
|
-
)
|
|
52
|
-
Http.get("#{host}/#{Configuration.prerelease_versions_file}").body
|
|
39
|
+
Http.get("#{host}/versions").body
|
|
53
40
|
end
|
|
54
41
|
|
|
55
42
|
##
|
data/lib/gemirro/utils.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'gemirro/gem_version'
|
|
4
|
+
|
|
3
5
|
module Gemirro
|
|
4
6
|
##
|
|
5
7
|
# The Utils class is responsible for executing specific traitments
|
|
@@ -33,12 +35,10 @@ module Gemirro
|
|
|
33
35
|
@gems_collection ||= { files: {}, values: nil }
|
|
34
36
|
|
|
35
37
|
file_paths =
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
)
|
|
41
|
-
end
|
|
38
|
+
Dir.glob(File.join(
|
|
39
|
+
Gemirro.configuration.destination,
|
|
40
|
+
'versions.*.*.list'
|
|
41
|
+
))
|
|
42
42
|
|
|
43
43
|
has_file_changed =
|
|
44
44
|
@gems_collection[:files] != file_paths.each_with_object({}) do |f, r|
|
|
@@ -50,12 +50,22 @@ module Gemirro
|
|
|
50
50
|
|
|
51
51
|
gems = []
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
CompactIndex::VersionsFile.new(file_paths.last).contents.each_line.with_index do |line, index|
|
|
54
|
+
next if index < 2
|
|
55
|
+
|
|
56
|
+
gem_name = line.split[0]
|
|
57
|
+
versions = line.split[1..-2].collect { |x| x.split(',') }.flatten # All except first and last
|
|
56
58
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
versions.each do |ver|
|
|
60
|
+
version, platform =
|
|
61
|
+
if ver.include?('-')
|
|
62
|
+
ver.split('-', 2)
|
|
63
|
+
else
|
|
64
|
+
[ver, 'ruby']
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
gems << Gemirro::GemVersion.new(gem_name, version, platform)
|
|
68
|
+
end
|
|
59
69
|
end
|
|
60
70
|
|
|
61
71
|
@gems_collection[:values] = GemVersionCollection.new(gems)
|
|
@@ -187,7 +197,7 @@ module Gemirro
|
|
|
187
197
|
# @return [Indexer]
|
|
188
198
|
#
|
|
189
199
|
def self.update_indexes
|
|
190
|
-
indexer = Gemirro::Indexer.new
|
|
200
|
+
indexer = Gemirro::Indexer.new
|
|
191
201
|
indexer.only_origin = true
|
|
192
202
|
indexer.ui = ::Gem::SilentUI.new
|
|
193
203
|
|
data/lib/gemirro/version.rb
CHANGED
|
@@ -22,29 +22,24 @@ module Gemirro
|
|
|
22
22
|
# @return [Gemirro::VersionsFile]
|
|
23
23
|
#
|
|
24
24
|
def fetch
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
)
|
|
25
|
+
return unless Gemirro.configuration.versions_file
|
|
26
|
+
|
|
27
|
+
VersionsFile.new(read_file(Gemirro.configuration.versions_file))
|
|
29
28
|
end
|
|
30
29
|
|
|
31
30
|
##
|
|
32
31
|
# Read file if exists otherwise download its from source
|
|
33
32
|
#
|
|
34
33
|
# @param [String] file name
|
|
35
|
-
# @param [TrueClass|FalseClass] prerelease Is prerelease or not
|
|
36
34
|
#
|
|
37
|
-
def read_file(file
|
|
38
|
-
|
|
39
|
-
file_dst = File.join(destination, file)
|
|
40
|
-
unless File.exist?(file_dst)
|
|
35
|
+
def read_file(file)
|
|
36
|
+
unless File.exist?(file)
|
|
41
37
|
throw 'No source defined' unless @source
|
|
42
38
|
|
|
43
|
-
File.write(
|
|
44
|
-
File.write(file_dst, @source.fetch_prerelease_versions) if prerelease
|
|
39
|
+
File.write(file, @source.fetch_versions)
|
|
45
40
|
end
|
|
46
41
|
|
|
47
|
-
File.read(
|
|
42
|
+
File.read(file)
|
|
48
43
|
end
|
|
49
44
|
end
|
|
50
45
|
end
|
|
@@ -11,36 +11,24 @@ module Gemirro
|
|
|
11
11
|
# @return [Hash]
|
|
12
12
|
#
|
|
13
13
|
class VersionsFile
|
|
14
|
-
attr_reader :
|
|
14
|
+
attr_reader :versions_string, :versions_hash
|
|
15
15
|
|
|
16
16
|
##
|
|
17
17
|
# Reads the versions file from the specified String.
|
|
18
18
|
#
|
|
19
|
-
# @param [String]
|
|
20
|
-
# @param [String] prerelease_content
|
|
19
|
+
# @param [String] versions_content
|
|
21
20
|
# @return [Gemirro::VersionsFile]
|
|
22
21
|
#
|
|
23
|
-
def self.load(spec_content, prerelease_content)
|
|
24
|
-
buffer = StringIO.new(spec_content)
|
|
25
|
-
reader = Zlib::GzipReader.new(buffer)
|
|
26
|
-
versions = Marshal.load(reader.read)
|
|
27
|
-
|
|
28
|
-
buffer = StringIO.new(prerelease_content)
|
|
29
|
-
reader = Zlib::GzipReader.new(buffer)
|
|
30
|
-
versions.concat(Marshal.load(reader.read))
|
|
31
|
-
|
|
32
|
-
instance = new(versions)
|
|
33
|
-
|
|
34
|
-
reader.close
|
|
35
|
-
|
|
36
|
-
instance
|
|
37
|
-
end
|
|
38
22
|
|
|
39
23
|
##
|
|
40
|
-
# @param [
|
|
24
|
+
# @param [String] versions
|
|
41
25
|
#
|
|
42
|
-
def initialize(
|
|
43
|
-
|
|
26
|
+
def initialize(versions_string)
|
|
27
|
+
unless versions_string.is_a? String
|
|
28
|
+
throw "#{versions_string.class} is wrong format, expect String; #{versions_string.inspect}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
@versions_string = versions_string
|
|
44
32
|
@versions_hash = create_versions_hash
|
|
45
33
|
end
|
|
46
34
|
|
|
@@ -53,10 +41,24 @@ module Gemirro
|
|
|
53
41
|
def create_versions_hash
|
|
54
42
|
hash = Hash.new { |h, k| h[k] = [] }
|
|
55
43
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
end
|
|
44
|
+
versions_string.each_line.with_index do |line, index|
|
|
45
|
+
next if index < 2
|
|
59
46
|
|
|
47
|
+
parts = line.split
|
|
48
|
+
gem_name = parts[0]
|
|
49
|
+
parts[-1]
|
|
50
|
+
versions = parts[1..-2].collect { |x| x.split(',') }.flatten # All except first and last
|
|
51
|
+
|
|
52
|
+
versions.each do |ver|
|
|
53
|
+
version, platform =
|
|
54
|
+
if ver.include?('-')
|
|
55
|
+
ver.split('-', 2)
|
|
56
|
+
else
|
|
57
|
+
[ver, 'ruby']
|
|
58
|
+
end
|
|
59
|
+
hash[gem_name] << [gem_name, ::Gem::Version.new(version), platform]
|
|
60
|
+
end
|
|
61
|
+
end
|
|
60
62
|
hash
|
|
61
63
|
end
|
|
62
64
|
|
data/lib/gemirro.rb
CHANGED
|
@@ -4,14 +4,15 @@ require 'builder'
|
|
|
4
4
|
require 'confstruct'
|
|
5
5
|
require 'digest/sha2'
|
|
6
6
|
require 'fileutils'
|
|
7
|
-
require 'httpclient'
|
|
8
7
|
require 'json'
|
|
9
8
|
require 'logger'
|
|
9
|
+
require 'net/http'
|
|
10
|
+
require 'openssl'
|
|
11
|
+
require 'optparse'
|
|
10
12
|
require 'parallel'
|
|
11
13
|
require 'rubygems'
|
|
12
14
|
require 'rubygems/indexer'
|
|
13
15
|
require 'rubygems/user_interaction'
|
|
14
|
-
require 'slop'
|
|
15
16
|
require 'stringio'
|
|
16
17
|
require 'tempfile'
|
|
17
18
|
require 'zlib'
|
|
@@ -34,8 +35,3 @@ require 'gemirro/versions_fetcher'
|
|
|
34
35
|
require 'gemirro/gems_fetcher'
|
|
35
36
|
|
|
36
37
|
require 'gemirro/cli'
|
|
37
|
-
require 'gemirro/cli/index'
|
|
38
|
-
require 'gemirro/cli/init'
|
|
39
|
-
require 'gemirro/cli/list'
|
|
40
|
-
require 'gemirro/cli/server'
|
|
41
|
-
require 'gemirro/cli/update'
|
data/spec/gemirro/cli_spec.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
require 'gemirro/cli'
|
|
3
3
|
require 'gemirro/mirror_file'
|
|
4
|
-
require 'slop'
|
|
5
4
|
|
|
6
5
|
# Gemirro tests
|
|
7
6
|
module Gemirro
|
|
@@ -10,21 +9,47 @@ module Gemirro
|
|
|
10
9
|
describe 'CLI' do
|
|
11
10
|
include FakeFS::SpecHelpers
|
|
12
11
|
|
|
13
|
-
it 'should
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
12
|
+
it 'should print version information for -v' do
|
|
13
|
+
expect { CLI.run(['-v']) }.to output(/gemirro v.* on ruby/).to_stdout
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'should print version information for --version' do
|
|
17
|
+
expect { CLI.run(['--version']) }.to output(/gemirro v.* on ruby/).to_stdout
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'should print usage for -h' do
|
|
21
|
+
expect { CLI.run(['-h']) }.to output(/Usage: gemirro \[COMMAND\] \[OPTIONS\]/).to_stdout
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'should print usage for --help' do
|
|
25
|
+
expect { CLI.run(['--help']) }.to output(/Usage: gemirro \[COMMAND\] \[OPTIONS\]/).to_stdout
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'should print usage when no argument is given' do
|
|
29
|
+
expect { CLI.run([]) }.to output(/Usage: gemirro \[COMMAND\] \[OPTIONS\]/).to_stdout
|
|
30
|
+
end
|
|
23
31
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
32
|
+
it 'should list the available commands in the usage banner' do
|
|
33
|
+
output = capture_stdout { CLI.run(['-h']) }
|
|
34
|
+
|
|
35
|
+
CLI::COMMANDS.each do |name, description|
|
|
36
|
+
expect(output).to match(/#{name}(\s+)#{Regexp.escape(description)}/)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'should set $PROGRAM_NAME' do
|
|
41
|
+
$PROGRAM_NAME = 'rspec'
|
|
42
|
+
CLI.run(['-v'])
|
|
43
|
+
expect($PROGRAM_NAME).to eq('gemirro')
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'should print an error and exit for an unknown command' do
|
|
47
|
+
expect { CLI.run(['nope']) }.to output(/Unknown command: nope/).to_stdout.and raise_error(SystemExit)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'should print an option error for an unknown flag' do
|
|
51
|
+
expect { CLI.run(['list', '--nope']) }
|
|
52
|
+
.to output(/invalid option: --nope/).to_stdout.and raise_error(SystemExit)
|
|
28
53
|
end
|
|
29
54
|
|
|
30
55
|
it 'should retrieve version information' do
|
|
@@ -45,6 +70,15 @@ module Gemirro
|
|
|
45
70
|
file.write('test')
|
|
46
71
|
expect { CLI.load_configuration('config.rb') }.to raise_error LoadError
|
|
47
72
|
end
|
|
73
|
+
|
|
74
|
+
def capture_stdout
|
|
75
|
+
original = $stdout
|
|
76
|
+
$stdout = StringIO.new
|
|
77
|
+
yield
|
|
78
|
+
$stdout.string
|
|
79
|
+
ensure
|
|
80
|
+
$stdout = original
|
|
81
|
+
end
|
|
48
82
|
end
|
|
49
83
|
end
|
|
50
84
|
end
|
|
@@ -39,8 +39,8 @@ module Gemirro
|
|
|
39
39
|
expect(Configuration.marshal_identifier).to match(/Marshal\.(\d+)\.(\d+)/)
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
-
it 'should
|
|
43
|
-
expect(
|
|
42
|
+
it 'should error on version since no source is defined' do
|
|
43
|
+
expect(Gemirro.configuration.versions_file).to match(nil)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
it 'should return marshal file' do
|
|
@@ -84,5 +84,12 @@ module Gemirro
|
|
|
84
84
|
expect(result.host).to eq('https://rubygems.org')
|
|
85
85
|
expect(result.name).to eq('rubygems')
|
|
86
86
|
end
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
it 'should return versions file with source' do
|
|
90
|
+
result = @config.define_source('RubyGems', 'https://rubygems.org') do
|
|
91
|
+
end
|
|
92
|
+
expect(@config.versions_file).to match(/rubygems_org_versions$/)
|
|
93
|
+
end
|
|
87
94
|
end
|
|
88
95
|
end
|
|
@@ -12,7 +12,7 @@ module Gemirro
|
|
|
12
12
|
|
|
13
13
|
before(:each) do
|
|
14
14
|
@source = Source.new('RubyGems', 'https://rubygems.org')
|
|
15
|
-
@versions_file = VersionsFile.new(
|
|
15
|
+
@versions_file = VersionsFile.new(%(created_at: 2025-04-24T03:46:59Z\n---\nrack 3.0.0,3.0.1 d545a45462d63b1b4865bbb89a109366))
|
|
16
16
|
@fetcher = GemsFetcher.new(@source, @versions_file)
|
|
17
17
|
Gemirro.configuration.ignored_gems.clear
|
|
18
18
|
end
|
|
@@ -47,7 +47,7 @@ module Gemirro
|
|
|
47
47
|
version = ::Gem::Version.new('0.0.1')
|
|
48
48
|
Utils.configuration.ignore_gem('gemirro', '0.0.1', 'ruby')
|
|
49
49
|
allow(@source).to receive(:fetch_gem)
|
|
50
|
-
.once.with('gemirro'
|
|
50
|
+
.once.with('gemirro-0.0.1.gem').and_raise(ArgumentError)
|
|
51
51
|
allow(Utils.logger).to receive(:error)
|
|
52
52
|
.once.with(/Failed to retrieve/)
|
|
53
53
|
allow(Utils.logger).to receive(:debug)
|
data/spec/gemirro/http_spec.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
|
-
require '
|
|
2
|
+
require 'webmock/rspec'
|
|
3
|
+
require 'gemirro/utils'
|
|
3
4
|
require 'gemirro/http'
|
|
4
5
|
|
|
5
6
|
module Gemirro
|
|
@@ -7,88 +8,91 @@ module Gemirro
|
|
|
7
8
|
let(:config) { double('Configuration') }
|
|
8
9
|
|
|
9
10
|
before do
|
|
10
|
-
Http.
|
|
11
|
+
Http.reset!
|
|
11
12
|
allow(Utils).to receive(:configuration).and_return(config)
|
|
12
13
|
end
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
it 'initializes a new HTTPClient' do
|
|
16
|
-
expect(Http.client).to be_a(HTTPClient)
|
|
17
|
-
end
|
|
15
|
+
after { Http.reset! }
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
describe '.get' do
|
|
18
|
+
context 'with successful response' do
|
|
19
|
+
it 'returns the response' do
|
|
20
|
+
stub_request(:get, 'http://example.com/').to_return(status: 200, body: 'content')
|
|
21
|
+
|
|
22
|
+
response = Http.get('http://example.com/')
|
|
23
|
+
expect(response).to be_a(Net::HTTPSuccess)
|
|
24
|
+
expect(response.body).to eq('content')
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'reuses the connection for a second request on the same host' do
|
|
28
|
+
stub_request(:get, 'http://example.com/one').to_return(status: 200, body: 'one')
|
|
29
|
+
stub_request(:get, 'http://example.com/two').to_return(status: 200, body: 'two')
|
|
30
|
+
|
|
31
|
+
Http.get('http://example.com/one')
|
|
32
|
+
Http.get('http://example.com/two')
|
|
22
33
|
|
|
23
|
-
expect(
|
|
34
|
+
expect(a_request(:get, 'http://example.com/one')).to have_been_made.once
|
|
35
|
+
expect(a_request(:get, 'http://example.com/two')).to have_been_made.once
|
|
24
36
|
end
|
|
25
37
|
end
|
|
26
38
|
|
|
27
|
-
context 'with
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
end
|
|
39
|
+
context 'with a redirect' do
|
|
40
|
+
it 'follows it and returns the final response' do
|
|
41
|
+
stub_request(:get, 'http://example.com/old')
|
|
42
|
+
.to_return(status: 302, headers: { 'Location' => 'http://example.com/new' })
|
|
43
|
+
stub_request(:get, 'http://example.com/new').to_return(status: 200, body: 'moved')
|
|
33
44
|
|
|
34
|
-
|
|
35
|
-
expect { Http.client }.to raise_error(SystemExit)
|
|
36
|
-
end
|
|
45
|
+
expect(Http.get('http://example.com/old').body).to eq('moved')
|
|
37
46
|
end
|
|
38
47
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
end
|
|
48
|
+
it 'raises after too many redirects' do
|
|
49
|
+
stub_request(:get, 'http://example.com/loop')
|
|
50
|
+
.to_return(status: 302, headers: { 'Location' => 'http://example.com/loop' })
|
|
43
51
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
end
|
|
52
|
+
expect { Http.get('http://example.com/loop') }
|
|
53
|
+
.to raise_error(Http::Error, /Too many redirects/)
|
|
47
54
|
end
|
|
48
55
|
end
|
|
49
56
|
|
|
50
|
-
context 'with
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
end
|
|
57
|
+
context 'with an error response' do
|
|
58
|
+
it 'raises Http::Error for a failed request' do
|
|
59
|
+
stub_request(:get, 'http://example.com/missing').to_return(status: [404, 'Not Found'])
|
|
54
60
|
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
expect { Http.get('http://example.com/missing') }
|
|
62
|
+
.to raise_error(Http::Error, '404 Not Found')
|
|
57
63
|
end
|
|
58
64
|
end
|
|
59
|
-
end
|
|
60
65
|
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
context 'with upstream authentication configured' do
|
|
67
|
+
it 'sends a basic auth header on every request' do
|
|
68
|
+
allow(config).to receive(:upstream_user).and_return('user')
|
|
69
|
+
allow(config).to receive(:upstream_password).and_return('pass')
|
|
70
|
+
stub_request(:get, 'http://example.com/private')
|
|
71
|
+
.with(basic_auth: %w[user pass])
|
|
72
|
+
.to_return(status: 200, body: 'ok')
|
|
63
73
|
|
|
64
|
-
|
|
65
|
-
|
|
74
|
+
expect(Http.get('http://example.com/private').body).to eq('ok')
|
|
75
|
+
end
|
|
66
76
|
end
|
|
67
77
|
|
|
68
|
-
context 'with
|
|
69
|
-
|
|
78
|
+
context 'with proxy configuration' do
|
|
79
|
+
it 'routes the request through the configured proxy' do
|
|
80
|
+
allow(config).to receive(:proxy).and_return('http://proxy.example.com:8080')
|
|
81
|
+
stub_request(:get, 'http://example.com/').to_return(status: 200, body: 'content')
|
|
70
82
|
|
|
71
|
-
|
|
72
|
-
allow(client).to receive(:get)
|
|
73
|
-
.with('http://example.com', follow_redirect: true)
|
|
74
|
-
.and_return(response)
|
|
83
|
+
Http.get('http://example.com/')
|
|
75
84
|
|
|
76
|
-
expect(
|
|
85
|
+
expect(WebMock).to have_requested(:get, 'http://example.com/')
|
|
77
86
|
end
|
|
78
87
|
end
|
|
79
88
|
|
|
80
|
-
context 'with
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
it 'raises BadResponseError for failed request' do
|
|
84
|
-
allow(client).to receive(:get)
|
|
85
|
-
.with('http://example.com', follow_redirect: true)
|
|
86
|
-
.and_return(response)
|
|
89
|
+
context 'with an invalid rootca path' do
|
|
90
|
+
it 'aborts with an error message' do
|
|
91
|
+
allow(config).to receive(:rootca).and_return('/nonexistent/ca.crt')
|
|
87
92
|
|
|
88
|
-
expect { Http.get('
|
|
89
|
-
.to raise_error(HTTPClient::BadResponseError, 'Not Found')
|
|
93
|
+
expect { Http.get('https://example.com/') }.to raise_error(SystemExit)
|
|
90
94
|
end
|
|
91
95
|
end
|
|
92
96
|
end
|
|
93
97
|
end
|
|
94
|
-
end
|
|
98
|
+
end
|
|
@@ -24,7 +24,7 @@ module Gemirro
|
|
|
24
24
|
|
|
25
25
|
dir = MirrorDirectory.new('/tmp')
|
|
26
26
|
dir.add_directory('test')
|
|
27
|
-
indexer = Indexer.new
|
|
27
|
+
indexer = Indexer.new
|
|
28
28
|
|
|
29
29
|
Struct.new('HttpGet', :code, :body)
|
|
30
30
|
http_get = Struct::HttpGet.new(200, 'bad')
|
|
@@ -43,7 +43,7 @@ module Gemirro
|
|
|
43
43
|
allow(::Gem.configuration).to receive(:really_verbose)
|
|
44
44
|
.once.and_return(true)
|
|
45
45
|
|
|
46
|
-
indexer = Indexer.new
|
|
46
|
+
indexer = Indexer.new
|
|
47
47
|
indexer.quick_marshal_dir = '/tmp/gem_generate_index/quick/Marshal.4.8'
|
|
48
48
|
indexer.dest_directory = '/tmp/test'
|
|
49
49
|
indexer.directory = '/tmp/gem_generate_index'
|
|
@@ -97,7 +97,7 @@ module Gemirro
|
|
|
97
97
|
end
|
|
98
98
|
|
|
99
99
|
it 'should build indices' do
|
|
100
|
-
indexer = Indexer.new
|
|
100
|
+
indexer = Indexer.new
|
|
101
101
|
dir = MirrorDirectory.new('/')
|
|
102
102
|
dir.add_directory('gems')
|
|
103
103
|
dir.add_directory('quick')
|
|
@@ -140,7 +140,7 @@ module Gemirro
|
|
|
140
140
|
end
|
|
141
141
|
|
|
142
142
|
it 'should update index and exit ruby gems' do
|
|
143
|
-
indexer = Indexer.new
|
|
143
|
+
indexer = Indexer.new
|
|
144
144
|
MirrorDirectory.new('/')
|
|
145
145
|
MirrorFile.new('/specs.4.8').write('')
|
|
146
146
|
expect { indexer.update_index }.to raise_error(::Gem::SystemExitException)
|
|
@@ -152,7 +152,7 @@ module Gemirro
|
|
|
152
152
|
dir.add_directory('test/gems')
|
|
153
153
|
dir.add_directory('test/quick')
|
|
154
154
|
|
|
155
|
-
indexer = Indexer.new
|
|
155
|
+
indexer = Indexer.new
|
|
156
156
|
indexer.quick_marshal_dir = '/tmp/gem_generate_index/quick/Marshal.4.8'
|
|
157
157
|
indexer.dest_directory = '/tmp/test'
|
|
158
158
|
indexer.directory = '/tmp/gem_generate_index'
|