在开发中经常需要打测试包,然后上传至蒲公英等三方平台,这其中需要经历的操作为:
- 拉取代码
- 设置项目的打包环境
- 利用xcode进行打包
- 上传至蒲公英等三方平台
每一次打包上面的过程必不可少,而且都是手工的,本篇文章我们采用CD(Continuous Delivery)持续交付
和CI(Continuous Integration)持续集成
来进行自动化打包一键操作,解放双手,拒绝手动的重复低效率劳动。
CD(Continuous Delivery)
工厂里的装配线以快速、自动化、可重复的方式从原材料生产出消费品。同样,软件交付管道以快速、自动化和可重复的方式从源代码生成发布版本,如何完成这项工作的总体设计称为“持续交付”(CD)
,启动装配线的过程称为“持续集成”(CI)
。持续交付是将应用程序推送到交付环境的自动化,大多数开发团队通常具有一个或多个开发和测试环境,在该环境中会进行应用程序更改以进行测试和审查,本文介绍的Jenkins
就可以完成上面打包过程必经步骤的后三步。
CI(Continuous Integration)
CD
往往和CI
是一起配合着使用的,CI
的概念很广(可以自己百度)在本文主要用来完成上面打包过程必经步骤的第一步,并执行CD
的动作, 这样就完成了上面所说的四个步骤,同时在指定时间内执行CI
动作,本文使用jenkins
来完成CI
动作。
使用CI
与CD
后开发的具体流程将会是如下图所示的:
fastlane和jenkins的安装
fastlane的安装
首先附上官网地址https://docs.fastlane.tools/里面会有详细的使用教程。
-
安装Xcode Command Line Tools
由于fastlane
需要使用Xcode Command Line Tools
所以使用此命令进行安装xcode-select --install
,安装完成后需要在Xcode
->Preferences
->Locations
中选择刚刚安装的Command Line Tools
。
安装bundle
由于fastlane
需要使用bundle
和gem
来管理fastlane
所使用的一些依赖,使用gem install bundler
命名来进行安装。-
安装xcbeautify
由于打包需要使用使用xcodebuild
来进行打包,使用brew install xcbeautify
进行安装,不然会报如下错误:
安装fastlane
使用brew install fastlane
命令进行安装,当然先要安装Homebrew
。
jenkins的安装
直接使用brew install jenkins
命令进行安装,安装完成后使用brew services start jenkins
来启动jenkins
(同样brew services stop jenkins
是关闭jenkins
)在浏览器中输入http://localhost:8080/进入到jenkins
的GUI
管理页面。接下来打开Jenkins
后会让去一个填写password
的页面如下图,存储password
的地方就是图片上那行红色字体目录下,使用终端cat +
红色字体路径就看到了。
然后安装推荐的插件:
进入后首先第一件事就是安装
Xcode integration
插件,选择系统管理’ -- ‘插件管理’
‘,然后搜索进行安装。
至此上面的二个算是初步安装配置完成。
项目的创建
为了更好的演示整个流程,我们采用新建一个项目进行讲解,同时利用.xcconfig
文件来进行环境的管理,具体build configuration
文件的使用可见我另外一篇文章iOS开发中xconfig和script脚本的使用,项目新建了一个stage
环境:
同时利用
Custom Flags
来切换环境:
stage.xcconfig
中定了APP_NAME = AutoBuildStage
,debug.xcconfig
中定了APP_NAME = AutoBuildDebug
,release.xcconfig
中定了APP_NAME = AutoBuildRelease
,同时并在info.plist
增加Bundle display name
这个key
并设置为$(APP_NAME)
:
ViewController
中的代码如下:
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var envLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
#if Debug
envLabel.text = "环境是Debug"
#elseif STG
envLabel.text = "环境是Stage"
#elseif Release
envLabel.text = "环境是Release"
#endif
}
}
这样配置后在Edit Scheme
中选择不同的build configuration
则会envLabel
显示不用的环境信息,同时安装的APP
名字也会是.xcconfig
文件中所设置的值。
利用fastlane初始化项目
cd
到项目所在的目录利用fastlane init swift
进行初始化,会让选择构建的方式,这里输入4采用自定义的方式进行构建。
fastlane
现在是支持使用swift
的,这里我们采用熟悉的swift
来编写脚本,当然swift
写的脚本也是可以装换成Ruby
格式的
构建完成会自动生成如下文件:
项目设置手动签名
在写打包代码前,首先设置项目的签名采用手动签名,项目较大团队较多时一定要采用手动签名的方式,不要使用Xcode
的自动签名方式,因为一旦某个人的签名出问题就会导致其他人拉完代码后也出问题,所以本项目采用手动签名的方式,在此先导出自己的.p12
证书和描述文件以备后面使用。
编写自动打包的代码
打开fastlane/swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj
所在的工程文件,并打开Fastfile.swift
文件里面代码如下所示:
import Foundation
class Fastfile: LaneFile {
func customLane() {
desc("Description of what the lane does")
// add actions here: https://docs.fastlane.tools/actions
}
}
在
Fastfile
类中函数以lane
结尾的称之为一个lane
,在终端中通过fastlane
命令来执行这个lane
,其实就等于执行这个函数。
- 首先新建一个Configuration协议
因为项目配置了Debug
,Release
和stage
三个Configuration
,所以利用协议能更好的改变Configuration
。
protocol Configuration {
/// file name of the certificate
var certificate: String { get }
/// file name of the provisioning profile
var provisioningProfile: String { get }
/// configuration name in xcode project
var buildConfiguration: String { get }
/// the app id for this configuration
var appIdentifier: String { get }
/// export methods, such as "ad-doc" or "appstore"
var exportMethod: String { get }
}
- 新建ProjectSetting枚举
新建一个ProjectSetting
枚举来保存一些项目打包过程中需要的常用配置信息。
enum ProjectSetting {
static var workspace = " iOSAutoBuild.xcworkspace"
static var project = "iOSAutoBuild.xcodeproj"
static var scheme = "iOSAutoBuild"
static var target = "iOSAutoBuild"
static var productName = "iOSAutoBuild"
static let devices: [String] = ["iPhone 8", "iPad Air"]
static let codeSigningPath = "certs"
// 采用环境变量的方式更加安全
static let keyChainDefaultPath = environmentVariable(get: "KEYCHAIN_DEFAULT_PATH").replacingOccurrences(of: "\"", with: "")
static let certificatePassword = ""
static let sdk = "iphoneos15.2"
// 蒲公英平台的信息
static let pgyerApiKey = "xxxxxxxxxxxx";// 填入自己在平台申请的
static let pgyerUserKey = "xxxxxxxxxxxxx";// 填入自己在平台申请的
}
对于
keyChainDefaultPath
变量采用了environmentVariable(get:)
的方法来获取系统的环境变量,系统的环境变量在终端中设置的命令为:export keyChainDefaultPath =”YOUR_ keyChainDefaultPath”
,采用系统变量的好处是更加安全,不用在代码中体现敏感信息。
- 定义一个Configuration
struct Staging: Configuration {
var certificate = "AppleDis"
var provisioningProfile = "AdHocMLife"
var buildConfiguration = "stage"
var appIdentifier = "com.mamba.iOSAutoBuild"
var exportMethod = "ad-hoc"
}
这样想切换环境时只需要更改
buildConfiguration
变量即可。
- 打包时的签名操作
class Fastfile: LaneFile {
var stubKeyChainPassword: String = environmentVariable(get: "KEYCHAIN_PASSWORD")
var keyChainName: String {
return "\(ProjectSetting.productName).keychain"
}
var keyChainDefaultFilePath: String {
return "\(ProjectSetting.keyChainDefaultPath)/\(keyChainName)-db"
}
func package(config: Configuration) {
if FileManager.default.fileExists(atPath: keyChainDefaultFilePath) {
deleteKeychain(name: "\(keyChainName)")
}
// 新建一个以项目名命名的钥匙串
createKeychain(
name: "\(keyChainName)",
password: stubKeyChainPassword,
defaultKeychain: false,
unlock: true,
timeout: 3600,
lockWhenSleeps: true
)
// 导入证书到自定义的钥匙串
importCertificate(
certificatePath: "\(ProjectSetting.codeSigningPath)/\(config.certificate).p12",
certificatePassword: "\(ProjectSetting.certificatePassword)",
keychainName: keyChainName,
keychainPassword: "\(stubKeyChainPassword)"
)
// 更新项目的签名设置
updateProjectProvisioning(
xcodeproj: "\(ProjectSetting.project)",
profile: "\(ProjectSetting.codeSigningPath)/\(config.provisioningProfile).mobileprovision",
targetFilter: "^\(ProjectSetting.target)$",
buildConfiguration: "\(config.buildConfiguration)",
certificate: "\(config.certificate).p12"
)
}
}
特别注意:
updateProjectProvisioning
是会更改项目的设置的,所以最好是打包的机器是额外的一台机器(熟称打包机),当然CD
是不会提交代码到远端仓库的,上面的一些action
可以查看fastlane的官网,里面有详细介绍。
- 打包构建buildApp
buildApp(
scheme: "\(ProjectSetting.scheme)",
clean: true,
outputDirectory: "./打包目录",
outputName: "\(ProjectSetting.productName)_\(dateStr).ipa",
configuration: "\(config.buildConfiguration)",
silent: true,
exportMethod: "\(config.exportMethod)",
exportOptions: optionsArr,
sdk: "\(ProjectSetting.sdk)"
)
dateStr
是当前时间变量,代码中没有体现。
- 最后是打包
func developerStrageLane() {
desc("Mamba Create a developer stage")
package(config: Staging())
}
至此我们就可以利用fastlane
进行打包了,在终端中输入bundle exec fastlane developerStrage
即可,最后会有打包完成的成功信息大致如下;
联动jenkins
上面的部分只是完成了打包,现在继续利用jenkins
来完成定时的远端代码拉取,并打包同时长传蒲公英。fastlane
现在支持swift
,同时也是支持第三方插件的,首先对上面的fastlane
过程中加入cocoapods
和蒲公英的插件。
加入cocoapods
由于前面说了fastlane
是利用gem
和bundle
来管理三方依赖的,所以打开Gemfile
文件,增加gem "cocoapods"
保存,然后当我们在终端执行bundle install — path vendor/bundler
时会安装Gemfile
文件中的依赖,当然一般我们的打包机是自己安装了cocoapods
的。
- 执行
cocoapods
在上面的package
方法上面增加如下代码:
func beforeAll() {
cocoapods()
}
加入蒲公英插件
- pgyer插件的安装
插件安装前修改gemfile
文件,增加如下信息:
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
eval_gemfile(plugins_path) if File.exist?(plugins_path)
按照官网的说法是需要链接gemfile
和后面生成的Pluginfile
文件,cd
到项目所在的目录执行sudo fastlane add_plugin pgyer
,会发现增加了一个Pluginfile
文件,具体的改变直接放官网说的吧:
- 执行pgyer的action
直接使用下面代码上传至蒲公英
pgyer(apiKey:"\(ProjectSetting.pgyerApiKey)", userKey: "\(ProjectSetting.pgyerUserKey)")
新建jenkins任务
进入jenkins
主页,点击新建任务,输入任务名后选择构建自由风格的软件项目。
在源码管理处填入远端仓库地址。
在构建触发器中输入定时构建的时间。
具体的时间语法百度即可,这里是每天每隔半小时自动执行任务。
在构建处选择执行shell,并填入需要执行的命令。
注意:这里可以填入上文提到的系统变量,例如在
cd
语句的后面加上export CERTIFICATE_PASSWORD=”xxx”
或者bundle install — path vendor/bundler
。
最后点击保存即可,可在构建历史中查看历史构建记录。
构建任务的过程中可能会报
bundle :fastlane command not find
的错误,解决办法是在jenkins
的系统管理->系统设置->全局属性->环境变量 增加键PATH
值:终端输出值:(echo $PATH
)
总结:
本文主要介绍了CD
和CI
在软件开发中的运用,利用fastlane
和jenkins
,并配合swift
来实现iOS
项目的自动化打包,并进行了实际的Demo
演示,实际操作可能因环境不同报错,只需要仔细阅读终端错误提示并进行相应修复即可,fastlane
的错误提示机制还是相对友善的。