#! /usr/bin/env ruby -w require 'enumerator' require 'fileutils' require 'http-access2' require 'yaml' require 'open-uri' $TESTING = false unless defined? $TESTING # HACK to fix http-access2 cookie selection bug class WebAgent # :nodoc: all module CookieUtils alias :old_domain_match :domain_match def domain_match(host, domain) case domain when /^\./ return tail_match?(host, domain) # was (domain, host) else return old_domain_match(host, domain) end end end end class RubyForge # :stopdoc: VERSION = '0.4.5' HOME = ENV["HOME"] || ENV["HOMEPATH"] || File::expand_path("~") RUBYFORGE_D = File::join HOME, ".rubyforge" CONFIG_F = File::join RUBYFORGE_D, "user-config.yml" COOKIE_F = File::join RUBYFORGE_D, "cookie.dat" # We must use __FILE__ instead of DATA because this is now a library # and DATA is relative to $0, not __FILE__. CONFIG = File.read(__FILE__).split(/__END__/).last.gsub(/#\{(.*)\}/) { eval $1 } # :startdoc: attr_reader :userconfig, :autoconfig def initialize(userconfig=CONFIG_F, opts={}) @userconfig = test(?e, userconfig) ? IO::read(userconfig) : CONFIG @userconfig = YAML.load(@userconfig).merge(opts) dir, file = File.split(userconfig) @autoconfig_path = File.join(dir, file.sub(/^user/, 'auto')) @autoconfig = test(?e, @autoconfig_path) ? YAML.load_file(@autoconfig_path) : YAML.load(CONFIG)["rubyforge"] @autoconfig["type_ids"] = YAML.load(CONFIG)['rubyforge']['type_ids'] @client = nil @uri = URI.parse @userconfig['uri'] raise "no " unless @userconfig["username"] raise "no " unless @userconfig["password"] raise "no " unless @userconfig["cookie_jar"] end def setup FileUtils::mkdir_p RUBYFORGE_D, :mode => 0700 unless test ?d, RUBYFORGE_D test ?e, CONFIG_F and FileUtils::mv CONFIG_F, "#{CONFIG_F}.bak" config = CONFIG[/\A.*(?=^\# AUTOCONFIG)/m] open(CONFIG_F, "w") { |f| f.write config } FileUtils::touch COOKIE_F edit = (ENV["EDITOR"] || ENV["EDIT"] || "vi") + " '#{CONFIG_F}'" system edit or puts "edit '#{CONFIG_F}'" end def save_autoconfig File.open(@autoconfig_path, "w") do |file| YAML.dump @autoconfig, file end end def scrape_config username = @userconfig['username'] %w(group package processor release).each do |type| @autoconfig["#{type}_ids"].clear end puts "Getting #{username}" html = URI.parse("http://rubyforge.org/users/#{username}/index.html").read projects = html.scan(%r%/projects/([^/]+)/%).flatten puts "Fetching #{projects.size} projects" projects.each do |project| next if project == "support" scrape_project(project) end end def scrape_project(project) data = { "group_ids" => {}, "package_ids" => {}, "processor_ids" => Hash.new { |h,k| h[k] = {} }, "release_ids" => Hash.new { |h,k| h[k] = {} }, } puts "Updating #{project}" unless data["group_ids"].has_key? project then html = URI.parse("http://rubyforge.org/projects/#{project}/index.html").read group_id = html[/(frs|tracker|mail)\/\?group_id=\d+/][/\d+/].to_i data["group_ids"][project] = group_id end group_id = data["group_ids"][project] html = URI.parse("http://rubyforge.org/frs/?group_id=#{group_id}").read package = nil html.scan(/

[^<]+|release_id=\d+">[^>]+|filemodule_id=\d+/).each do |s| case s when /

([^<]+)/ then package = $1.strip when /release_id=(\d+)">([^<]+)/ then data["release_ids"][package][$2] = $1.to_i when /filemodule_id=(\d+)/ then data["package_ids"][package] = $1.to_i end end if not data['release_ids'][package].empty? and @autoconfig['processor_ids'].empty? then puts "Fetching processor ids" login html = client.get_content "http://rubyforge.org/frs/admin/qrs.php?package=&group_id=#{group_id}" html =~ /