iOS 自動打包


更新

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包,這對于測試來說是非常方便的。

最關鍵的還是那句話:一次配置,到處打包。

希望這兩個打包腳本會對大家有所幫助。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,835評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,676評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,730評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,118評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,873評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,266評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,330評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,482評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,036評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,846評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,025評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,575評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,279評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,684評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,953評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,751評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,016評論 2 375

推薦閱讀更多精彩內容