diff -r 308f7dc97753 -r 143e61e24c08 rake/svn.rb --- a/rake/svn.rb Wed Aug 06 17:38:56 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,328 +0,0 @@ -##################################################################### -### S U B V E R S I O N T A S K S A N D H E L P E R S -##################################################################### - -require 'pp' -require 'yaml' -require 'English' - -# Strftime format for tags/releases -TAG_TIMESTAMP_FORMAT = '%Y%m%d-%H%M%S' -TAG_TIMESTAMP_PATTERN = /\d{4}\d{2}\d{2}-\d{6}/ - -RELEASE_VERSION_PATTERN = /\d+\.\d+\.\d+/ - -DEFAULT_EDITOR = 'vi' -DEFAULT_KEYWORDS = %w[Date Rev Author URL Id] -KEYWORDED_FILEDIRS = %w[applets bin etc lib misc] -KEYWORDED_FILEPATTERN = /^(?:Rakefile|.*\.(?:rb|js|html|template))$/i - -COMMIT_MSG_FILE = 'commit-msg.txt' - -### -### Subversion-specific Helpers -### - -### Return a new tag for the given time -def make_new_tag( time=Time.now ) - return time.strftime( TAG_TIMESTAMP_FORMAT ) -end - - -### Get the subversion information for the current working directory as -### a hash. -def get_svn_info( dir='.' ) - info = IO.read( '|-' ) or exec 'svn', 'info', dir - return YAML.load( info ) # 'svn info' outputs valid YAML! Yay! -end - - -### Get a list of the objects registered with subversion under the specified directory and -### return them as an Array of Pathame objects. -def get_svn_filelist( dir='.' ) - list = IO.read( '|-' ) or exec 'svn', 'st', '-v', '--ignore-externals', dir - - # Split into lines, filter out the unknowns, and grab the filenames as Pathnames - # :FIXME: This will break if we ever put in a file with spaces in its name. This - # will likely be the least of our worries if we do so, however, so it's not worth - # the additional complexity to make it handle that case. If we do need that, there's - # always the --xml output for 'svn st'... - return list.split( $/ ). - reject {|line| line =~ /^\?/ }. - collect {|fn| Pathname(fn[/\S+$/]) } -end - -### Return the URL to the repository root for the specified +dir+. -def get_svn_repo_root( dir='.' ) - info = get_svn_info( dir ) - return info['URL'].sub( %r{/trunk$}, '' ) -end - - -### Return the Subversion URL to the given +dir+. -def get_svn_url( dir='.' ) - info = get_svn_info( dir ) - return info['URL'] -end - - -### Return the path of the specified +dir+ under the svn root of the -### checkout. -def get_svn_path( dir='.' ) - root = get_svn_repo_root( dir ) - url = get_svn_url( dir ) - - return url.sub( root + '/', '' ) -end - - -### Return the keywords for the specified array of +files+ as a Hash keyed by filename. -def get_svn_keyword_map( files ) - cmd = ['svn', 'pg', 'svn:keywords', *files] - - # trace "Executing: svn pg svn:keywords " + files.join(' ') - output = IO.read( '|-' ) or exec( 'svn', 'pg', 'svn:keywords', *files ) - - kwmap = {} - output.split( "\n" ).each do |line| - next if line !~ /\s+-\s+/ - path, keywords = line.split( /\s+-\s+/, 2 ) - kwmap[ path ] = keywords.split - end - - return kwmap -end - - -### Return the latest revision number of the specified +dir+ as an Integer. -def get_svn_rev( dir='.' ) - info = get_svn_info( dir ) - return info['Revision'] -end - - -### Return a list of the entries at the specified Subversion url. If -### no +url+ is specified, it will default to the list in the URL -### corresponding to the current working directory. -def svn_ls( url=nil ) - url ||= get_svn_url() - list = IO.read( '|-' ) or exec 'svn', 'ls', url - - trace 'svn ls of %s: %p' % [url, list] if $trace - - return [] if list.nil? || list.empty? - return list.split( $INPUT_RECORD_SEPARATOR ) -end - - -### Return the URL of the latest timestamp in the tags directory. -def get_latest_svn_timestamp_tag - rooturl = get_svn_repo_root() - tagsurl = rooturl + '/tags' - - tags = svn_ls( tagsurl ).grep( TAG_TIMESTAMP_PATTERN ).sort - return nil if tags.nil? || tags.empty? - return tagsurl + '/' + tags.last -end - - -### Get a subversion diff of the specified targets and return it. If no targets are -### specified, the current directory will be diffed instead. -def get_svn_diff( *targets ) - targets << BASEDIR if targets.empty? - trace "Getting svn diff for targets: %p" % [targets] - log = IO.read( '|-' ) or exec 'svn', 'diff', *(targets.flatten) - - return log -end - - -### Return the URL of the latest timestamp in the tags directory. -def get_latest_release_tag - rooturl = get_svn_repo_root() - releaseurl = rooturl + '/releases' - - tags = svn_ls( releaseurl ).grep( RELEASE_VERSION_PATTERN ).sort_by do |tag| - tag.split('.').collect {|i| Integer(i) } - end - return nil if tags.empty? - - return releaseurl + '/' + tags.last -end - - -### Extract a diff from the specified subversion working +dir+, rewrite its -### file lines as Trac links, and return it. -def make_svn_commit_log( dir='.' ) - editor_prog = ENV['EDITOR'] || ENV['VISUAL'] || DEFAULT_EDITOR - - diff = IO.read( '|-' ) or exec 'svn', 'diff' - fail "No differences." if diff.empty? - - return diff -end - - - -### -### Tasks -### - -desc "Subversion tasks" -namespace :svn do - - desc "Copy the HEAD revision of the current trunk/ to tags/ with a " + - "current timestamp." - task :tag do - svninfo = get_svn_info() - tag = make_new_tag() - svntrunk = svninfo['URL'] - svntagdir = svninfo['URL'].sub( %r{trunk$}, 'tags' ) - svntag = svntagdir + '/' + tag - - desc = "Tagging trunk as #{svntag}" - ask_for_confirmation( desc ) do - msg = prompt_with_default( "Commit log: ", "Tagging for code push" ) - run 'svn', 'cp', '-m', msg, svntrunk, svntag - end - end - - - desc "Copy the most recent tag to releases/#{PKG_VERSION}" - task :release do - last_tag = get_latest_svn_timestamp_tag() - svninfo = get_svn_info() - release = PKG_VERSION - svnrel = svninfo['URL'] + '/releases' - svnrelease = svnrel + '/' + release - - if last_tag.nil? - error "There are no tags in the repository" - fail - end - - releases = svn_ls( svnrel ) - trace "Releases: %p" % [releases] - if releases.include?( release ) - error "Version #{release} already has a branch (#{svnrelease}). Did you mean" + - "to increment the version in #{PKG_VERSION_FROM}?" - fail - else - trace "No #{svnrel} version currently exists" - end - - desc = "Release tag\n #{last_tag}\nto\n #{svnrelease}" - ask_for_confirmation( desc ) do - msg = prompt_with_default( "Commit log: ", "Branching for release" ) - run 'svn', 'cp', '-m', msg, last_tag, svnrelease - end - end - - ### Task for debugging the #get_target_args helper - task :show_targets do - $stdout.puts "Targets from ARGV (%p): %p" % [ARGV, get_target_args()] - end - - - desc "Generate a commit log" - task :commitlog => [COMMIT_MSG_FILE] - - desc "Show the (pre-edited) commit log for the current directory" - task :show_commitlog => [COMMIT_MSG_FILE] do - ask_for_confirmation( "Confirm? " ) do - args = get_target_args() - puts get_svn_diff( *args ) - end - end - - - file COMMIT_MSG_FILE do - args = get_target_args() - diff = get_svn_diff( *args ) - - File.open( COMMIT_MSG_FILE, File::WRONLY|File::EXCL|File::CREAT ) do |fh| - fh.print( diff ) - end - - editor = ENV['EDITOR'] || ENV['VISUAL'] || DEFAULT_EDITOR - system editor, COMMIT_MSG_FILE - unless $?.success? - fail "Editor exited uncleanly." - end - end - - - desc "Update from Subversion" - task :update do - run 'svn', 'up', '--ignore-externals' - end - - - desc "Check in all the changes in your current working copy" - task :checkin => ['svn:update', 'coverage:verify', 'svn:fix_keywords', COMMIT_MSG_FILE] do - targets = get_target_args() - $deferr.puts '---', File.read( COMMIT_MSG_FILE ), '---' - ask_for_confirmation( "Continue with checkin?" ) do - run 'svn', 'ci', '-F', COMMIT_MSG_FILE, targets - rm_f COMMIT_MSG_FILE - end - end - task :commit => :checkin - task :ci => :checkin - - - task :clean do - rm_f COMMIT_MSG_FILE - end - - - desc "Check and fix any missing keywords for any files in the project which need them" - task :fix_keywords do - log "Checking subversion keywords..." - paths = get_svn_filelist( BASEDIR ). - select {|path| path.file? && path.to_s =~ KEYWORDED_FILEPATTERN } - - trace "Looking at %d paths for keywords:\n %p" % [paths.length, paths] - kwmap = get_svn_keyword_map( paths ) - - buf = '' - PP.pp( kwmap, buf, 132 ) - trace "keyword map is: %s" % [buf] - - files_needing_fixups = paths.find_all do |path| - (kwmap[path.to_s] & DEFAULT_KEYWORDS) != DEFAULT_KEYWORDS - end - - unless files_needing_fixups.empty? - $deferr.puts "Files needing keyword fixes: ", - files_needing_fixups.collect {|f| - " %s: %s" % [f, kwmap[f] ? kwmap[f].join(' ') : "(no keywords)"] - } - ask_for_confirmation( "Will add default keywords to these files." ) do - run 'svn', 'ps', 'svn:keywords', DEFAULT_KEYWORDS.join(' '), *files_needing_fixups - end - else - log "Keywords are all up to date." - end - end - - - task :debug_helpers do - methods = [ - :make_new_tag, - :get_svn_info, - :get_svn_repo_root, - :get_svn_url, - :get_svn_path, - :svn_ls, - :get_latest_svn_timestamp_tag, - ] - maxlen = methods.collect {|sym| sym.to_s.length }.max - - methods.each do |meth| - res = send( meth ) - puts "%*s => %p" % [ maxlen, colorize(meth.to_s, :cyan), res ] - end - end -end -