iOS/macOS 真的颇有趣。 你能够在不少领域得到知识!你可能会了解 Bezier 或 3D 变换等图形技术。你也须要了解如何使用数据库、设计高效的架构。此外,你应该掌握嵌入式系统的内存管理方式(特别是那些处于 MRC 时代的人)。全部这些使得 iOS/macOS 的开发如此多元化而且具备挑战性。前端
在这篇文章里,咱们将要学习你可能想了解的另外一些知识:持续交付(CD)。持续交付是一种软件方法,可帮助你随时可靠地发布产品。持续交付(CD)一般带有术语持续集成(CI)。CI 也是一种软件工程技术。这意味着系统始终将开发者的工做不断地合并到主线。CI 和 CD 不只对一个大团队有用,并且对单人团队也有用。若是你是单人团队的惟一开发人员,CD 对你来讲可能意味着更多,由于每一个应用程序开发人员都没法避免交付。所以,本文将重点介绍如何为你的应用程序构建 CD 系统。幸运的是,全部这些技术也能够用于构建 CI 系统。android
想象一下咱们正在开发一款名为 Brewer 的 iOS 应用,咱们的工做流大概像下图这样:ios
首先,咱们开发。而后 QA 组的同事帮咱们手工测试应用。QA 人员批准构建测试后,咱们放出(提交到 AppStore 待审核)咱们的应用。在不一样的阶段中,咱们有不一样的环境。在开发期,咱们建立的应用在一个测试环境中,以备平时测试。当 QA 组正在测试时,咱们准备一个生产环境的应用,这多是每周专门提供给 QA 测试用。最后,咱们提交一个生产环境的应用。这样,最终构建可能没有一个明确的时间表。git
让咱们深刻了解交付部分。你可能会发现咱们在构建测试应用程序方面有不少重复的工做。这是 CD 系统能够帮助你的。具体来讲,咱们的 CD 系统须要:github
如下是咱们在本文中要作的事情:正则表达式
开始以前,你可能须要看看这些:数据库
若是你是个忙碌的帅哥或靓女,别担忧,我为你在公共仓库里准备了 Brewer 应用以及一些示例脚本!swift
那么,让咱们开始吧!后端
咱们一般在开发人员测试阶段链接到开发服务器或测试服务器。咱们还须要在将应用发给 QA 组测试,或 AppStore 时,链接到生产服务器。经过编辑代码切换服务器可能不是一个好主意。这里咱们使用 Xcode 中的构建配置和编译器标志。咱们不会详细介绍配置。若是你对该设置该兴趣,能够看看这篇 Yuri Chukhlib 写的不错的文章:xcode
在咱们的 Brewer 项目中,咱们有三个构建选项:
每一个都映射到特定的 Bundle 标识:
咱们经过设置标识,来让代码知晓咱们正在用哪一个环境。
所以咱们能够像这样来写:
#if PROD
print(“We brew beer in the Production”)
#elseif STG
print(“We brew beer in the Staging”)
#endif
复制代码
如今咱们可以不用修改代码,经过构建选项来切换测试环境与生产环境了!🎉
这是每一个 iOS / macOS 开发人员熟知的红色按钮。咱们经过取消选中此框来启动每一个项目。但为何它如此臭名昭着?你可能知道它会下载证书和配置文件,并将其嵌入到你的项目和系统中。若是有任何文件遗漏,它会为你制做一个新文件。对于单人的项目组来讲,这里不会有问题。可是若是你在一个大团队中,你可能会无心中刷新原始证书,而后因为证书无效而致使构建系统中止工做。对咱们来讲,这是一个隐藏了太多信息的黑匣子。
因此在咱们 Brewer 项目中,咱们想手动作这件事,在咱们的配置中有有三个应用 ID:
咱们将在这篇文章里关注前两个配置,如今咱们要准备:
提醒下,咱们须要 p12 格式的证书文件,由于咱们但愿其可用在不一样的机器上,只有 .p12 格式包含了证书的私钥。看这篇文章来了解如何将 .cer(DEM 格式)文件转换为 .p12(P12 格式)格式文件。
如今目录下有咱们的证书签名文件:
这些文件由 CD 系统使用,因此请将该文件夹放在 CD 机器上。 请不要将这些文件放到你的项目中,不要将它们提交到你的项目仓库。 将代码签名文件托管在不一样的私有仓库中能够。你可能但愿了解有关安全问题的讨论,能够看看 match — fastlane docs。
fastlane 是让开发和发布工做流自动化的工具。好比,它能够经过一个脚本构建应用、运行单元测试、向 Crashlytics 上传二进制。你不须要一步一步手动地作这些事。
在这个项目中,咱们将要用 fastlane 完成两项任务:
这两种方法之间的区别仅仅在于配置。共同的任务包括:
明确了任务,咱们如今能够开始编写 fastlane 脚本了。咱们将使用 Swift 版 fastlane 在咱们的项目中编写咱们的脚本。Swift 版 fastlane 还在测试阶段,运行一切良好,但除了:
可是用 Swift 编写脚本使得开发人员更易于阅读和维护。并且你能够轻松地将 Swift 脚本转换为 Ruby 脚本。因此让咱们试试吧!
首先初始化咱们的项目(还记得 Bundler 吧?):
bundler exec fastlane init swift
复制代码
而后,你能够在 fastlane/Fastfile.swift 中找到脚本。在脚本中,有一个 fastfile 类。这是咱们的主要程序。在本类中用 Lane 为后缀命名的每个方法都是一个 lane。咱们能够将预约义的动做添加到 lane,并使用命令执行 lane:
bundle exec fastlane <lane name>.
复制代码
让咱们填充一些代码:
class Fastfile: LaneFile {
func developerReleaseLane() {
desc("Create a developer release")
package(config: Staging())
crashlytics
}
func qaReleaseLane() {
desc("Create a qa release")
package(config: Production())
crashlytics
}
}
复制代码
咱们为任务建立两个 lane:developerRelease 和 qaRelease。这两个任务都作了一样的事:用指定配置来构建打包,并将导出的 ipa 上传到 Crashlytics。
两个 lane 都有一个 package 方法。package() 方法的声明看起来是这样:
func package(config: Configuration) {
}
复制代码
参数时一个遵循 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 }
}
复制代码
而后咱们建立两个两个遵循该协议的结构体:
struct Staging: Configuration {
var certificate = "ios_distribution"
var provisioningProfile = "Brewer_Staging"
var buildConfiguration = "Staging"
var appIdentifier = "works.sth.brewer.staging"
var exportMethod = "ad-hoc"
}
struct Production: Configuration {
var certificate = "ios_distribution"
var provisioningProfile = "Brewer_Production"
var buildConfiguration = "Production"
var appIdentifier = "works.sth.brewer.production"
var exportMethod = "ad-hoc"
}
复制代码
使用该协议,咱们可以确保每一个配置都具备所需的设置。每当咱们有新的配置时,咱们不须要编写 package 的详细信息。
那么,package(config:) 看起来如何?说爱你他须要从文件系统中导入证书。记住咱们的代码签名文件夹,咱们用 importCertificate action 来实现咱们的目标。
importCertificate(
keychainName: environmentVariable(get: "KEYCHAIN_NAME"),
keychainPassword: environmentVariable(get: "KEYCHAIN_PASSWORD"),
certificatePath: "\(ProjectSetting.codeSigningPath)/\(config.certificate).p12",
certificatePassword: ProjectSetting.certificatePassword
)
复制代码
keychainName是你的钥匙串的名称,默认名称是『登陆』。keychainPassword 是你钥匙串的密码,fastlane 使用它来解锁你的钥匙串。因为咱们将 Fastfile.swift 提交到仓库以确保交付代码在每台计算机中都是一致的,所以在 Fastfile.swift 中将密码写为字符串文字可不是一个好主意。所以,咱们使用环境变量来替换字符串文字。在系统中,咱们用这个方式来保存环境变量:
export KEYCHAIN_NAME=”KEYCHAIN_NAME”;
export KEYCHAIN_PASSWORD=”YOUR_PASSWORD”;
复制代码
在 Fastfile 中,咱们用 environmentVariable(get:) 得到环境变量的值。经过使用环境变量,咱们能够避免代码中出现密码,来显著提升安全性。
回到 importCertificate(),certificatePath 是你的 .p12 证书文件的路径。咱们建立一个名为 ProjectSetting 的枚举来标识共享的项目设置。这里咱们也用环境变量来传递密码。
enum ProjectSetting {
static let codeSigningPath = environmentVariable(get: "CODESIGNING_PATH")
static let certificatePassword = environmentVariable(get: "CERTIFICATE_PASSWORD")
}
复制代码
导入证书后,咱们将设置配置文件。咱们用 updateProjectProvisioning:
updateProjectProvisioning(
xcodeproj: ProjectSetting.project,
profile: "\(ProjectSetting.codeSigningPath)/\(config.provisioningProfile).mobileprovision",
targetFilter: "^\(ProjectSetting.target)$",
buildConfiguration: config.buildConfiguration
)
复制代码
此操做获取配置文件,导入配置文件并在指定的配置中修改你的项目设置。配置文件参数是提供配置文件的路径。目标过滤器使用正则表达式符号来查找咱们要修改的目标。请注意,updateProjectProvisioning 不会修改你的项目文件,所以若是你想在本地计算机上运行它,请当心。CD 任务可有可无,由于 CD 系统不会对代码库进行任何更改。
好的,咱们完成了代码签名部分!如下部分将很是简单明了,请耐心等待!
让咱们如今来构建应用:
buildApp(
workspace: ProjectSetting.workspace,
scheme: ProjectSetting.scheme,
clean: true,
outputDirectory: "./",
outputName: "\(ProjectSetting.productName).ipa",
configuration: config.buildConfiguration,
silent: true,
exportMethod: config.exportMethod,
exportOptions: [
"signingStyle": "manual",
"provisioningProfiles": [config.appIdentifier: config.provisioningProfile] ],
sdk: ProjectSetting.sdk
)
复制代码
buildApp 帮助你构建并导出项目。它底层是调用 xcodebuild 的。除了 exportOptions,每一个参数都很直观。让咱们看看它长啥样:
exportOptions: [
"signingStyle": "manual",
"provisioningProfiles": [config.appIdentifier: config.provisioningProfile] ]
复制代码
不像其余参数,它是一个 dictionary。signingStyle 是你想要代码签名的方式,咱们在这里放置了 manual。provisioningProfiles 也是一个字典。这是应用程序 ID 和相应的配置文件之间的映射。最后咱们完成了 fastlane 设置!如今你能够直接执行:
bundle exec fastlane qaRelease
复制代码
或是这样:
bundle exec fastlane developerRelease
复制代码
来用合适的配置发布测试构建!
Jenkins是一个自动化服务器,可帮助你执行 CI 与 CD 任务。它运行一个 Web GUI 界面,而且很容易定制,因此它对于敏捷团队来讲是一个很好的选择。Jenkins 在咱们项目中的规则如图所示:
Jenkins 获取项目的最新代码并按期为你运行任务。在执行脚本的部分,咱们能够看到 Jenkins 实际上执行了咱们在前几节中所作的任务。但如今咱们不须要本身作,Jenkins 无缝地为你完成了这些!
从每晚构建做业开始,让咱们开始建立一个 Jenkins 任务。首先,咱们建立一个『自定义项目』,并进入它的『配置』页面。咱们须要配置的第一件事是源代码管理(SCM)部分:
Repository URL 是项目源代码的地址。若是你的仓库是私有的,你须要添加 Credentials 以得到仓库读取权限。你能够在 Branches to build 中设置目标分支,一般它是你的默认分支。
而后,接下来咱们能够看到 Builder Trigger 部分。在本节中,咱们能够决定是什么触发了构建做业。根据咱们的工做流,咱们但愿它每周周末晚上开始。
而后咱们检查 Poll SCM,这意味着 Jenkins 会按期轮询指定的仓库。日程安排文本区域要写上如下内容:
H 0 * * 0–4
复制代码
这是什么意思呢?让咱们先看看官方说明:
这个字段遵循 cron 的语法(有细微差异)。具体而言,每行包含由 TAB 或空格分隔的 5 个字段: MINUTE HOUR DOM MONTH DOW MINUTE 分钟小时内的分钟数(0-59) HOUR 小时一天中的小时(0-23) DOM 每个月的一天(1-31) MONTH(1-12) DOW 星期几(0-7),其中0和7是星期日。
它由五部分构成
该字段能够是数字。 咱们也能够用『*』来表示『全部』数字。 咱们用『H』表示一个 hash,自动选择『某个』数字。
因此咱们会这样写:
H 0 * * 0–4
复制代码
意思是:任务将在周日到周四,每晚零点到一点执行。
最后,可是最重要的,来检查下 Build 部分的内容,这是咱们但愿 Jenkins 执行的东西:
export LC_ALL=en_US.UTF-8;
export LANG=en_US.UTF-8;
export CODESIGNING_PATH=”/path/to/cert”;
export CERTIFICATE_PASSWORD=”xxx”;
export KEYCHAIN_NAME=”XXXXXXXX”;
export KEYCHAIN_PASSWORD=”xxxxxxxxxxxxxx”
bundle install — path vendor/bundler
bundle exec fastlane developerRelease
复制代码
前 6 行是设置咱们以前描述的环境变量。第 7 行安装依赖项,包括 fastlane。而后最后一行执行一个名为『developerRelease』的 lane。总之,这个任务每一个工做日晚上都会创建并上传一个 developerRelease。这是咱们第一次每晚构建!🚀
你能够经过单击 Jenkins 项目页面的侧面菜单中的内部版本号来检查构建状态:
咱们一块儿学会了如何用 fastlane 和 Jenkins 建立 CD 系统。咱们了解如何手动管理代码签名。咱们自动为咱们建立了一条运行任务。咱们还探讨了如何在不更改代码的状况下切换配置。最后,咱们创建了一个天天晚上构建应用程序的 CD 系统。
尽管许多 iOS 与 macOS 应用程序是由单人团队建立的,但自动化交付流程仍然是一项高效的改进。经过自动化流程,咱们能够下降配置错误的风险,避免被过时的代码签名所阻塞,并减小构建上传的等待时间。
本文中介绍的工做流程可能与你的工做流程不彻底相同,但掌握每一个团队本身的工做流程和步伐很是重要。因此你必须建立本身的 CD 系统来知足你的团队的须要。经过将这些技术用做构建模块,你必定可以构建本身定制的、更好的 CD 系统!
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。