一、使用fastlane

1、安装 fastlane

使用 macOS 或 Linux Ruby 2.0.0及以上版本

输入终端命令

  1. sudo gem install fastlane --verbose

或者

  1. sudo gem install -n /usr/local/bin fastlane

确保 Xcode 安装了最新版本的 命令行工具

  1. xcode-select --install

如果 fastlane 加载缓慢

  1. gem cleanup

2、使用

进入项目根目录

  1. cd ~/xxx

初始化 fastlane

  1. fastlane init

会询问你要安装哪个版本,可以选择手动配置。
image.png
各个选项的意思:

  • 自动截屏
  • 自动 testFlight 配置
  • 自动 AppStore 配置
  • 手动配置

常见问题

fastlane init 过程中,卡在了 bundle update

  1. Ruby 源可能被墙了,查看下源,可以替换为国内最新的,确保只有 gems.ruby-china.com
  1. $ gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/
  2. $ gem sources -l
  3. https://gems.ruby-china.com


2. 假如 ruby 源没有问题的话,看下工程中的 Gemfile 文件。

  1. source "https://rubygems.org"
  2. 替换为
  3. source "https://gems.ruby-china.com"


替换完成后删除 fastlane 文件夹,执行 bundle update 命令,然后重新执行 fastlane init 命令 。

上一步更换 source 后出现报错

  1. [11:00:10]: You have a local Gemfile, but RubyGems isn't defined as source
  2. [11:00:10]: Please update your Gemfile at path `Gemfile` to include
  3. [11:00:10]:
  4. [11:00:10]: source "https://rubygems.org"
  5. [11:00:10]:
  6. [11:00:10]: Update your Gemfile, and run `bundle update` afterwards

不用 care ,不会影响打包。

3、根据项目需求配置文件

可以把 fastlane 工作原理理解成具有一个个的操作,每个操作叫做 lane,每个 lane 中又可以有多个操作单元 action,比如打包,创建文件等。这些 action 顺序执行的。主要配置文件在 fastlane/Fastfile 中配置。

针对 iOS 打包,可以根据需求配置多个 lane,比如

  • lane ad-hoc
  • lane distribution

每个 lane 可以配置多个 action,如打包 gym,截图,上传 fir。最简单的流程只有 gym 这一步。

例如我们想打一个 ad-hoc 包,环境为 Debug,Fastfile 中这样写:

  1. lane:adHoc do # 1 adHoc 代表这次操作的名称
  2. # add actions here: https://docs.fastlane.tools/actions
  3. gym(
  4. workspace: "ChinaExhibition.xcworkspace", # 项目 workspace 名称
  5. configuration: "Debug", # 项目 Build Configuration Debug,还是Release
  6. scheme: "ChinaExhibition", # scheme
  7. silent: true, # 编译时隐藏不必要的信息
  8. clean: true,
  9. output_directory: "/Users/eassy/Documents/MaoCu/打包存储", # ipa 输出 目录
  10. output_name: "ChinaExhibition.ipa", # ipa 输出名称
  11. export_method: "ad-hoc", # 打包类型
  12. export_options:{
  13. provisioningProfiles:{
  14. "com.huizhan.huizhan2c" => "huizhan2cAdHoc" # 指定证书
  15. }
  16. },
  17. export_xcargs: "-allowProvisioningUpdates", # 允许访问证书权限
  18. build_path: "/Users/eassy/Documents/MaoCu/打包存储/ChinaExhibition/" # .xcarchive 文件储存位置
  19. )
  20. end

有个坑,这里我们使用手动证书管理。首先把证书跟.p12 文件安装到电脑上,然后指定 provisioningProfiles 文件的时候,文件后缀 .mobildprovision 是不能带上的,否则一直验证不通过。

  1. export_options:{
  2. provisioningProfiles:{
  3. "com.huizhan.huizhan2c" => "huizhan2cAdHoc" # 指定证书
  4. }
  5. },

4、上传到 fir

打包 action ok 后,添加上传 fir 的action,需要安装插件

  1. fastlane add_plugin fir_cli

然后编辑我们的 lane adHoc,在 gym() 后面添加

  1. fir_cli api_token: "YOUR FIR API TOKEN", changelog: "Hello fir.im"

token 从 fir 获得。

5、配合 jenkins 进行完整流程打包

大体流程如下:

  1. Jenkins 指定分支进行 git pull 。
  2. Jenkins 进行 cocoapods 的安装。
  3. Jenkins 指定版本号,之后会传递给 fastlane 。
  4. fastlane 根据传递过来的版本号,进行 buildNumber 的自增,和 version 的确定。
  5. fastlane 根据版本号和项目名,创建文件夹。
  6. fastlane 打包,并将文件存储到第 5 步创建的文件夹下面。
  7. 上传到 fir 平台或者 AppStore 。

下面从第四步开始,首先要编写我们的 Fastlane 文件,完整的文件在附件中

1). Fastlane 文件编写

要有几个环境变量:

  1. 工程名
  2. scheme 名称
  3. workspace 名称
  4. fir 的 token key
  5. ipa 存储的位置
  1. platform :ios do
  2. desc "paqu lane"
  3. # 几个环境变量,新建工程 Fastlane 需要进行更改
  4. APP_NAME = 'ChinaExhibition'
  5. SCHEME_NAME = 'ChinaExhibition'
  6. WORKSPACE_NAME = 'ChinaExhibition.xcworkspace'
  7. FIR_TOKEN = '337872c92b4b71f50c6c45f8ff2f950a'
  8. SAVE_PATH = '/Users/eassy/Documents/MaoCu/打包存储' # ipa 和 build 文件保存地址

2). 从 jenkins 传递过来 version 并设置

传递给某个指定的 lane

  1. # fastlane [lane] key:value key:value
  2. # 比如传递参数为 version, 具体值为 5.2.0 给 lane adHoc_debug:
  3. fastlane adHoc_debug version:"5.2.0"

Fastlane 文件中这样接收:

  1. lane: adHoc_debug do |options|
  2. buildVersion = options[:version]
  3. increment_version_number(
  4. version_number: "#{buildVersion}"
  5. )
  6. end

3).自定义 action,更改版本号和 build number

自定义 action update_buildnumber_version ,里面使用已发布的 action increment_version_number increment_build_number 来设置 version 和 build number 。

并且将 build number 返回出去,提供给下一步使用。

  1. version = params[:version]
  2. # 确定 version 版本号
  3. other_action.increment_version_number(
  4. version_number: version
  5. )
  6. # 确定 build number 当前日期,加上今天第几次打包,比如今天是 2020.8.31,第一次打包,build number 为 2020083101,第二次打包就是 2020083102
  7. currentTime = Time.new.strftime("%Y%m%d")
  8. build = other_action.get_build_number()
  9. if build.include?"#{currentTime}"
  10. lastStr = build[build.length-2..build.length-1]
  11. lastNumber = lastStr.to_i
  12. lastNumber = lastNumber + 1
  13. lastStr = lastNumber.to_s
  14. if lastNumber < 10
  15. lastStr = lastStr.insert(0,"0")
  16. end
  17. build = "#{currentTime}#{lastStr}"
  18. else
  19. build = "#{currentTime}01"
  20. end
  21. puts("******************| 更新 build #{build} |******************")
  22. other_action.increment_build_number(
  23. build_number:"#{build}"
  24. )
  25. puts("******************| 更新 build 结束 |*******************")
  26. return build

4).创建文件夹,用来保存待会编译出的包

自定义 action create_finder ,创建一个文件夹,供保存使用。

  1. # 根据输入的 version 创建文件夹 项目名 + version + build number
  2. projVersion = params[:version] # 版本号
  3. projName = params[:projName] # 项目名
  4. projBuild = params[:buildNumber]
  5. savePath = params[:savePath]
  6. path = "#{savePath}/#{projName}-#{projVersion}-#{projBuild}"
  7. command = "mkdir #{path}"
  8. Actions.sh(command)
  9. return path

5).调用 gym 打包

具体代码在上面有写

6).上传到 fir

使用插件 fir_cli 来完成

  1. # FIR_TOKEN 要改成具体值
  2. fir_cli api_token: FIR_TOKEN, changelog:"fir 更新 文案"

二、使用shell命令

以下是shell自动打包脚本,可以结合jenkins做自动化打包部署。

  1. # !/bin/bash
  2. # ===============================项目自定义部分(自定义好下列参数后再执行该脚本)============================= #
  3. # 计时
  4. SECONDS=0
  5. # 是否编译工作空间 (例:若是用Cocopods管理的.xcworkspace项目,赋值true;用Xcode默认创建的.xcodeproj,赋值false)
  6. is_workspace="true"
  7. # 指定项目的scheme名称
  8. # (注意: 因为shell定义变量时,=号两边不能留空格,若scheme_name与info_plist_name有空格,脚本运行会失败,暂时还没有解决方法,知道的还请指教!)
  9. scheme_name="LFDFundProject"
  10. # 工程中Target对应的配置plist文件名称, Xcode默认的配置文件为Info.plist
  11. info_plist_name="Info"
  12. # 指定要打包编译的方式 : Release,Debug...
  13. build_configuration="Release"
  14. web_upload="http://10.58.84.183:5001/upload"
  15. # ===============================自动打包部分(无特殊情况不用修改)============================= #
  16. # 导出ipa所需要的plist文件路径
  17. ExportOptionsPlistPath="./AutopackagedScripts/AdHocExportOptionsPlist.plist"
  18. # 返回上一级目录,进入项目工程目录
  19. cd ..
  20. # 获取项目名称
  21. project_name=`find . -name *.xcodeproj | awk -F "[/.]" '{print $(NF-1)}'`
  22. # 获取版本号,内部版本号,bundleID
  23. info_plist_path="Fund/$info_plist_name.plist"
  24. #获取plist信息
  25. bundle_version=`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" $info_plist_path`
  26. bundle_build_version=`/usr/libexec/PlistBuddy -c "Print CFBundleVersion" $info_plist_path`
  27. bundle_identifier=`/usr/libexec/PlistBuddy -c "Print CFBundleIdentifier" $info_plist_path`
  28. bundle_displayName=`/usr/libexec/PlistBuddy -c "Print CFBundleDisplayName" $info_plist_path`
  29. # 指定输出ipa路径
  30. export_path="./build"
  31. # 指定输出归档文件地址
  32. export_archive_path="$export_path/$scheme_name.xcarchive"
  33. # 指定输出ipa地址
  34. export_ipa_path="$export_path"
  35. #设定时间
  36. ipa_date=`date +%m%d%H%M`
  37. # 指定输出ipa名称 : scheme_name + bundle_version
  38. ipa_name="$bundle_displayName-$ipa_date-UAT"
  39. echo "\033[32m************************* 移除旧的构建项目 ************************* \033[0m"
  40. if [ -d "$export_path" ] ; then
  41. echo $export_path
  42. else
  43. mkdir -pv $export_path
  44. fi
  45. # 删除旧.xcarchive文件
  46. rm -rf $export_path/$scheme_name.xcarchive
  47. # 删除旧包
  48. rm -rf $export_path/*.ipa
  49. echo "\033[32m************************* 切换到UAT环境 ************************* \033[0m"
  50. #切换环境
  51. configPlist="Classes/Config/config.plist"
  52. modify_uat=`/usr/libexec/PlistBuddy -c 'Set :environment:uat "YES"' $configPlist`
  53. modify_dev=`/usr/libexec/PlistBuddy -c 'Set :environment:dev "NO"' $configPlist`
  54. modify_pro=`/usr/libexec/PlistBuddy -c 'Set :environment:pro "NO"' $configPlist`
  55. echo "\033[32m************************* 开始构建项目 ************************* \033[0m"
  56. # 判断编译的项目类型是workspace还是project
  57. if $is_workspace ; then
  58. # 编译前清理工程
  59. xcodebuild clean -workspace ${project_name}.xcworkspace \
  60. -scheme ${scheme_name} \
  61. -configuration ${build_configuration}
  62. xcodebuild archive -workspace ${project_name}.xcworkspace \
  63. -scheme ${scheme_name} \
  64. -configuration ${build_configuration} \
  65. -archivePath ${export_archive_path}
  66. else
  67. # 编译前清理工程
  68. xcodebuild clean -project ${project_name}.xcodeproj \
  69. -scheme ${scheme_name} \
  70. -configuration ${build_configuration}
  71. xcodebuild archive -project ${project_name}.xcodeproj \
  72. -scheme ${scheme_name} \
  73. -configuration ${build_configuration} \
  74. -archivePath ${export_archive_path}
  75. fi
  76. # 检查是否构建成功
  77. if [ -d "$export_archive_path" ] ; then
  78. echo "\033[32;1m项目构建成功 🚀 🚀 🚀 \033[0m"
  79. else
  80. echo "\033[31;1m项目构建失败 😢 😢 😢 \033[0m"
  81. exit 1
  82. fi
  83. echo "\033[32m************************* 开始导出ipa文件 ************************* \033[0m"
  84. xcodebuild -exportArchive \
  85. -archivePath ${export_archive_path} \
  86. -exportPath ${export_ipa_path} \
  87. -exportOptionsPlist ${ExportOptionsPlistPath}
  88. mv $export_ipa_path/$scheme_name.ipa $export_ipa_path/${ipa_name}.ipa
  89. # 检查文件是否存在
  90. if [ -f "$export_ipa_path/$ipa_name.ipa" ] ; then
  91. echo "\033[32;1m导出 ${ipa_name}.ipa 包成功 🎉 🎉 🎉 \033[0m"
  92. #open $export_path
  93. else
  94. echo "\033[31;1m导出 ${ipa_name}.ipa 包失败 😢 😢 😢 \033[0m"
  95. exit 1
  96. fi
  97. echo "\033[32m************************* 上传文件 ************************* \033[0m"
  98. #进入文件目录
  99. cd $export_ipa_path
  100. #上传文件到web服务器
  101. fileName=${ipa_name}.ipa
  102. echo $fileName
  103. status=`curl -F "file=@$fileName" $web_upload`
  104. echo "\033[32;1m $status \033[0m"
  105. # 输出打包总用时
  106. echo "\033[36;1m使用PPAutoPackageScript打包总用时: ${SECONDS}s \033[0m"

AutopackagedScripts.zip