gemサイトのローカルミラーの正しい作り方

とあるインターネットにつなげないサーバでRails環境をセットアップする必要があったので、作業用WindowsPCにgem配布サイト(http://rubygems.org)のローカルミラーを作成することにした。
ローカルミラーの作成方法はネット検索でいくつか書かれているのを見つけたが、どれも成功しなかったので正しい作成方法を記載しておく。

まずミラーするサイトと保存するディレクトリを定義する設定ファイルを作成する。

[HOME]/.gemmirrorrc に作成。fromにサイトURL、toに保存先のディレクトリを書く。保存先ディレクトリは予め作成しておくこと。
[HOME]WindowsではC:\Document and Settings\[user]となる。

---
- from: http://rubygems.org/
  to: C:\[path_to_download]

次にミラーするコマンド、gem mirrorを実行。gem help mirrorでコマンドのヘルプを見ると--config-fileオプションでgemmirrorrcファイルを指定できるらしくなっているけど、これはうまく動かなかったので上の手順のようにちゃんとホームディレクトリに作成するように。

> gem mirror

2,3日実行しっぱなしにしておくとすべてのgemファイルが[path_to_download]/gemsにダウンロードされ、gem mirrorコマンドは完了する。
しかし必要なファイルはこれだけではない。他の必要なファイルは手動でダウンロードしなければならなかった。

まず、[path_to_download]直下に下記ファイルをダウンロードした。

次に[path_to_download]/quickディレクトリを作成し下記ファイルをダウンロードした。

そして[path_to_download]/quick/quick/Marshal.4.8 ディレクトリを作成しここにhttp://rubygems.org/quick/Marshal.4.8/以下のgemspecファイルをすべてダウンロードする必要がある。

数が多いので一つ一つ手動でダウンロードしていくのは無理、適当にスクリプトを書いて自動化しないと現実的でない。
gemspecファイルのファイル名はどうやら[gemファイル名から拡張子抜いたもの].gemspec.rzというようになるようだ。
たとえばgemファイルが rubyzip2-2.0.0.gemならgemspecファイルはrubyzip2-2.0.0.gemspec.rzとなる。

適当に下記のようにスクリプトを書いてみてダウンロードさせてみた。[path_to_download]直下に保存して実行。これもかなり時間がかかる。

GEMSITE_DOMAIN='rubygems.org'
GEMSITE_PORT=80
#--
require 'net/http'
def fetch(uri_str)
  response = Net::HTTP.get_response(URI.parse(uri_str))
  case response
  when Net::HTTPSuccess     then response
  when Net::HTTPRedirection then fetch(response['Location'])
  else
    return nil
  end
end
Dir.foreach('gems') do |gem_filename|
  next if gem_filename == '.' || gem_filename == '..'
  gem_name = File.basename gem_filename, '.gem'
  gemspec_filename = gem_name + '.gemspec.rz'
  url =  "http://#{GEMSITE_DOMAIN}:#{GEMSITE_PORT}" + '/quick/Marshal.4.8/' + gemspec_filename
  puts "Downloading..#{url}"  
  res = fetch(url)
  if res.nil?
    puts " Error: #{url}"
    next
  end
  File.open('quick/Marshal.4.8/' + gemspec_filename, 'wb') do |io|
    io.write(res.body)
  end
end

すべてダウンロードして10.9GBになる。

あとは[path_to_download]をサイトのドキュメントルートとするようにhttpdを立てて、gemをインストールするクライアントの方からは下記のように--sourceオプションでソースサイトを指定する。

> gem install rubyzip --source http://your.rubygems.mirror

--sourceオプションをつけるのが面倒ならば下記のように[HOME]/.gemrcを作成するとデフォルトで設定したミラーサイトにアクセスしてくれる。

:sources:
- http://your.rubygems.mirror

ちなみに関係無い話だが、http://rubygems.orgではgemファイルはAWSのCloudFrontに、gemspecファイルはS3(CloudFrontではない)に置かれていてrubygems.orgからGETしようとすると上記のAWSの方にリダイレクトされた。