geminabox-jgraichen 0.12.2.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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.markdown +109 -0
- data/lib/geminabox.rb +76 -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/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 +93 -0
- data/lib/geminabox/proxy/hostess.rb +88 -0
- data/lib/geminabox/proxy/splicer.rb +67 -0
- data/lib/geminabox/rubygems_dependency.rb +27 -0
- data/lib/geminabox/server.rb +285 -0
- data/lib/geminabox/version.rb +3 -0
- data/lib/geminabox_client.rb +60 -0
- data/lib/rubygems/commands/inabox_command.rb +91 -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 +8 -0
- metadata +167 -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,70 @@
|
|
1
|
+
module Geminabox
|
2
|
+
|
3
|
+
class IncomingGem
|
4
|
+
def initialize(gem_data, root_path = Geminabox.settings.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')
|
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
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'httpclient'
|
2
|
+
module Geminabox
|
3
|
+
module Proxy
|
4
|
+
class FileHandler
|
5
|
+
|
6
|
+
attr_reader :file_name
|
7
|
+
|
8
|
+
def initialize(file_name)
|
9
|
+
@file_name = file_name
|
10
|
+
ensure_destination_exists
|
11
|
+
end
|
12
|
+
|
13
|
+
def local_path
|
14
|
+
File.expand_path(file_name, root_path)
|
15
|
+
end
|
16
|
+
|
17
|
+
def root_path
|
18
|
+
Geminabox.data
|
19
|
+
end
|
20
|
+
|
21
|
+
def local_file_exists?
|
22
|
+
file_exists? local_path
|
23
|
+
end
|
24
|
+
|
25
|
+
def proxy_file_exists?
|
26
|
+
file_exists? proxy_path
|
27
|
+
end
|
28
|
+
|
29
|
+
def proxy_path
|
30
|
+
File.expand_path(file_name, proxy_folder_path)
|
31
|
+
end
|
32
|
+
|
33
|
+
def file_exists?(path)
|
34
|
+
File.exists? path
|
35
|
+
end
|
36
|
+
|
37
|
+
def proxy_folder_path
|
38
|
+
File.join(root_path, proxy_folder_name)
|
39
|
+
end
|
40
|
+
|
41
|
+
def proxy_folder_name
|
42
|
+
'proxy'
|
43
|
+
end
|
44
|
+
|
45
|
+
def remote_content
|
46
|
+
HTTPClient.get_content(remote_url).force_encoding(encoding)
|
47
|
+
end
|
48
|
+
|
49
|
+
def remote_url
|
50
|
+
"http://rubygems.org/#{file_name}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def local_content
|
54
|
+
File.read(local_path).force_encoding(encoding)
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
def encoding
|
59
|
+
"UTF-8"
|
60
|
+
end
|
61
|
+
|
62
|
+
def ensure_destination_exists
|
63
|
+
create_local_folder unless local_folder_exists?
|
64
|
+
create_proxy_folder unless proxy_folder_exists?
|
65
|
+
end
|
66
|
+
|
67
|
+
def proxy_file_folder
|
68
|
+
File.dirname proxy_path
|
69
|
+
end
|
70
|
+
|
71
|
+
def proxy_folder_exists?
|
72
|
+
Dir.exists?(proxy_file_folder)
|
73
|
+
end
|
74
|
+
|
75
|
+
def create_proxy_folder
|
76
|
+
FileUtils.mkdir_p(proxy_file_folder)
|
77
|
+
end
|
78
|
+
|
79
|
+
def local_file_folder
|
80
|
+
File.dirname local_path
|
81
|
+
end
|
82
|
+
|
83
|
+
def local_folder_exists?
|
84
|
+
Dir.exists?(local_file_folder)
|
85
|
+
end
|
86
|
+
|
87
|
+
def create_local_folder
|
88
|
+
FileUtils.mkdir_p(local_file_folder)
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
module Geminabox
|
5
|
+
module Proxy
|
6
|
+
class Hostess < Sinatra::Base
|
7
|
+
attr_accessor :file_handler
|
8
|
+
|
9
|
+
def serve
|
10
|
+
if file_handler
|
11
|
+
send_file file_handler.proxy_path
|
12
|
+
else
|
13
|
+
send_file(File.expand_path(File.join(Server.data, *request.path_info)), :type => response['Content-Type'])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
%w[specs.4.8.gz
|
18
|
+
latest_specs.4.8.gz
|
19
|
+
prerelease_specs.4.8.gz
|
20
|
+
].each do |index|
|
21
|
+
get "/#{index}" do
|
22
|
+
splice_file index
|
23
|
+
content_type 'application/x-gzip'
|
24
|
+
serve
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
%w[quick/Marshal.4.8/*.gemspec.rz
|
29
|
+
yaml.Z
|
30
|
+
Marshal.4.8.Z
|
31
|
+
].each do |deflated_index|
|
32
|
+
get "/#{deflated_index}" do
|
33
|
+
copy_file request.path_info[1..-1]
|
34
|
+
content_type('application/x-deflate')
|
35
|
+
serve
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
%w[yaml
|
40
|
+
Marshal.4.8
|
41
|
+
specs.4.8
|
42
|
+
latest_specs.4.8
|
43
|
+
prerelease_specs.4.8
|
44
|
+
].each do |old_index|
|
45
|
+
get "/#{old_index}" do
|
46
|
+
splice_file old_index
|
47
|
+
serve
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
get "/gems/*.gem" do
|
52
|
+
get_from_rubygems_if_not_local
|
53
|
+
serve
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
def get_from_rubygems_if_not_local
|
58
|
+
|
59
|
+
file = File.expand_path(File.join(Server.data, *request.path_info))
|
60
|
+
|
61
|
+
unless File.exists?(file)
|
62
|
+
net_http_class.start("production.cf.rubygems.org") do |http|
|
63
|
+
path = File.join(*request.path_info)
|
64
|
+
response = http.get(path)
|
65
|
+
GemStore.create(IncomingGem.new(StringIO.new(response.body)))
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
def net_http_class
|
72
|
+
return ::Net::HTTP if ENV['http_proxy'].empty?
|
73
|
+
proxy_uri = URI.parse(ENV['http_proxy'])
|
74
|
+
::Net::HTTP::Proxy(proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password)
|
75
|
+
end
|
76
|
+
|
77
|
+
def splice_file(file_name)
|
78
|
+
self.file_handler = Splicer.make(file_name)
|
79
|
+
end
|
80
|
+
|
81
|
+
def copy_file(file_name)
|
82
|
+
self.file_handler = Copier.copy(file_name)
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|