Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Building and Deploying iOS Projects through Rake
require 'yaml'
require 'uri'
require 'tempfile'
require 'tmpdir'
SDK_DIR = "/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.3.sdk"
TESTFLIGHT_URL = 'http://testflightapp.com/api/builds.json'
PROJECT_DIR = File.dirname __FILE__
RUBIOS_DIR = File.join(PROJECT_DIR, 'rubios')
BUILDS_DIR = File.join(PROJECT_DIR, "build")
PROVISIONING_PROFILES_DIR = File.join(RUBIOS_DIR, 'provisioning')
@project = OpenStruct.new(YAML.load_file(File.join(RUBIOS_DIR, 'project.yml')))
environments = YAML.load_file(File.join(RUBIOS_DIR, 'environments.yml'))
environment = OpenStruct.new(environments[ENV['IOS_ENV'] || 'development'])
@testflight_api_token = ENV['TESTFLIGHT_API_TOKEN']
namespace :xcode do
desc 'List available sdks'
task :sdks do
system_or_exit(%q[xcodebuild -showsdks])
end
desc 'List available schemes'
task :schemes do
system_or_exit(%Q[xcodebuild -list -workspace #{@project.name}.xcodeproj/project.xcworkspace])
end
end
task :environment, [:configuration, :sdk, :scheme, :profile] do |t, args|
args.with_defaults( :scheme => ENV['IOS_SCHEME'] || environment.scheme || @project.name,
:sdk => ENV['IOS_SDK'] || environment.sdk || 'iphonesimulator',
:configuration => ENV['IOS_CONFIGURATION'] || environment.configuration || 'Debug',
:profile => ENV['IOS_PROVISIONING_PROFILE'] || environment.profile )
@configuration = args.configuration
@sdk = args.sdk
@scheme = args.scheme
@profile = args.profile
end
desc 'Cleans compiled application'
task :clean => :environment do
xcodebuild('clean')
end
desc 'Build the application'
task :build => :environment do
xcodebuild('build')
end
desc 'Signs an application for distribution'
task :sign => :build do
signing_command = []
signing_command << %Q[xcrun -sdk iphoneos PackageApplication]
signing_command << "#{BUILDS_DIR}/#{@configuration}-iphoneos/#{@project.name}.app"
signing_command << %Q[--sign]
signing_command << %Q["#{@project.code_signing_identity}"]
signing_command << %Q[--embed]
signing_command << %Q["#{File.join(PROVISIONING_PROFILES_DIR, @profile)}.mobileprovision"]
system_or_exit signing_command.join(' ')
end
task :verify => :sign do
verify_command = []
verify_command << %Q[codesign -d --file-list -]
verify_command << "#{BUILDS_DIR}/#{@configuration}-iphoneos/#{@project.name}.app"
system_or_exit verify_command.join(' ')
end
desc 'Runs through the specs to ensure functionality'
task :specs do
Rake::Task[:environment].invoke('Debug', 'iphonesimulator', 'Specs')
Rake::Task[:build].execute()
ENV["DYLD_ROOT_PATH"] = SDK_DIR
ENV["IPHONE_SIMULATOR_ROOT"] = SDK_DIR
ENV["CFFIXED_USER_HOME"] = Dir.tmpdir
ENV["CEDAR_HEADLESS_SPECS"] = "1"
ENV["CEDAR_REPORTER_CLASS"] = "CDRColorizedReporter"
system_or_exit(%Q[#{SDK_DIR}/usr/libexec/securityd &])
system_or_exit(%Q[#{File.join(built_app_location('Debug', 'iphonesimulator', 'Specs'), 'Specs')} -RegisterForSystemEvents])
Rake::Task[:environment].execute()
end
desc 'Assembles a deployable app'
task :package do
ENV['IOS_SDK'] = 'iphoneos'
Rake::Task[:verify].invoke
end
namespace :version do
desc 'Bumps up the current bundle version'
task :bump do
system_or_exit(%Q[agvtool next-version -all])
end
desc 'Explicitly set the BUNDLE_VERSION or MARKETING_VERSION'
task :write do
if bundle_version = ENV['BUNDLE_VERSION']
system_or_exit(%Q[agvtool new-version -all #{bundle_version}])
end
if marketing_version = ENV['MARKETING_VERSION']
system_or_exit(%Q[agvtool new-marketing-version #{marketing_version}])
end
end
end
desc 'Push a build up to TestFlight. Ensure TESTFLIGHT_API_TOKEN has been set with your TestFlight API token.'
task :deploy => [:environment, :package] do
if @testflight_api_token.nil? || @testflight_api_token.empty?
raise "Please set TESTFLIGHT_API_TOKEN with your TestFlight API token."
end
ipa_file = File.join(BUILDS_DIR, "#{@configuration}-iphoneos", "#{@project.name}.ipa")
notes_file = release_notes_file
begin
deploy_command = []
deploy_command << %Q[curl #{TESTFLIGHT_URL} -F file=@#{ipa_file}]
deploy_command << %Q[-F api_token='#{@testflight_api_token}']
deploy_command << %Q[-F team_token='#{@project.testflight_api_team_token}']
deploy_command << %Q[-F "notes=<#{notes_file.path}"]
if distribution_lists = environment.testflight_distribution_lists
deploy_command << %Q[ -F notify=True -F distribution_lists='#{distribution_lists.join(',')}']
end
system_or_exit(deploy_command.join(' '))
ensure
notes_file.unlink
end
end
task :default => [:specs, :build]
def release_notes_file
notes = ENV['RELEASE_NOTES']
if notes.nil? || notes.empty?
notes_file = Tempfile.new(@project.name)
begin
notes_file.write('# Please Enter the Release Notes For This Deployment')
notes_file.rewind
notes_file.close
editor = ENV['IOS_RELEASE_EDITOR'] || ENV['EDITOR']
system_or_exit(%Q[#{editor} #{notes_file.path}])
notes_file.open
notes = File.read(notes_file.path)
ensure
notes_file.close
notes_file.unlink
end
end
notes.strip!
if notes.empty?
raise "Release notes are required"
end
notes_file = Tempfile.new(@project.name)
begin
notes_file.write notes
notes_file
ensure
notes_file.close
end
end
def build_dir(configuration, effective_platform_name)
File.join(BUILDS_DIR, [configuration, effective_platform_name].join('-'))
end
def built_app_location(configuration, effective_platform_name, application)
File.join(build_dir(configuration, effective_platform_name), "#{application}.app")
end
def system_or_exit(cmd, stdout = nil)
puts "Executing #{cmd}"
cmd += " >#{stdout}" if stdout
system(cmd) or raise "******** Build failed ********"
end
def output_file(target)
Dir.mkdir(BUILDS_DIR) unless File.exists?(BUILDS_DIR)
output_file = File.join(BUILDS_DIR, "#{target}.output")
puts "Output: #{output_file}"
output_file
end
def xcodebuild(build_action = 'build')
output_file = [ @configuration, @sdk, build_action ].join('-')
xcode_command = []
xcode_command << %Q[xcodebuild -workspace #{@project.name}.xcodeproj/project.xcworkspace]
xcode_command << %Q[-scheme #{@scheme}]
xcode_command << %Q[-sdk #{@sdk}]
xcode_command << %Q[-configuration #{@configuration}]
xcode_command << build_action
xcode_command << %Q[SYMROOT=#{BUILDS_DIR}]
if !(@profile.nil? || @profile.empty?)
system_or_exit(%Q[cp "#{File.join(PROVISIONING_PROFILES_DIR, @profile)}.mobileprovision" "#{ENV['HOME']}/Library/MobileDevice/Provisioning Profiles/."])
xcode_command << %Q[CODE_SIGN_IDENTITY="#{@project.code_signing_identity}"]
xcode_command << %Q("PROVISIONING_PROFILE[sdk=#{@sdk}*]"=#{@profile})
end
system_or_exit(xcode_command.join(' '), output_file(output_file))
end
development:
scheme: MyApp
configuration: Debug
sdk: iphonesimulator
test:
scheme: Specs
configuration: Debug
sdk: iphonesimulator
acceptance:
scheme: MyApp
configuration: Debug
sdk: iphoneos
profile: Bootylog_Acceptance
testflight_distribution_lists:
- Acceptance
production:
scheme: MyApp
configuration: Release
sdk: iphoneos
name: MyApp
testflight_api_team_token: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx' #replace with your own token
code_signing_identity: 'iPhone Distribution: Rudy Jahchan'
Owner

rudyjahchan commented Jun 9, 2011

This is a snapshot of a Rakefile we use to build our iOS application and deploy it to TestFlight. Documentation to come, but one quick item is that anywhere you see a YOUR_* token should be replaced with an appropriate value.

Nice!

Do you still use this approach, or moved long to/better things?

yea bro i'd like to know too!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment