require "enumerator" require 'reap/task' require "reap/vendor/http-access2" # ___ _ _____ _ # | _ \___| |___ __ _ ___ ___ |_ _|_ _ __| |__ # | / -_) / -_) _` (_- [:gem] do # Rake::XForge::Release.new(project) {} # end # # The previous example will use defaults where it can. It will prompt # you for your xForge user name and password before it uploads all # gems under the pkg folder and creates a RubyForge release. # # While defaults are nice, you may want a little more control. You can # specify additional attributes: # # * #user_name # * #password # * #changes_file # * #version # * #files # * #release_name # * #release_notes # * #release_changes # * #package_name # # Example: # project = MetaProject::Project::XForge::RubyForge.new('xforge') # task :release => [:gem] do # release_files = FileList[ 'pkg/*.gem', 'CHANGES' ] # # Rake::XForge::Release.new(project) do |xf| # # Never hardcode user name and password in the Rakefile! # xf.user_name = ENV['RUBYFORGE_USER'] # xf.password = ENV['RUBYFORGE_PASSWORD'] # xf.files = release_files.to_a # xf.release_name = "XForge 0.1" # end # # This can be invoked from the command line: # # rake release RUBYFORGE_USER=myuser \ # RUBYFORGE_PASSWORD=mypassword # # If you don't like blocks, you can do like this: # # project = MetaProject::Project::XForge::RubyForge.new('xforge') # task :release => [:gem] do # xf = Rake::XForge::Release.new(project) # ... # Set additional attributes # xf.execute # end class Reap::Release < Reap::Task section_required true task_desc "Release distribution files." task_help %{ reap release Release distribution to Rubyforge, or other GForge based service. host URL of host service username Username of host service project Project name at host package Package name groupid Group id number architecture Architecture (Any, i386, PPC, etc.) release Release name (default is "%s") private Private release? changelog Change log file notelog Release notes file dir Distribution directory exclude Distribution types to exclude The release option can be a template by using %s in the string. The version number of your project will be sub'd in for the %s. This saves you from having to update the release name before every release. } alias_method :rel, :task def run # setup rel.host ||= 'rubyforge.org' rel.host.chomp!('/') rel.username ||= master.rubyforge.username #rel.password ||= master.rubyforge.password rel.project ||= master.rubyforge.project || master.name rel.package ||= master.rubyforge.package #rel.packageid ||= master.rubyforge.packageid rel.groupid ||= master.rubyforge.groupid rel.private ||= false rel.processor ||= 'Any' rel.date ||= Time::now.strftime('%Y-%m-%d %H:%M') rel.exclude ||= [] rel.exclude << 'tar.gz' if rel.exclude.include?( 'tgz' ) rel.exclude << 'tgz' if rel.exclude.include?( 'tar.gz' ) rel.exclude << 'tar.bz2' if rel.exclude.include?( 'tbz' ) rel.exclude << 'tbz' if rel.exclude.include?( 'tar.bz2' ) rel.cookie_jar ||= File::join(File::expand_path("~"), ".rubyforge.cookie_jar") # do not inherit these rel.dir = section.dir || master.project.dir rel.release = (section.release || "%s") % (master.version || rel.date) # verify abort "missing field -- package" unless rel.package #abort "missing field -- packageid" unless rel.packageid abort "missing field -- groupid" unless rel.groupid # add more... # case rel.host # when 'rubyforge', 'rubyforge.org' # else # puts %{Unrecognized release host '#{rel.host}'. Skipped.} # skip = true # end # release puts "Reap is preparing release ..." rtypes = [ 'tgz', 'tbz', 'tar.gz', 'tar.bz2', 'deb', 'gem', 'ebuild' ] rtypes -= rel.exclude rtypes = rtypes.collect { |rt| Regexp.escape( rt ) } re_rtypes = Regexp.new( '[.](' << rtypes.join('|') << ')$' ) dir = File.join( rel.dir, "#{rel.name}-#{rel.version}" ) files = Dir.entries(dir).select { |f| f =~ re_rtypes or f == 'PKGBUILD' } files = files.collect { |f| File.join( dir, f ) } if files.empty? puts "No files to release at #{dir}." exit -1 end # ask for password unless $PASSWORD print "Password for #{rel.username}: " until passwd = $stdin.gets.strip ; sleep 1 ; end @password = passwd else @password = $PASSWORD end login { unless package? puts "Package (#{rel.package}) does not exist." puts "Use -f option to force package creation." exit 0 unless $FORCE create_package puts "Created package -- #{rel.package}" end if release? unless $FORCE puts "Release (#{rel.release}) already exists." puts "Use -f option to force re-release." exit 0 end files.each do |f| remove_file( f ) if file?( f ) puts "Removed old file -- #{ f }" add_file( f ) puts "Added new file -- #{ f }" end else add_release( files.first ) unless release? puts "Release creation failed -- #{rel.release}" exit -1 end puts "Added release -- #{ rel.release }" puts "Added file -- #{ files.first }" files[1..-1].each do |f| add_file( f ) puts "Added file -- #{ f }" end end } puts "Release complete!" end private FILETYPES = { ".deb" => 1000, ".rpm" => 2000, ".zip" => 3000, ".bz2" => 3100, ".gz" => 3110, ".src.zip" => 5000, ".src.bz2" => 5010, ".src.gz" => 5020, ".src.rpm" => 5100, ".src" => 5900, ".jpg" => 8000, ".txt" => 8100, ".text" => 8100, ".htm" => 8200, ".html" => 8200, ".pdf" => 8300, ".oth" => 9999, ".ebuild" => 1300, ".exe" => 1100, ".dmg" => 1200, ".tar.gz" => 5000, ".tgz" => 5000, ".gem" => 1400, ".pgp" => 8150, ".sig" => 8150, } PROCESSORS = { "i386" => 1000, "IA64" => 6000, "Alpha" => 7000, "Any" => 8000, "PPC" => 2000, "MIPS" => 3000, "Sparc" => 4000, "UltraSparc" => 5000, "Other" => 9999, } # login def login( &block ) # login page = "/account/login.php" method = "post_content" form = { "return_to" => "", "form_loginname" => rel.username, "form_pw" => @password, "login" => "Login" } http_transaction( page, method, form ) # do whatever block.call # logout page = "/account/logout.php" method = "post_content" form = {} http_transaction( page, method, form ) end # Package exists? def package? page = "/frs/" method = "post_content" form = { "group_id" => rel.groupid } scrape = http_transaction( page, method, form ) restr = '' restr << Regexp.escape( rel.package ) restr << '\s*' restr << Regexp.escape( %{ rel.groupid, "package_name" => rel.package, "func" => "add_package", "is_public" => (rel.private ? 0 : 1), "submit" => "Create This Package" } http_transaction( page, method, form ) end # Release exits? def release? page = "/frs/admin/showreleases.php" method = "post_content" form = { "package_id" => rel.packageid, "group_id" => rel.groupid } scrape = http_transaction( page, method, form ) restr = '' restr << Regexp.escape( %{"editrelease.php?group_id=#{rel.groupid}} ) restr << Regexp.escape( %{&package_id=#{rel.packageid}} ) restr << Regexp.escape( %{&release_id=} ) restr << '(\d+)' restr << Regexp.escape( %{">#{rel.release}} ) re = Regexp.new( restr ) md = re.match( scrape ) if md rel.releaseid = md[1] end end # Add a new release. def add_release( userfile ) page = "/frs/admin/qrs.php" method = "post_content" type_id = rel.type || userfile[%r|\.[^\./]+$|] type_id = FILETYPES[type_id] proc_id = PROCESSORS[rel.processor] # how to use these? notes = rel.notes ? rel.notes : ( rel.notelog ? open(rel.notelog) : nil ) changes = rel.changes ? rel.changes : ( rel.changelog ? open(rel.changelog) : nil ) userfile = open(userfile) preformatted = '1' form = { "group_id" => rel.groupid, "package_id" => rel.packageid, "release_name" => rel.release, "release_date" => rel.date, "type_id" => type_id, "processor_id" => proc_id, "preformatted" => preformatted, "userfile" => userfile, "submit" => "Release File" } boundary = Array::new(8){ "%2.2d" % rand(42) }.join('__') extheader = { 'content-type'=>"multipart/form-data; boundary=___#{ boundary }___" } http_transaction( page, method, form, extheader ) end # Does file exist? # Note this is a little bit fragile. # If two releases have the same exact file name in them # there could be a problem --that's probably not likely, # maybe even impossible, but as of yet, I can't yet rule it out. def file?( file ) page = "/frs/" method = "post_content" form = { "group_id" => rel.groupid } scrape = http_transaction( page, method, form ) restr = '' restr << Regexp.escape( rel.package ) restr << '\s*' restr << Regexp.escape( %{ rel.groupid, "package_id" => rel.packageid, "release_id" => rel.releaseid, "file_id" => rel.fileid, "step3" => "Delete File", "im_sure" => '1', "submit" => "Delete File " } http_transaction( page, method, form ) end # Add file to release. def add_file( userfile ) page = '/frs/admin/editrelease.php' method = "post_content" type_id = rel.type || userfile[%r|\.[^\./]+$|] type_id = FILETYPES[type_id] proc_id = PROCESSORS[rel.processor] userfile = open( userfile ) form = { "group_id" => rel.groupid, "package_id" => rel.packageid, "release_id" => rel.releaseid, "step2" => '1', "userfile" => userfile, "type_id" => type_id, "processor_id" => proc_id, "submit" => "Add This File" } boundary = Array::new(8){ "%2.2d" % rand(42) }.join('__') extheader = { 'content-type'=>"multipart/form-data; boundary=___#{ boundary }___" } http_transaction( page, method, form, extheader ) end # http transaction def http_transaction( page, method, form, extheader={} ) client = HTTPAccess2::Client::new ENV['HTTP_PROXY'] client.debug_dev = STDERR if ENV['DEBUG'] client.set_cookie_store( rel.cookie_jar ) # fixes http-access2 bug client.redirect_uri_callback = lambda do |res| page = res.header['location'].first page = page =~ %r/http/ ? page : "http://#{ rel.host }/#{ page }" page end page.sub!(/^\//, '') uri = "http://#{ rel.host }/#{ page }" response = client.send method, uri, form, extheader client.save_cookie_store return response end end # fixes http-access2 bug BEGIN { require "reap/vendor/http-access2" module WebAgent::CookieUtils def domain_match(host, domain) case domain when /\d+\.\d+\.\d+\.\d+/ return (host == domain) when '.' return true when /^\./ #return tail_match?(domain, host) return tail_match?(host, domain) else return (host == domain) end end end }