#! /usr/bin/env ruby # automates a binary search between the specified revision and HEAD. # comma-separated list of revisions to skip skiprevs = "8827,8934,8947,8977,9812" buildcmd = "ant clean build" deploycmd = "ant webapp.deploy" require 'optparse' require 'ostruct' options = OpenStruct.new options.first = 0 options.help = false options.verbose = false options.last = 'HEAD' opts = OptionParser.new do |opts| opts.banner = "Usage: binsearch.rb [options]" opts.on("-r", "--revision FIRST", "The first revision to start searching from, e.g. 8900.") do |s| options.first = s end opts.on("-l", "--last LAST", "The last revision to search to. Defaults to HEAD.") do |l| options.last = l end opts.on("-v", "--verbose", "Show more information") do |v| options.verbose = v end opts.on_tail("-h", "--help", "Show this usage statement.") do |h| options.help = true end end begin opts.parse!(ARGV) rescue Exception => e puts e, "", opts exit end if (options.help == true) then puts opts exit elsif (options.first == 0) then puts "You must specify the first revision number to begin searching from, e.g 'binsearch.rb -f 8080' See 'binsearch.rb -h' for help." exit end first = options.first last = options.last statstr = `svn stat --non-interactive .` statstr.each_line do |line| line =~ /^M\s+(.+)/ if $1 then print "\aWARNING: found changes in the repository: #{$1}\nWould you like to continue (y/n) " answer = gets.chomp if answer !~ /y|Y|yes|Yes/ then exit end end end revs = [] revstr = `svn log --non-interactive --xml -r#{first}:#{last} . | grep revision=` revstr.each_line do |line| line =~ /(\d+)/ if skiprevs.include? $1 then puts "Skipping revision #{$1}." if (options.verbose) else revs.push $1 end end svninfo = `svn info` svninfo =~ /Revision: (\d+)/ startrev = $1 puts "Current revision is #{startrev}." if (options.verbose) curindex = first = 0 lastindex = last = revs.length - 1 puts "Searching between #{revs[first]} and #{revs[last]}." error = false while true do currev = revs[curindex] puts "Synchronizing and building #{currev}. #{(lastindex - curindex + 1).abs} remaining." if (currev != startrev) then svncmd = "svn up -r#{currev} ." puts "> #{svncmd}" if (options.verbose) svnresult = `#{svncmd}` if "#{$?.exitstatus}" != "0" then puts svnresult puts "\n\nWARNING: '#{svncmd}' exited with an error. Please make you are inside the top level directory of a clean working copy and restart binsearch.rb." error = true break end puts "#{svnresult}" if (options.verbose) else puts "Already at build #{currev}. Skipping svn up." if (options.verbose) end puts "> #{buildcmd}" if (options.verbose) buildresult = `#{buildcmd}` if "#{$?.exitstatus}" != "0" then puts buildresult puts "\n\nWARNING: '#{buildcmd}' exited with an error. Please be sure you're working on a clean branch with no changes. If you're sure this revision is problematic, please add '#{currev}' to the list of skipped builds at the top of binsearch.rb." error = true break end puts "#{buildresult}" if (options.verbose) puts "> #{deploycmd}" if (options.verbose) deployresult = `#{deploycmd}` if "#{$?.exitstatus}" != "0" then puts deployresult puts "\n\nWARNING: '#{deploycmd}' exited with an error. Please make sure your server is running." error = true break end puts "#{deployresult}" if (options.verbose) print "\aDone. Do you still see the problem? (y/n) " answer = gets.chomp lastindex = curindex if answer =~ /y|Y|yes|Yes/ then if (curindex == first) then puts "You must start from a revision number that does not show the problem." exit end # problem is between first and curindex last = curindex d = first - curindex if d == 1 || d == -1 then break end else # problem is between last and curindex first = curindex d = last - curindex if d == 1 || d == -1 then curindex += 1 break end end curindex += (d / 2).to_i end if error == false then puts "r#{revs[curindex]} is the revision with the problem:" puts `svn log -r#{revs[curindex]}` end