• iOS基于jenkins打包+上传蒲公英+钉钉群通知


    背景:iOS稍微大一点的项目,archive一次需要十几分钟,对开发而言,有时候需要切换分支打包,如果异地办公的情况还要提供测试包,非常麻烦,所以提供远程打包是必不可少的。

    一、本地配置jenkins

    安装:

    方式一:建议使用brew安装jenkins
    brew install jenkins

    安装Jenkins.png

    如果是M1芯片,根据提示使用 arch -x86_64 brew install jenkins

    方式二:到官网下载war包,地址:Jenkins download and deployment

    卸载:

    brew uninstall jenkins 

    卸载Jenkins.png

    Jenkins的启动 & 关闭 & 重启
    brew services start jenkins
    brew services stop jenkins
    brew services restart jenkins

    共享本机jenkins服务

    局域网访问本机的jenkins,localhost:8080 替换成自己的IP:8080即可。

    局域网使用IP访问jenkins
    使用brew安装jenkins会避免很多其他安装方式产生的用户权限问题,但是会将httpListenAddress默认设置为127.0.0.1,这样我们虽然可以在本地用localhost:8080访问,但是本机和局域网均无法用ip访问。解决办法为修改两个路径下的plist配置。
    ~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist
    /usr/local/opt/jenkins/homebrew.mxcl.jenkins.plist 

    第二路径下没有的话,可以去看下homebrew所在文件

    终端输入 /opt/homebrew/opt/jenkins 回车。输入open .  回车, 打开文件夹
    将上面两个plist中的httpListenAddress后的ip地址,修改为本机IP或者0.0.0.0即可。

    上述两个配置文件修改完后,重启Jenkins服务

    创建项目

    配置代码仓库地址及账号

    General 配置中,选择参数化构建过程中的 Git Parameter选项,然后按下面进行配置。

     

    jenkins效果:

    更多使用参考:http://www.mydlq.club/article/45/

     可选jenkins插件

    Ant
    Build Timeout
    Email Extension
    Environment Injector Plugin
    fir-plugin
    GitHub Branch Source
    GitLab
    Gitlab Authentication plugin
    Gitlab Hook Plugin
    Gradle Plugin
    Keychains and Provisioning Profiles Management
    LDAP Plugin
    PAM Authentication
    Pipeline
    Pipeline: GitHub Groovy Libraries
    SSH Slaves
    Subversion
    Timestamper
    Workspace Cleanup
    Xcode integration

     二、配置plist文件

    以采用直接项目内预置exportPlist文件,也可以用脚本生成

     三、xcode打包

    1、设置全局gitlab账号、邮箱

    1. /usr/bin/git config --global user.name "zhangsan"
    2. /usr/bin/git config --global user.email "zhangsan@xxx.com"

    2、钥匙串访问

    1. ##钥匙串路径 这几句的意思是允许访问钥匙串,因为我们在打包的时候需要访问钥匙串 用里面的证书文件
    2. KEY_PATH="/Users/Library/Keychains/login.keychain"
    3. /usr/bin/security unlock-keychain -p '钥匙串密码' ${KEY_PATH}
    4. /usr/bin/security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k '钥匙串密码' ${KEY_PATH}
    5. /usr/bin/security set-keychain-settings ${KEY_PATH}

     3、clean -> pod update-> archive -> 生成export.plist-> export ipa

    1. export LANG=en_US.UTF-8
    2. #version="3.6.8"
    3. #defaultEnv='0'
    4. #switchEnvEnable='1'
    5. #buildType="inhouse"
    6. echo "打包选项为:branch=${branch}\nbuild_type=${buildType}\nversion=${version}\ndefaultEnv=${defaultEnv}\nswitchEnvEnable=${switchEnvEnable}"
    7. echo '正在清理工程'
    8. xcodebuild \
    9. clean -configuration ${development_mode} -quiet || exit
    10. echo '清理完成'
    11. #pod更新检测
    12. echo '删除Pods文件夹'
    13. rm -rf Pods
    14. echo "开始pod update"
    15. pod_result=$(/usr/local/bin/pod update --no-repo-update)
    16. echo 'pod 结果' + $pod_result
    17. error_string='pod repo update'
    18. if [[ $pod_result =~ $error_string ]]; then
    19. echo 'pod报错,需要更新repo,开始更新repo'
    20. /usr/local/bin/pod update --repo-update
    21. fi
    22. ####################参数、环境变量定义#########################
    23. # method,打包的方式。方式分别为 app-store, ad-hoc, enterprise, development, validation
    24. if [ $buildType = "inhouse" ];then
    25. echo '///-----------'
    26. echo '/// 编译环境:内测环境'
    27. echo '///-----------'
    28. method="development"
    29. #"development"
    30. bundle_id="com.xxx.xxx"
    31. team_id="BW7T59ZXXX"
    32. provision_name="deleloper_profile"
    33. # preprocessor_define="INHOUSE=1"
    34. # sign="Ad Hoc Distribution"
    35. # "iPhone Distribution"#"Apple Development"
    36. build_config="Release"
    37. else
    38. echo '///-----------'
    39. echo '/// 编译环境:PRD环境'
    40. echo '///-----------'
    41. method="app-store"
    42. bundle_id="com.xxx.xxx"
    43. team_id="BW7T59ZXXX"
    44. provision_name="distribution_appstore"
    45. sign="Apple Distribution"
    46. build_config="Release"
    47. fi
    48. #echo "配置信息:\nbundle_id=${bundle_id}\nteam_id=${team_id}\nmethod=${method}\nprovision_name=${provision_name}\npreprocessor_define=${preprocessor_define}\nsign=${sign}\nbuild_config=${build_config}"
    49. #工程项目路径
    50. projectPath="$(pwd)"
    51. #工程项目名称
    52. projectName="eduiPhone"
    53. #工程项目打包模式
    54. buildConfiguration="Release"
    55. #IPA配置文件
    56. #exportOptionsPlist="${projectPath}/Package/${Archive}.plist"
    57. exportOptionsPlist="exportOptionsPlist.plist"
    58. #证书
    59. ADHOCCODE_SIGN_IDENTITY="Apple Distribution: XXXX XXXX Information Technology LTD "
    60. DEVELOPMENT_TEAM="BW7T59ZXXX"
    61. #描述文件
    62. Main_Provisioning_Profile="3db7db7e-e783-48e7-8faf-dfef20xxxxxx"
    63. #build文件路径
    64. buildPath="${projectPath}/build"
    65. #发布文件路径
    66. releasePath="${projectPath}/build/Release-iphoneos"
    67. #archive保存路径
    68. archivePath="${projectPath}/archive"
    69. archiveName="${projectName}.xcarchive"
    70. archiveFilePath="${archivePath}/${archiveName}"
    71. #ipa保存路径
    72. ipaPath="${projectPath}/ipa"
    73. #log日志路径
    74. logfilePath="${projectPath}/ChangeLog"
    75. #取当前时间字符串添加到文件结尾
    76. dateString=$(date +"%Y%m%d%H%M")
    77. infoPlistPath="eduiPhone/Info.plist"
    78. #配置版本号
    79. /usr/libexec/PlistBuddy -c "Set :CFBundleVersion ${dateString}" ${infoPlistPath}
    80. if [[ ${version} != "" ]]; then
    81. echo "输入了版本号"+$version
    82. /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString ${version}" ${infoPlistPath}
    83. fi
    84. version_final=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" ${infoPlistPath})
    85. buildId_final=$dateString
    86. echo "版本号为"+$version_final
    87. echo "buildId为"+$buildId_final
    88. #echo "info.plist内容为"
    89. #/usr/libexec/PlistBuddy -c "print" ${infoPlistPath}
    90. #配置环境属性
    91. echo "选择了打包环境"
    92. echo ${defaultEnv}
    93. echo "是否允许切换环境选择了"
    94. echo ${switchEnvEnable}
    95. echo "开始将上述配置写入到代码中"
    96. constantFilePath="eduiPhone/EDUDefineFile/EDUInterfaceDefine.h"
    97. sed -i '' -e "s;#define linetest.*;#define linetest ${defaultEnv};g" ${constantFilePath}
    98. sed -i '' -e "s;#define EDU_SWITCH_ENV_ENABLE.*;#define EDU_SWITCH_ENV_ENABLE ${switchEnvEnable};g" ${constantFilePath}
    99. #echo "写入成功,写入后的文件内容为"
    100. #cat ${constantFilePath}
    101. #先删除存在的文件目录
    102. rm -rdf "$buildPath"
    103. rm -rdf "$archivePath"
    104. rm -rdf "$ipaPath"
    105. rm -rdf "${logfilePath}"
    106. #再创建新的文件目录
    107. mkdir "$buildPath"
    108. mkdir "$releasePath"
    109. mkdir "$archivePath"
    110. mkdir "$ipaPath"
    111. touch "${logfilePath}"
    112. echo "***********************参数、环境变量***********************"
    113. echo "当前目录路径-------->${projectPath}"
    114. echo '打包模式:'$buildConfiguration
    115. echo '工程目录:'$projectPath
    116. echo '工程名称:'$projectName
    117. echo '安装包路径 '$archiveFilePath
    118. echo '\n'
    119. echo "***********************开始build archive app文件***********************"
    120. #打包的命令
    121. archive_result=$(xcodebuild archive -workspace ${projectName}.xcworkspace -scheme "$projectName" -configuration ${build_config} -archivePath ${archiveFilePath} CONFIGURATION_BUILD_DIR="${releasePath}")
    122. echo 'archive_result='+${archive_result}
    123. if [[ $archive_result =~ 'ARCHIVE FAILED' ]]; then
    124. exit
    125. fi
    126. echo "***********************结束build archive app文件***********************"
    127. echo "先删除导出选项plist"
    128. rm -rf exportOptionsPlist.plist
    129. #配置导出文件
    130. #/usr/libexec/PlistBuddy -c "Set :aps-environment string distribution" 恒大宝.entitlements
    131. if [ $buildType = "inhouse" ];then
    132. /usr/libexec/PlistBuddy -c "Add :compileBitcode bool YES" exportOptionsPlist.plist
    133. /usr/libexec/PlistBuddy -c "Add :method string ${method}" exportOptionsPlist.plist
    134. /usr/libexec/PlistBuddy -c "Add :provisioningProfiles dict" exportOptionsPlist.plist
    135. /usr/libexec/PlistBuddy -c "Add :provisioningProfiles:${bundle_id} string ${provision_name}" exportOptionsPlist.plist
    136. # /usr/libexec/PlistBuddy -c "Add :APP_IDENTIFIER string ${sign}" exportOptionsPlist.plist
    137. # /usr/libexec/PlistBuddy -c "Add :signingCertificate string ${sign}" exportOptionsPlist.plist
    138. /usr/libexec/PlistBuddy -c "Add :signingStyle string automatic" exportOptionsPlist.plist
    139. /usr/libexec/PlistBuddy -c "Add :stripSwiftSymbols bool true" exportOptionsPlist.plist
    140. /usr/libexec/PlistBuddy -c "Add :teamID string ${team_id}" exportOptionsPlist.plist
    141. /usr/libexec/PlistBuddy -c "Add :thinning string " exportOptionsPlist.plist
    142. /usr/libexec/PlistBuddy -c "Add :destination string export" exportOptionsPlist.plist
    143. /usr/libexec/PlistBuddy -c "print" exportOptionsPlist.plist
    144. else
    145. #/usr/libexec/PlistBuddy -c "Add :compileBitcode bool YES" exportOptionsPlist.plist
    146. /usr/libexec/PlistBuddy -c "Add :method string ${method}" exportOptionsPlist.plist
    147. /usr/libexec/PlistBuddy -c "Add :provisioningProfiles dict" exportOptionsPlist.plist
    148. /usr/libexec/PlistBuddy -c "Add :provisioningProfiles:${bundle_id} string ${provision_name}" exportOptionsPlist.plist
    149. /usr/libexec/PlistBuddy -c "Add :signingCertificate string ${sign}" exportOptionsPlist.plist
    150. /usr/libexec/PlistBuddy -c "Add :signingStyle string manual" exportOptionsPlist.plist
    151. /usr/libexec/PlistBuddy -c "Add :stripSwiftSymbols bool true" exportOptionsPlist.plist
    152. /usr/libexec/PlistBuddy -c "Add :teamID string ${team_id}" exportOptionsPlist.plist
    153. /usr/libexec/PlistBuddy -c "Add :uploadBitcode bool false" exportOptionsPlist.plist
    154. /usr/libexec/PlistBuddy -c "Add :uploadSymbols bool true" exportOptionsPlist.plist
    155. /usr/libexec/PlistBuddy -c "Add :manageAppVersionAndBuildNumber bool true" exportOptionsPlist.plist
    156. /usr/libexec/PlistBuddy -c "Add :destination string export" exportOptionsPlist.plist
    157. /usr/libexec/PlistBuddy -c "print" exportOptionsPlist.plist
    158. fi
    159. echo "begin archive"
    160. #导出ipa
    161. export_result=$(xcodebuild -exportArchive -archivePath ${archiveFilePath} -exportPath ${ipaPath} -exportOptionsPlist ${exportOptionsPlist})
    162. echo 'export_result='+${export_result}
    163. if [[ $export_result =~ 'EXPORT FAILED' ]]; then
    164. exit
    165. fi
    166. echo "***********************设置包名称信息***********************"
    167. #app文件存放位置和命名
    168. appPath="${archiveFilePath}/Products/Applications"
    169. appFile="${appPath}/${projectName}.app"
    170. #app文件中Info.plist文件路径
    171. appInfoPlistPath=$appFile/Info.plist
    172. #取版本号
    173. version=$(/usr/libexec/PlistBuddy -c "print CFBundleShortVersionString" ${appInfoPlistPath})
    174. #取Build号
    175. buildNo=$(/usr/libexec/PlistBuddy -c "print CFBundleVersion" ${appInfoPlistPath})
    176. #取bundle id
    177. bundleId=$(/usr/libexec/PlistBuddy -c "print CFBundleIdentifier" ${appInfoPlistPath})
    178. #取应用名称
    179. appName=$(/usr/libexec/PlistBuddy -c "print CFBundleDisplayName" ${appInfoPlistPath})
    180. #包编译类型(ad-hoc,enterprise...)
    181. buildMethod=$(/usr/libexec/PlistBuddy -c "print method" ${exportOptionsPlist})
    182. #打包的时间
    183. date=$(date +%Y%m%d%H%M)
    184. #判断放ipa包的目录是否存在
    185. destinationPath="${projectPath}/Package/${buildMethod}/${projectName}/${version}"
    186. if [ ! -d "$destinationPath" ]; then
    187. mkdir -p "$destinationPath"
    188. fi
    189. ipaFile="${projectName}_${buildMethod}_${version}(${date}).ipa"
    190. dSYMFile="${projectName}_${buildMethod}_${version}(${date}).app.dSYM"
    191. ipaFilePath="${destinationPath}/${ipaFile}"
    192. dSYMFilePath="${destinationPath}/${dSYMFile}"
    193. #将ipa跟dSYM移动到指定目录下
    194. mv -f "${ipaPath}/${appName}.ipa" $ipaFilePath
    195. mv -f "${releasePath}/${projectName}.app.dSYM" $dSYMFilePath
    196. echo "** 安装包最终存放路径--->${ipaFilePath} **"
    197. #移除日志文件
    198. #rm -rdf "${logfileDir}"
    199. exit

    4、上传蒲公英 OR fir

    1. # 配置蒲公英ukey
    2. pgyerUKey="97b9feec86be53b5f0389cf1b4xxxxxx"
    3. # 配置蒲公英apiKey
    4. pgyerApiKey="01473ebcd9c9742a93b0cd250axxxxxx"
    5. echo "*************************开始上传到蒲公英**************************"
    6. RESULT=$(curl -F "file=@${ipaFilePath}" -F "uKey=$pgyerUKey" -F "_api_key=$pgyerApiKey" http://www.pgyer.com/apiv1/app/upload)
    7. echo $RESULT
    8. echo "*************************结束上传到蒲公英**************************"
    1. #echo "*************************开始上传到fir**************************"
    2. #fir login "fir 登录的 token"
    3. #
    4. #fir me
    5. #
    6. #
    7. #if [ ! -f "$logfileDir" ]; then
    8. #fir publish ${ipaFilePath} -c "无更新记录"
    9. #else
    10. #fir publish ${ipaFilePath} -c ${logfileDir}
    11. #fi
    12. #echo "*************************结束上传到fir**************************"

    上传蒲公英后,Result解析方法

       4.1 函数解析

    1. ##json解析函数
    2. function jsonParse() { # $1 $2 json lable
    3. JSON_CONTENT=$1
    4. KEY='"'$2'":'
    5. echo ${JSON_CONTENT} | awk -F ${KEY} '{print $2}' | awk -F '"' '{print $2}'
    6. }
    7. ##删除斜杠'\'
    8. function trimSlash() {
    9. TEXT=$1
    10. echo ${TEXT//'\'/''}
    11. }
    12. appQRCodeURL=$(trimSlash $(jsonParse "${RESULT}" "appQRCodeURL"))
    13. appVersion=$(jsonParse "${RESULT}" "appVersion")
    14. appBuildVersion=$(jsonParse "${RESULT}" "appBuildVersion")
    15. appUpdated=$(jsonParse "${RESULT}" "appUpdated")

      4.2 json裁剪获取 QRCode,只针对二维码地址获取

    1. testStr="}}"
    2. ##蒲公英二维码
    3. httpstr=${RESULT#*appQRCodeURL}
    4. httpstr=${httpstr#*:}
    5. httpstr=${httpstr//''/''}
    6. httpstr=${httpstr//'"'/''}
    7. QRCode=${httpstr%${testStr}*}

    5、发送钉钉消息 

    1. ##钉钉消息推送
    2. curl 'https://oapi.dingtalk.com/robot/send?access_token=0de1c87a48d26e9fd31c81c80b39d0ca6395dde7ab98eede2e8xxxxxxxx' \
    3. -H 'Content-Type: application/json' \
    4. -d '{
    5. "msgtype": "markdown",
    6. "markdown": {
    7. "title":"iOS打包",
    8. "text": "#### [ XXX iOS ]构建成功\n> #### 版本号:'${version}'\n> #### 代码分支:'${branch}'\n> #### 打包环境:'${environment}'\n> #### 下载地址:'https://www.pgyer.com/apSK/'\n>\n\n> ![screenshot]('${QRCode}')\n> ###### 出包时间:'${currentTime}' [jenkins网址](http://xxx) \n"
    9. },
    10. "at": {
    11. "atMobiles": [
    12. "132XXXXXXXX"
    13. ],
    14. "isAtAll": false
    15. }
    16. }'

    钉钉消息发送还可以使用插件的形式

     可参考:https://blog.csdn.net/zhangcongyi420/article/details/105797271

     钉钉效果:

  • 相关阅读:
    支持存档的书签服务LinkWarden
    windows11任务管理器快捷弹出
    Redis 集群 & Redis 事务 & Redis 流水线 & Redis 发布订阅 & Redis Lua脚本操作
    即时通讯技术文集(第7期):长连接网关、P2P等 [共10篇]
    一致性算法介绍(一)
    C语言基础篇 —— 3.4 二重指针
    vue3+element-plus 高度封装搜索组件,支持多种类型
    导入MySQL元数据到Atlas
    Python【高效摸鱼】告别pip手动安装模块,实现全自动安装第三方库
    数据标注是一块肥肉,盯上这块肉的不止中国丨曼孚科技
  • 原文地址:https://blog.csdn.net/mqyeweiyang/article/details/126127284