geminabox 0.11.1 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of geminabox might be problematic. Click here for more details.

data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2012 Tom Lea, Jack Foy, and Rob Nichols
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown CHANGED
@@ -26,7 +26,7 @@ Create a config.ru as follows:
26
26
  require "geminabox"
27
27
 
28
28
  Geminabox.data = "/var/geminabox-data" # ... or wherever
29
- run Geminabox
29
+ run Geminabox::Server
30
30
 
31
31
  Start your gem server with 'rackup' to run WEBrick or hook up the config.ru as you normally would ([passenger][passenger], [thin][thin], [unicorn][unicorn], whatever floats your boat).
32
32
 
@@ -36,6 +36,18 @@ RubyGems supports generating indexes for the so called legacy versions (< 1.2),
36
36
 
37
37
  Geminabox.build_legacy = false
38
38
 
39
+ ## RubyGems Proxy
40
+
41
+ Geminabox can be configured to pull gems, it does not currently have, from rubygems.org. To enable this mode you can either:
42
+
43
+ Set RUBYGEM_PROXY to true in the environment:
44
+
45
+ RUBYGEMS_PROXY=true rackup
46
+
47
+ Or in config.ru (before the run command), set:
48
+
49
+ Geminabox.rubygems_proxy = true
50
+
39
51
  ## Client Usage
40
52
 
41
53
  Since version 0.10, Geminabox supports the standard gemcutter push API:
@@ -88,7 +100,7 @@ Simples!
88
100
 
89
101
  ## Licence
90
102
 
91
- Fork it, mod it, choose it, use it, make it better. All under the [do what the fuck you want to + beer/pizza public license][WTFBPPL].
103
+ Fork it, mod it, choose it, use it, make it better. All under the MIT License.
92
104
 
93
105
  [WTFBPPL]: http://tomlea.co.uk/WTFBPPL.txt
94
106
  [sinatra]: http://www.sinatrarb.com/
data/lib/geminabox.rb CHANGED
@@ -4,234 +4,68 @@ require 'builder'
4
4
  require 'sinatra/base'
5
5
  require 'rubygems/indexer'
6
6
  require 'rubygems/package'
7
- require 'hostess'
8
- require 'geminabox/version'
9
- require 'geminabox/gem_store'
10
- require 'geminabox/gem_store_error'
11
7
  require 'rss/atom'
12
8
  require 'tempfile'
9
+ require 'json'
13
10
 
14
- class Geminabox < Sinatra::Base
15
- enable :static, :methodoverride
11
+ module Geminabox
16
12
 
17
- set :public_folder, File.join(File.dirname(__FILE__), *%w[.. public])
18
- set :data, File.join(File.dirname(__FILE__), *%w[.. data])
19
- set :build_legacy, false
20
- set :incremental_updates, true
21
- set :views, File.join(File.dirname(__FILE__), *%w[.. views])
22
- set :allow_replace, false
23
- set :gem_permissions, 0644
24
- set :allow_delete, true
25
- use Hostess
13
+ require_relative 'geminabox/version'
14
+ require_relative 'geminabox/proxy'
26
15
 
27
- class << self
28
- def disallow_replace?
29
- ! allow_replace
30
- end
31
-
32
- def allow_delete?
33
- allow_delete
34
- end
35
-
36
- def fixup_bundler_rubygems!
37
- return if @post_reset_hook_applied
38
- Gem.post_reset{ Gem::Specification.all = nil } if defined? Bundler and Gem.respond_to? :post_reset
39
- @post_reset_hook_applied = true
40
- end
41
-
42
- def reindex(force_rebuild = false)
43
- Geminabox.fixup_bundler_rubygems!
44
- force_rebuild = true unless Geminabox.incremental_updates
45
- if force_rebuild
46
- indexer.generate_index
47
- dependency_cache.flush
48
- else
49
- begin
50
- require 'geminabox/indexer'
51
- updated_gemspecs = Geminabox::Indexer.updated_gemspecs(indexer)
52
- Geminabox::Indexer.patch_rubygems_update_index_pre_1_8_25(indexer)
53
- indexer.update_index
54
- updated_gemspecs.each { |gem| dependency_cache.flush_key(gem.name) }
55
- rescue => e
56
- puts "#{e.class}:#{e.message}"
57
- puts e.backtrace.join("\n")
58
- reindex(:force_rebuild)
59
- end
60
- end
61
- end
62
-
63
- def indexer
64
- Gem::Indexer.new(Geminabox.data, :build_legacy => Geminabox.build_legacy)
65
- end
66
-
67
- def dependency_cache
68
- @dependency_cache ||= Geminabox::DiskCache.new(File.join(Geminabox.data, "_cache"))
69
- end
70
- end
71
-
72
- autoload :GemVersionCollection, "geminabox/gem_version_collection"
73
- autoload :GemVersion, "geminabox/gem_version"
74
- autoload :DiskCache, "geminabox/disk_cache"
75
- autoload :IncomingGem, "geminabox/incoming_gem"
76
-
77
- before do
78
- headers 'X-Powered-By' => "geminabox #{GeminaboxVersion}"
79
- end
80
-
81
- get '/' do
82
- @gems = load_gems
83
- @index_gems = index_gems(@gems)
84
- erb :index
85
- end
86
-
87
- get '/atom.xml' do
88
- @gems = load_gems
89
- erb :atom, :layout => false
90
- end
91
-
92
- get '/api/v1/dependencies' do
93
- query_gems = params[:gems].to_s.split(',')
94
- deps = query_gems.inject([]){|memo, query_gem| memo + gem_dependencies(query_gem) }
95
- Marshal.dump(deps)
16
+ def self.geminabox_path(file)
17
+ File.join File.dirname(__FILE__), 'geminabox', file
96
18
  end
97
19
 
98
- get '/upload' do
99
- erb :upload
100
- end
101
-
102
- get '/reindex' do
103
- self.class.reindex(:force_rebuild)
104
- redirect url("/")
105
- end
106
-
107
- get '/gems/:gemname' do
108
- gems = Hash[load_gems.by_name]
109
- @gem = gems[params[:gemname]]
110
- halt 404 unless @gem
111
- erb :gem
112
- end
113
-
114
- delete '/gems/*.gem' do
115
- unless Geminabox.allow_delete?
116
- error_response(403, 'Gem deletion is disabled - see https://github.com/cwninja/geminabox/issues/115')
117
- end
118
- File.delete file_path if File.exists? file_path
119
- self.class.reindex(:force_rebuild)
120
- redirect url("/")
121
- end
20
+ autoload :Hostess, geminabox_path('hostess')
21
+ autoload :GemStore, geminabox_path('gem_store')
22
+ autoload :GemStoreError, geminabox_path('gem_store_error')
23
+ autoload :RubygemsDependency, geminabox_path('rubygems_dependency')
24
+ autoload :GemListMerge, geminabox_path('gem_list_merge')
25
+ autoload :GemVersion, geminabox_path('gem_version')
26
+ autoload :GemVersionCollection, geminabox_path('gem_version_collection')
27
+ autoload :Server, geminabox_path('server')
28
+ autoload :DiskCache, geminabox_path('disk_cache')
29
+ autoload :IncomingGem, geminabox_path('incoming_gem')
122
30
 
123
- post '/upload' do
124
- unless params[:file] && params[:file][:filename] && (tmpfile = params[:file][:tempfile])
125
- @error = "No file selected"
126
- halt [400, erb(:upload)]
127
- end
128
- handle_incoming_gem(IncomingGem.new(tmpfile))
129
- end
31
+ class << self
130
32
 
131
- post '/api/v1/gems' do
132
- begin
133
- handle_incoming_gem(IncomingGem.new(request.body))
134
- rescue Object => o
135
- File.open "/tmp/debug.txt", "a" do |io|
136
- io.puts o, o.backtrace
33
+ attr_accessor(
34
+ :data,
35
+ :public_folder,
36
+ :build_legacy,
37
+ :incremental_updates,
38
+ :views,
39
+ :allow_replace,
40
+ :gem_permissions,
41
+ :allow_delete,
42
+ :rubygems_proxy
43
+ )
44
+
45
+ def set_defaults(defaults)
46
+ defaults.each do |method, default|
47
+ variable = "@#{method}"
48
+ instance_variable_set(variable, default) unless instance_variable_get(variable)
137
49
  end
138
50
  end
139
- end
140
-
141
- private
142
-
143
- def handle_incoming_gem(gem)
144
- begin
145
- GemStore.create(gem, params[:overwrite])
146
- rescue GemStoreError => error
147
- error_response error.code, error.reason
148
- end
149
51
 
150
- if api_request?
151
- "Gem #{gem.name} received and indexed."
152
- else
153
- redirect url("/")
52
+ def settings
53
+ Server.settings
154
54
  end
155
55
  end
156
56
 
157
- def api_request?
158
- request.accept.first != "text/html"
159
- end
57
+ set_defaults(
160
58
 
161
- def error_response(code, message)
162
- halt [code, message] if api_request?
163
- html = <<HTML
164
- <html>
165
- <head><title>Error - #{code}</title></head>
166
- <body>
167
- <h1>Error - #{code}</h1>
168
- <p>#{message}</p>
169
- </body>
170
- </html>
171
- HTML
172
- halt [code, html]
173
- end
174
-
175
- def indexer
176
- Gem::Indexer.new(settings.data, :build_legacy => settings.build_legacy)
177
- end
178
-
179
- def file_path
180
- File.expand_path(File.join(settings.data, *request.path_info))
181
- end
182
-
183
- def dependency_cache
184
- @dependency_cache ||= Geminabox::DiskCache.new(File.join(settings.data, "_cache"))
185
- end
59
+ data: File.join(File.dirname(__FILE__), *%w[.. data]),
60
+ public_folder: File.join(File.dirname(__FILE__), *%w[.. public]),
61
+ build_legacy: false,
62
+ incremental_updates: true,
63
+ views: File.join(File.dirname(__FILE__), *%w[.. views]),
64
+ allow_replace: false,
65
+ gem_permissions: 0644,
66
+ rubygems_proxy: (ENV['RUBYGEMS_PROXY'] == 'true'),
67
+ allow_delete: true
186
68
 
187
- def all_gems
188
- %w(specs prerelease_specs).map{ |specs_file_type|
189
- specs_file_path = File.join(settings.data, "#{specs_file_type}.#{Gem.marshal_version}.gz")
190
- if File.exists?(specs_file_path)
191
- Marshal.load(Gem.gunzip(Gem.read_binary(specs_file_path)))
192
- else
193
- []
194
- end
195
- }.inject(:|)
196
- end
197
-
198
- def load_gems
199
- @loaded_gems ||= Geminabox::GemVersionCollection.new(all_gems)
200
- end
201
-
202
- def index_gems(gems)
203
- Set.new(gems.map{|gem| gem.name[0..0].downcase})
204
- end
205
-
206
- helpers do
207
- def spec_for(gem_name, version)
208
- spec_file = File.join(settings.data, "quick", "Marshal.#{Gem.marshal_version}", "#{gem_name}-#{version}.gemspec.rz")
209
- Marshal.load(Gem.inflate(File.read(spec_file))) if File.exists? spec_file
210
- end
211
-
212
- # Return a list of versions of gem 'gem_name' with the dependencies of each version.
213
- def gem_dependencies(gem_name)
214
- dependency_cache.marshal_cache(gem_name) do
215
- load_gems.
216
- select { |gem| gem_name == gem.name }.
217
- map { |gem| [gem, spec_for(gem.name, gem.number)] }.
218
- reject { |(_, spec)| spec.nil? }.
219
- map do |(gem, spec)|
220
- {
221
- :name => gem.name,
222
- :number => gem.number.version,
223
- :platform => gem.platform,
224
- :dependencies => runtime_dependencies(spec)
225
- }
226
- end
227
- end
228
- end
229
-
230
- def runtime_dependencies(spec)
231
- spec.
232
- dependencies.
233
- select { |dep| dep.type == :runtime }.
234
- map { |dep| [dep.name, dep.requirement.to_s] }
235
- end
236
- end
69
+ )
70
+
237
71
  end
@@ -1,72 +1,73 @@
1
1
  require "fileutils"
2
+ module Geminabox
3
+ class Geminabox::DiskCache
4
+ attr_reader :root_path
5
+
6
+ def initialize(root_path)
7
+ @root_path = root_path
8
+ ensure_dir_exists!
9
+ end
10
+
11
+ def flush_key(key)
12
+ path = path(key_hash(key))
13
+ FileUtils.rm_f(path)
14
+ end
15
+
16
+ def flush
17
+ FileUtils.rm_rf(root_path)
18
+ ensure_dir_exists!
19
+ end
20
+
21
+ def cache(key)
22
+ key_hash = key_hash(key)
23
+ read(key_hash) || write(key_hash, yield)
24
+ end
25
+
26
+ def marshal_cache(key)
27
+ key_hash = key_hash(key)
28
+ marshal_read(key_hash) || marshal_write(key_hash, yield)
29
+ end
30
+
31
+ protected
32
+
33
+ def ensure_dir_exists!
34
+ FileUtils.mkdir_p(root_path)
35
+ end
36
+
37
+ def key_hash(key)
38
+ Digest::MD5.hexdigest(key)
39
+ end
40
+
41
+ def path(key_hash)
42
+ File.join(root_path, key_hash)
43
+ end
44
+
45
+ def read(key_hash)
46
+ read_int(key_hash) { |path| File.read(path) }
47
+ end
48
+
49
+ def marshal_read(key_hash)
50
+ read_int(key_hash) { |path| Marshal.load(File.open(path)) }
51
+ end
52
+
53
+ def read_int(key_hash)
54
+ path = path(key_hash)
55
+ yield(path) if File.exists?(path)
56
+ end
57
+
58
+ def write(key_hash, value)
59
+ write_int(key_hash) { |f| f << value }
60
+ value
61
+ end
62
+
63
+ def marshal_write(key_hash, value)
64
+ write_int(key_hash) { |f| Marshal.dump(value, f) }
65
+ value
66
+ end
67
+
68
+ def write_int(key_hash)
69
+ File.open(path(key_hash), 'wb') { |f| yield(f) }
70
+ end
2
71
 
3
- class Geminabox::DiskCache
4
- attr_reader :root_path
5
-
6
- def initialize(root_path)
7
- @root_path = root_path
8
- ensure_dir_exists!
9
- end
10
-
11
- def flush_key(key)
12
- path = path(key_hash(key))
13
- FileUtils.rm_f(path)
14
- end
15
-
16
- def flush
17
- FileUtils.rm_rf(root_path)
18
- ensure_dir_exists!
19
- end
20
-
21
- def cache(key)
22
- key_hash = key_hash(key)
23
- read(key_hash) || write(key_hash, yield)
24
72
  end
25
-
26
- def marshal_cache(key)
27
- key_hash = key_hash(key)
28
- marshal_read(key_hash) || marshal_write(key_hash, yield)
29
- end
30
-
31
- protected
32
-
33
- def ensure_dir_exists!
34
- FileUtils.mkdir_p(root_path)
35
- end
36
-
37
- def key_hash(key)
38
- Digest::MD5.hexdigest(key)
39
- end
40
-
41
- def path(key_hash)
42
- File.join(root_path, key_hash)
43
- end
44
-
45
- def read(key_hash)
46
- read_int(key_hash) { |path| File.read(path) }
47
- end
48
-
49
- def marshal_read(key_hash)
50
- read_int(key_hash) { |path| Marshal.load(File.open(path)) }
51
- end
52
-
53
- def read_int(key_hash)
54
- path = path(key_hash)
55
- yield(path) if File.exists?(path)
56
- end
57
-
58
- def write(key_hash, value)
59
- write_int(key_hash) { |f| f << value }
60
- value
61
- end
62
-
63
- def marshal_write(key_hash, value)
64
- write_int(key_hash) { |f| Marshal.dump(value, f) }
65
- value
66
- end
67
-
68
- def write_int(key_hash)
69
- File.open(path(key_hash), 'wb') { |f| yield(f) }
70
- end
71
-
72
73
  end