lib/geminabox/server.rb in geminabox-0.12.4 vs lib/geminabox/server.rb in geminabox-0.13.0

- old
+ new

@@ -14,11 +14,14 @@ :incremental_updates, :views, :allow_replace, :gem_permissions, :allow_delete, - :rubygems_proxy + :lockfile, + :retry_interval, + :rubygems_proxy, + :ruby_gems_url ) if Server.rubygems_proxy use Proxy::Hostess else @@ -51,16 +54,19 @@ require 'geminabox/indexer' updated_gemspecs = Geminabox::Indexer.updated_gemspecs(indexer) Geminabox::Indexer.patch_rubygems_update_index_pre_1_8_25(indexer) indexer.update_index updated_gemspecs.each { |gem| dependency_cache.flush_key(gem.name) } + rescue Errno::ENOENT + reindex(:force_rebuild) rescue => e puts "#{e.class}:#{e.message}" puts e.backtrace.join("\n") reindex(:force_rebuild) end end + rescue Gem::SystemExitException end def indexer Gem::Indexer.new(data, :build_legacy => build_legacy) end @@ -98,12 +104,14 @@ get '/upload' do erb :upload end get '/reindex' do - self.class.reindex(:force_rebuild) - redirect url("/") + serialize_update do + self.class.reindex(:force_rebuild) + redirect url("/") + end end get '/gems/:gemname' do gems = Hash[load_gems.by_name] @gem = gems[params[:gemname]] @@ -113,35 +121,62 @@ delete '/gems/*.gem' do unless self.class.allow_delete? error_response(403, 'Gem deletion is disabled - see https://github.com/cwninja/geminabox/issues/115') end - File.delete file_path if File.exists? file_path - self.class.reindex(:force_rebuild) - redirect url("/") + + serialize_update do + File.delete file_path if File.exists? file_path + self.class.reindex(:force_rebuild) + redirect url("/") + end + end post '/upload' do - unless params[:file] && params[:file][:filename] && (tmpfile = params[:file][:tempfile]) + if params[:file] && params[:file][:filename] && (tmpfile = params[:file][:tempfile]) + serialize_update do + handle_incoming_gem(Geminabox::IncomingGem.new(tmpfile)) + end + else @error = "No file selected" halt [400, erb(:upload)] end - handle_incoming_gem(Geminabox::IncomingGem.new(tmpfile)) end post '/api/v1/gems' do begin - handle_incoming_gem(Geminabox::IncomingGem.new(request.body)) + serialize_update do + handle_incoming_gem(Geminabox::IncomingGem.new(request.body)) + end rescue Object => o File.open "/tmp/debug.txt", "a" do |io| io.puts o, o.backtrace end end end private + def serialize_update(&block) + with_lock(&block) + rescue AlreadyLocked + halt 503, { 'Retry-After' => settings.retry_interval }, 'Repository lock is held by another process' + end + + def with_lock + file_class.open(settings.lockfile, File::RDWR | File::CREAT) do |f| + raise AlreadyLocked unless f.flock(File::LOCK_EX | File::LOCK_NB) + yield + end + end + + # This method provides a test hook, as stubbing File is painful... + def file_class + File + end + def handle_incoming_gem(gem) begin GemStore.create(gem, params[:overwrite]) rescue GemStoreError => error error_response error.code, error.reason @@ -153,10 +188,10 @@ redirect url("/") end end def api_request? - request.accept.first != "text/html" + request.accept.first.to_s != "text/html" end def error_response(code, message) halt [code, message] if api_request? html = <<HTML