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阅读 200,392评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,258评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,417评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,992评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,930评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,199评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,652评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,327评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,463评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,382评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,432评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,118评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,704评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,787评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,999评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,476评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,057评论 2 341

推荐阅读更多精彩内容