module Buildr class ReleaseTask < Rake::Task VERSION_NUMBER_PATTERN = /VERSION_NUMBER\s*=\s*(["'])(.*)\1/ NEXT_VERSION_PATTERN = /NEXT_VERSION\s*=\s*(["'])(.*)\1/ def initialize(*args) super enhance do |task| # Make sure we don't have anything uncommitted in SVN. fail "Uncommitted SVN files violate the First Principle Of Release!" unless svn("status").empty? # Load the Rakefile and find the version numbers. next_ver = update_version # Run the deployment externally using the new version number. sh "rake deploy" update_next_version next_ver tag_repository end end # Change the Rakefile and update the current version number to the # next version number (VERSION_NUMBER = NEXT_VERSION). We need this # before making a release with the next version. Return the next version. def update_version() rakefile = File.read(Rake.application.rakefile) version = rakefile.scan(VERSION_NUMBER_PATTERN)[0][1] or fail "Looking for VERSION_NUMBER = \"...\" in your Rakefile, none found" next_ver = rakefile.scan(NEXT_VERSION_PATTERN)[0][1] or fail "Looking for NEXT_VERSION = \"...\" in your Rakefile, none found" if verbose puts "Current version: #{version}" puts "Next version: #{next_ver}" end # Switch version numbers. rakefile.gsub!(VERSION_NUMBER_PATTERN) { |ver| ver.sub(/(["']).*\1/, %Q{"#{next_ver}"}) } File.open(Rake.application.rakefile, "w") { |file| file.write rakefile } next_ver end # Change the Rakefile and update the next version number to one after # (NEXT_VERSION = NEXT_VERSION + 1). We do this to automatically increment # future version number after each successful release. def update_next_version(version) # Update to new version number. nums = version.split(".") nums[-1] = nums[-1].to_i + 1 next_ver = nums.join(".") rakefile = File.read(Rake.application.rakefile) rakefile.gsub!(NEXT_VERSION_PATTERN) { |ver| ver.sub(/(["']).*\1/, %Q{"#{next_ver}"}) } File.open(Rake.application.rakefile, "w") { |file| file.write rakefile } # Commit new version number. svn "commit", "-m", "Changed release number to #{version}" end # Create a tag in the SVN repository. def tag_repository() # Copy to tag. cur_url = svn("info").scan(/URL: (.*)/)[0][0] new_url = cur_url.sub(/trunk$/, "tags/#{cur_ver}") svn "copy", cur_url, new_url end def svn(*args) stdin, stdout, stderr = Open3.popen3("svn", *args) stdin.close error = stderr.read fail error unless error.empty? stdout.read end end # Handles the build and clean tasks. desc "Clean all projects" LocalDirectoryTask.define_task("clean") desc "Build all projects" LocalDirectoryTask.define_task("build") desc "Make a release" ReleaseTask.define_task "release" class Project def build(*args, &block) returning(@build_task ||= recursive_task("build")) do |task| task.enhance args, &block end end def clean(*args, &block) returning(@clean_task ||= recursive_task("clean")) do |task| task.enhance args, &block end end end Project.on_create do |project| desc "Clean all files generated during the build process" project.clean desc "Build this project" project.build end end