├── tmp └── .gitkeep ├── debian ├── compat ├── changelog ├── control ├── postinst ├── copyright └── rules ├── public ├── .htaccess └── favicon.ico ├── config.ru ├── Gemfile ├── .gitignore ├── Gemfile.lock ├── views ├── 404.erb └── index.erb ├── README.rdoc └── rubygems_proxy.rb /tmp/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 7 2 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | AddType application/octet-stream .gem -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | require "./rubygems_proxy" 2 | run RubygemsProxy 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://gems.simplesideias.com.br" 2 | gem "rack", ">= 1.0.0" 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.log 3 | restart.txt 4 | public/gems 5 | public/quick 6 | *.gz -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnando/rubygems_proxy/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | rubygems-proxy (1.0) testing; urgency=low 2 | * initial package 3 | -- Bricklayer Builder Tue, 27 Jul 2010 16:29:54 -0300 4 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://gems.simplesideias.com.br/ 3 | specs: 4 | rack (1.2.2) 5 | 6 | PLATFORMS 7 | ruby 8 | 9 | DEPENDENCIES 10 | rack (>= 1.0.0) 11 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: rubygems-proxy 2 | Section: web 3 | Priority: extra 4 | Maintainer: Nando Vieira 5 | Standards-Version: 1.0 6 | Homepage: http://github.com/fnando/rubygems_proxy 7 | 8 | Package: rubygems-proxy 9 | Architecture: any 10 | Depends: ruby1.9.1, libapache2-mod-passenger, librack-ruby1.9.1 11 | Description: Rubygems proxy 12 | Receive and cache gems from rubygems.org 13 | -------------------------------------------------------------------------------- /views/404.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | d8888b.  .d8b.   d888b  d88888b      d8b   db  .d88b.  d888888b      d88888b  .d88b.  db    db d8b   db d8888b.
11 | 88  `8D d8' `8b 88' Y8b 88'          888o  88 .8P  Y8. `~~88~~'      88'     .8P  Y8. 88    88 888o  88 88  `8D
12 | 88oodD' 88ooo88 88      88ooooo      88V8o 88 88    88    88         88ooo   88    88 88    88 88V8o 88 88   88
13 | 88~~~   88~~~88 88  ooo 88~~~~~      88 V8o88 88    88    88         88~~~   88    88 88    88 88 V8o88 88   88
14 | 88      88   88 88. ~8~ 88.          88  V888 `8b  d8'    88         88      `8b  d8' 88b  d88 88  V888 88  .8D
15 | 88      YP   YP  Y888P  Y88888P      VP   V8P  `Y88P'     YP         YP       `Y88P'  ~Y8888P' VP   V8P Y8888D'
16 | 		
17 | 18 | 19 | -------------------------------------------------------------------------------- /debian/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # postinst script for #PACKAGE# 3 | # 4 | # see: dh_installdeb(1) 5 | 6 | set -e 7 | 8 | # summary of how this script can be called: 9 | # * `configure' 10 | # * `abort-upgrade' 11 | # * `abort-remove' `in-favour' 12 | # 13 | # * `abort-remove' 14 | # * `abort-deconfigure' `in-favour' 15 | # `removing' 16 | # 17 | # for details, see http://www.debian.org/doc/debian-policy/ or 18 | # the debian-policy package 19 | 20 | 21 | case "$1" in 22 | configure) 23 | chown -R www-data /var/www/rubygems-proxy/ 24 | chmod 444 /var/www/rubygems-proxy/public/.htaccess 25 | ;; 26 | 27 | abort-upgrade|abort-remove|abort-deconfigure) 28 | ;; 29 | 30 | *) 31 | echo "postinst called with unknown argument \`$1'" >&2 32 | exit 1 33 | ;; 34 | esac 35 | 36 | # dh_installdeb will replace this with shell code automatically 37 | # generated by other debhelper scripts. 38 | 39 | #DEBHELPER# 40 | 41 | exit 0 42 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | This work was packaged for Debian by: 2 | 3 | root on Mon, 14 Jun 2010 11:51:41 -0300 4 | 5 | It was downloaded from: 6 | 7 | 8 | 9 | Upstream Author(s): 10 | 11 | 12 | 13 | 14 | Copyright: 15 | 16 | 17 | 18 | 19 | License: 20 | 21 | 22 | 23 | The Debian packaging is: 24 | 25 | Copyright (C) 2010 root 26 | 27 | # Please chose a license for your packaging work. If the program you package 28 | # uses a mainstream license, using the same license is the safest choice. 29 | # Please avoid to pick license terms that are more restrictive than the 30 | # packaged work, as it may make Debian's contributions unacceptable upstream. 31 | # If you just want it to be GPL version 3, leave the following lines in. 32 | 33 | and is licensed under the GPL version 3, 34 | see "/usr/share/common-licenses/GPL-3". 35 | 36 | # Please also look if there are files or directories which have a 37 | # different copyright/license attached and list them here. 38 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # Sample debian/rules that uses debhelper. 3 | # This file is public domain software, originally written by Joey Hess. 4 | # 5 | # This version is for packages that are architecture dependent. 6 | 7 | # Uncomment this to turn on verbose mode. 8 | #export DH_VERBOSE=1 9 | DEBIAN_PATH=debian/rubygems-proxy/var/www/rubygems-proxy 10 | 11 | build: build-stamp 12 | build-stamp: 13 | dh_testdir 14 | 15 | # Add here commands to compile the package. 16 | #$(MAKE) 17 | 18 | clean: 19 | dh_testdir 20 | dh_testroot 21 | rm -f build-stamp 22 | 23 | # Add here commands to clean up after the build process. 24 | #$(MAKE) clean 25 | #$(MAKE) distclean 26 | 27 | install: build 28 | dh_testdir 29 | dh_testroot 30 | dh_prep 31 | dh_installdirs 32 | 33 | 34 | # Add here commands to install the package into debian/ 35 | mkdir -p $(DEBIAN_PATH) 36 | cp -R public tmp views config.ru rubygems_proxy.rb $(DEBIAN_PATH) 37 | 38 | # Build architecture-independent files here. 39 | binary-indep: build install 40 | # We have nothing to do by default. 41 | 42 | # Build architecture-dependent files here. 43 | binary-arch: build install 44 | dh_testdir 45 | dh_testroot 46 | dh_installchangelogs 47 | # dh_installdocs 48 | # dh_installexamples 49 | dh_install 50 | # dh_installmenu 51 | # dh_installdebconf 52 | # dh_installlogrotate 53 | # dh_installemacsen 54 | # dh_installcatalogs 55 | # dh_installpam 56 | # dh_installmime 57 | dh_installinit 58 | # dh_installcron 59 | # dh_installinfo 60 | # dh_installwm 61 | # dh_installudev 62 | # dh_lintian 63 | # dh_bugfiles 64 | # dh_undocumented 65 | dh_installman 66 | dh_link 67 | dh_strip 68 | dh_compress 69 | dh_fixperms 70 | # dh_perl 71 | # dh_makeshlibs 72 | dh_installdeb 73 | # dh_shlibdeps 74 | dh_gencontrol 75 | dh_md5sums 76 | dh_builddeb 77 | 78 | binary: binary-indep binary-arch 79 | .PHONY: build clean binary-indep binary-arch binary install 80 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = Rubygems Proxy 2 | 3 | == Setting up 4 | 5 | Clone this repository. 6 | 7 | $ git clone git://github.com/fnando/rubygems_proxy.git 8 | 9 | Set up Apache + Passenger or something similar. 10 | 11 | 12 | ServerName gems.simplesideias.com.br 13 | DocumentRoot /home/fnando/gems.simplesideias.com.br/public 14 | 15 | 16 | AllowOverride all 17 | Options -MultiViews 18 | 19 | 20 | 21 | Remember to chmod public/.htaccess: 22 | 23 | $ chmod 644 public/.htaccess 24 | 25 | Set up your client to use this new Rubygems proxy. 26 | Add the following line to your Gemfile: 27 | 28 | source "http://gems.simplesideias.com.br" 29 | 30 | And, finally, add this source to your ~/.gemrc. 31 | 32 | --- 33 | :sources: 34 | - http://gems.simplesideias.com.br/ 35 | gem: --no-rdoc --no-ri 36 | 37 | The specs.4.8.gz is cached for 24 hours. This file contains all gems/versions 38 | and is over 3MB. You can invalidate this cache by running 39 | curl -X DELETE http://gems.simplesideias.com.br. 40 | 41 | = Maintainer 42 | 43 | * Nando Vieira - http://nandovieira.com.br 44 | 45 | == License 46 | 47 | (The MIT License) 48 | 49 | Permission is hereby granted, free of charge, to any person obtaining 50 | a copy of this software and associated documentation files (the 51 | 'Software'), to deal in the Software without restriction, including 52 | without limitation the rights to use, copy, modify, merge, publish, 53 | distribute, sublicense, and/or sell copies of the Software, and to 54 | permit persons to whom the Software is furnished to do so, subject to 55 | the following conditions: 56 | 57 | The above copyright notice and this permission notice shall be 58 | included in all copies or substantial portions of the Software. 59 | 60 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 61 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 62 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 63 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 64 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 65 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 66 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 67 | -------------------------------------------------------------------------------- /views/index.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Rubygems Proxy/Mirror by Simples Ideias 6 | 21 | 22 | 23 | 24 |
25 |

Rubygems Proxy/Mirror

26 |

Rack app for caching RubyGems files. Very useful in our build server that sometimes fails due to our network or rubygems.org timeout.

27 | 28 |

Setup your client

29 |

Add the following line to your Gemfile:

30 |
source "<%= server_url %>"
31 |

And change your ~/.gemrc to something like this:

32 |
---
33 | :sources:
34 | - <%= server_url %>
35 | gem: --no-rdoc --no-ri
36 | 37 |

38 | Want to implement your own mirror? Get the 39 | one-file Rack app at Github. 40 |

41 | 42 |

Available gems

43 |

44 | <%= gem_list.size %> gems cached so far! 45 |

46 |
    47 | <% gems = grouped_gems; names = gems.keys.sort %> 48 | <% names.each do |name| %> 49 |
  • 50 | <%= name %> 51 | 52 |
      53 | <% gems[name].sort.each do |version| %> 54 |
    • 55 | <%= version %> 56 |
    • 57 | <% end %> 58 |
    59 |
  • 60 | <% end %> 61 |
62 |
63 | 64 | Fork me on GitHub 65 | 66 | -------------------------------------------------------------------------------- /rubygems_proxy.rb: -------------------------------------------------------------------------------- 1 | require "open-uri" 2 | require "fileutils" 3 | require "logger" 4 | require "erb" 5 | 6 | class RubygemsProxy 7 | attr_reader :env 8 | 9 | def self.call(env) 10 | new(env).run 11 | end 12 | 13 | def initialize(env) 14 | @env = env 15 | logger.level = Logger::INFO 16 | end 17 | 18 | def run 19 | logger.info "#{env["REQUEST_METHOD"]} #{env["PATH_INFO"]}" 20 | 21 | return update_specs if env["REQUEST_METHOD"] == "DELETE" 22 | 23 | case env["PATH_INFO"] 24 | when "/" 25 | [200, {"Content-Type" => "text/html"}, [erb(:index)]] 26 | else 27 | [200, {"Content-Type" => "application/octet-stream"}, [contents]] 28 | end 29 | rescue Exception 30 | [200, {"Content-Type" => "text/html"}, [erb(404)]] 31 | end 32 | 33 | private 34 | def erb(view) 35 | ERB.new(template(view)).result(binding) 36 | end 37 | 38 | def server_url 39 | env["rack.url_scheme"] + "://" + File.join(env["SERVER_NAME"], env["PATH_INFO"]) 40 | end 41 | 42 | def rubygems_url(gemname) 43 | "http://rubygems.org/gems/%s" % Rack::Utils.escape(gemname) 44 | end 45 | 46 | def gem_url(name, version) 47 | File.join(server_url, "gems", Rack::Utils.escape("#{name}-#{version}.gem")) 48 | end 49 | 50 | def gem_list 51 | Dir[File.dirname(__FILE__) + "/public/gems/**/*.gem"] 52 | end 53 | 54 | def grouped_gems 55 | gem_list.inject({}) do |buffer, file| 56 | basename = File.basename(file) 57 | parts = basename.gsub(/\.gem/, "").split("-") 58 | version = parts.pop 59 | name = parts.join("-") 60 | 61 | buffer[name] ||= [] 62 | buffer[name] << version 63 | buffer 64 | end 65 | end 66 | 67 | def template(name) 68 | @templates ||= {} 69 | @templates[name] ||= File.read(File.dirname(__FILE__) + "/views/#{name}.erb") 70 | end 71 | 72 | def root_dir 73 | File.expand_path "..", __FILE__ 74 | end 75 | 76 | def logger 77 | @logger ||= Logger.new("#{root_dir}/tmp/server.log", 10, 1024000) 78 | end 79 | 80 | def cache_dir 81 | "#{root_dir}/public" 82 | end 83 | 84 | def contents 85 | if File.directory?(filepath) 86 | erb(404) 87 | elsif cached? 88 | logger.info "Read from cache: #{filepath}" 89 | open(filepath).read 90 | else 91 | logger.info "Read from interwebz: #{url}" 92 | open(url).read.tap {|content| save(content)} 93 | end 94 | rescue Exception => error 95 | # Just try to load from file if something goes wrong. 96 | # This includes HTTP timeout, or something. 97 | # If it fails again, we won't have any files anyway! 98 | logger.error "Error: #{error.class} => #{error.message}" 99 | open(filepath).read 100 | end 101 | 102 | def save(contents) 103 | FileUtils.mkdir_p File.dirname(filepath) 104 | File.open(filepath, "wb") {|handler| handler << contents} 105 | end 106 | 107 | def cached? 108 | case File.basename(filepath) 109 | when /^specs\./ 110 | File.exist?(filepath) && (Time.now - File.mtime(filepath)).to_i < 84600 111 | when /\.gz$/ 112 | false 113 | else 114 | File.file?(filepath) 115 | end 116 | end 117 | 118 | def specs? 119 | env["PATH_INFO"] =~ /specs\..+\.gz$/ 120 | end 121 | 122 | def filepath 123 | if specs? 124 | File.join(root_dir, env["PATH_INFO"]) 125 | else 126 | File.join(cache_dir, env["PATH_INFO"]) 127 | end 128 | end 129 | 130 | def url 131 | File.join("http://rubygems.org", env["PATH_INFO"]) 132 | end 133 | 134 | def update_specs 135 | Dir[File.dirname(__FILE__) + "/*.gz"].each {|file| File.unlink(file)} 136 | [200, {"Content-Type" => "text/plain"}, [""]] 137 | end 138 | end 139 | --------------------------------------------------------------------------------