做完了iOS jenkins自动打包,做个小总结,最好申请个MacBook或者MacMini作为专门的打包机.
有些老插件很久不更新了,新版本的jenkins不兼容这些老插件,或者有bug,最后几乎都是用脚本搞定的.
有些问题找了很久,网上的文章很多都是很久以前的,现在不适用了; 而且很多文章看着日期是最近的,结果内容是拷贝其他的老文章; 有的甚至把很久以前过时不能用的文章抄袭过来,还特么的收费,鄙视CSDN....
后面有时间的话还需要研究组件化怎么自动打包...
参考文章: 参考文档
一.开发环境
1.安装Xcode
2.安装homebrew, rvm等工具
3.安装java,配置java环境变量,安装Jenkins
这一步没啥好说的,网上内容很多
二.Jenkins配置
1.全局属性:系统管理-系统配置-全局属性:
key: PATH
value: 在终端中输入echo $PATH将输出内容复制填写。
2.插件
Git Parameter(用于git分支参数化构建)
xcode integration(没用到)
Keychains and Code Signing Identities(没用到,很长时间不更新的老插件, 新版本的jenkins上能安装,但是上传有bug; 2.263.4 版本以前的jenkins才能用)
Environment Injector(没用到,job内部环境变量)
3.参数化构建
git参数
Git Parameter插件: 分支可智能排序,我用的DESCENDING_SMART,按时间倒序,最新的分支在上面
选项参数:
Scheme(项目target), Configuration(Debug,Release), Method(development, app-store)
字符参数:
Tips(简短信息,比如哪块需要重点测,等等)
三.打包方案
1.shell:
脚本打包方式,较为主流,灵活可控,不用侵入工程中
a.生成项目所需的plist文件
b.有用的脚本
#!/bin/sh
#!/bin/bash --login
(fir-cli需要用这个)
export LANG=en_US.UTF-8
export LANGUAGE=en_US.UTF-8
export LC_ALL=en_US.UTF-8
解锁对login.keychain的访问,codesign可能会用到( 我暂时没用到这些,如果需要使用,自己试试路径啥的对不对)
这两个不知道哪个对,可以都试试
(1).security unlock-keychain -p "password" $HOME/Library/Keychains/login.keychain
(2).
ln -s ~/Library/Keychains/login.keychain-db ~/Library/Keychains/login.keychain
security unlock-keychain -p "login pwd" ~/Library/Keychains/login.keychain
c.获取git最后一次commit提交记录的message信息
网上能查到的信息是一个jenkins插件,但最新版本jenkins不能用; 后来想到git命令估计可以实现,果然有这个命令,cd到工程下,然后执行这个脚本命令
git_change_log=$(git log --pretty=format:“%s” -1)
d.最终脚本
这个脚本不能直接拿去用,只能作为参考,因为是根据我自己项目需要所写的脚本,需要做一些提前设置,比如安装配置一些cli工具,创建plist文件和一些文件夹等等.另外做了脱敏处理,删掉了一部分脚本,去掉了一些项目信息,XXX的地方要根据自己项目,或者是根据自己的电脑用户路径等等做修改.
#!/bin/sh --login
export LANG=en_US.UTF-8
echo "===========构建参数=========="
if test "$Configuration" = "Test"; then
Configuration_CN="测试环境"
elif test "$Configuration" = "TF"; then
Configuration_CN="TestFlight环境"
else
Configuration_CN="正式环境"
fi
if test "$Method" = "development"; then
Method_CN="普通包"
else
Method_CN="上线包"
fi
echo "=====分支: ${Branch}"
echo "=====target: ${Scheme}"
echo "=====网络环境: ${Configuration_CN}${Configuration}"
echo "=====包类型: ${Method_CN}${Method}"
echo "=====小提示: ${Tips}"
echo "===========打包前的参数=========="
# 计时
SECONDS=0
# 工程名(根据项目自行填写)
WORKSPACE_NAME="XXX"
# TARGET名称(根据项目自行填写)
SCHEME_NAME=${Scheme}
# 设置配置标识,编译环境
configuration=${Configuration}
# 工程源码目录
CODE_PATH="${WORKSPACE}"
# 导包方式
EXPORT_METHOD=${Method}
# 日期
DATE=$(date +%Y-%m-%d_%H-%M-%S)
# xcarchive文件名字
ARCHIVE_NAME="${SCHEME_NAME}_${DATE}_${Configuration}_${Method}.xcarchive"
# 存放ipa的文件夹名称
IPA_FOLDER_NAME="${SCHEME_NAME}_${DATE}_${Configuration}_${Method}"
#路径
ROOT_PATH="/Users/adamshi/Documents/Work/Archive"
# xcarchive文件路径
ARCHIVE_PATH="${ROOT_PATH}/XCArchive/${SCHEME_NAME}/${ARCHIVE_NAME}"
#要上传的ipa文件路径
IPA_PATH="${ROOT_PATH}/IPA/${SCHEME_NAME}/${IPA_FOLDER_NAME}"
echo "ARCHIVE_PATH: ${ARCHIVE_PATH}"
echo "IPA_PATH: ${IPA_PATH}"
# 导包方式配置文件路径
if test "$EXPORT_METHOD" = "ad-hoc"; then
EXPORT_METHOD_PLIST_PATH=${ROOT_PATH}/Plist/AdHocExportOptions.plist
elif test "$EXPORT_METHOD" = "app-store"; then
EXPORT_METHOD_PLIST_PATH=${ROOT_PATH}/Plist/AppStoreExportOptios.plist
elif test "$EXPORT_METHOD" = "enterprise"; then
EXPORT_METHOD_PLIST_PATH=${ROOT_PATH}/Plist/EnterpriseExportOptions.plist
else
EXPORT_METHOD_PLIST_PATH=${ROOT_PATH}/Plist/DevelopmentExportOptions.plist
fi
# 指ipa定输出文件夹,如果有删除后再创建,如果没有就直接创建
if test -d ${IPA_PATH}; then
rm -rf ${IPA_PATH}
mkdir -pv ${IPA_PATH}
echo ${IPA_PATH}
else
mkdir -pv ${IPA_PATH}
fi
# 进入工程源码根目录
cd "${CODE_PATH}"
# 执行pod
#pod install --verbose --no-repo-update
#mkdir -p build
echo "===========开始打包=========="
# 清除工程
echo "++++++++++++++++clean++++++++++++++++"
xcodebuild clean -workspace ${WORKSPACE_NAME}.xcworkspace \
-scheme ${SCHEME_NAME} \
-configuration ${configuration}
# 将app打包成xcarchive格式文件
echo "+++++++++++++++++archive+++++++++++++++++"
xcodebuild archive -workspace ${WORKSPACE_NAME}.xcworkspace \
-scheme ${SCHEME_NAME} \
-configuration ${configuration} \
-archivePath ${ARCHIVE_PATH}
#xcarchive文件非空判断
if [ -e "${ARCHIVE_PATH}" ];then
echo "生成xcarchive成功"
else
echo "生成xcarchive失败,停止构建"
exit 1
fi
# 将xcarchive格式文件打包成ipa
echo "+++++++++++++++++ipa+++++++++++++++++"
xcodebuild -exportArchive \
-archivePath ${ARCHIVE_PATH} \
-exportPath "${IPA_PATH}" \
-exportOptionsPlist ${EXPORT_METHOD_PLIST_PATH} \
-allowProvisioningUpdates
#IPA文件非空判断
if [ -e "${IPA_PATH}" ];then
echo "生成IPA成功"
else
echo "生成IPA失败,停止构建"
exit 1
fi
echo "===========打包完成后的参数=========="
#最后一次git提交记录的message信息
git_change_log=$(git log --pretty=format:“%s” -1)
#代码工程内商户对应的info.plist路径
scheme_infoplist_path="${CODE_PATH}/${SCHEME_NAME}/${SCHEME_NAME}.plist"
#获取DisplayName(有的target和displayName不一样)
Bundle_Display_Name=$(/usr/libexec/PlistBuddy -c "print :CFBundleDisplayName" "${scheme_infoplist_path}")
echo "Bundle_Display_Name: ${Bundle_Display_Name}"
#xcarchive包内的plist路径
xcarchive_infoplist_path="${ARCHIVE_PATH}/Info.plist"
#BundleID
BundleID=$(/usr/libexec/PlistBuddy -c "print :ApplicationProperties:CFBundleIdentifier" "${xcarchive_infoplist_path}")
#版本号
versionNumber=$(/usr/libexec/PlistBuddy -c "print :ApplicationProperties:CFBundleShortVersionString" "${xcarchive_infoplist_path}")
#build号
buildNumber=$(/usr/libexec/PlistBuddy -c "print :ApplicationProperties:CFBundleVersion" "${xcarchive_infoplist_path}")
echo "BundleID: ${BundleID}"
echo "versionNumber: ${versionNumber}"
echo "buildNumber: ${buildNumber}"
#符号表上传bugly相关参数
dSYM_Path=${ARCHIVE_PATH}/dSYMs/${Bundle_Display_Name}.app.dSYM
bugly_platform="IOS"
if test "$SCHEME_NAME" = "XXX"; then
bugly_appid="1234567890"
bugly_appkey="1234567890"
elif test "$SCHEME_NAME" = "YYY"; then
bugly_appid="abcdefgh"
bugly_appkey="abcdefgh"
else
bugly_appid=""
bugly_appkey=""
fi
echo "==========上传内测托管平台=========="
if test "$EXPORT_METHOD" = "app-store"; then
echo "==========线上包逻辑=========="
echo "==========开始上传阿里云=========="
#平台名
Platform_Name="ipa包已上传阿里云OSS, \n\
符号表文件已上传bugly"
#app icon base64
Image_Base64=$(base64 ${ROOT_PATH}/Icon/${SCHEME_NAME}.png)
#ipa名字
Ipa_Name=${SCHEME_NAME}_${DATE}_V${versionNumber}_${Ipa_Configuration_Type}.ipa
Ipa_Name_UTF8=${SCHEME_NAME}_${DATE}_V${versionNumber}_${Ipa_Configuration_UTF8}.ipa
#下载地址
Download_URL="https://shenzhen.aliyuncs.com/iOS/${SCHEME_NAME}/${Ipa_Name_UTF8}"
echo "阿里云OSS下载地址: ${Download_URL}"
#app-store, 企业签或TF签, 上传alioss
/Users/adamshi/ossutilmac64 cp ${IPA_PATH}/${Bundle_Display_Name}.ipa oss://file-app/iOS/${SCHEME_NAME}/${Ipa_Name}
echo "==========阿里云上传结束=========="
echo "==========即将上传符号表到bugly=========="
#上传符号表到bugly
java -jar /Users/adamshi/Documents/Work/Archive/Bugly/buglyqq-upload-symbol.jar \
-appid ${bugly_appid} \
-appkey ${bugly_appkey} \
-bundleid ${BundleID} \
-version ${versionNumber} \
-platform ${bugly_platform} \
-inputSymbol ${dSYM_Path}
echo "==========符号表已上传bugly=========="
else
echo "==========测试包逻辑==========="
echo "==========开始上传fir=========="
Short="${SCHEME_NAME}${configuration}"
#测试包, 上传fir
fir publish ${IPA_PATH}/${Bundle_Display_Name}.ipa -H \
-Q \
-S \
-s "${Short}" \
-T aaaaaaaaaa \
-c "${SCHEME_NAME}_${BUILD_NUMBER}
分支: ${Branch}
changeLog: ${git_change_log}
Tips: ${Tips}"
#平台名
Platform_Name="已上传至Fir"
#二维码base64
Image_Base64=$(base64 ${IPA_PATH}/fir-${Bundle_Display_Name}.png)
Ipa_Configuration_Type="IPA包"
#ipa名字
Ipa_Name=${SCHEME_NAME}_${DATE}_V${versionNumber}_${Ipa_Configuration_Type}.ipa
#下载地址
Download_URL="http://d.maps9.com/${Short}"
echo "fir下载地址: ${Download_URL}"
echo "==========上传fir结束=========="
fi
echo "==========删除工程文件=========="
rm -rf $ARCHIVE_PATH
rm -rf $IPA_PATH
echo "已删除构建时生成的xcarchive和IPA文件"
echo "==========开始发送teams通知=========="
#图片url
Image_Base64_URL="data:image/png;base64,${Image_Base64}"
#Image_Base64_URL="https://image.baidu.com/search/detail?ct=503316480&z=0&ipn=d&word=Applelogo&step_word=&hs=0&pn=3&spn=0&di=7108135681917976577&pi=0&rn=1&tn=baiduimagedetail&is=0%2C0&istype=2&ie=utf-8&oe=utf-8&in=&cl=2&lm=-1&st=-1&cs=2040973445%2C2450782249&os=1098261057%2C2881870715&simid=4105555652%2C494769902&adpicid=0&lpn=0&ln=1341&fr=&fmq=1657265865057_R&fm=result&ic=&s=undefined&hd=1&latest=0©right=0&se=&sme=&tab=0&width=&height=&face=undefined&ist=&jit=&cg=&bdtype=0&oriquery=&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%3A%2F%2Fpic.51yuansu.com%2Fpic3%2Fcover%2F01%2F21%2F32%2F5905a3b241978_610.jpg%26refer%3Dhttp%3A%2F%2Fpic.51yuansu.com%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Dauto%3Fsec%3D1659857895%26t%3D26f6c80dd9bbde7af85a66b6b07f69b1&fromurl=ippr_z2C%24qAzdH3FAzdH3Fooo_z%26e3Bc8y7wgf7_z%26e3Bv54AzdH3FfvAzdH3Ffssgphxzzp_z%26e3Bip4s&gsm=4&rpstart=0&rpnum=0&islist=&querylist=&nojc=undefined&dyTabStr=MCwzLDYsMiw0LDEsNSw3LDgsOQ%3D%3D"
echo "teams参数:
Ipa_Name: ${Ipa_Name}
git_change_log:${git_change_log}
Tips: ${Tips}
Platform_Name:${Platform_Name}
Download_URL:${Download_URL}
Image_Base64_URL:${Image_Base64_URL}"
#teams发通知
curl -H 'Content-Type: application/json' \
-d "{
\"type\":\"message\",
\"attachments\":[
{
\"contentType\":\"application/vnd.microsoft.card.adaptive\",
\"contentUrl\":null,
\"content\":{
\"$schema\": \"http://adaptivecards.io/schemas/adaptive-card.json\",
\"type\": \"AdaptiveCard\",
\"version\": \"1.0\",
\"body\": [
{
\"type\": \"FactSet\",
\"facts\": [
{
\"title\": \"Git分支:\",
\"value\": \"${Branch}\"
},
{
\"title\": \"Target:\",
\"value\": \"${Scheme}\"
}
]
},
{
\"type\": \"FactSet\",
\"facts\": [
{
\"title\": \"网络环境:\",
\"value\": \"${Configuration_CN}${Configuration}\"
},
{
\"title\": \"上线包或普通包:\",
\"value\": \"${Method_CN}\"
}
]
},
{
\"type\": \"FactSet\",
\"facts\": [
{
\"title\": \"Tips:\",
\"value\": \"${Tips}\"
},
{
\"title\": \"Git最后提交内容:\",
\"value\": \"${git_change_log}\"
}
]
},
{
\"type\": \"FactSet\",
\"facts\": [
{
\"title\": \"iOS-IPA包名:\",
\"value\": \"${Ipa_Name}\"
},
{
\"title\": \"内测托管平台:\",
\"value\": \"${Platform_Name}\"
}
]
},
{
\"type\": \"Image\",
\"url\": \"${Image_Base64_URL}\",
\"size\": \"Stretch\",
\"width\": \"200px\",
\"height\": \"200px\",
\"horizontalAlignment\": \"Center\"
},
{
\"type\": \"ActionSet\",
\"actions\": [
{
\"type\": \"Action.OpenUrl\",
\"id\": \"9cbbcc71-be19-8f72-524d-5aa6c8a096e8\",
\"title\": \"去下载\",
\"url\": \"${Download_URL}\"
}
],
\"horizontalAlignment\": \"Left\"
}
],
\"padding\": \"None\",
\"@type\": \"AdaptiveCard\",
\"@context\": \"http://schema.org/extensions\"
}
}
]
}" \
https://hyperlegend.webhook.office.com/webhookb2/xxxxx
#发送teams通知结束
echo "==========发送teams通知结束=========="
# 输出打包总用时
echo "==========打包总用时: ${SECONDS}s=========="
2.plugin:
xcode integration插件打包,最终没用这个方案
3.fastlane:
类似cocoapods的模式,比较方便,也很强大,但是会有一些配置文件侵入到工程中,也没用这个方案
4.Xcode Server
苹果官方的自动打包方案,集成在xcode里面,比较简单方便,但是好像用的人不多,不如jenkins功能那么全面
四.上传内测平台
1.蒲公英
网上文章较多,不多介绍
2.fir
安装fir-cli工具,添加脚本
3.阿里云oss
安装和配置阿里云oss工具:ossutil和ossbrowser; 添加上传ipa包到oss的脚本
五.符号表上传
1.友盟
2.bugly
下载bugly的jar工具,配置脚本
六.发送通知
1.钉钉
配置webhook
2.企业微信
配置webhook
3.微软Teams(这玩意真难用....)
配置Incoming Webhooks, 调用curl命令
七.节点
把打包机设置成打包子节点,然后在运维的jenkins网站上配置好子节点,就可以多人使用了,开发和测试都可以比较舒服的打包
八.内网穿透,内网映射到外网
如果想远程多人使用,又没有配置节点,可以做内网穿透; 收费版本会稳定,网速也比较好, 但是其实免费版本就够用了,只不过不太稳定...
有几种方式:
(一).ngrok
1.国外官网ngrok
https://ngrok.com/
国外服务器,比较慢,而且会有个中间页,需要做其他配置才能去除中间页; 而且没有免费版
2.小米球ngrok
也是基于ngrok,但是服务器在国内,速度较快; 不过免费版不太稳定,经常报错,报错的话,刷新一下网址就行; VIP是一次性付款,永久使用,还算合适
3.Sunny-Ngrok
https://www.ngrok.cc/
服务器在国内,也有免费版,免费版也是不稳定; 收费版是每月订阅,需要一直付钱,看需要吧
4.natapp
(二). Frp
国内的:
https://gofrp.org/
(三).其他工具
内网穿透工具比较(ngrok,frp,lanproxy,goproxy,nps)
九.提高打包编译速度
ccache
十.遇到的问题和坑
2022-10-12 17:49:29.074 xcodebuild[31843:10154938] DVTPortal: Service '<DVTPortalViewDeveloperService: 0x600003729100; action='viewDeveloper'>' encountered an unexpected result code from the portal ('1100')
2022-10-12 17:49:29.074 xcodebuild[31843:10154938] DVTPortal: Error:
Error Domain=DVTPortalServiceErrorDomain Code=1100 "Your session has expired. Please log in." UserInfo={payload={
creationTimestamp = "2022-10-12T09:49:29Z";
httpCode = 200;
protocolVersion = QH65B2;
requestUrl = "[https://developerservices2.apple.com/services/QH65B2/viewDeveloper.action](https://developerservices2.apple.com/services/QH65B2/viewDeveloper.action)";
responseId = "3f0a5d82-c60b-4332-bd82-fcff6b2a6d17";
resultCode = 1100;
resultString = "Your session has expired. Please log in.";
userLocale = "en_US";
userString = "Your session has expired. Please log in.";
}, NSLocalizedDescription=Your session has expired. Please log in.}
遇到这样的报错,说是登录过期,请重新登录.
解决办法:
我试过前两种方法,都是好不了多长时间,可能又会出现; 然后我试了第三种方法,目前解决了,没再出现这个问题
1.重新下载证书文件
Xcode-Preferences-Accounts-选中账号,点击Download Manuel Profiles.重新下载证书文件
2.重新登录
Xcode-Preferences-Accounts退出账号,然后重新登录,然后下载证书文件
3.重新获取权限
先把xcode账户退出登录 - 把xcode退出-终端执行如下命令
defaults write com.apple.dt.Xcode DVTDeveloperAccountUseKeychainService_2 -bool NO
打开xcode,重新登录账户,然后下载相关证书; 打包过程中会重新申请权限,点始终允许
来源: Using Automatic Code Signing from the Command Line via SSH Session
4.打包命令添加解锁逻辑
security unlock-keychain -p PASSWORD
来源: Constant "Your session has expired" (error 1100)
5.打开两步验证
Xcode export/upload error: Your session has expired. Please log in
网友评论