一、背景
1.1 问题
移动端开发交付项目中,由于交付团队技术沉淀参差不齐,开发流程极不规范,经常出现这打包在本地,甚至是集中在某个外包开发自己,这样的方式存在以下问题:
- 不安全:代码、证书、签名泄漏风险
- 低效:环境不可靠,稳定,打包效率慢,小的改动打包可能涉及到大量的人工操作
- 低质量:受本地环境如缓存、分支、配置等因素影响较大,出包质量参差不齐
- 协同性低:基本集中在某个人身上,高度依赖
- 版本管理差:个人记录,归档基本不存在
- 其他(持续集成、安装分发、权限控制没法做····)
1.2 目标
也是由于存在以上诸多问题,所以,统一的构建流水线是必须的,至少要实现云端统一构建,支持证书管理、版本管理、权限管控、安装分发等能力
1.3 方案
想要实现流水线构建,需要具备以下基本的环境:
- 公网访问
- 打包机(Android linux环境即可、iOS需要xcode环境)
1.3.1 公网访问
这个是交付项目的客观存在,所以不可能直接面向集团内部资源,我们这里就直接考虑入口在GTS统一工作台的大禹研发域
1.3.2 打包机
- Android,linux环境即可,我们平时时候的流水线jenkins服务都运行在linux环境上,所以Android没有太大的问题(Android由于难度较低,本文不做分析。)
- iOS,需要xcode环境,iOS生态是比较封闭的,所以目前调研的有3种方案:
- mac机器,需要解决机器的托管以及外网访问问题
- mac虚拟技术,有一定的技术壁垒,成本较高
- 技术合作
决策:技术合作是成本最低,见效最快的方式。
合作方给我们提供了非常友好的对接方式,他们提供xcode运行环境,剩下的我们自己想怎么玩就怎么玩。
二、设计
想要设计一个移动端流水线,当然不能简单的打个包就行了,我们需要解决的是上面提到的各种问题,以及要基于流水线以及移动端产品的特性而开发一些额外的功能
2.1 功能大图
这里功能简单罗列构建相关核心功能点
项目 |
说明 |
证书管理 |
证书的上传、下载、访问权限管理 |
版本管理 |
构建记录 构建code与源码commit映射 |
产物分发 |
支持扫码安装 支持访问权限配置 支持有效期配置 |
构建参数 |
|
2.2 环境准备
在正式的开始构建前,这里先解决两个技术点:
2.2.1. 合作方的调用链路;
合作方案调用方式
2.2.2 .code specs的访问权限
大部分iOS开发已经是基于pods管理的组件化的方案,所以需要打通私有specs仓库和公共仓库的访问
代码以及仓库访问权限管理
2.3 构建思路
构建步骤主要为以下清单:
由于资源都是在自己的服务器上,所以这里的步骤跳过了所需要的资源的拉取以及xcode,pods版本的切换
步骤 |
项目 |
说明 |
1 |
资源拉取 |
工程源码、证书、profile文件 |
2 |
xcode、pods版本切换 |
根据工程实际情况选择 |
3 |
证书安装 |
核心:证书文件,必须在当前机器安装才能使用 |
4 |
profile描述文件安装 |
核心:描述文件,必须在当前机器安装才能使用 |
5 |
xcodeproj修改配置 |
核心:修改version,bundleId,app_name,证书、描述文件等信息 |
6 |
额外的插件安装 |
比如mPaaS,需要安装mPaaS相关的资源,这个也是基于项目使用了mPaaS插件,所以也就增加了这个的扩展,一般项目可以在配置单默认关闭即可 |
7 |
pods update/install |
核心:依赖拉取 |
8 |
archive |
核心:xocdebuild 构建 |
9 |
exportArchive |
核心:导出ipa |
10 |
deploy |
核心:安装分发配置 |
三、核心点剖析
安全需要,为了讲清楚核心点,以下代码为部分核心代码,并非完整代码,额外需要处理大量的判断逻辑比如:cert和profile以及bundleId、打包类型一致性;证书、描述文件大量时候的删除重装逻辑等。
3.1 证书安装
iOS构建的证书是安装在OSX系统的钥匙链里面,需要使用系统的`security`指令执行创建和访问配置:
#1. 创建security create-keychain -p$keychain_password$keychain_name#2. 解锁security unlock-keychain -p$keychain_password$keychain_name#3. 导入security import $p12_path-k$keychain_name-P$p12_password-A#4. 设置访问权限security set-key-partition-list -S apple-tool:,apple: -k$keychain_password$keychain_name#5. 更改钥匙串配置security set-keychain-settings $keychain_name
3.2 privosion profile描述文件安装
这个描述文件安装就比较简单,复制到指定目录
`${HOME}/Library/MobileDevice/Provisioning\ Profiles`即可,同时修改文件名为profile对应的uuid cp$profile_file${HOME}/Library/MobileDevice/Provisioning\ Profiles/${INSTALL_PROFILE_UUID}.mobileprovision
3.3 xcodeproj修改配置
这一步是个难点,也是个核心大头,由于xcode工程配置用的ruby脚本管理,所以这里的配置也是由ruby进行修改,大体流程如下:
- 找到对应target
- 修改cert和profile
- 修改bundle_id、app_name、build、version_code
- 修改签名机制手动挡
#读出project信息project=Xcodeproj::Project.open(project_path) #检索targetproject.targets.eachdo |target| #读取config对象config=target.build_configuration_list[configuration] #对config配置config.build_settings["CODE_SIGN_STYLE"] ="Manual"config.build_settings["PRODUCT_BUNDLE_IDENTIFIER"] =bundle_idconfig.build_settings["DEVELOPMENT_TEAM"] =team_idconfig.build_settings["CODE_SIGN_IDENTITY"] =full_cert_nameconfig.build_settings["PROVISIONING_PROFILE_SPECIFIER"] =profile_nameend
3.4 pod install
podspec 有墙的因素,导致很多github specs以及墙外资源经常性拉取失败,所以在pods install这个阶段做了三种优化:
- 使用自建/国内specs镜像
- Pods目录上传到代码库,跳过pods install阶段
- 使用缓存机制,对Pods资源缓存
3.5 Archive
3.5.1 首先clean
# 编译前清理工程xcodebuild clean -workspace${workspace_name}.xcworkspace \ -scheme${scheme_name} \ -configuration${build_configuration}
3.5.2 执行archive
xcodebuild archive -workspace${workspace_name}.xcworkspace \ -scheme${scheme_name} \ -configuration${build_configuration} \ -archivePath${export_archive_path} \ CHANNEL=true \ GCC_PREPROCESSOR_DEFINITIONS='$(inherited) CUST_NUM1=99'
可以通过GCC_PREPROCESSOR_DEFINITIONS配置自定义的运行时变量
3.6 exportArchive ipa阶段
执行真正的exportArchive前,需要根据参数`生成export_options_plist文件`
#1. 根据参数生成export_options_plist文件/usr/libexec/PlistBuddy -c"Add :compileBitcode bool ${compileBitcode}"$export_options_plist_path/usr/libexec/PlistBuddy -c"Add :method string ${method}"$export_options_plist_path/usr/libexec/PlistBuddy -c"Add :provisioningProfiles:"$export_options_plist_path### 这里是做了个扩展点echo"$extension_export_plist" | while read line do/usr/libexec/PlistBuddy -c"$line"$export_options_plist_pathdone
#2. 执行exportArchivexcodebuild -exportArchive \ -archivePath${export_archive_path} \ -exportPath${export_ipa_path} \ -exportOptionsPlist${export_options_plist_path} \ -allowProvisioningUpdates
3.7 Deply 内测分发
这个阶段又叫内测分发,iOS 打包产物想要安装到iOS设备上,比Android复杂很多,除了testflight和App Store正常安装外,日常测试也是需要安装,我们这里做了分发功能,通过扫描二维码即可安装(遵循设备注册和企业包灯等的安装限制)
核心点:
itms-services:///?action=download-manifest&url=https://resource.dayu.work/build-packages/xxx/ios-test.plist
启用一个itms-services标签,指向一个plist描述文件,plist格式如下,有几个点必须注意,严格遵守:
- itms-services标签下的url链接协议必须是https
- itms-services标签下的url链接不能有特殊字符,否则会导致plist文件解析失败,比如'%'不能有
<plistversion="1.0"><dict><key>items</key><array><dict><key>assets</key><array><dict><key>kind</key><string>software-package</string><key>url</key><string>https://xxxx.com/xxxxxxx/download</string></dict></array><key>metadata</key><dict><key>bundle-identifier</key><string>com.dayu.buildios</string><key>bundle-version</key><string>1.0.0-MTL-SNAPSHOT</string><key>kind</key><string>software</string><key>title</key><string>流水线测试包</string></dict></dict></array></dict></plist>
总结
本篇文章主要是对IOS构建做了一个从xcodebuild 基础命令的,包括流水线构建流程做了个剖析,感兴趣的可以自行尝试,目前市面也有很多比较成熟的产品,比如fastlane,托管式的集成方式,甚至不用管理证书,但是涉及到一些扩展或者是修改,灵活性就不那么强了,所以懂了原理之后,想怎么玩看读者自己了。