更新
2016-07-21 設置測試項目,并加上一些實用說明
iOS 的打包是一件令人心煩的事,經常會因為打包而打斷當前做的事情。程序員時間本來就寶貴,能不能把打包這件事完全交給機器去做,自己只需要發一個打包命令給機器就不用管呢?
答案是肯定的。
測試項目地址(記得先放好證書):DailyBuild
第一版腳本代碼:RakeFile
第二版腳本代碼:build.py
強烈建議:不要直接從簡書上復制代碼,不然會有格式問題,造成腳本無法運行。如需要腳本代碼,請從測試項目中copy相關文件出來進行修改。
第一版 最簡單的打包上傳蒲公英腳本
這里以項目名為test,workspace為test,scheme 為 testScheme , 生成 archieve 項目為 testArcheve 和 IPA 為 test.ipa 為例。腳本文件名為RakeFile,放在項目工程test.xcodeproj的同級目錄dailybuild目錄下。蒲公英uKey配置AAAAA,apiKey配置BBBBB。打包配置為:Debug。
note:證書跟腳本都在同一目錄dailybuild

使用:終端進入dailybuild文件夾(test/dailybuild,RakeFile所在的文件夾),然后輸入終端命令rake
即可
task :default => [:CI] # 默認的任務,rake即可調用
desc "自動打包"
task :CI do
Rake::Task["xcodebuild:ReleasePgyer"].invoke # 在一個任務中調用另外的任務
end
namespace :xcodebuild do # 加入命名空間,xcodebuild
desc "任務-移除build目錄"
task :RemoveBuildDir do
puts "Remove Build Dir"
sh "rm -r -f ../build"
end
desc "任務-歸檔"
task :ArchiveAPP => :RemoveBuildDir do
puts "Archive APP"
sh "xcodebuild -workspace ../test.xcworkspace -sdk iphoneos -scheme testScheme -archivePath ../build/testArchieve.xcarchive -configuration Debug archive"
end
desc "任務-導出IPA包"
task :ExportIPA => :ArchiveAPP do # 加入依賴關系,ExportIPA依賴ArchiveAPP先執行
puts "Export IPA"
sh "xcodebuild -exportArchive -exportFormat IPA -archivePath ../build/testArchieve.xcarchive -exportPath ../build/test.ipa -exportProvisioningProfile \"證書名(最好是開發根證書iOS Team 那個。關于如何獲取證書名,請上開發者官網看證書信息)\""
end
desc "任務-發布蒲公英"
task :ReleasePgyer => :ExportIPA do
puts "Release IPA"
sh "curl -F \"file=@../build/test.ipa\" -F \"uKey=AAAAA\" -F \"_api_key=B\" BBBBhttp://www.pgyer.com/apiv1/app/upload"
end
end
但是,使用這個打包腳本在多人協作且同時開發不同功能打出不同的包時,有著先天的不足,不能作為一個通用的打包腳本通過配置來生成不同的ipa包。
第二版 基于配置的通用的多人自動打包腳本(Python實現)
項目跟上面第一版一樣。
# encoding: utf-8
import sys, getopt, os, time, threading
try:
import xml.etree.cElementTree as ET
except ImportError:
import xml.etree.ElementTree as ET
time = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(time.time()))
archiveConfiguration = 'Release' # 打包的配置,一般區分Debug和Release
provisioning_profile = "\"iOS Team Provisioning Profile: *\"" # 證書名,不是證書文件名,兩者是不同的
workspace = "../test.xcworkspace" #工作空間
scheme = "test" # scheme 名
archivePath = "../build/test-%s.xcarchive" % (time) # 歸檔路徑
exportPath = "../build/test-%s.ipa" % (time) # 導出IPA的路徑
upload = "yes" # 是否上傳蒲公英
# 蒲公英用戶配置,不同用戶不同
pgyer_uKey_li = "A"
pgyer_apiKey_li = "A"
pgyer_uKey_lei = "B"
pgyer_apiKey_lei = "B"
pgyer_uKey_zhao = "C"
pgyer_apiKey_zhao = "C"
pgyer_uKey_team = "D"
pgyer_apiKey_team = "D"
pgyer_uKey = pgyer_uKey_lei
pgyer_apiKey = pgyer_apiKey_lei
# bundleName 和 bundleID 配置,用于打不同的包
bundleDisplayName_li = u'li'
bundleProductID_li = u'com.test.li'
bundleDisplayName_lei = u'lei'
bundleProductID_lei = u'com.test.lei'
bundleDisplayName_zhao = u'zhao'
bundleProductID_zhao = u'com.test.zhao'
bundleDisplayName_team = u'team'
bundleProductID_team = u'com.test.team'
bundleDisplayName = bundleDisplayName_team
bundleProductID = bundleProductID_team
developer = 'team'
infolist = '../test/Info.plist'
copy_infolist = '../test/_Info.plist'
def main(argv):
# 移除build目錄
# os.system("rm -r -f ../build")
global archiveConfiguration
global workspace
global provisioning_profile
global scheme
global archivePath
global exportPath
global pgyer_uKey
global pgyer_apiKey
global upload
global developer
global bundleProductID
global bundleDisplayName
global pgyer_uKey
global pgyer_apiKey
shortargs = 'c'
longargs = ['workspace', 'provisioning_profile', 'archivePath', 'exportPath', 'pgyer_uKey', 'pgyer_apiKey',
'scheme', 'upload', 'li','lei','zhao']
opts, args = getopt.getopt(argv[1:], shortargs, longargs)
for op, value in opts:
if op == "-c":
archiveConfiguration = value
elif op == "--workspace":
workspace = value
elif op == "--provisioning_profile":
provisioning_profile = value
elif op == "--scheme":
scheme = value
elif op == "--archivePath":
archivePath = value
elif op == "--exportPath":
exportPath = value
elif op == "--pgyer_uKey":
pgyer_uKey = value
elif op == "--pgyer_apiKey":
pgyer_apiKey = value
elif op == "--upload":
upload = value
elif op == "--li":
developer = 'li'
elif op == "--lei":
developer = 'lei'
elif op == "--zhao":
developer = 'zhao'
print "任務0 - 更改bundleId和bundleName"
os.rename(infolist,copy_infolist)
if developer == "team":
bundleDisplayName = bundleDisplayName_team
bundleProductID = bundleProductID_team
pgyer_apiKey = pgyer_apiKey_team
pgyer_uKey = pgyer_uKey_team
elif developer == "li":
bundleDisplayName = bundleDisplayName_li
bundleProductID = bundleProductID_li
pgyer_apiKey = pgyer_apiKey_li
pgyer_uKey = pgyer_uKey_li
elif developer == "lei":
bundleDisplayName = bundleDisplayName_lei
bundleProductID = bundleProductID_lei
pgyer_apiKey = pgyer_apiKey_lei
pgyer_uKey = pgyer_uKey_lei
elif developer == "zhao":
bundleDisplayName = bundleDisplayName_zhao
bundleProductID = bundleProductID_zhao
pgyer_apiKey = pgyer_apiKey_zhao
pgyer_uKey = pgyer_uKey_zhao
print "準備對bundleId為: " + bundleProductID.encode('utf-8') + " bundleName為:" + bundleDisplayName.encode('utf-8') + " 進行打包"
threading._sleep(2)
print "Begin Archieve"
utf8_parser = ET.XMLParser(encoding='utf-8')
tree = ET.parse(copy_infolist, parser=utf8_parser)
root = tree.getroot()
dict = root[0]
for child in dict:
childText = child.text
if childText != None:
if u'test' in childText:
childText = bundleDisplayName
elif childText == u'$(PRODUCT_NAME)':
childText = bundleDisplayName
elif childText == u'$(PRODUCT_BUNDLE_IDENTIFIER)':
childText = bundleProductID
child.text = childText
tree.write(infolist, encoding='utf-8')
print "任務1 - 歸檔"
os.system("xcodebuild -workspace %s -sdk iphoneos -scheme %s -archivePath %s -configuration %s archive" % (workspace, scheme, archivePath, archiveConfiguration))
print "任務2 - 導出IPA"
os.system("xcodebuild -exportArchive -exportFormat IPA -archivePath %s -exportPath %s -exportProvisioningProfile %s" % (archivePath, exportPath, provisioning_profile))
if upload == "yes":
print "任務3 - 上傳蒲公英"
os.system("curl -F \"file=@%s\" -F \"uKey=%s\" -F \"_api_key=%s\" http://www.pgyer.com/apiv1/app/upload" % (exportPath, pgyer_uKey, pgyer_apiKey))
os.remove(infolist)
os.rename(copy_infolist,infolist)
if __name__ == '__main__':
main(sys.argv)
版本二的腳本能夠通過配置為不同成員設定不同的打包配置,使用也較為簡單。
具體使用:終端進入dailybuild文件夾(test/dailybuild,RakeFile所在的文件夾),運行python build.py
即會運行默認配置的打包,如果加上相關參數即可進行相關配置的打包。
參數列表:
- "-c": 打包配置,默認Release,可配置成Debug或其它配置
- "--workspace":工作空間
- "--provisioning_profile":證書名
- "--archivePath":導出歸檔路徑
- "--exportPath":IPA導出路徑
- "--pgyer_uKey":蒲公英uKey,如果使用了預設的li,lei或zhao,則被配置參數無效
- "--pgyer_apiKey":蒲公英apiKey,如果使用了預設的li,lei或zhao,則被配置參數無效
- "--scheme":scheme名
- "--upload":是否上傳到蒲公英,默認yes,可設置為no
- "--li":使用預設的li配置來打包,不可與lei或zhao共用
- "--lei":使用預設的li配置來打包,不可與li或zhao共用
- "--zhao":使用預設的li配置來打包,不可與lei或lei共用
使用參數配置例子:
python build.py -c Debug --li --upload no
上面例子意思是例子是實用Debug配置 和 li預設配置 打包,不上傳蒲公英。可以看出基于配置的打包腳本是非常方便的
后記
使用打包腳本是iOS打包的其中一個選擇,相對于XCode打包來說跟自動化更方便一些,特別是團隊開發是每個人負責同一個應用不同模塊時,能夠針對性自動打出每個人各自的iPA包,這對于測試來說是非常方便的。
最關鍵的還是那句話:一次配置,到處打包。
希望這兩個打包腳本會對大家有所幫助。