ps: 脚本在文末
在开发一款APP的时候,一般需要构建ios端和android端以及两端APP的后台配置管理系统(web端)。在开发以及后续维护它们的过程中,避免不了要持续地测试和发布。
如果项目每添加或修复一个功能时,都去手动构建出来安装包(web就是build后发布到测试服务器)给测试同事。先不说费时吧,起码逼格就降下来了。站在测试同事(部门)的角度考虑这样一个情景: 啊,这个,由于我不是测试确实编不出来了,我不编了,我摊牌了,知道两端准备上线一个植树节活动,活动的一些信息是通过后台被运营部门设定的。在测试这个功能的时候,测试同事...jenkins CI天下第一,牛逼,就行了。
jenkins 部署环境
-OS系统
Mac和虚拟机都可以,因为一直需要处于开机状态构建所以建议是用windows机器装虚拟机配置好后把显示器卸掉,一直放在那里就可以了。当然是要开启一下远程的,方便控制。在你用局域网去访问这台机器或者这台机器上的服务时,建议将ip地址固定。
-Android SDK
gradle
-Xcode
CocoaPods
node
Git等
安装jenkins
配置jenkins
注意:以下的配置基于macOS Catalina 10.15.2
三个项目都使用git做版本控制,托管在云端。
一般构建流程先clone => 安装项目依赖 => 本地环境构建 => 上传构建产物 => (线上部署) => 结果通知
在这个过程中jenkins几乎可以帮我们完成所有的工作,我将按照这个流程来配置jenkins。
clone
每当有提交时,clone代码到本地要用jenkins自动完成这个步骤,在本地我不知道托管服务器怎么hook我的jenkins,所以我折中了一下每五分钟去看下有没有新的提交,有就clone。
IOS
Android
web
-源码管理
这里我使用的是gitee托管,在jenkins安装Gitee Plugin
在系统配置中配置gitee,可以参考这里
安装依赖
IOS
export LANGUAGE=en_US.UTF-8
export LC_ALL=en_US.UTF-8
#最后一次git commit信息
MSG=`git log -1 --pretty=%B`
#提交信息中出现这个字符代表不构建ios
#PASSSTR="IC pass build ios"
#result=$(echo $MSG | grep "${PASSSTR}")
#if [[ "$result" != "" ]]
#then
# exit 0
#fi
cd /Users/johndiamond
source .bash_profile
cd /Users/johndiamond/.jenkins/workspace/iOS_glh-dev/ios
/usr/local/bin/pod install
Android
Android的依赖和构建可以用一个命令
#工程绝对路径
project_path=/Users/johndiamond/.jenkins/workspace/Android_glh-dev
#工程名
project_name=app
#打包模式 Debug/Release
development_mode=debug
#最后一次git commit信息
MSG=`git log -1 --pretty=%B`
#提交信息中出现这个字符代表不构建ios
#PASSSTR="IC pass build android"
#result=$(echo $MSG | grep "${PASSSTR}")
#if [[ "$result" != "" ]]
#then
# exit 0
#fi
cd /Users/johndiamond
source .bash_profile
cd $project_path/android
#gradle clean
gradle assembleDebug
web
cd /Users/johndiamond/.jenkins/workspace/glh-admin
npm install
本地构建
用最开始配置的环境去构建
IOS
ios构建之前还需要配置Keychains and Provisioning Profiles Management具体查看这里
配置好就是这样
export LANGUAGE=en_US.UTF-8
export LC_ALL=en_US.UTF-8
#最后一次git commit信息
MSG=`git log -1 --pretty=%B`
#提交信息中出现这个字符代表不构建ios
#PASSSTR="IC pass build ios"
#result=$(echo $MSG | grep "${PASSSTR}")
#if [[ "$result" != "" ]]
#then
# exit 0
#fi
cd /Users/johndiamond
source .bash_profile
cd /Users/johndiamond/.jenkins/workspace/iOS_glh-dev
flutter pub get
cd /Users/johndiamond/.jenkins/workspace/iOS_glh-dev/ios
/usr/local/bin/pod install
#工程绝对路径
project_path=/Users/johndiamond/.jenkins/workspace/iOS_glh-dev/ios
#工程名
project_name=Runner
#scheme名
scheme_name=Runner
#打包模式 Debug/Release
development_mode=Release
#build文件夹路径
build_path=${project_path}/build
#plist文件所在路径
exportOptionsPlistPath=/Users/johndiamond/.jenkins/workspace/ExportOptions.plist
#导出.ipa文件所在路径
exportIpaPath=${project_path}/ipa/${development_mode}
echo '///-----------'
echo '/// 正在清理工程'
echo '///-----------'
#xcodebuild \
#clean -configuration ${development_mode} -quiet || exit
xcodebuild -workspace ${project_path}/${scheme_name}.xcworkspace -scheme ${project_name} -configuration ${development_mode} clean
echo '///--------'
echo '/// 清理完成'
echo '///--------'
echo ''
echo '///-----------'
echo '/// 正在编译工程:'${development_mode}
echo '///-----------'
xcodebuild \
archive -workspace ${project_path}/${project_name}.xcworkspace \
-scheme ${scheme_name} \
-configuration ${development_mode} \
-archivePath ${build_path}/${project_name}.xcarchive -quiet || exit
echo '///--------'
echo '/// 编译完成'
echo '///--------'
echo ''
echo '///----------'
echo '/// 开始ipa打包'
echo '///----------'
xcodebuild -exportArchive -archivePath ${build_path}/${project_name}.xcarchive \
-configuration ${development_mode} \
-exportPath ${exportIpaPath} \
-exportOptionsPlist ${exportOptionsPlistPath} \
-quiet || exit
if [ -e $exportIpaPath/$scheme_name.ipa ]; then
echo '///----------'
echo '/// ipa包已导出'
echo '///----------'
open $exportIpaPath
else
echo '///-------------'
echo '/// ipa包导出失败 '
echo '///-------------'
fi
echo '///------------'
echo '/// 打包ipa完成 '
echo '///-----------='
echo ''
#打印信息,以供匹配(?<=is)(.+?)(?=end)
echo "MSGis${MSG}end"
Android
#就是【安装依赖】的脚本
web
npm run build:prod
上传构建产物
这里上传到蒲公英,文档可以看这里,firm也是可以的。
IOS
#上传到蒲公英
#蒲公英aipKey
MY_PGY_API_K=这里填写aipKey
PASSWORD=这里填写安装密码apiV2必填
#打印信息,以供匹配(?<=is)(.+?)(?=end)
echo "MSGis${MSG}end"
curl -F "file=@${exportIpaPath}/${scheme_name}.ipa" -F buildInstallType=2 -F "buildPassword=${PASSWORD}" -F "_api_key=${MY_PGY_API_K}" https://www.pgyer.com/apiv2/app/upload
Android
echo "开始上传到蒲公英"
#蒲公英aipKey
MY_PGY_API_K=这里填写aipKey
#蒲公英安装密码
PASSWORD=你的密码
#打印信息,以供匹配(?<=is)(.+?)(?=end)
echo "MSGis${MSG}end"
curl -F "file=@${project_path}/build/app/outputs/apk/debug/${project_name}-${development_mode}.apk" -F buildInstallType=2 -F "buildPassword=${PASSWORD}" -F "_api_key=${MY_PGY_API_K}" https://www.pgyer.com/apiv2/app/upload
web
配置Publish Over SSH
(线上部署)
构建结果通知
我这里是在钉钉上用自定义的hook机器人通知,配置方法请看这里
IOS
DINGDING_URL=https://oapi.dingtalk.com/robot/send?access_token=这里填写你的机器人hook-token
curl $DINGDING_URL \
-H 'Content-Type: application/json' \
-d '{
"msgtype": "link",
"link": {
"text": "'"$MSG"'",
"title": "'"glh-IOS_${development_mode} 安装密码:******"'",
"picUrl": "https://www.pgyer.com/app/qrcode/tkq6?sign=&auSign=&code=",
"messageUrl": "https://www.pgyer.com/tkq6"
},
"at": {
"atMobiles": [
"15697481731"
],
"isAtAll": false
},
}'
Android
echo "开始通知钉钉"
DINGDING_URL=https://oapi.dingtalk.com/robot/send?access_token=这里填写你的机器人hook-token
curl $DINGDING_URL \
-H 'Content-Type: application/json' \
-d '{
"msgtype": "link",
"link": {
"text": "'"$MSG"'",
"title": "'"glh-Android_${development_mode} 安装密码:******"'",
"picUrl": "https://www.pgyer.com/app/qrcode/sQXt",
"messageUrl": "https://www.pgyer.com/sQXt"
},
"at": {
"atMobiles": [
"15697481731"
],
"isAtAll": false
},
}'
web
类似的方法,我这里没有进行通知
附上脚本
IOS
export LANGUAGE=en_US.UTF-8
export LC_ALL=en_US.UTF-8
#最后一次git commit信息
MSG=`git log -1 --pretty=%B`
#提交信息中出现这个字符代表不构建ios
#PASSSTR="IC pass build ios"
#result=$(echo $MSG | grep "${PASSSTR}")
#if [[ "$result" != "" ]]
#then
# exit 0
#fi
cd /Users/johndiamond
source .bash_profile
cd /Users/johndiamond/.jenkins/workspace/iOS_glh-dev
flutter pub get
cd /Users/johndiamond/.jenkins/workspace/iOS_glh-dev/ios
/usr/local/bin/pod install
#工程绝对路径
project_path=/Users/johndiamond/.jenkins/workspace/iOS_glh-dev/ios
#工程名
project_name=Runner
#scheme名
scheme_name=Runner
#打包模式 Debug/Release
development_mode=Release
#build文件夹路径
build_path=${project_path}/build
#plist文件所在路径
exportOptionsPlistPath=/Users/johndiamond/.jenkins/workspace/ExportOptions.plist
#导出.ipa文件所在路径
exportIpaPath=${project_path}/ipa/${development_mode}
#echo "Place enter the number you want to export ? [ 1:app-store 2:ad-hoc] "
##
#read number
#while([[ $number != 1 ]] && [[ $number != 2 ]])
#do
#echo "Error! Should enter 1 or 2"
#echo "Place enter the number you want to export ? [ 1:app-store 2:ad-hoc] "
#read number
#done
#if [ $number == 1 ];then
#development_mode=Release
#exportOptionsPlistPath=${project_path}/exportTest.plist
#else
#development_mode=Debug
#exportOptionsPlistPath=${project_path}/exportTest.plist
#fi
echo '///-----------'
echo '/// 正在清理工程'
echo '///-----------'
#xcodebuild \
#clean -configuration ${development_mode} -quiet || exit
xcodebuild -workspace ${project_path}/${scheme_name}.xcworkspace -scheme ${project_name} -configuration ${development_mode} clean
echo '///--------'
echo '/// 清理完成'
echo '///--------'
echo ''
echo '///-----------'
echo '/// 正在编译工程:'${development_mode}
echo '///-----------'
xcodebuild \
archive -workspace ${project_path}/${project_name}.xcworkspace \
-scheme ${scheme_name} \
-configuration ${development_mode} \
-archivePath ${build_path}/${project_name}.xcarchive -quiet || exit
echo '///--------'
echo '/// 编译完成'
echo '///--------'
echo ''
echo '///----------'
echo '/// 开始ipa打包'
echo '///----------'
xcodebuild -exportArchive -archivePath ${build_path}/${project_name}.xcarchive \
-configuration ${development_mode} \
-exportPath ${exportIpaPath} \
-exportOptionsPlist ${exportOptionsPlistPath} \
-quiet || exit
if [ -e $exportIpaPath/$scheme_name.ipa ]; then
echo '///----------'
echo '/// ipa包已导出'
echo '///----------'
open $exportIpaPath
else
echo '///-------------'
echo '/// ipa包导出失败 '
echo '///-------------'
fi
echo '///------------'
echo '/// 打包ipa完成 '
echo '///-----------='
echo ''
echo "开始上传到蒲公英"
#上传到蒲公英
#蒲公英aipKey
MY_PGY_API_K=你的key
PASSWORD=你的密码
#打印信息,以供匹配(?<=is)(.+?)(?=end)
echo "MSGis${MSG}end"
curl -F "file=@${exportIpaPath}/${scheme_name}.ipa" -F buildInstallType=2 -F "buildPassword=${PASSWORD}" -F "_api_key=${MY_PGY_API_K}" https://www.pgyer.com/apiv2/app/upload
DINGDING_URL=https://oapi.dingtalk.com/robot/send?access_token=你的token
curl $DINGDING_URL \
-H 'Content-Type: application/json' \
-d '{
"msgtype": "link",
"link": {
"text": "'"$MSG"'",
"title": "'"glh-IOS_${development_mode} 安装密码:****"'",
"picUrl": "https://www.pgyer.com/app/qrcode/tkq6?sign=&auSign=&code=",
"messageUrl": "https://www.pgyer.com/tkq6"
},
"at": {
"atMobiles": [
"15697481731"
],
"isAtAll": false
},
}'
Android
#工程绝对路径
project_path=/Users/johndiamond/.jenkins/workspace/Android_glh-dev
#工程名
project_name=app
#打包模式 Debug/Release
development_mode=debug
#最后一次git commit信息
MSG=`git log -1 --pretty=%B`
#提交信息中出现这个字符代表不构建ios
#PASSSTR="IC pass build android"
#result=$(echo $MSG | grep "${PASSSTR}")
#if [[ "$result" != "" ]]
#then
# exit 0
#fi
cd /Users/johndiamond
source .bash_profile
cd $project_path
flutter pub get
cd $project_path/android
#gradle clean
gradle assembleDebug
echo "开始上传到蒲公英"
#蒲公英aipKey
MY_PGY_API_K=你的key
#蒲公英安装密码
PASSWORD=你的密码
#打印信息,以供匹配(?<=is)(.+?)(?=end)
echo "MSGis${MSG}end"
curl -F "file=@${project_path}/build/app/outputs/apk/debug/${project_name}-${development_mode}.apk" -F buildInstallType=2 -F "buildPassword=${PASSWORD}" -F "_api_key=${MY_PGY_API_K}" https://www.pgyer.com/apiv2/app/upload
echo "开始通知钉钉"
DINGDING_URL=https://oapi.dingtalk.com/robot/send?access_token=你的token
curl $DINGDING_URL \
-H 'Content-Type: application/json' \
-d '{
"msgtype": "link",
"link": {
"text": "'"$MSG"'",
"title": "'"glh-Android_${development_mode} 安装密码:****"'",
"picUrl": "https://www.pgyer.com/app/qrcode/sQXt",
"messageUrl": "https://www.pgyer.com/sQXt"
},
"at": {
"atMobiles": [
"15697481731"
],
"isAtAll": false
},
}'