背景:iOS稍微大一点的项目,archive一次需要十几分钟,对开发而言,有时候需要切换分支打包,如果异地办公的情况还要提供测试包,非常麻烦,所以提供远程打包是必不可少的。
安装:
方式一:建议使用brew安装jenkins
brew install jenkins
如果是M1芯片,根据提示使用 arch -x86_64 brew install jenkins
方式二:到官网下载war包,地址:Jenkins download and deployment
卸载:
brew uninstall jenkins
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效果:
可选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
可以采用直接项目内预置exportPlist文件,也可以用脚本生成

1、设置全局gitlab账号、邮箱
- /usr/bin/git config --global user.name "zhangsan"
- /usr/bin/git config --global user.email "zhangsan@xxx.com"
2、钥匙串访问
- ##钥匙串路径 这几句的意思是允许访问钥匙串,因为我们在打包的时候需要访问钥匙串 用里面的证书文件
- KEY_PATH="/Users/Library/Keychains/login.keychain"
- /usr/bin/security unlock-keychain -p '钥匙串密码' ${KEY_PATH}
- /usr/bin/security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k '钥匙串密码' ${KEY_PATH}
- /usr/bin/security set-keychain-settings ${KEY_PATH}
3、clean -> pod update-> archive -> 生成export.plist-> export ipa
- export LANG=en_US.UTF-8
-
- #version="3.6.8"
- #defaultEnv='0'
- #switchEnvEnable='1'
- #buildType="inhouse"
-
- echo "打包选项为:branch=${branch}\nbuild_type=${buildType}\nversion=${version}\ndefaultEnv=${defaultEnv}\nswitchEnvEnable=${switchEnvEnable}"
-
- echo '正在清理工程'
-
- xcodebuild \
- clean -configuration ${development_mode} -quiet || exit
-
- echo '清理完成'
-
- #pod更新检测
- echo '删除Pods文件夹'
-
- rm -rf Pods
-
- echo "开始pod update"
-
- pod_result=$(/usr/local/bin/pod update --no-repo-update)
- echo 'pod 结果' + $pod_result
- error_string='pod repo update'
- if [[ $pod_result =~ $error_string ]]; then
- echo 'pod报错,需要更新repo,开始更新repo'
- /usr/local/bin/pod update --repo-update
- fi
-
- ####################参数、环境变量定义#########################
-
- # method,打包的方式。方式分别为 app-store, ad-hoc, enterprise, development, validation
- if [ $buildType = "inhouse" ];then
- echo '///-----------'
- echo '/// 编译环境:内测环境'
- echo '///-----------'
- method="development"
- #"development"
- bundle_id="com.xxx.xxx"
- team_id="BW7T59ZXXX"
- provision_name="deleloper_profile"
- # preprocessor_define="INHOUSE=1"
- # sign="Ad Hoc Distribution"
- # "iPhone Distribution"#"Apple Development"
- build_config="Release"
- else
- echo '///-----------'
- echo '/// 编译环境:PRD环境'
- echo '///-----------'
- method="app-store"
- bundle_id="com.xxx.xxx"
- team_id="BW7T59ZXXX"
- provision_name="distribution_appstore"
- sign="Apple Distribution"
- build_config="Release"
- fi
-
- #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}"
-
- #工程项目路径
- projectPath="$(pwd)"
- #工程项目名称
- projectName="eduiPhone"
- #工程项目打包模式
- buildConfiguration="Release"
- #IPA配置文件
- #exportOptionsPlist="${projectPath}/Package/${Archive}.plist"
- exportOptionsPlist="exportOptionsPlist.plist"
-
- #证书
- ADHOCCODE_SIGN_IDENTITY="Apple Distribution: XXXX XXXX Information Technology LTD "
- DEVELOPMENT_TEAM="BW7T59ZXXX"
- #描述文件
- Main_Provisioning_Profile="3db7db7e-e783-48e7-8faf-dfef20xxxxxx"
-
- #build文件路径
- buildPath="${projectPath}/build"
- #发布文件路径
- releasePath="${projectPath}/build/Release-iphoneos"
- #archive保存路径
- archivePath="${projectPath}/archive"
- archiveName="${projectName}.xcarchive"
- archiveFilePath="${archivePath}/${archiveName}"
-
- #ipa保存路径
- ipaPath="${projectPath}/ipa"
- #log日志路径
- logfilePath="${projectPath}/ChangeLog"
-
- #取当前时间字符串添加到文件结尾
- dateString=$(date +"%Y%m%d%H%M")
- infoPlistPath="eduiPhone/Info.plist"
-
- #配置版本号
- /usr/libexec/PlistBuddy -c "Set :CFBundleVersion ${dateString}" ${infoPlistPath}
- if [[ ${version} != "" ]]; then
- echo "输入了版本号"+$version
- /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString ${version}" ${infoPlistPath}
- fi
-
- version_final=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" ${infoPlistPath})
- buildId_final=$dateString
- echo "版本号为"+$version_final
- echo "buildId为"+$buildId_final
-
- #echo "info.plist内容为"
- #/usr/libexec/PlistBuddy -c "print" ${infoPlistPath}
-
- #配置环境属性
- echo "选择了打包环境"
- echo ${defaultEnv}
-
- echo "是否允许切换环境选择了"
- echo ${switchEnvEnable}
-
- echo "开始将上述配置写入到代码中"
- constantFilePath="eduiPhone/EDUDefineFile/EDUInterfaceDefine.h"
- sed -i '' -e "s;#define linetest.*;#define linetest ${defaultEnv};g" ${constantFilePath}
- sed -i '' -e "s;#define EDU_SWITCH_ENV_ENABLE.*;#define EDU_SWITCH_ENV_ENABLE ${switchEnvEnable};g" ${constantFilePath}
- #echo "写入成功,写入后的文件内容为"
- #cat ${constantFilePath}
-
- #先删除存在的文件目录
- rm -rdf "$buildPath"
- rm -rdf "$archivePath"
- rm -rdf "$ipaPath"
- rm -rdf "${logfilePath}"
- #再创建新的文件目录
- mkdir "$buildPath"
- mkdir "$releasePath"
- mkdir "$archivePath"
- mkdir "$ipaPath"
- touch "${logfilePath}"
-
- echo "***********************参数、环境变量***********************"
- echo "当前目录路径-------->${projectPath}"
- echo '打包模式:'$buildConfiguration
- echo '工程目录:'$projectPath
- echo '工程名称:'$projectName
- echo '安装包路径 '$archiveFilePath
- echo '\n'
-
- echo "***********************开始build archive app文件***********************"
-
- #打包的命令
- archive_result=$(xcodebuild archive -workspace ${projectName}.xcworkspace -scheme "$projectName" -configuration ${build_config} -archivePath ${archiveFilePath} CONFIGURATION_BUILD_DIR="${releasePath}")
-
- echo 'archive_result='+${archive_result}
- if [[ $archive_result =~ 'ARCHIVE FAILED' ]]; then
- exit
- fi
-
- echo "***********************结束build archive app文件***********************"
-
- echo "先删除导出选项plist"
- rm -rf exportOptionsPlist.plist
-
- #配置导出文件
- #/usr/libexec/PlistBuddy -c "Set :aps-environment string distribution" 恒大宝.entitlements
- if [ $buildType = "inhouse" ];then
- /usr/libexec/PlistBuddy -c "Add :compileBitcode bool YES" exportOptionsPlist.plist
- /usr/libexec/PlistBuddy -c "Add :method string ${method}" exportOptionsPlist.plist
- /usr/libexec/PlistBuddy -c "Add :provisioningProfiles dict" exportOptionsPlist.plist
- /usr/libexec/PlistBuddy -c "Add :provisioningProfiles:${bundle_id} string ${provision_name}" exportOptionsPlist.plist
- # /usr/libexec/PlistBuddy -c "Add :APP_IDENTIFIER string ${sign}" exportOptionsPlist.plist
- # /usr/libexec/PlistBuddy -c "Add :signingCertificate string ${sign}" exportOptionsPlist.plist
- /usr/libexec/PlistBuddy -c "Add :signingStyle string automatic" exportOptionsPlist.plist
- /usr/libexec/PlistBuddy -c "Add :stripSwiftSymbols bool true" exportOptionsPlist.plist
- /usr/libexec/PlistBuddy -c "Add :teamID string ${team_id}" exportOptionsPlist.plist
- /usr/libexec/PlistBuddy -c "Add :thinning string
" exportOptionsPlist.plist - /usr/libexec/PlistBuddy -c "Add :destination string export" exportOptionsPlist.plist
- /usr/libexec/PlistBuddy -c "print" exportOptionsPlist.plist
- else
- #/usr/libexec/PlistBuddy -c "Add :compileBitcode bool YES" exportOptionsPlist.plist
- /usr/libexec/PlistBuddy -c "Add :method string ${method}" exportOptionsPlist.plist
- /usr/libexec/PlistBuddy -c "Add :provisioningProfiles dict" exportOptionsPlist.plist
- /usr/libexec/PlistBuddy -c "Add :provisioningProfiles:${bundle_id} string ${provision_name}" exportOptionsPlist.plist
- /usr/libexec/PlistBuddy -c "Add :signingCertificate string ${sign}" exportOptionsPlist.plist
- /usr/libexec/PlistBuddy -c "Add :signingStyle string manual" exportOptionsPlist.plist
- /usr/libexec/PlistBuddy -c "Add :stripSwiftSymbols bool true" exportOptionsPlist.plist
- /usr/libexec/PlistBuddy -c "Add :teamID string ${team_id}" exportOptionsPlist.plist
- /usr/libexec/PlistBuddy -c "Add :uploadBitcode bool false" exportOptionsPlist.plist
- /usr/libexec/PlistBuddy -c "Add :uploadSymbols bool true" exportOptionsPlist.plist
- /usr/libexec/PlistBuddy -c "Add :manageAppVersionAndBuildNumber bool true" exportOptionsPlist.plist
- /usr/libexec/PlistBuddy -c "Add :destination string export" exportOptionsPlist.plist
- /usr/libexec/PlistBuddy -c "print" exportOptionsPlist.plist
- fi
-
- echo "begin archive"
-
- #导出ipa
- export_result=$(xcodebuild -exportArchive -archivePath ${archiveFilePath} -exportPath ${ipaPath} -exportOptionsPlist ${exportOptionsPlist})
- echo 'export_result='+${export_result}
- if [[ $export_result =~ 'EXPORT FAILED' ]]; then
- exit
- fi
-
- echo "***********************设置包名称信息***********************"
- #app文件存放位置和命名
- appPath="${archiveFilePath}/Products/Applications"
- appFile="${appPath}/${projectName}.app"
- #app文件中Info.plist文件路径
- appInfoPlistPath=$appFile/Info.plist
- #取版本号
- version=$(/usr/libexec/PlistBuddy -c "print CFBundleShortVersionString" ${appInfoPlistPath})
- #取Build号
- buildNo=$(/usr/libexec/PlistBuddy -c "print CFBundleVersion" ${appInfoPlistPath})
- #取bundle id
- bundleId=$(/usr/libexec/PlistBuddy -c "print CFBundleIdentifier" ${appInfoPlistPath})
- #取应用名称
- appName=$(/usr/libexec/PlistBuddy -c "print CFBundleDisplayName" ${appInfoPlistPath})
- #包编译类型(ad-hoc,enterprise...)
- buildMethod=$(/usr/libexec/PlistBuddy -c "print method" ${exportOptionsPlist})
- #打包的时间
- date=$(date +%Y%m%d%H%M)
- #判断放ipa包的目录是否存在
- destinationPath="${projectPath}/Package/${buildMethod}/${projectName}/${version}"
- if [ ! -d "$destinationPath" ]; then
- mkdir -p "$destinationPath"
- fi
- ipaFile="${projectName}_${buildMethod}_${version}(${date}).ipa"
- dSYMFile="${projectName}_${buildMethod}_${version}(${date}).app.dSYM"
- ipaFilePath="${destinationPath}/${ipaFile}"
- dSYMFilePath="${destinationPath}/${dSYMFile}"
-
- #将ipa跟dSYM移动到指定目录下
- mv -f "${ipaPath}/${appName}.ipa" $ipaFilePath
- mv -f "${releasePath}/${projectName}.app.dSYM" $dSYMFilePath
- echo "** 安装包最终存放路径--->${ipaFilePath} **"
-
- #移除日志文件
- #rm -rdf "${logfileDir}"
-
- exit
4、上传蒲公英 OR fir
- # 配置蒲公英ukey
- pgyerUKey="97b9feec86be53b5f0389cf1b4xxxxxx"
- # 配置蒲公英apiKey
- pgyerApiKey="01473ebcd9c9742a93b0cd250axxxxxx"
-
- echo "*************************开始上传到蒲公英**************************"
-
- RESULT=$(curl -F "file=@${ipaFilePath}" -F "uKey=$pgyerUKey" -F "_api_key=$pgyerApiKey" http://www.pgyer.com/apiv1/app/upload)
-
- echo $RESULT
-
- echo "*************************结束上传到蒲公英**************************"
- #echo "*************************开始上传到fir**************************"
-
- #fir login "fir 登录的 token"
- #
- #fir me
- #
- #
- #if [ ! -f "$logfileDir" ]; then
- #fir publish ${ipaFilePath} -c "无更新记录"
- #else
- #fir publish ${ipaFilePath} -c ${logfileDir}
- #fi
- #echo "*************************结束上传到fir**************************"
上传蒲公英后,Result解析方法
4.1 函数解析
- ##json解析函数
- function jsonParse() { # $1 $2 json lable
-
- JSON_CONTENT=$1
- KEY='"'$2'":'
-
- echo ${JSON_CONTENT} | awk -F ${KEY} '{print $2}' | awk -F '"' '{print $2}'
- }
-
- ##删除斜杠'\'
- function trimSlash() {
- TEXT=$1
- echo ${TEXT//'\'/''}
- }
- appQRCodeURL=$(trimSlash $(jsonParse "${RESULT}" "appQRCodeURL"))
- appVersion=$(jsonParse "${RESULT}" "appVersion")
- appBuildVersion=$(jsonParse "${RESULT}" "appBuildVersion")
- appUpdated=$(jsonParse "${RESULT}" "appUpdated")
4.2 json裁剪获取 QRCode,只针对二维码地址获取
- testStr="}}"
- ##蒲公英二维码
- httpstr=${RESULT#*appQRCodeURL}
- httpstr=${httpstr#*:}
- httpstr=${httpstr//''/''}
- httpstr=${httpstr//'"'/''}
- QRCode=${httpstr%${testStr}*}
5、发送钉钉消息
- ##钉钉消息推送
- curl 'https://oapi.dingtalk.com/robot/send?access_token=0de1c87a48d26e9fd31c81c80b39d0ca6395dde7ab98eede2e8xxxxxxxx' \
- -H 'Content-Type: application/json' \
- -d '{
- "msgtype": "markdown",
- "markdown": {
- "title":"iOS打包",
- "text": "#### [ XXX iOS ]构建成功\n> #### 版本号:'${version}'\n> #### 代码分支:'${branch}'\n> #### 打包环境:'${environment}'\n> #### 下载地址:'https://www.pgyer.com/apSK/'\n>\n\n> \n> ###### 出包时间:'${currentTime}' [jenkins网址](http://xxx) \n"
- },
- "at": {
- "atMobiles": [
- "132XXXXXXXX"
- ],
- "isAtAll": false
- }
- }'
钉钉消息发送还可以使用插件的形式
可参考:https://blog.csdn.net/zhangcongyi420/article/details/105797271
钉钉效果:
