geminabox-jgraichen 0.12.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|