iOS App自动化打包发布(Jenkins + Fastlane)

============2018.5.15 updte=================
xcode 9 需要在gym action加入 export_xcargs: "-allowProvisioningUpdates",
参考: https://www.jianshu.com/p/e0c51ab314f7
=========================================

目标

搭建一个自动化打包发布的平台。无论是App Store的发布,是开发期间持续化集成,都需要一个自动化的方案,代替人工来处理那些繁琐的打包发布流程。
本文采用的方案:jenkins + fastlane + svn + firim

Jenkins

关于搭建jenkins 看这篇文集就可以了 -> 手把手教你利用Jenkins持续集成iOS项目
只需要按步骤完成安装、新建job、源码管理就可以了,构建环境暂时不需要设置。

  • 不要使用Xcode构建,因为在导出ipa的时候,jenkins会使用到PackageApplication,这个玩意在xcode 8.3已经被删除了 (当然你可以从旧版本的Xcode拷贝一个过来
  • 多安装一个插件 Message Injector Plugin 用来配置环境变量

FastLane

fastlane 就是一个自动化发布的命令行工具-> 小团队的自动化发布-Fastlane带来的全自动化发布 或者 fastlane github && fastlane doc

安装命令 跟上述文章略有不同

    [sudo] gem install fastlane -n /usr/local/bin

加上 -n /usr/local/bin是因为Mac OS X 10.11 已经禁止修改/usr/bin目录了,那就换个目录安装

Firim

    [sudo] gem install firim -n /usr/local/bin

还需要到fir.im注册个账号,上传ipa需要一个token。

脚本

在工程目录下 完成 fastlane init之后,会生成fastlane文件夹,里面有三个文件需要修改

  • Fastfile => 用来定义所有的lane任务Fastfile帮助
  • Appfile => 是用来存储一些公共信息的,比如app_identifier,apple_id,team_id,itc_team_id等。Appfile帮助
  • Deliverfile => deliver的配置文件Deliverfile帮助

安装帮助文档应该可以轻松完成脚本编写,然而你的工程如果有多个Tareget,你可能还需要在fastlane目录下定义一个env配置文件。

    touch .env.TargetName

.env.TargetName文件内容:

    APP_IDENTIFIER = "com.xxx.xxxxx"

    #苹果开发者账号
    APPLE_ID = "xxxxxxxxxx@xxx.cn"
    FASTLANE_PASSWORD = "xxxxxxx"
    
    TEAM_ID = "xxxxxxxxxx"

    SCHEME_NAME = "TargetName"

    #App 元数据及截图存放路径
    METADATA_PATH = "./metadata/XXXXXXXX"
    SCREENSHOTS_PATH = "./screenshots/XXXXXXXXX"

    #App 元数据及截图下载时 直接覆盖 不询问
    DELIER_FORCE_OVERWRITE= true

    #版本号 如果不使用,就按照Xcode配置
    #APP_VERSION = 1.0.1

    #证书
    CODESIGNING_IDENTITY_TO_FIRIM = "iPhone Developer: XXXXX (XXXXXXX)"
    CODESIGNING_IDENTITY_TO_APPSTORE = "iPhone Distribution: XXXXX (XXXXXXXXX)"

Appfile文件:ENV[‘xxxx’] 就是读取.env文件里面的配置

    app_identifier ENV['APP_IDENTIFIER'] # The bundle identifier of your app
    apple_id ENV['APPLE_ID'] # Your Apple email address

    team_id ENV['TEAM_ID'] # Developer Portal Team ID

    # you can even provide different app identifiers, Apple IDs and team names per lane:
    # More information: https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Appfile.md

Deliverfile文件:如果你想使用deliver上传元数据截图以及完成提交,就需要根据帮助文档完善后续的参数

    ###################### More Options ######################
    # If you want to have even more control, check out the documentation
    # https://github.com/fastlane/fastlane/blob/master/deliver/Deliverfile.md


    ###################### Automatically generated ######################
    # Feel free to remove the following line if you use fastlane (which you should)

    # The bundle identifier of your app
    app_identifier ENV['APP_IDENTIFIER'] 

    # your Apple ID user
    username ENV['APPLE_ID'] 

    # 元数据的路径
    metadata_path ENV['METADATA_PATH']
    screenshots_path ENV['SCREENSHOTS_PATH']

    #App store 中待发布的 App 版本,若每个 target 的版本号不同,可以通过.env 文件来分别定义
    app_version ENV['APP_VERSION']

    #下载 metadata 及 screenshots 时直接覆盖,不询问
    force true

可以使用以下命令 从iTC上下载 元数据和截图

    fastlane deliver download_metadata -a  com.xxx.xxxxx -m ./metadata/XXXXXXXX -u  xxxxxxxxxx@xxx.cn
    fastlane deliver download_screenshots -a com.xxx.xxxxx -m ./screenshots/XXXXXXXX -u  xxxxxxxxxx@xxx.cn

接下来就是重头戏 fastfile文件:

   fastlane_version "2.28.3"

   default_platform :ios

   platform :ios do

   desc "Submit one more new Beta Build to Fir.im"
   lane :to_firim_multi do | options |
      schemes = options[:'schemes']
      schemes = schemes[1..-2]
      schemes = schemes.split(",")
      schemes.each do |i| 
        puts "do fastlane to_firim --env " + i.to_str
        sh "fastlane to_firim --env " + i.to_str
     end
   end

   desc "Submit one more new Release Build to AppStore"
   lane :to_appStore_multi do | options |
      schemes = options[:'schemes']
      schemes = schemes[1..-2]
      schemes = schemes.split(",")
      schemes.each do |i| 
         puts "do fastlane to_appStore --env " + i.to_str
         sh "fastlane to_appStore --env " + i.to_str
      end
   end

   desc "Submit a new Beta Build to Fir.im"
   lane :to_firim do | options |
   buildScheme = ENV['SCHEME_NAME']#options[:buildScheme]
   # 如果你用 pod install
   # cocoapods
   # 以下两个action来自fastlane-plugin-versioning,
   # 第一个递增 Build,第二个设定Version。
   # 如果你有多个target,就必须指定target的值,否则它会直接找找到的第一个plist修改
   # 在这里我建议每一个打的包的Build都要不一样,这样crash了拿到日志,可以对应到ipa上
   increment_build_number_in_plist(target: buildScheme)
   # increment_version_number_in_plist(
   #   target: buildScheme,
   #   version_number: ENV['APP_VERSION']
   #   )

   # gym用来编译ipa
   outputDir = "~/firim/ipa/#{Time.now.strftime('%y%m%d')}"
   outputName = "#{buildScheme}-#{Time.now.strftime("%Y-%m-%d %H:%M:%S")}"
   gym(
      scheme: buildScheme,
      workspace: "xxxxxxx.xcworkspace",
      configuration: "Release",
      output_directory: outputDir,
      output_name: outputName,
      include_bitcode: false,
      include_symbols: true,
      codesigning_identity: ENV["CODESIGNING_IDENTITY_TO_FIRIM"],
      silent: true,
      export_options: {
        method: "development", 
        thinning: "<none>"
      }
  )
   # 上传ipa到fir.im服务器,在fir.im获取firim_api_token
   firim(firim_api_token: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
   end

    desc "Submit a new Release Build to AppStore"
    lane :to_appStore do | options |
    buildScheme = ENV['SCHEME_NAME']#options[:buildScheme]
    # 如果你用 pod install
    # cocoapods

    # 以下两个action来自fastlane-plugin-versioning,
    # 第一个递增 Build,第二个设定Version。
    # 如果你有多个target,就必须指定target的值,否则它会直接找找到的第一个plist修改
    # 在这里我建议每一个打的包的Build都要不一样,这样crash了拿到日志,可以对应到ipa上
    increment_build_number_in_plist(target: buildScheme)
    # increment_version_number_in_plist(
    #   target: buildScheme,
    #   version_number: ENV['APP_VERSION']
    #   )
    # gym用来编译ipa
    outputDir = "~/AppStore/ipa/#{Time.now.strftime('%y%m%d')}"
    outputName = "#{buildScheme}-#{Time.now.strftime("%Y-%m-%d %H:%M:%S")}"
    gym(
      scheme: buildScheme,
      workspace: "xxxxxxx.xcworkspace",
      configuration: "Distribution",
      output_directory: outputDir,
      output_name: outputName,
      include_bitcode: false,
      include_symbols: true,
      codesigning_identity: ENV["CODESIGNING_IDENTITY_TO_APPSTORE"],
      silent: true,
      export_options: {
        method: "app-store", 
        thinning: "<none>"
      }
      )
     # 上传appstore 只传ipa包
    deliver(
      skip_screenshots: true,
      skip_metadata: true,
      force: true
     )
     end

完成配置文件的编写就可以使用脚本来自动打包发布

    #完成打包后,自动上传firim,用于开发测试 
    fastlane to_frim_multi schemes:[Target1,Target2,Target3,...] 

    #完成打包后,自动上传AppStore,用于正式发布 
    fastlane to_appStore_multi schemes:[Target1,Target2,Target3,...] 

证书 profile之类的配置都在Xcode中完成,要确保使用Xcode可以正常编译签名

最后一步

在jenkins新增两个构建步骤

屏幕快照

第一步导入一个环境变量
第二步就是执行fastlane命令
最后要做的就是提交到svn,执行jenkins的job就可以自动完成打包发布啦
总的来说,程序员的懒,一定程度上推动了技术的进步~

参考资料

  1. 24. 自动化工具Fastlane笔记一: 安装, 打包,上传(testFlight,app store)
  2. 使用 fastlane 实现对 iOS Multi-Target 的一键打包部署
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容