ROOT_DIR = File.expand_path('.') TMP_ROOT = '/tmp' TMP_DIR = File.join(TMP_ROOT, 'totalterminal') BUILD_DIR = File.join(ROOT_DIR, 'build') RELEASE_DIR = File.join(ROOT_DIR, 'release') INSTALLER_DIR = File.join(ROOT_DIR, 'installer') PAYLOADS_DIR = File.join(ROOT_DIR, 'payloads') PARENT_DIR = File.expand_path('..') PLUGIN_BUNDLE = "TotalTerminal.bundle" XCODE_WORKSPACE = File.join(ROOT_DIR, 'TotalTerminal.xcworkspace') SPARKLE_DIR = File.join(ROOT_DIR, 'sparkle') OSAX_DIR = File.join(PARENT_DIR, 'totalterminal-osax') I18N_DIR = File.join(PARENT_DIR, 'totalterminal-i18n') TMP_GIT_MODULES = File.join(TMP_DIR, '.gitmodules') TMP_TOTALTERMINAL_RESOURCES = File.join(TMP_DIR, 'totalterminal-i18n', 'plugin') TMP_TOTALTERMINAL_DIR = File.join(TMP_DIR, 'totalterminal-plugin') TMP_BIN_DIR = File.join(TMP_DIR, 'bin') TMP_BIN_TOTALTERMINAL_FRAMEWORKS = File.join(TMP_BIN_DIR, 'TotalTerminal.osax/Contents/Resources/TotalTerminal.bundle/Contents/Frameworks') PLUGIN_INFO_PLIST = File.join(TMP_DIR, 'totalterminal-plugin', 'Info.plist') PLUGIN_VISOR_M = File.join(TMP_DIR, 'totalterminal-plugin', 'TotalTerminal.mm') PLUGIN_PREFERENCES_MM = File.join(TMP_DIR, 'totalterminal-plugin', 'TotalTerminal+Preferences.mm') PLUGIN_VISOR_XIB = File.join(TMP_DIR, 'totalterminal-i18n', 'plugin', 'TotalTerminal.xib') OSAX_INFO_PLIST = File.join(TMP_DIR, 'totalterminal-osax', 'Info.plist') CRASHWATCHER_INFO_PLIST = File.join(TMP_DIR, 'crashwatcher', 'CrashWatcher-Info.plist') PUBLISH_PREFIX_URL = "http://downloads.binaryage.com" CHANGELOG_PREFIX_URL = "http://totalterminal.binaryage.com" ################################################################################################ # dependencies begin require 'colored' rescue LoadError raise 'You must "gem install colored" to use terminal colors' end def file_color(text); text.yellow; end def dir_color(text); text.blue; end def cmd_color(text); text.magenta; end ############################################################################################ def die(msg, status=1) puts "Error[#{status||$?}]: #{msg}".red exit status||$? end def version() $version = ENV["version"] or die("specify version") end def revision() $revision = `git rev-parse HEAD`.strip $short_revision = $revision[0...7] end def dirty_repo_warning() is_clean = `git status`.match(/working directory clean/) puts "Repository is not clean! You should commit all changes before releasing.".red unless is_clean end def patch(path, replacers) puts "#{cmd_color('Patching')} #{file_color(path)}" lines = [] File.open(path, "r") do |f| f.each do |line| replacers.each do |r| line.gsub!(r[0], r[1]) end lines << line end end File.open(path, "w") do |f| f << lines.join end end def announce(cmd) puts "> " + cmd.yellow end def sys(cmd) announce(cmd) system(cmd) end ############################################################################################ def generate_payload_for_pkg(tmp, pkg) tree = "" exes = "" tmp2 = File.join(tmp, "payload-extractor") sys("rm -rf \"#{tmp2}\"") if File.exist? tmp2 sys("mkdir -p \"#{tmp2}\"") sys("cp \"#{pkg}\" \"#{tmp2}\"") name = File.basename pkg Dir.chdir(tmp2) do sys("xar -xf \"#{name}\"") Dir.chdir("TotalTerminal_Plugin.pkg") do if (File.exist? "Payload") then sys("mv Payload Payload.gz") sys("gunzip Payload.gz") sys("cpio -id < Payload") end tree = `tree --dirsfirst -apsugif` exes = "" Dir.glob("**/MacOS/*") do |exe| exes += `file "#{exe}"` + "\n" end end end res = "" res << "\n" res << "#{name}\n" res << "=======================================================================\n" res << tree res << "\n" res << exes res end def generate_payload(dmg, out) puts "Generating payload for: ".green + dmg.blue volume = "/Volumes/TotalTerminal" tmp = File.join(TMP_DIR, "payloads", File.basename(dmg, ".dmg")) sys("rm -rf \"#{tmp}\"") if File.exist? tmp sys("mkdir -p \"#{tmp}\"") puts dmg cmd = "hdiutil attach \"#{dmg}\" | grep Apple_partition_scheme" announce(cmd) res = `#{cmd}` puts res disk = res.split("\n")[0].split("\t")[0] die("bad disk") unless disk =~ /\/dev/ sys("cp -r #{volume}/* \"#{tmp}\"") tree1 = "" Dir.chdir(tmp) do tree1 = `tree --dirsfirst -apsugif` end pkgs = [] Dir.glob(File.join(tmp, "**/*.pkg")) do |pkg| pkgs << generate_payload_for_pkg(tmp, pkg) end outdir = File.dirname out `mkdir -p #{outdir}` unless File.exist? outdir File.open(out, "w") do |f| f << "BASIC DMG LAYOUT\n" f << "================\n" f << tree1 pkgs.each do |pkg| f << pkg end end puts " -> ".green + out.blue sys("hdiutil detach #{disk}") end def retag_submodule(tags, submodule) tags.each do |tag| x = `git ls-tree #{tag} #{submodule}` sha = x.split(" ")[2] Dir.chdir(submodule) do sha = `git log --reverse`.split("\n")[0].split(" ")[1] unless sha # take first commit in case submodule wasn't defined at this point sys("git tag -d #{tag}") puts "#{tag} #{sha}" sys("git tag #{tag} #{sha}") end end Dir.chdir(submodule) do sys("git push --tags") end end def release_version_from_filename(n) # /Users/darwin/code/totalterminal/payloads/TotalTerminal-0.7.1.txt p = File.basename(n, ".txt").split("-")[1] n = p.split(".") while n.size < 3 do n << "0" end x = (n[0]||"0").to_i y = (n[1]||"0").to_i z = (n[2]||"0").to_i x*1000000 + y*1000 + z end def patch_gitmodules patch(TMP_GIT_MODULES, [ ['git://github.com/binaryage', ROOT_DIR], ['.git', ''] ]) end ################################################################################################################### desc "opens XCode project" task :open do `open "#{XCODE_WORKSPACE}"` end desc "prepare stage" task :stage do puts "#{cmd_color('Cloning sources to stage ...')}" `rm -rf "#{TMP_DIR}"` `mkdir -p "#{TMP_ROOT}"` Dir.chdir(TMP_ROOT) do `git clone #{ROOT_DIR}` end puts "#{cmd_color('Initializing submodules ...')}" Dir.chdir(TMP_DIR) do `git reset --hard HEAD` patch_gitmodules `git submodule update --init --recursive` end end desc "prepares release build" task :release do unless File.exists?(TMP_DIR) then die('Doing it for first time? => rake stage') end puts "#{cmd_color('Checking environment ...')}" dirty_repo_warning() version() revision() mkdir_p(RELEASE_DIR) unless File.exists? RELEASE_DIR unless ENV["fast"] then puts "#{cmd_color('Updating stage ...')}" Dir.chdir(TMP_DIR) do sys('git clean -fd') sys('git reset --hard HEAD^') # use previous commit to make working tree resilient to amends sys('git submodule foreach --recursive "git clean -fd"') sys('git submodule foreach --recursive "git reset --hard HEAD"') sys('git submodule update --recursive --checkout') sys('git pull --ff-only') patch_gitmodules sys('git submodule update --recursive --checkout') end puts "#{cmd_color('Patching version info ...')}" patch(PLUGIN_VISOR_XIB, [['##VERSION##', $version], ['##REVISION##', $short_revision]]) patch(PLUGIN_INFO_PLIST, [['##VERSION##', $version], ['##REVISION##', $short_revision]]) patch(PLUGIN_VISOR_M, [['##VERSION##', $version], ['##REVISION##', $short_revision], ['##SHA##', $revision]]) patch(PLUGIN_PREFERENCES_MM, [['##VERSION##', $version], ['##REVISION##', $short_revision], ['##SHA##', $revision]]) patch(OSAX_INFO_PLIST, [['##VERSION##', $version], ['##REVISION##', $short_revision]]) patch(OSAX_INFO_PLIST, [['##VERSION##', $version], ['##REVISION##', $short_revision]]) patch(CRASHWATCHER_INFO_PLIST, [['##VERSION##', $version], ['##REVISION##', $short_revision]]) Dir.chdir(TMP_DIR) do unless ENV["noclean"] then puts "#{cmd_color('Cleaning')} #{file_color(TMP_TOTALTERMINAL_DIR)}" sys('xcodebuild -workspace TotalTerminal.xcworkspace -scheme "TotalTerminal Package" -configuration Release clean') end puts "#{cmd_color('Building')} #{file_color(TMP_TOTALTERMINAL_DIR)}" sys('xcodebuild -workspace TotalTerminal.xcworkspace -scheme "TotalTerminal Package" -configuration Release') die("build failed") unless $?==0 end Dir.glob(File.join(TMP_BIN_TOTALTERMINAL_FRAMEWORKS, "*")) do |framework| patch(File.join(framework, 'Resources', 'Info.plist'), [['##VERSION##', $version], ['##REVISION##', $short_revision]]) end end releasedmg = File.join(RELEASE_DIR, "TotalTerminal-#{$version}.dmg") sys("rm -rf \"#{releasedmg}\"") if File.exist? releasedmg die("build failed") unless $?==0 Dir.chdir(File.join(TMP_DIR, "totalterminal-installer")) do sys("rake build version=#{$version} products=\"../bin\" release=\"#{RELEASE_DIR}\"") die("installer build failed") unless $?==0 end size = File.size(releasedmg) sig = `ruby "sparkle/sign_update.rb" "#{releasedmg}" keys/dsa_priv.pem`.strip die("build failed") unless $?==0 snippet = "\ Version #{$version} #{CHANGELOG_PREFIX_URL}/changelog-beta.html #{Time.new} 10.6.0 " puts snippet puts puts "Don't forget: ".red puts " git tag -a v#{$version} -m \"Release #{$version}\"".green puts " test sparkle updating on it via touch ~/.use-test-appcast, see totalterminal-plugin/TotalTerminal+Sparkle.mm" puts "#{PUBLISH_PREFIX_URL}/TotalTerminal-#{$version}.dmg".blue # Dir.chdir(ROOT_DIR) do # sys("mkdir -p ttdata") # sys("mount -t smbfs //guest:@whale/Store/ttdata ttdata") # sys("rsync -av --delete release ttdata") # sys("umount ttdata") # sys("rm -rf ttdata") # end end desc "removes intermediate build files" task :clean do puts "#{cmd_color('Removing')} #{dir_color(TMP_DIR)}" `rm -rf "#{TMP_DIR}"` end desc "removes all release builds" task :purge do puts "#{cmd_color('Removing')} #{dir_color(RELEASE_DIR)}" `rm -rf "#{RELEASE_DIR}"` end desc "beautify sources using uncrustify => see uncrustify.cfg" task :beautify do config = File.join(ROOT_DIR, 'configs', 'uncrustify.cfg') what = ENV["filter"] || "*" base = File.join(ROOT_DIR, "totalterminal-plugin") Dir.glob(File.join(base, "**/#{what}")) do |file| next unless file=~/\.(mm|m|c|h|cc|cpp|hpp)$/ puts file[base.size+1..-1] `uncrustify -c "#{config}" --replace --no-backup --mtime "#{file}"` end end desc "build missing payloads" task :payload do Dir.chdir(RELEASE_DIR) do Dir.glob("*.dmg").each do |file| name = File.basename(file, ".dmg") dest = File.join(PAYLOADS_DIR, name+".txt") unless File.exist? dest then generate_payload(file, dest) end end end end desc "diff two most recent payloads" task :paydiff do res = `ls -1 "#{PAYLOADS_DIR}"/*.txt` res = res.split("\n") res = res.sort do |a, b| va = release_version_from_filename a vb = release_version_from_filename b vb<=>va end a = res[1] b = res[0] `ksdiff "#{a}" "#{b}"` end desc "update tags in submodules v1.0+" task :retag do tags = `git tag`.split("\n").reject {|x| x =~ /^v0/ } # skip v0.x.x puts "found #{tags.size} tags..." retag_submodule(tags, "totalterminal-i18n") retag_submodule(tags, "totalterminal-installer") retag_submodule(tags, "totalterminal-osax") # NO! retag_submodule(tags, "crashwatcher") # crashwatcher is shared between TotalTerminal and TotalFinder # NO! retag_submodule(tags, "sparkle") # sparkle is shared between TotalTerminal and TotalFinder `git push --tags` end desc "pull all" task :pull do Dir.chdir("totalterminal-i18n") do puts "in " + Dir.pwd sys("git pull") end Dir.chdir("totalterminal-installer") do puts "in " + Dir.pwd sys("git pull") end Dir.chdir("totalterminal-osax") do puts "in " + Dir.pwd sys("git pull") end Dir.chdir("sparkle") do puts "in " + Dir.pwd sys("git pull") end Dir.chdir("crashwatcher") do puts "in " + Dir.pwd sys("git pull") end puts "in " + Dir.pwd sys("git pull") end desc "push all" task :push do Dir.chdir("totalterminal-i18n") do puts "in " + Dir.pwd sys("git push") end Dir.chdir("totalterminal-installer") do puts "in " + Dir.pwd sys("git push") end Dir.chdir("totalterminal-osax") do puts "in " + Dir.pwd sys("git push") end Dir.chdir("sparkle") do puts "in " + Dir.pwd sys("git push") end Dir.chdir("crashwatcher") do puts "in " + Dir.pwd sys("git push") end puts "in " + Dir.pwd sys("git push") end task :default => :open