|
|
@@ -0,0 +1,438 @@ |
|
|
require "rubygems/version" |
|
|
require "rake/clean" |
|
|
require "date" |
|
|
|
|
|
# Application info |
|
|
APP_NAME = "Ubiregi2" |
|
|
SDK = "iphoneos" |
|
|
WORKSPACE = File.expand_path("#{APP_NAME}.xcworkspace") |
|
|
SCHEME = "#{APP_NAME}-Release" |
|
|
INFO_PLIST = File.expand_path("#{APP_NAME}/#{APP_NAME}-Info.plist") |
|
|
REPO = "ubiregiinc/ubiregi-client" |
|
|
|
|
|
# Code signing |
|
|
CODE_SIGN_IDENTITY_APPSTORE = "iPhone Distribution: XXXXX Inc. (XXXXXXXXXX)"; |
|
|
CODE_SIGN_IDENTITY_INHOUSE = "iPhone Distribution: XXXXX K.K."; |
|
|
PROFILE_APPSTORE = "#{APP_NAME}_App_Store.mobileprovision" |
|
|
PROFILE_ADHOC = "#{APP_NAME}_Ad_Hoc.mobileprovision" |
|
|
PROFILE_INHOUSE = "#{APP_NAME}_Beta.mobileprovision" |
|
|
PROFILE_NAME_ADHOC = "#{APP_NAME} Ad Hoc" |
|
|
PROFILE_DIR = "$HOME/Library/MobileDevice/Provisioning Profiles" |
|
|
KEYCHAIN_NAME = "ios-build.keychain" |
|
|
|
|
|
# Build paths |
|
|
BUILD_DIR = File.expand_path("build") |
|
|
APP_FILE = File.expand_path("#{BUILD_DIR}/#{APP_NAME}.app") |
|
|
ARCHIVE_FILE = File.expand_path("#{BUILD_DIR}/#{APP_NAME}.xcarchive") |
|
|
IPA_FILE = File.expand_path("#{BUILD_DIR}/#{APP_NAME}.ipa") |
|
|
DSYM_FILE = File.expand_path("#{BUILD_DIR}/#{APP_NAME}.app.dSYM") |
|
|
DSYM_ZIP_FILE = File.expand_path("#{BUILD_DIR}/#{APP_NAME}.app.dSYM.zip") |
|
|
|
|
|
# Test info |
|
|
DESTINATIONS = [ |
|
|
"name=iPad,OS=7.1", |
|
|
"name=iPad Retina,OS=7.1", |
|
|
"name=iPad Retina (64-bit),OS=7.1", |
|
|
] |
|
|
|
|
|
# Crittercism info |
|
|
APP_ID = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx" |
|
|
API_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx" |
|
|
|
|
|
# TestFlight info |
|
|
API_TOKEN = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" |
|
|
TEAM_TOKEN = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" |
|
|
|
|
|
CLEAN.include(BUILD_DIR) |
|
|
CLOBBER.include(BUILD_DIR) |
|
|
|
|
|
task :default => [:build] |
|
|
|
|
|
desc "Setting up a development environment" |
|
|
task :setup do |
|
|
sh "bundle install" |
|
|
sh "pod install" |
|
|
end |
|
|
|
|
|
desc "Test application" |
|
|
task test: ["test:release"] |
|
|
|
|
|
namespace :test do |
|
|
task :release do |
|
|
test(configuration: "Release") |
|
|
end |
|
|
|
|
|
task :debug do |
|
|
test(configuration: "Debug") |
|
|
end |
|
|
|
|
|
task :adhoc do |
|
|
test(configuration: "AdHoc") |
|
|
end |
|
|
end |
|
|
|
|
|
def test(configuration: "Release") |
|
|
options = build_options(sdk: "iphonesimulator", configuration: configuration) |
|
|
options << DESTINATIONS.map { |destination| %[-destination "#{destination}"] }.join(" ") |
|
|
sh %[xcodebuild #{options} GCC_SYMBOLS_PRIVATE_EXTERN="NO" test | xcpretty -c; exit ${PIPESTATUS[0]}] |
|
|
end |
|
|
|
|
|
desc "Build application (Release)" |
|
|
task build: ["build:release"] |
|
|
|
|
|
namespace :build do |
|
|
desc "Build application (Release)" |
|
|
task :release do |
|
|
build |
|
|
end |
|
|
|
|
|
desc "Build application (AdHoc)" |
|
|
task :adhoc do |
|
|
build(configuration: "AdHoc") |
|
|
end |
|
|
end |
|
|
|
|
|
def build(configuration: "Release") |
|
|
options = build_options(configuration: configuration) |
|
|
settings = build_settings(configuration: configuration) |
|
|
sh %[xcodebuild #{options} #{settings} build | xcpretty -c; exit ${PIPESTATUS[0]}] |
|
|
end |
|
|
|
|
|
desc "Build artifacts (.xcarchive, ipa) (Release)" |
|
|
task archive: ["archive:release"] |
|
|
|
|
|
namespace :archive do |
|
|
desc "Build artifacts (.xcarchive, ipa) (Release)" |
|
|
task :release do |
|
|
archive |
|
|
end |
|
|
|
|
|
desc "Build artifacts (.xcarchive, ipa) (AdHoc)" |
|
|
task :adhoc do |
|
|
archive(configuration: "AdHoc") |
|
|
end |
|
|
end |
|
|
|
|
|
def archive(configuration: "Release") |
|
|
build_xcarchive(configuration: configuration) |
|
|
clean_ipa |
|
|
export_ipa |
|
|
zip_dsyms |
|
|
end |
|
|
|
|
|
def build_xcarchive(configuration: "Release") |
|
|
options = build_options(configuration: configuration) |
|
|
settings = build_settings(configuration: configuration) |
|
|
|
|
|
sh %[xcodebuild #{options} #{settings} archive -archivePath #{ARCHIVE_FILE} | xcpretty -c; exit ${PIPESTATUS[0]}] |
|
|
end |
|
|
|
|
|
def clean_ipa |
|
|
system "rm -f #{IPA_FILE}" |
|
|
end |
|
|
|
|
|
def export_ipa |
|
|
sh %[xcodebuild -exportArchive #{export_options} | xcpretty -c; exit ${PIPESTATUS[0]}] |
|
|
end |
|
|
|
|
|
def export_options(configuration: "Release", **others) |
|
|
options = { |
|
|
exportFormat: "IPA", |
|
|
archivePath: ARCHIVE_FILE, |
|
|
exportPath: IPA_FILE, |
|
|
exportProvisioningProfile: PROFILE_NAME_ADHOC, |
|
|
}.merge others |
|
|
join_options(options: options, prefix: "-", separator: " ") |
|
|
end |
|
|
|
|
|
def resign_ipa |
|
|
sh %[unzip -o #{IPA_FILE} -d build 1>/dev/null] |
|
|
sh %[rm -rf #{BUILD_DIR}/Payload/#{APP_NAME}.app/_CodeSignature/] |
|
|
sh %[cp #{PROFILE_ADHOC} #{BUILD_DIR}/Payload/#{APP_NAME}.app/embedded.mobileprovision] |
|
|
sh %[codesign --verify --force --sign "#{CODE_SIGN_IDENTITY_APPSTORE}" --resource-rules #{BUILD_DIR}/Payload/#{APP_NAME}.app/ResourceRules.plist #{BUILD_DIR}/Payload/#{APP_NAME}.app] |
|
|
sh %[(cd #{BUILD_DIR}; zip -ryq #{APP_NAME}.ipa Payload)] |
|
|
end |
|
|
|
|
|
def zip_dsyms |
|
|
sh "(cd #{BUILD_DIR}; zip -ryq #{APP_NAME}.app.dSYM.zip #{APP_NAME}.app.dSYM)" |
|
|
sh "mv #{DSYM_FILE} #{BUILD_DIR}/#{APP_NAME}.xcarchive/dSYMs/#{APP_NAME}.app.dSYM" |
|
|
|
|
|
sh "(cd #{BUILD_DIR}; zip -ryq #{APP_NAME}.xcarchive.zip #{APP_NAME}.xcarchive)" |
|
|
end |
|
|
|
|
|
def build_options(configuration: "Release", **others) |
|
|
options = { |
|
|
sdk: SDK, |
|
|
workspace: WORKSPACE, |
|
|
scheme: SCHEME, |
|
|
configuration: configuration, |
|
|
}.merge others |
|
|
join_options(options: options, prefix: "-", separator: " ") |
|
|
end |
|
|
|
|
|
def build_settings(configuration: "Release") |
|
|
code_sign_identity = configuration == "Release" ? CODE_SIGN_IDENTITY_APPSTORE : CODE_SIGN_IDENTITY_INHOUSE |
|
|
settings = { |
|
|
CONFIGURATION_BUILD_DIR: BUILD_DIR, |
|
|
CONFIGURATION_TEMP_DIR: "#{BUILD_DIR}/temp", |
|
|
CODE_SIGN_IDENTITY: code_sign_identity, |
|
|
} |
|
|
settings = join_options(options: settings, prefix: "", separator: "=") |
|
|
end |
|
|
|
|
|
desc "Upload IPA file to TestFlight (Release)" |
|
|
task distribute: ["distribute:release"] |
|
|
|
|
|
namespace :distribute do |
|
|
desc "Upload IPA file to TestFlight (Release)" |
|
|
task :release => ["archive:release"] do |
|
|
distribute |
|
|
end |
|
|
|
|
|
desc "Upload IPA file to TestFlight (AdHoc)" |
|
|
task :adhoc => ["version:set_build_version", "archive:adhoc"] do |
|
|
distribute |
|
|
end |
|
|
end |
|
|
|
|
|
def distribute |
|
|
crittercism |
|
|
testflight |
|
|
end |
|
|
|
|
|
def crittercism |
|
|
options = { |
|
|
dsym: "@#{DSYM_ZIP_FILE}", |
|
|
key: API_KEY, |
|
|
} |
|
|
upload_form("https://api.crittercism.com/api_beta/dsym/#{APP_ID}", options) |
|
|
end |
|
|
|
|
|
def testflight |
|
|
pr_number = ENV["TRAVIS_PULL_REQUEST"] |
|
|
branch_name = ENV["TRAVIS_BRANCH"] |
|
|
|
|
|
release_date = DateTime.now.strftime("%Y/%m/%d %H:%M:%S") |
|
|
build_version = InfoPlist.marketing_and_build_version |
|
|
repo_url = "https://github.com/#{REPO}" |
|
|
|
|
|
release_notes = "Build: #{build_version}\nUploaded: #{release_date}\n" |
|
|
|
|
|
if pull_request? |
|
|
release_notes << "Branch: #{repo_url}/commits/#{branch_name}\n" |
|
|
release_notes << "Pull Request: #{repo_url}/pull/#{pr_number}\n" |
|
|
release_notes << %x[git log --date=short --pretty=format:"* %h - %s (%cd) <%an>" --no-merges #{branch_name}..] |
|
|
else |
|
|
commit_hash = %x[git rev-parse HEAD].strip |
|
|
release_notes << "Branch: #{repo_url}/commits/#{branch_name}\n" |
|
|
if branch_name == "master" |
|
|
release_notes << %x[git log --date=short --pretty=format:"* %h - %s (%cd) <%an>" --no-merges $(git describe --abbrev=0 --tags)..] |
|
|
else |
|
|
release_notes << %x[git log --date=short --pretty=format:"* %h - %s (%cd) <%an>" --no-merges] |
|
|
end |
|
|
end |
|
|
|
|
|
options = { |
|
|
file: "@#{IPA_FILE}", |
|
|
api_token: API_TOKEN, |
|
|
team_token: TEAM_TOKEN, |
|
|
notify: true, |
|
|
replace: true, |
|
|
distribution_lists: pull_request? ? "Dev" : "Internal", |
|
|
notes: release_notes, |
|
|
} |
|
|
upload_form("http://testflightapp.com/api/builds.json", options) |
|
|
end |
|
|
|
|
|
def upload_form(url, options = {}) |
|
|
curl_options = ["curl", "-sL", "-w", "%{http_code} %{url_effective}\\n", "#{url}"] |
|
|
form_fields = options.flat_map { |k, v| ["-F", "#{k}=#{v}"] } |
|
|
output_options = ["-o", "/dev/null"] |
|
|
sh *(curl_options + form_fields + output_options) |
|
|
end |
|
|
|
|
|
desc "Submit application" |
|
|
task :release do |
|
|
sh "git checkout -b release/$(git rev-parse --short HEAD)" |
|
|
|
|
|
InfoPlist.bump_release_version |
|
|
InfoPlist.update_build_number |
|
|
InfoPlist.commit |
|
|
|
|
|
branch_name = "release/#{InfoPlist.marketing_version}" |
|
|
sh "git branch -M #{branch_name}" |
|
|
|
|
|
sh "git push [email protected]:#{REPO}.git #{branch_name}" |
|
|
end |
|
|
|
|
|
namespace :profile do |
|
|
desc "Download provisioning profiles from Apple Developer Center" |
|
|
task :download do |
|
|
profile_download("[email protected]", "xxxxxxxxxxxxxxxxxx") |
|
|
end |
|
|
|
|
|
task :install do |
|
|
profile_install |
|
|
end |
|
|
end |
|
|
|
|
|
def profile_download(user, passowrd) |
|
|
system "ios profiles:download:all -u #{user} -p #{password} --type distribution" |
|
|
end |
|
|
|
|
|
def profile_install |
|
|
sh %[mkdir -p "#{PROFILE_DIR}"] |
|
|
sh %[cp "#{PROFILE_INHOUSE}" "#{PROFILE_DIR}"] |
|
|
sh %[cp "#{PROFILE_ADHOC}" "#{PROFILE_DIR}"] |
|
|
end |
|
|
|
|
|
namespace :certificate do |
|
|
task :add do |
|
|
add_certificates |
|
|
end |
|
|
|
|
|
task :remove do |
|
|
remove_certificates |
|
|
end |
|
|
end |
|
|
|
|
|
def add_certificates |
|
|
passphrase = "xxxxxxxx" |
|
|
|
|
|
sh "security create-keychain -p travis #{KEYCHAIN_NAME}" |
|
|
sh "security import ./certificates/apple.cer -k #{KEYCHAIN_NAME} -T /usr/bin/codesign" |
|
|
sh "security import ./certificates/appstore.cer -k #{KEYCHAIN_NAME} -T /usr/bin/codesign" |
|
|
sh "security import ./certificates/appstore.p12 -k #{KEYCHAIN_NAME} -P #{passphrase} -T /usr/bin/codesign" |
|
|
sh "security import ./certificates/inhouse.cer -k #{KEYCHAIN_NAME} -T /usr/bin/codesign" |
|
|
sh "security import ./certificates/inhouse.p12 -k #{KEYCHAIN_NAME} -P #{passphrase} -T /usr/bin/codesign" |
|
|
sh "security default-keychain -s #{KEYCHAIN_NAME}" |
|
|
end |
|
|
|
|
|
def remove_certificates |
|
|
sh "security delete-keychain #{KEYCHAIN_NAME}" |
|
|
end |
|
|
|
|
|
def pull_request? |
|
|
pr_number = ENV["TRAVIS_PULL_REQUEST"] |
|
|
pr_number != nil && !pr_number.empty? && pr_number != "false" |
|
|
end |
|
|
|
|
|
def join_options(options: {}, prefix: "", separator: "") |
|
|
options.map { |k, v| %(#{prefix}#{k}#{separator}"#{v}") }.join(" ") |
|
|
end |
|
|
|
|
|
namespace :version do |
|
|
module InfoPlist |
|
|
extend self |
|
|
|
|
|
def [](key) |
|
|
output = %x[/usr/libexec/PlistBuddy -c "Print #{key}" #{INFO_PLIST}].strip |
|
|
raise "The key `#{key}' does not exist in `#{INFO_PLIST}'." if output.include?('Does Not Exist') |
|
|
output |
|
|
end |
|
|
|
|
|
def set(key, value, file = "#{INFO_PLIST}") |
|
|
%x[/usr/libexec/PlistBuddy -c 'Set :#{key} "#{value}"' '#{file}'].strip |
|
|
end |
|
|
def []=(key, value) |
|
|
set(key, value) |
|
|
end |
|
|
|
|
|
def build_version |
|
|
self['CFBundleVersion'] |
|
|
end |
|
|
def build_version=(revision) |
|
|
self['CFBundleVersion'] = revision |
|
|
end |
|
|
|
|
|
def marketing_version |
|
|
self['CFBundleShortVersionString'] |
|
|
end |
|
|
def marketing_version=(version) |
|
|
self['CFBundleShortVersionString'] = version |
|
|
end |
|
|
|
|
|
def marketing_and_build_version |
|
|
"#{marketing_version} (#{build_version})" |
|
|
end |
|
|
|
|
|
def bump_minor_version |
|
|
segments = Gem::Version.new(marketing_version).segments |
|
|
segments[1] = "%02d" % (segments[1].to_i + 1) |
|
|
version = segments.join('.') |
|
|
self.marketing_version = version |
|
|
segments[1] |
|
|
end |
|
|
|
|
|
def bump_major_version |
|
|
segments = Gem::Version.new(marketing_version).segments |
|
|
segments[0] = segments[0].to_i + 1 |
|
|
segments[1] = "00" |
|
|
version = segments.join('.') |
|
|
self.marketing_version = version |
|
|
segments[0] |
|
|
end |
|
|
|
|
|
def bump_release_version |
|
|
if bump_minor_version.to_i.odd? |
|
|
bump_minor_version |
|
|
end |
|
|
end |
|
|
|
|
|
def update_build_number |
|
|
build_number = %x[git rev-list HEAD | wc -l | tr -d " "].strip |
|
|
self.build_version = build_number.to_s |
|
|
end |
|
|
|
|
|
def commit |
|
|
system("git commit #{INFO_PLIST} -m 'Bump to #{marketing_and_build_version}'") |
|
|
end |
|
|
end |
|
|
|
|
|
desc "Print the current version" |
|
|
task :current do |
|
|
puts InfoPlist.marketing_and_build_version |
|
|
end |
|
|
|
|
|
desc "Sets build version to last git commit (happens on each build)" |
|
|
task :set_build_version do |
|
|
rev = "" |
|
|
pr_number = ENV["TRAVIS_PULL_REQUEST"] |
|
|
if pull_request? |
|
|
rev << %x[git rev-parse --short HEAD^2].strip |
|
|
rev << " ##{pr_number}" |
|
|
else |
|
|
rev << %x[git rev-parse --short HEAD].strip |
|
|
end |
|
|
|
|
|
puts "Setting build version to: #{rev}" |
|
|
|
|
|
InfoPlist.build_version = rev |
|
|
end |
|
|
|
|
|
namespace :bump do |
|
|
desc "Bump minor version (0.XX)" |
|
|
task :minor do |
|
|
InfoPlist.bump_minor_version |
|
|
InfoPlist.update_build_number |
|
|
InfoPlist.commit |
|
|
end |
|
|
|
|
|
desc "Bump major version (X.00)" |
|
|
task :major do |
|
|
InfoPlist.bump_major_version |
|
|
InfoPlist.update_build_number |
|
|
InfoPlist.commit |
|
|
end |
|
|
|
|
|
desc "Bump release version (0.XY)" |
|
|
task :release do |
|
|
InfoPlist.bump_release_version |
|
|
InfoPlist.update_build_number |
|
|
InfoPlist.commit |
|
|
end |
|
|
end |
|
|
end |
|
|
|
|
|
desc "Print the current version" |
|
|
task :version => 'version:current' |