前言 一般大型的公司都有部署好的Jenkins环境,所以这里不再赘述Jenkins的搭建环境过程,对于Jenkins不了解的童鞋可以自行google下。
Jenkins作为可扩展的自动化服务器,可用作简单的持续集成(CI,Continuous Integration)服务器,或是持续交付(CD,Continuous Deployment)中心。其特点如下:
即时性
。Jenkins是一个基于Java运行的程序,可立即运行,包含Windows、MacOSX、Linux等。
配置简单。可以通过网页轻松配置
插件丰富。目前有1000多插件
扩展性强,Jenkins可以通过插件架构进行扩展,从而为Jenkins提供了无限可能
分布式部署,可以在多态机器上分配工作,帮助开发者快速构建、测试等
所以,对于开发者来说,Jenkins可以简单的通过配置git源和xcode插件配置打包项,就可以实现服务器自动打包了。
Jenkins实现自动打包的方式主要有两种:
备注:以下配置的前提都是基于已部署好的Jenkins服务器进行的。
Jenkins环境配置 全局的配置主要是配置打包所需的证书和签名。使用管理员账户登录Jenkins,安装以下插件:
上传证书
3、填写正确的code signing,如下所示,红框中的内容就是code signing的值
最终效果如下:
上传签名
Mac从节点创建
【前提】:Jenkins的master是Linux系统,首先尝试在Linux打包,但是结果不尽人意,主要有以下问题: 1、pod 命令找不到,使用npm,但是npm命令不会生成 .xcworkspace
文件 2、.xcodebuild
命令找不到,有文档提示使用Facebook提供的xcbuild,但是仍然报错
【结论】 iOS在Linux打包比较复杂,需要找到pod、xcode命令相关的替代命令,以及其他未发现的坑。 所以为了更好的将自动化应用起来,采用了jenkins配置MaxOSX从节点,用于解决pod、xcode命令找不到的问题。
创建Mac从节点的步骤如下:
1、开启要绑定的MacOSX系统的电脑的远程登录:系统偏好设置☞共享☞勾选☞远程登录
。用于获取远程登录的用户名 + IP
在 MacOSX
的user文件中创建 Jenkins
文件夹:/User/Shared/Jenkins
,需要开放读写权限
3、管理员账户登录jenkins,创建从节点:Manage Jenkins ☞ Manage Node and Clouds ☞ New Node
其中Credentials的配置如下所示:add -> Jenkins -> 选择Username with password
5、到此为止,节点创建完成了,会自动连接:MacOSX -> log查看连接日志,以便及时排查问题
创建iOS项目 1、新建项目:New Item,填写项目名称,选择项目风格
通用配置
2、【特别提醒!!!】在项目时,需要配置运行的节点:项目 -> configure -> General
源码配置
3、配置源码 - Source Code Management
说明:
git源码链接:最好是SSH的链接,不要https的
配置源码的分支,一般是master
redential:Add - Jenkins - SSH Username with private key,粘贴SSH
Item环境配置
4、配置编译环境 - Build Environment 配置keychain,选择对应的code signing(注:创建项目后,需要点击Apply,可能才会显示配置好的证书签名)
自动打包脚本配置 构建打包shell脚本,主要分为两部分:
git clone源码 + pod install
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 # 使用方法 # 1、替换自己项目:项目文件夹名(同git名一致)、git地址 # 2、 # 使用cocoapods需要加入这句 #!/bin/bash -l export LANG=en_US.UTF-8 export LANGUAGE=en_US.UTF-8 export LC_ALL=en_US.UTF-8 # 项目的参数 build:表示是否进行编译 if [ $build == false ]; then exit fi if [ -d "xxxxx" ]; then echo '----------已有项目----------' else echo '----------下载项目----------' # 需要替换成自己项目的地址 git clone git@xxxxx.git fi # 进入项目文件夹 cd xxxxx echo 'pod install路径--'${workdir} # 切换分支 git checkout dev # 拉取最新代码 git pull origin 【远程分支名】:【本地分支名】 git pull origin dev:dev # 更新pod # 跳转至podfile所在目录 cd xxxxx cur_dir=$(pwd) echo '-----------install路径--'${cur_dir} echo '---------------------------------------' pod install
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 #项目名 PROJECT_NAME='xxxxx' #App名称 APP_NAME='xxxxx' #scheme名称 SCHEME_NAME='xxxxx' #打包模式 CONFIGURATION=release # 下面两个参数只是在手动指定Pofile文件的时候用到,如果使用Xcode自动管理Profile,直接留空就好 # (跟method对应的)mobileprovision文件名,需要先双击安装.mobileprovision文件.手动管理Profile时必填 mobileprovision_name="" # 项目的bundleID,手动管理Profile时必填 bundle_identifier="" echo "--------------------脚本配置参数检查--------------------" echo "PROJECT_NAME = ${PROJECT_NAME}" echo "APP_NAME = ${APP_NAME}" echo "SCHEME_NAME = ${SCHEME_NAME}" echo "CONFIGURATION = ${CONFIGURATION}" echo "mobileprovision_name = ${mobileprovision_name}" echo "bundle_identifier = ${bundle_identifier}" # 指定输出导出文件夹路径,version是项目配置的参数,每次可手动输入 export_path=${cur_dir}/build-${version} #archive文件存放路径 ARCHIVE_PATH=$export_path/$SCHEME_NAME.xcarchive #ipa文件存放路径 IPA_PATH=${export_path}/ #plist 文件路径:导出ipa所需的文件 EXPORT_OPTIONS_PLIST_PATH=${cur_dir}/ExportOptions.plist echo "--------------------脚本固定路径检查--------------------" echo "export_path = ${export_path}" echo "ARCHIVE_PATH = ${ARCHIVE_PATH}" echo "IPA_PATH = ${IPA_PATH}" echo "EXPORT_OPTIONS_PLIST_PATH = ${EXPORT_OPTIONS_PLIST_PATH}" # 先删除export_path文件 if [ -f "$export_path" ] ; then echo "${export_path}文件存在,进行删除" rm -f $export_path fi #清理工程 echo "--------------------正在清理工程--------------------" xcodebuild clean -workspace ${PROJECT_NAME}.xcworkspace -scheme ${SCHEME_NAME} echo "--------------------清理完成 🎉 🎉 🎉--------------------" security unlock-keychain -p "1109221208l" ~/Library/Keychains/login.keychain #生成archive文件 echo "--------------------正在生成 .xcarchive 文件--------------------" xcodebuild \ -workspace ${PROJECT_NAME}.xcworkspace \ -scheme ${SCHEME_NAME} \ -configuration ${CONFIGURATION} \ -archivePath ${ARCHIVE_PATH} \ archive # 检查是否构建成功 # xcarchive 实际是一个文件夹不是一个文件所以使用 -d 判断 if [ -d "$ARCHIVE_PATH" ] ; then echo " 项目构建成功 🚀 🚀 🚀 " else echo " 项目构建失败 😢 😢 😢 " exit 1 fi echo "------------------------------------------------------" #打ipa包 echo "--------------------正在导出 .ipa 文件--------------------" echo '正在 导出 .ipa 文件' xcodebuild -exportArchive \ -archivePath ${ARCHIVE_PATH} \ -exportPath ${IPA_PATH} \ -exportOptionsPlist ${EXPORT_OPTIONS_PLIST_PATH} \ -allowProvisioningUpdates # 检查文件是否存在 if [ -f "$IPA_PATH/$SCHEME_NAME.ipa" ] ; then echo " 导出 ${SCHEME_NAME}.ipa 包成功 🎉 🎉 🎉 " echo " ipa包路径:$export_path " else echo " 导出 ${SCHEME_NAME}.ipa 包失败 😢 😢 😢 " exit 1 fi # 输出打包总用时 echo "使用Jenkins打包总用时: ${SECONDS}s " echo "------------------------------------------------------" # 将包上传AppStore cd build-${version} ipa_path="$SCHEME_NAME.ipa" # 上传AppStore的密钥ID、Issuer ID api_key="FFX6T2CD4W" issuer_id="69a6de70-bd1a-47e3-e053-5b8c7c11a4d1" echo "--------------------AppStore上传固定参数检查--------------------" echo "ipa_path = ${ipa_path}" echo "api_key = ${api_key}" echo "issuer_id = ${issuer_id}" # 项目的参数 upload:表示是否上传AppStore if [ $upload == false ]; then echo "-------未选择上传AppStore--------" else echo "------------------------------------------------------" echo "-------准备上传AppStore--------" # 验证 validate="xcrun altool --validate-app -f ${ipa_path} -t ios --apiKey ${api_key} --apiIssuer ${issuer_id} --verbose" echo "running validate cmd" $validate uvalidateApp="$($validate)" if [ -z "$uvalidateApp" ]; then echo " 校验IPA失败😢 😢 😢 " echo "------------------------------------------------------" else echo " 校验IPA成功🎉 🎉 🎉 " echo "------------------------------------------------------" # 上传 upload="xcrun altool --upload-app -f ${ipa_path} -t ios --apiKey ${api_key} --apiIssuer ${issuer_id} --verbose" echo "running upload cmd" $upload uploadApp="$($upload)" echo uploadApp if [ -z "$uploadApp" ]; then echo " 传IPA失败😢 😢 😢 " echo "------------------------------------------------------" # 发送上传失败邮件通知 mail -s "ipa上传AppStore失败啦 😢 😢 😢 ,请联系管理员" ${notiEmail} else echo "上传IPA成功🎉 🎉 🎉 " echo "------------------------------------------------------" # 发送上传成功邮件通知 mail -s "ipa上传AppStore成功啦 🎉 🎉 🎉" ${notiEmail} fi fi fi exit 0
备注:mac电脑需要配置上传相关的密钥,参考文章:使用 xcrun altool 上传ipa
使用
遇到的问题
在执行pod 命令时,提示pod command not found
解决方法:在shell脚本最前面加上 #!/bin/bash -l
1 2 #!/bin/bash -l export LANG=en_US.UTF-8
【解决方法】将https://xxxx.git 改成 https://username:password@xxxx.git 这里的username、password就是登录gitlab的用户名+密码
报错:Command CodeSign failed with a nonzero exit code 原因:是 Jenkins,ssh 方式到 slave 机上,默认是没有账户的,但是访问钥匙串要求必须有用户身份,所以添加一步输入密码解锁钥匙串,可以给 Jenkins 一个用户身份。 【解决办法】: build 步骤前添加一步解锁钥匙串
1 security unlock-keychain -p "login pwd" ~/Library/Keychains/login.keychain
所以,综上所述,Jenkins自动打包流程如下: