r5_geminabox 0.0.1
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.
- data/MIT-LICENSE +20 -0
- data/README.markdown +133 -0
- data/lib/geminabox.rb +79 -0
- data/lib/geminabox/disk_cache.rb +73 -0
- data/lib/geminabox/gem_list_merge.rb +51 -0
- data/lib/geminabox/gem_store.rb +99 -0
- data/lib/geminabox/gem_store_error.rb +13 -0
- data/lib/geminabox/gem_version.rb +40 -0
- data/lib/geminabox/gem_version_collection.rb +65 -0
- data/lib/geminabox/hostess.rb +46 -0
- data/lib/geminabox/http_adapter.rb +27 -0
- data/lib/geminabox/http_adapter/http_client_adapter.rb +32 -0
- data/lib/geminabox/http_adapter/template_faraday_adapter.rb +51 -0
- data/lib/geminabox/http_adapter_config_error.rb +7 -0
- data/lib/geminabox/incoming_gem.rb +70 -0
- data/lib/geminabox/indexer.rb +46 -0
- data/lib/geminabox/proxy.rb +13 -0
- data/lib/geminabox/proxy/copier.rb +29 -0
- data/lib/geminabox/proxy/file_handler.rb +95 -0
- data/lib/geminabox/proxy/hostess.rb +81 -0
- data/lib/geminabox/proxy/splicer.rb +67 -0
- data/lib/geminabox/rubygems_dependency.rb +29 -0
- data/lib/geminabox/server.rb +290 -0
- data/lib/geminabox/version.rb +3 -0
- data/lib/geminabox_client.rb +56 -0
- data/lib/rubygems/commands/inabox_command.rb +95 -0
- data/lib/rubygems_plugin.rb +2 -0
- data/public/favicon.ico +0 -0
- data/public/jquery.js +16 -0
- data/public/master.css +155 -0
- data/public/master.js +5 -0
- data/views/atom.erb +29 -0
- data/views/gem.erb +38 -0
- data/views/index.erb +56 -0
- data/views/layout.erb +16 -0
- data/views/upload.erb +10 -0
- metadata +166 -0
@@ -0,0 +1,40 @@
|
|
1
|
+
module Geminabox
|
2
|
+
|
3
|
+
class GemVersion
|
4
|
+
attr_accessor :name, :number, :platform
|
5
|
+
|
6
|
+
def initialize(name, number, platform)
|
7
|
+
@name = name
|
8
|
+
@number = number
|
9
|
+
@platform = platform
|
10
|
+
end
|
11
|
+
|
12
|
+
def ruby?
|
13
|
+
!!(platform =~ /ruby/i)
|
14
|
+
end
|
15
|
+
|
16
|
+
def version
|
17
|
+
Gem::Version.create(number)
|
18
|
+
end
|
19
|
+
|
20
|
+
def <=>(other)
|
21
|
+
sort = other.name <=> name
|
22
|
+
sort = version <=> other.version if sort.zero?
|
23
|
+
sort = (other.ruby? && !ruby?) ? 1 : -1 if sort.zero? && ruby? != other.ruby?
|
24
|
+
sort = other.platform <=> platform if sort.zero?
|
25
|
+
|
26
|
+
sort
|
27
|
+
end
|
28
|
+
|
29
|
+
def ==(other)
|
30
|
+
return false unless other.class == self.class
|
31
|
+
[name, number, platform] == [other.name, other.number, other.platform]
|
32
|
+
end
|
33
|
+
|
34
|
+
def gemfile_name
|
35
|
+
included_platform = ruby? ? nil : platform
|
36
|
+
[name, number, included_platform].compact.join('-')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Geminabox
|
2
|
+
|
3
|
+
# This class represents a sorted collection of Geminabox::GemVersion objects.
|
4
|
+
# It it used widely throughout the system for displaying and filtering gems.
|
5
|
+
class GemVersionCollection
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
# Array of Geminabox::GemVersion objects, or an array of [name, version,
|
9
|
+
# platform] triples.
|
10
|
+
def initialize(initial_gems=[])
|
11
|
+
@gems = initial_gems.map{|object|
|
12
|
+
coerce_to_gem_version(object)
|
13
|
+
}.sort
|
14
|
+
end
|
15
|
+
|
16
|
+
# FIXME: Terminology makes no sense when the version are not all of the same
|
17
|
+
# name
|
18
|
+
def oldest
|
19
|
+
@gems.first
|
20
|
+
end
|
21
|
+
|
22
|
+
# FIXME: Terminology makes no sense when the version are not all of the same
|
23
|
+
# name
|
24
|
+
def newest
|
25
|
+
@gems.last
|
26
|
+
end
|
27
|
+
|
28
|
+
def size
|
29
|
+
@gems.size
|
30
|
+
end
|
31
|
+
|
32
|
+
def each(&block)
|
33
|
+
@gems.each(&block)
|
34
|
+
end
|
35
|
+
|
36
|
+
# The collection can contain gems of different names, this method groups them
|
37
|
+
# by name, and then sorts the different version of each name by version and
|
38
|
+
# platform.
|
39
|
+
#
|
40
|
+
# yields 'foo_gem', version_collection
|
41
|
+
def by_name(&block)
|
42
|
+
@grouped ||= @gems.group_by(&:name).map{|name, collection|
|
43
|
+
[name, Geminabox::GemVersionCollection.new(collection)]
|
44
|
+
}.sort_by{|name, collection|
|
45
|
+
name.downcase
|
46
|
+
}
|
47
|
+
|
48
|
+
if block_given?
|
49
|
+
@grouped.each(&block)
|
50
|
+
else
|
51
|
+
@grouped
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def coerce_to_gem_version(object)
|
57
|
+
if object.is_a?(Geminabox::GemVersion)
|
58
|
+
object
|
59
|
+
else
|
60
|
+
Geminabox::GemVersion.new(*object)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
|
3
|
+
module Geminabox
|
4
|
+
|
5
|
+
class Hostess < Sinatra::Base
|
6
|
+
def serve
|
7
|
+
send_file(File.expand_path(File.join(Server.data, *request.path_info)), :type => response['Content-Type'])
|
8
|
+
end
|
9
|
+
|
10
|
+
%w[/specs.4.8.gz
|
11
|
+
/latest_specs.4.8.gz
|
12
|
+
/prerelease_specs.4.8.gz
|
13
|
+
].each do |index|
|
14
|
+
get index do
|
15
|
+
content_type 'application/x-gzip'
|
16
|
+
serve
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
%w[/quick/Marshal.4.8/*.gemspec.rz
|
21
|
+
/yaml.Z
|
22
|
+
/Marshal.4.8.Z
|
23
|
+
].each do |deflated_index|
|
24
|
+
get deflated_index do
|
25
|
+
content_type('application/x-deflate')
|
26
|
+
serve
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
%w[/yaml
|
31
|
+
/Marshal.4.8
|
32
|
+
/specs.4.8
|
33
|
+
/latest_specs.4.8
|
34
|
+
/prerelease_specs.4.8
|
35
|
+
].each do |old_index|
|
36
|
+
get old_index do
|
37
|
+
serve
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
get "/gems/*.gem" do
|
42
|
+
serve
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative 'http_adapter_config_error'
|
2
|
+
|
3
|
+
module Geminabox
|
4
|
+
class HttpAdapter
|
5
|
+
|
6
|
+
def get_content(*args)
|
7
|
+
raise HttpAdapterConfigError.new(:get_content, 'the response body')
|
8
|
+
end
|
9
|
+
|
10
|
+
def get(*args)
|
11
|
+
raise HttpAdapterConfigError.new(:get, 'a response object')
|
12
|
+
end
|
13
|
+
|
14
|
+
def post(*args)
|
15
|
+
raise HttpAdapterConfigError.new(:post, 'a response object')
|
16
|
+
end
|
17
|
+
|
18
|
+
def set_auth(*args)
|
19
|
+
raise HttpAdapterConfigError.new(:set_auth, 'true')
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
Dir[File.expand_path('http_adapter/*.rb', File.dirname(__FILE__))].each do |file|
|
26
|
+
require file
|
27
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'httpclient'
|
2
|
+
|
3
|
+
module Geminabox
|
4
|
+
|
5
|
+
class HttpClientAdapter < HttpAdapter
|
6
|
+
|
7
|
+
def get(*args)
|
8
|
+
http_client.get(*args)
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_content(*args)
|
12
|
+
http_client.get_content(*args)
|
13
|
+
end
|
14
|
+
|
15
|
+
def post(*args)
|
16
|
+
http_client.post(*args)
|
17
|
+
end
|
18
|
+
|
19
|
+
def set_auth(url, username = nil, password = nil)
|
20
|
+
http_client.set_auth(url, username, password) if username or password
|
21
|
+
http_client.www_auth.basic_auth.challenge(url) # Workaround: https://github.com/nahi/httpclient/issues/63
|
22
|
+
http_client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
23
|
+
http_client.send_timeout = 0
|
24
|
+
http_client.receive_timeout = 0
|
25
|
+
end
|
26
|
+
|
27
|
+
def http_client
|
28
|
+
@http_client ||= HTTPClient.new(ENV['http_proxy'])
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
module Geminabox
|
4
|
+
|
5
|
+
class TemplateFaradayAdapter < HttpAdapter
|
6
|
+
|
7
|
+
def get(*args)
|
8
|
+
adapter.get(*args)
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_content(*args)
|
12
|
+
response = adapter.get(*args)
|
13
|
+
response.body
|
14
|
+
end
|
15
|
+
|
16
|
+
def post(*args)
|
17
|
+
adapter.post(*args)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Note that this configuration turns SSL certificate verification off.
|
21
|
+
# To set up the adapter for your environment see:
|
22
|
+
# https://github.com/lostisland/faraday/wiki/Setting-up-SSL-certificates
|
23
|
+
def set_auth(uri, username = nil, password = nil)
|
24
|
+
connection = Faraday.new url: uri, ssl: {verify: false} do |faraday|
|
25
|
+
faraday.adapter http_engine
|
26
|
+
faraday.proxy(ENV['http_proxy']) if ENV['http_proxy']
|
27
|
+
end
|
28
|
+
connection.basic_auth username, password if username
|
29
|
+
connection
|
30
|
+
end
|
31
|
+
|
32
|
+
def adapter
|
33
|
+
@adapter ||= Faraday.new do |faraday|
|
34
|
+
faraday.adapter http_engine
|
35
|
+
faraday.proxy(ENV['http_proxy']) if ENV['http_proxy']
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def http_engine
|
40
|
+
:net_http # make requests with Net::HTTP
|
41
|
+
end
|
42
|
+
|
43
|
+
def options
|
44
|
+
lambda {|faraday|
|
45
|
+
faraday.adapter http_engine
|
46
|
+
faraday.proxy(ENV['http_proxy']) if ENV['http_proxy']
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Geminabox
|
2
|
+
|
3
|
+
class IncomingGem
|
4
|
+
def initialize(gem_data, root_path = Geminabox.data)
|
5
|
+
unless gem_data.respond_to? :read
|
6
|
+
raise ArgumentError, "Expected an instance of IO"
|
7
|
+
end
|
8
|
+
|
9
|
+
digest = Digest::SHA1.new
|
10
|
+
if RbConfig::CONFIG["MAJOR"].to_i <= 1 and RbConfig::CONFIG["MINOR"].to_i <= 8
|
11
|
+
@tempfile = Tempfile.new("gem")
|
12
|
+
else
|
13
|
+
@tempfile = Tempfile.new("gem", :encoding => 'binary', :binmode => true)
|
14
|
+
end
|
15
|
+
|
16
|
+
while data = gem_data.read(1024**2)
|
17
|
+
@tempfile.write data
|
18
|
+
digest << data
|
19
|
+
end
|
20
|
+
|
21
|
+
@tempfile.close
|
22
|
+
@sha1 = digest.hexdigest
|
23
|
+
|
24
|
+
@root_path = root_path
|
25
|
+
end
|
26
|
+
|
27
|
+
def gem_data
|
28
|
+
File.open(@tempfile.path, "rb")
|
29
|
+
end
|
30
|
+
|
31
|
+
def valid?
|
32
|
+
spec && spec.name && spec.version
|
33
|
+
rescue Gem::Package::Error
|
34
|
+
false
|
35
|
+
end
|
36
|
+
|
37
|
+
def spec
|
38
|
+
@spec ||= extract_spec
|
39
|
+
end
|
40
|
+
|
41
|
+
def extract_spec
|
42
|
+
if Gem::Package.respond_to? :open
|
43
|
+
Gem::Package.open(gem_data, "r", nil) do |pkg|
|
44
|
+
return pkg.metadata
|
45
|
+
end
|
46
|
+
else
|
47
|
+
Gem::Package.new(@tempfile.path).spec
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def name
|
52
|
+
@name ||= get_name
|
53
|
+
end
|
54
|
+
|
55
|
+
def get_name
|
56
|
+
filename = %W[#{spec.name} #{spec.version}]
|
57
|
+
filename.push(spec.platform) if spec.platform && spec.platform != "ruby"
|
58
|
+
filename.join("-") + ".gem"
|
59
|
+
end
|
60
|
+
|
61
|
+
def dest_filename
|
62
|
+
File.join(@root_path, "gems", name)
|
63
|
+
end
|
64
|
+
|
65
|
+
def hexdigest
|
66
|
+
@sha1
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
|
2
|
+
# This module addresses Geminabox issue
|
3
|
+
# https://github.com/cwninja/geminabox/issues/70
|
4
|
+
#
|
5
|
+
# The underlying problem is rubygems issue
|
6
|
+
# https://github.com/rubygems/rubygems/issues/232, fixed by
|
7
|
+
# https://github.com/rubygems/rubygems/pull/364
|
8
|
+
#
|
9
|
+
# This library (and its call) should be deleted once that pull request is resolved.
|
10
|
+
|
11
|
+
require 'geminabox'
|
12
|
+
require 'rubygems/indexer'
|
13
|
+
|
14
|
+
module Geminabox::Indexer
|
15
|
+
def self.germane?
|
16
|
+
gem_version = Gem::Version.new(Gem::VERSION)
|
17
|
+
v1_8 = Gem::Version.new('1.8')
|
18
|
+
v1_8_25 = Gem::Version.new('1.8.25')
|
19
|
+
|
20
|
+
(gem_version >= v1_8) && (gem_version < v1_8_25)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.updated_gemspecs(indexer)
|
24
|
+
specs_mtime = File.stat(indexer.dest_specs_index).mtime
|
25
|
+
newest_mtime = Time.at 0
|
26
|
+
|
27
|
+
updated_gems = indexer.gem_file_list.select do |gem|
|
28
|
+
gem_mtime = File.stat(gem).mtime
|
29
|
+
newest_mtime = gem_mtime if gem_mtime > newest_mtime
|
30
|
+
gem_mtime >= specs_mtime
|
31
|
+
end
|
32
|
+
|
33
|
+
indexer.map_gems_to_specs updated_gems
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.patch_rubygems_update_index_pre_1_8_25(indexer)
|
37
|
+
if germane?
|
38
|
+
specs = updated_gemspecs(indexer)
|
39
|
+
|
40
|
+
unless specs.empty?
|
41
|
+
Gem::Specification.dirs = []
|
42
|
+
Gem::Specification.add_specs(*specs)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
|
2
|
+
module Geminabox
|
3
|
+
module Proxy
|
4
|
+
def self.proxy_path(file)
|
5
|
+
File.join File.dirname(__FILE__), 'proxy', file
|
6
|
+
end
|
7
|
+
|
8
|
+
autoload :Hostess, proxy_path('hostess')
|
9
|
+
autoload :FileHandler, proxy_path('file_handler')
|
10
|
+
autoload :Splicer, proxy_path('splicer')
|
11
|
+
autoload :Copier, proxy_path('copier')
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Geminabox
|
4
|
+
module Proxy
|
5
|
+
class Copier < FileHandler
|
6
|
+
|
7
|
+
def self.copy(file_name)
|
8
|
+
copier = new(file_name)
|
9
|
+
copier.get_file
|
10
|
+
copier
|
11
|
+
end
|
12
|
+
|
13
|
+
def get_file
|
14
|
+
return true if proxy_file_exists?
|
15
|
+
return copy_local if local_file_exists?
|
16
|
+
get_remote
|
17
|
+
end
|
18
|
+
|
19
|
+
def copy_local
|
20
|
+
FileUtils.cp local_path, proxy_path
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_remote
|
24
|
+
File.open(proxy_path, 'w'){|f| f.write(remote_content)}
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|