require 'reap/utilities/shellutils' require 'reap/utilities/fileutils' module Reap class Subversion include Utilities::ShellUtils include Utilities::FileUtils # Project name (for repository). attr_accessor :project # Current version of project. attr_accessor :version # Developers URL to repository. Defaults to Rubyforge address. attr_accessor :repository # Username. Defaults to ENV['RUBYFORGE_USERNAME']. attr_accessor :username # The URL protocol to use. Defaults to "svn+ssh". attr_accessor :protocol # Prefix to use on tag folder. Default is no prefix. attr_accessor :prefix # Optional commit message. This is intended for commandline # usage. (Use -m for shorthand). attr_accessor :message # Directory to store tags. Defaults to tags/. attr_accessor :tagpath # Directory to store branches. Defaults to branches/. attr_accessor :branchpath # Operate in dryrun mode. attr_accessor :dryrun # New Subversion object. # # TODO: Perhaps format prefix, like: # prefix = prefix + '_' if prefix && prefix !~ /[_-]$/ def initialize(options={}) options.each do |k,v| send("#{k}=", v) if respond_to?("#{k}=") end # defaults are for rubyforge @repository ||= "rubyforge.org/var/svn/#{project}" @username ||= ENV['RUBYFORGE_USERNAME'] @protocol ||= "svn+ssh" @tagpath ||= "tags" @branchpath ||= "branches" if i = @repository.index('//') @repository = @repository[i+2..-1] end end # Developer domain is "username@repository". def developer_domain "#{username}@#{repository}" end # Branch current version. # # message Optional commit message. This is intended for commandline # usage. (Use -m for shorthand). # def branch(options={}) msg = options['message'] || options['m'] name = "#{prefix}#{version}" path = branchpath.to_s if path == '.' or path.empty? url = "#{protocol}://" + File.join(developer_domain, name) else url = "#{protocol}://" + File.join(developer_domain, path, name) end case ask("Branch: #{url} ? [yN]").strip.downcase when 'y', 'yes' #sh "svn copy #{protocol}://#{username}@#{repository}/trunk #{url}" sh "svn copy . #{url}" end end # Tag current version. # # message Optional commit message. This is intended for commandline # usage. (Use -m for shorthand). # def tag(options={}) msg = options['message'] || options['m'] name = "#{prefix}#{version}" path = tagpath.to_s if path == '.' or path.empty? url = "#{protocol}://" + File.join(developer_domain, name) else url = "#{protocol}://" + File.join(developer_domain, path, name) end case ask("Tag: #{url} ? [yN]").strip.downcase when 'y', 'yes' #sh "svn copy #{protocol}://#{username}@#{repository}/trunk #{url}" sh "svn copy . #{url}" end end # Create a change log. # # change File name to store rdoc formated changelog. Default is 'changelog.txt'. # # output Path to store rdoc formated changelog. Default is 'log'. # # xmlchange File name to store XML formated changelog. Default is 'changelog.xml'. # This also creates a file, if needed, by the same name but with .xsl extension. # # xmloutput Path to store XML-formated changelog. Default is 'doc/log'. # # If change or xmlchange contain a path separator ('/', not '\'), then it is assumed they # provide their own path and the output fields will not be used. This allows you to set # +change+ to "./CHANGES" for instance, without effecting the location of other logs. # You can also set change or xmlchange to 'false' to supress creation altogether. # # TODO: Allow for a way to dump the text-based Changelog to standard out. "$stdout" as the filename? # TODO: How to apply naming policy from here? def log(file=nil) #txtlog = file #options['change'] || 'changelog.txt' #txtdir = options['output'] || 'log' #xmllog = options['xmlchange'] || 'changelog.xml' #xmldir = options['xmloutput'] || 'doc/log' #txtlog = File.join(txtdir, txtlog) unless xmllog.include?('/') #xmllog = File.join(xmldir, xmllog) unless xmllog.include?('/') text = changelog_text if dryrun? puts "svn log > #{file}" elsif file mkdir_p(File.dirname(file)) File.open(file, 'w'){|f| f << text } puts "Updated #{file}" else puts text end end # def log_xml(file) #xmldir = options['xmloutput'] || 'doc/log' xslfile = file.chomp(File.extname(file)) + '.xsl' log_xsl(xslfile) text = changelog_xml i = text.index("?>\n") text.insert(i+2, "\n" + %[]) if dryrun? puts "svn log --xml > #{file}" elsif file mkdir_p(File.dirname(file)) File.open(file, 'w'){ |f| f << text } puts "Updated #{file}" else puts text end end private def changelog_xml @changelog_xml ||= `svn log --xml` end def changelog_text @changelog_text ||= log_format(log_organize(log_parse(changelog_xml))) end # Parse xml log into rdoc markup. def log_parse(xml) require "rexml/document" changes = [] doc = REXML::Document.new(xml) doc.root.elements.each do |element| revision = element.attributes["revision"] author = element.elements["author"].text date = element.elements["date"].text message = element.elements["msg"].text revision = revision.strip author = author.strip date = Date.parse(date) message = message.strip changes << [ date, author, revision, message ] end changes end # def log_organize(changes) mapped = {} changes.each do |date, who, rev, text| mapped[[date, who]] ||= [] mapped[[date, who]] << [text, rev] end sorted = [] mapped.each do |(date, who), entries| sorted << [ date, who, entries ] end sorted.sort{ |a,b| b[0] <=> a[0] } end # def log_format(sorted) string = "= Subversion Changelog\n\n" sorted.each do |date, who, entries| string << "== #{date} #{who}\n\n" # no email :( entries.each do |entry| string << "* #{entry.first} (#{entry.last})\n" end string << "\n" end string end # def log_xsl(xslfile) if force? or not File.exist?(xslfile) mkdir_p(File.dirname(xslfile)) if dryrun? puts "touch #{xslfile}" else File.open(xslfile, 'w') do |f| f << DEFAULT_LOG_XSL end end end end DEFAULT_LOG_XSL = <<-END Changelog

Changelog

END end end =begin # def svn_repository_configuration(options, *entries) entries << 'svn' options = configure_options(options, *entries) options['repository'] ||= metadata.repository options['protocol'] ||= 'svn+ssh' options['message'] ||= options.delete('m') options['tagpath'] ||= 'tags' unless repository rubyforge = configuration['rubyforge'] || {} projectname = rubyforge['project'] || metadata.name options['repository'] = "rubyforge.org/var/svn/#{projectname}" end if i = options['repository'].index('//') options['repository'] = options['repository'][i+2..-1] end if /rubyforge.org/i =~ options['repository'] options['username'] ||= ENV['RUBYFORGE_USERNAME'] end return options end =end