iOS使用Jenkins自动打包(坑已踩好)

花了2天时间才搞定Jenkins自动打包,记录下流程及遇到的坑;有需要的伙伴可以一步步跟着来,可以省很多时间;

Java环境安装

Jenkins的运行需要Java环境,在安装Jenkins前,确保已经安装了正确版本的Java环境;

官网下载 JDKJRE并安装(之前下载的时候,是必须注册oracle账号才行的)
这里需要注意的是,Java SE的版本一定要选对;之前不懂直接下了个最新版本Java SE 13;结果安装Jenkins后打不开localhost:8080;终端执行jenkins命令时才发现问题:

Jenkins requires Java versions [8, 11] but you are running with Java 13

java SE
JRE

Jenkins安装

下载

安装Jenkins的方式有很多种,我这里是直接官网下载的pkg包安装的;但是不建议大家这样安装,因为这种安装方式会创建一个共享的用户“Jenkins”;所有有关文件的操作都会受权限限制,这也是我之前遇到这么多坑的问题所在;

共享用户

其他安装方式如:

  • 下载jenkins.war, 然后运行Java -jar jenkins.war,进行安装;
  • 使用Homebrew命令安装:brew install jenkins;前提是先安装了Homebrew命令;

下载pkg安装:

傻瓜式安装,一直点继续,最终安装成功;

安装完成之后,Safari可能会自动打开,如果没有自动打开,打开浏览器,输入http://localhost:8080
如果打不开,就可能是Java环境的问题,检查下是否安装及安装的版本是否正确;

unlock

能正常打开,会有如下界面,需要输入密码解锁;

unlock

按照提示,找到/Users/Shared/Jenkins/Home/ 这个目录;这个目录文件只有“ Jenkins”用户才有权限读写:

可以将everyone的权限改为读与写,保险点最好点击左下角添加当前用户的读与写权限;更改后就可以打开initialAdminPassword文件,复制出密码并输入unlock成功;

自定义安装

选择install suggested plugins安装,一路继续下来

最后配置用户名,密码等,安装成功;

安装插件

需要安装的插件列表如下,可以将这些插件提前一次性全添加好,免得之后构建项目的时候再去添加,那样要麻烦很多;

  • 源代码管理插件,我们项目使用的是GitLab,所以需要安装GitLab Plugin,Gitlab Hook Plugin两个插件;其他源码管理也有相关的插件
  • Xcode插件Xcode integration,如果使用脚本打包可以不用这个插件;(从Xcode某个版本开始,已不支持Xcode插件打包)
  • 钥匙串、证书相关插件Keychains and Provisioning Profiles Plugin

插件安装:
选择Manage Jenkins --> Manage Plugins --> 可选插件 --> 搜索需要的插件下载安装

构建项目

创建项目

左侧选择新建-->输入项目名称-->项目类型选择“Freestyle project”

free

"General"一栏填写项目描述即可,其他根据需要勾选;

源码管理

因为使用的是GitLab,这里选择Git;填写自己项目仓库URL:URL也是支持http,ssh两种协议的;需要注意的是协议不同,需要的Credentials凭证也不一样;http协议需要账户名密码,ssh协议需要配置公钥,私钥;我这里直接选用http协议URL,配置Credentials简单点;
没有Credentials,需要添加:

http协议的,凭据类型选择“Username with password”,设置用户名,密码即可;

一个比较坑的地方是,添加完账号密码后,拉取源码时仍会报错:Failed to connect to repository : Command "git ls-remote -h -- http://xxxx HEAD" returned status code 128:

要解决这个问题,还需要配置GitLab的token:
登录GitLab-->设置-->访问令牌-->填写姓名,选择api-->创建;

git

创建成功后会得到token字符串,将其复制;
回到Jenkins,再添加一个凭证,选择“GitLab API token”,输入刚复制的token添加完成;

回到刚才的构建界面,源码的Credentials选择之前创建的Username with password用户凭证;没有错误提示的话,可以往下配置;

构建触发器

这里可以根据自己的需求,设置如何触发运行项目;比如使用脚本或按时间周期获取或Git有pushed的时候等等;最常用的是定时触发:Build periodically

示例:

30 17 * * *
MINUTE HOUR DOM MONTH DOW
第一个参数代表的是分钟 minute,取值 0~59;
第二个参数代表的是小时 hour,取值 0~23;
第三个参数代表的是天 day,取值 1~31;
第四个参数代表的是月 month,取值 1~12;
最后一个参数代表的是星期 week,取值 0~7,0 和 7 都是表示星期天。

所以 30 17 * * * 表示的就是每天17:30执行一次构建。
如果想指定多个时间的话,以,隔开即可:
30 17,20 * * * 表示每天17:30,20:30执行一次构建;

构建环境

这步是至关重要的,稍有不慎就可能sign失败,打包不了;

  • 首先确保电脑的开发证书,发布证书及描述文件Provisioning Profiles是正常的:证书需要是上传到开发者账号的那台电脑的证书,如果不是的话可以将之前电脑的证书导出p12文件发送到自己电脑再安装即可;实在不行的话,可以将证书描述文件全部删除,由本机重新生成;
  • 将当前账号下的keychain及描述文件复制到Jenkins用户相应目录下;即将当前用户/Users/user/Library的两个文件夹Keychains,MobileDevice复制到/Users/Shared/Jenkins/Library目录下;(坑:这里同样是因为Jenkins用户权限受限,不复制到Jenkins用户下打包时会找不到证书)

由于mac系统的不同,Keychains文件下的login钥匙串文件后缀是不同的;有些版本是login.keychain,有些是login.keychain-db,有些是两者皆有;Jenkins配置证书时需要.keychain文件,因此只有login.keychain-db文件的,可以复制一份将文件名login.keychain-db改为login.keychain就可以了;

  • 回到Jenkins列表,左侧选择Manage Jenkins --> 选择Keychains and Provisioning Profiles Management

  • 配置Keychains:点击选取文件,选取之前复制的/Users/Shared/Jenkins/Library/Keychains/login.keychain文件并上传;然后添加两个Code signing Identity,分别填入钥匙串中开发证书和发布证书的名称(钥匙串选择对应证书-->显示简介-->复制名称全称);

  • 配置Provisioning Profiles:
    点击选取文件,选取之前复制的/Users/Shared/Jenkins/Library/MobileDevice/Provisioning Profiles文件下的描述文件:一般会包括开发描述文件、发布描述文件、iOS team描述文件;将这些描述文件都上传;
    Provisioning Profiles Diretory Path填入Jenkins的描述文件路径:/Users/Shared/Jenkins/Library/MobileDevice/Provisioning Profiles;
    保存即配置完成;

  • 回到构建环境界面,选择Keychains and Code Siging Identifies,分别添加之前配置的开发证书和发布证书

  • 选择Mobile Provisioning Profiles,添加之前配置好的所有描述文件

点击保存,环境构建完毕;

构建

编译、打包

这步主要执行打包操作,因为Xcode打包插件用不了需要使用脚本;
点击增加构建步骤,选择Execute shell,然后在输入框中输入打包脚本;

打包脚本的命令有多种,这里选用xcodebuild及xcrun;
以下是打包脚本,可供参考:

# 工程名
APP_NAME="name"
# 证书
CODE_SIGN_DEVELOPER="iPhone Developer"
# info.plist路径
project_infoplist_path="./${APP_NAME}/Info.plist"

#取版本号
bundleShortVersion=$(/usr/libexec/PlistBuddy -c "print CFBundleShortVersionString" "${project_infoplist_path}")

#取build值
bundleVersion=$(/usr/libexec/PlistBuddy -c "print CFBundleVersion" "${project_infoplist_path}")

DATE="$(date +%Y%m%d)"
IPANAME="${APP_NAME}_V${bundleShortVersion}_${DATE}.ipa"

#要上传的ipa文件路径
IPA_PATH="$HOME/${IPANAME}"
echo ${IPA_PATH}
#echo "${IPA_PATH}">> text.txt

#集成有Cocopods的用法
echo "=================clean================="
xcodebuild -workspace "${APP_NAME}.xcworkspace" -scheme "${APP_NAME}"  -configuration 'Release' clean

echo "+++++++++++++++++build+++++++++++++++++"
xcodebuild -workspace "${APP_NAME}.xcworkspace" -scheme "${APP_NAME}" -sdk iphoneos -configuration 'Release' CODE_SIGN_IDENTITY="${CODE_SIGN_DEVELOPER}" SYMROOT='$(PWD)'

xcrun -sdk iphoneos PackageApplication "./Release-iphoneos/${APP_NAME}.app" -o ~/"${IPANAME}"

这里需要注意的是,脚本中的CODE_SIGN_DEVELOPER设置的就是Xcode的Code Signing Identity:


由于我们项目Signing设置是自动的Automatically manage signing

所以Code Signing Identity也一定要对应,不能选择具体某个证书,而应该设置为iPhone Developer(和Xcode中设置的iOS Developer同样作用,但是不能设置为iOS Developer),让Xcode自动选择,否则打包会报错;
而如果项目Signing设置不是自动的,而是手动选择了team和证书,那Code Signing Identity一定要设置具体的某个证书,而不能是iPhone Developer,否则一样报错;报错信息如下:

Code Signing Error: xxx has conflicting provisioning settings

当觉得sign等一切都没问题后,保存并运行项目,打包时还是报错了:

codesign failed with exit code 1

看情况,还是因为代码签名的原因;
查找了好久的资料,得知这个错误是因为证书访问权限的问题;打包访问证书时,需要输入当前用户密码才能访问,而我们使用的脚本并没有提供所需要的密码;
可以在脚本的build代码前,添加一句设置访问证书的密码的代码:

security unlock-keychain -p "password" /Users/Shared/Jenkins/Library/Keychains/login.keychain

password替换为你自己用户的密码即可;
如果还是报同样的错,就还需要在钥匙串中,设置私钥的访问权限范围:钥匙串-->密钥-->选择开发、发布证书私钥-->右击-->显示简介-->选择访问控制-->设置为允许所有应用程序访问此项目;

这里同样有个坑:更改了密钥的访问控制权限,/Users/user/Library/Keychains也就更改了,所以要像之前一样再次将这个文件复制到/Users/Shared/Jenkins/Library目录下;否则Jenkins用户下的Keychains还是没有更新,还会是签名失败;

当觉得大工快要告成时,运行后遭当头一棒:
这次是xcrun报错

xcrun:error: unable to find utility "PackageApplication"

这是因为从某个版本开始,Xcode没有PackageApplication这个包了;解决方法很简单,github上下载一个,复制到/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/目录下就可以了;

再次运行项目,成功了;

上传至蒲公英平台

以上虽然成功了,但我们只是导出了ipa包;最终目的是上传,交由测试团队测试;这步同样支持脚本方式,上传至蒲公英的脚本如下:

#蒲公英上的User Key
uKey="xxxxxxxx"
#蒲公英上的API Key
apiKey="xxxxxx"

#执行上传至蒲公英的命令
echo "++++++++++++++upload+++++++++++++"
curl -F "file=@${IPA_PATH}" -F "uKey=${uKey}" -F "_api_key=${apiKey}" https://upload.pgyer.com/apiv1/app/upload

这部分脚本可以和上面打包脚本区分,也可以直接写在一起;

最终的构建脚本如图:


参数化构建

经过以上配置后,虽然可以成功打包并上传;但却还不够灵活,比如说现在我们只能打包固定分支的代码(master),如果要打包其他分支的代码,难道每次都要改Git配置吗;还有些App一个项目是有多个target的,我们也没有支持多个target;
类似种种,我们都可以添加参数化构建;

分支参数
  • 下载并安装Git Parameter插件,配置项目:选择参数化项目This projiect is parameterzied-->选择添加参数-->选择Git Parameter-->填写参数名称及描述;参数类型选择Branch or Tag并填写默认分支;

  • 源码管理,指定分支填写刚添加的分支参数:


保存后回到主页,之前的Build变成了Build with Parameters,点击进去就能看到项目分支列表了,选择一个分支点击“开始构建”即可;

target参数

同样的,target我们也可以设置好选择列表项,由Build项目时选择;

  • 选择添加参数-->选择Choice Parameter(还有很多其他参数类型,都是Jenkins自带的)-->填写参数名称,描述,选项;多个选项空行即可;
  • 修改脚本
    之前打包脚本scheme是写死的,此时可以替换为参数形式${Target}:
xcodebuild -workspace "${APP_NAME}.xcworkspace" -scheme "${Target}" -sdk iphoneos -configuration 'Release' CODE_SIGN_IDENTITY="${CODE_SIGN_DEVELOPER}" SYMROOT='$(PWD)' -allowProvisioningUpdates

保存成功后再Build项目,就可以选择Target了:

上传至蒲公英相关参数

以上传蒲公英的更新说明为例:

  • 添加String Parameter类型参数,步骤和前面的类似;
  • 修改脚本updateDescription传入刚添加的参数:
curl -F "file=@${IPA_PATH}" -F "uKey=${uKey}" -F "_api_key=${apiKey}" -F "updateDescription=${Distribution_Comment}" https://upload.pgyer.com/apiv1/app/upload
其他参数,均可以根据实际的参数类型添加


以上,只是完成了最基本的打包工作
强大的Jenkins也不仅限于打包,使用Jenkins进行CI/CD(持续集成,持续部署)现在只迈出了一小步
这些有待后续研究

参考:
Jenkins一:iOS自动打包完整实践
手把手教你利用Jenkins持续集成iOS项目

推荐阅读更多精彩内容