从0-1搭建Jenkins,实现Facebook,Android,IOS自动化打包
前言
文章仅是一个初学者的记录,欢迎各位佬交流。
目前项目测试包需要发布Web-mobile,正式包需要发布Facebook Instant Game,Android和IOS。
最终
目前功能实现
- 项目构建
- 生成产物
- Android ( AAB & APK )
- IOS ( IPA )
- 产物归档
- Android ( AAB & APK )
- IOS ( IPA )
- Facebook ( Zip )
- Web-mobile ( Zip )
- 安装
- Android
- 发布
- Android – ftp服务器
- IOS – Appstore
- Facebook – facebook instant game 后台
- Web-mobile – ftp服务器
- 通知
- 飞书
环境,脚本,Cocos版本
Mac,Shell,CocosCreator3.8
安装与配置Jenkins
-
通过Homebrew安装 安装Jenkins
brew install jenkins-lts -
配置
-
启动配置(在
/opt/homebrew/Cellar/jenkins-lts/2.462.1/homebrew.mxcl.jenkins-lts.plist文件中)-
因为要用本地仓库做测试,需要添加
-Dhudson.plugins.git.GitSCM.ALLOW_LOCAL_CHECKOUT=true
-
因为要局域网访问,需要修改
httpListenAddress 改成0.0.0.0
就可以通过http://本机ip地址:端口访问
-
-
Jenkins系统配置
-
URL(Jenkins-System-Jenkins URL)
-
配置Git Credentials 使用 credentials
-
配置环境变量(Jenkins-System-Environment Variables)
ANDROID_HOME(CocosCreator打包需要)
CocosCreator(命令行调用CocosCreator打包)
HOME_BREW(命令行第三方库管理)
PATH+RUBY+GEM(IOS构建)
PATH+NPM(项目需要用到的npm管理第三方库,如果没有,则可以忽略)
-
-
安装插件
- Parameter Separator Plugin (parameter显示优化)
- ThinBackup (备份Jenkins)
- Git Parameter Plug-In (允许按Tag惯例Pipeline)
- Pipeline Utility Steps (扩展steps的功能,如zip压缩,findFiles查找文件)
-
新建项目
- 配置
- Branch Source(数据源,配置远程Git地址/本地Git目录)
- Behaviours
Discover tags(Branch就可以按照分支或tag导出)
Filter by name(通过名字过滤) - Property strategy
Suppress automatic SCM triggering (禁止git更新后,自动触发Build)
- Behaviours
- Build Configuration
Script Path (配置Jenkinsfile在仓库的相对路径)
- Branch Source(数据源,配置远程Git地址/本地Git目录)
扫描仓库分支(或Tag)的Jenkinsfile。Scan Multibranch Pipeline Now
编写Jenkinsfile
-
vscode
插件:
jenkins-pipeline-linter-connector 检测Jenkinsfile是否正确
Jenkins Doc 代码补全 -
语法
有2种方式,Declarative Pipeline 和 Scripted Pipeline,这里用Declarative Pipeline
//pipeline开始
pipeline {
//agent 定义代理节点
agent any
//parameters 定义参数,在构建面板显示并应用在构建中
parameters {
...
}
//stages 所有的阶段 在pipeline下,也可以在stage下
stages {
//stage 在界面显示出一个节点阶段
stage('STAGE1') {
//when 条件判断,是否执行该阶段
when {
...
}
//steps 阶段执行的步骤
steps {
//echo 打印xxx
echo "..."
//script 执行groovy代码块
script {
...
}
//sh 用命令行执行文件
sh "..."
//input 获取用户输入才继续进行下面步骤
input ...
}
//post 后处理阶段
post {
//success 成功后的处理
success {
...
}
...
}
}
stage('STAGE2') {
//stage下可以用parallel内嵌多个stage,使得他们并发执行
parallel {
stage('SUB STAGE1') {
...
}
stage('SUB STAGE2') {
...
}
...
}
}
}
- 参考
Using a Jenkinsfile
Pipeline Syntax
了解全局参数和自定义参数http://${JENKINS_URL}/pipeline-syntax/globals
源码
-
参数
-
公共参数
JENKIN_BUILD_PLATFORM 选择构建平台
JENKIN_AUTO_INSTALL 是否自动安装
JENKIN_AUTO_DEPLOY 是否自动发布 -
构建平台参数
-
Android
JENKIN_ANDROID_DEBUG_FLAG 是否构建debug包
JENKIN_ANDROID_VERSION_CODE android的Version Code
JENKIN_ANDROID_VERSION_NAME android的Version Name
JENKIN_ANDROID_ARTIFACT_TYPE 构建android产物类型APK/AAB -
IOS
JENKIN_IOS_INTERNAL_TESTING_ONLY_FLAG 是否testflight internal testing only
JENKIN_IOS_BUNDLE_VERSION_SHORT IOS的Version Short
JENKIN_IOS_BUNDLE_VERSION IOS的Version -
Facebook
JENKIN_FB_COMMENT fb发布的comment
-
Jenkinsfile
parameters { //公共参数 choice(name:'JENKIN_BUILD_PLATFORM', choices: ['FB', 'WEB-MOBILE', 'ANDROID', 'IOS'], description: '选择构建平台') booleanParam(name:'JENKIN_AUTO_INSTALL', defaultValue: false, description: '是否自动安装') booleanParam(name:'JENKIN_AUTO_DEPLOY', defaultValue: false, description: '是否自动发布') //FB参数 string(name:'JENKIN_FB_COMMENT', defaultValue: 'no comment', description: 'fb发布的comment') //Android参数 booleanParam(name:'JENKIN_ANDROID_DEBUG_FLAG', defaultValue: false, description: '是否构建android debug包') string(name:'JENKIN_ANDROID_VERSION_CODE', defaultValue: '3', description: 'android的Version Code') string(name:'JENKIN_ANDROID_VERSION_NAME', defaultValue: '1.0.7.2', description: 'android的Version Name') choice(name:'JENKIN_ANDROID_ARTIFACT_TYPE',choices: ['APK', 'AAB'], description: '构建android产物类型') //IOS参数 booleanParam(name:'JENKIN_IOS_INTERNAL_TESTING_ONLY_FLAG', defaultValue: false, description: '是否testflight internal testing only') string(name:'JENKIN_IOS_BUNDLE_VERSION_SHORT', defaultValue: '1.0.0', description: 'IOS的Version Short') string(name:'JENKIN_IOS_BUNDLE_VERSION', defaultValue: '1.0.0.0', description: 'IOS的Version') } -
-
流程
-
Print Information 打印参数
略 -
Build 构建
-
构建Cocos项目
npm install
CocosCreator命令行构建 参考Jenkinsfile
steps { echo 'Building FB..' sh "$WORKSPACE/jenkins/common-build-cocos.sh" }common-build-cocos.sh
# 安装项目引用node库 npm cache clean --force npm i npm run install:core_yz # Cocos命令行构建 CocosCreator \ --project \ --build \ "configPath=指定对应平台Build Config;" # 36是构建成功 if [ $? -eq 36 ]; then exit 0 else exit 1 fi -
构建产物
-
Android 参考
Jenkinsfile
steps { echo 'Building ANDROID AAB..' //构建项目 sh "$WORKSPACE/jenkins/common-build-cocos.sh" //打包aab sh "$WORKSPACE/jenkins/android/build_aab.sh" }build_aab.sh
#ANDROID_VERSION_CODE,ANDROID_VERSION_NAME就是 gradlew \ --project-dir \ -PversionCode=$ANDROID_VERSION_CODE \ -PversionName=$ANDROID_VERSION_NAME \ clean :app:bundleGpDebug
-
IOS
Jenkinsfile
略,参考Androidbuild_ipa.sh
#执行pod install echo "==========开始pod install==========" export LANG=en_US.UTF-8 pod install --repo-update echo "==========结束pod install==========" #执行xcode clean echo "==========开始项目清理==========" xcodebuild clean \ -workspace $XCODEBUILD_PARAM_WORKSPACE_FILE \ -scheme $XCODEBUILD_PARAM_SCHEME_NAME \ -configuration $XCODEBUILD_PARAM_CONFIGURATION_TYPE \ -destination $XCODEBUILD_PARAM_DESTINATION \ | xcpretty echo "==========结束项目清理==========" #执行对plist版本号的修改 使用/usr/libexec/PlistBuddy echo "==========开始修改info.plist==========" /usr/libexec/PlistBuddy -c "set CFBundleShortVersionString $IOS_BUNDLE_VERSION_SHORT" $PROJECT_INFO_PLIST_FILE /usr/libexec/PlistBuddy -c "set CFBundleVersion $IOS_BUNDLE_VERSION" $PROJECT_INFO_PLIST_FILE echo "==========结束修改info.plist==========" #执行xcode archive echo "==========开始archive==========" xcodebuild archive \ -workspace $XCODEBUILD_PARAM_WORKSPACE_FILE \ -scheme $XCODEBUILD_PARAM_SCHEME_NAME \ -sdk $XCODEBUILD_PARAM_SDK \ -configuration $XCODEBUILD_PARAM_CONFIGURATION_TYPE \ -destination $XCODEBUILD_PARAM_DESTINATION \ -archivePath $XCODEBUILD_PARAM_ARCHIVE_OUTPUT_FILE \ | xcpretty echo "==========结束archive==========" #执行xcode 导出ipa echo "==========开始导出ipa==========" xcodebuild -exportArchive \ -allowProvisioningUpdates \ -archivePath $XCODEBUILD_PARAM_ARCHIVE_OUTPUT_FILE \ -exportPath $XCODEBUILD_PARAM_IPA_OUTPUT_DIR \ -exportOptionsPlist $EXPORT_OPTIONS_PLIST_FILE \ | xcpretty echo "==========结束导出ipa=========="
-
-
Facebook 跳过
-
Web-mobile 跳过
-
-
产物归档(做备份)参考
-
Android
Jenkinsfile
post { success { //产物归档 echo 'Save Artifacts' archiveArtifacts artifacts: '**/build/android/proj/build/app/outputs/bundle/**/*.aab', fingerprint: true } } -
IOS
略,参考Android -
Web-mobile
Jenkinsfile
post { success { //zip压缩 zip archive: false, zipFile: "./build/web-mobile/archive-web-mobile-$env.BUILD_TAG-${getCurrentTime()}.zip", dir: './build/web-mobile' //存储产物 echo 'Zip Artifacts' archiveArtifacts artifacts: '**/build/web-mobile/archive-web-mobile-*.zip', fingerprint: true } } -
Facebook
略,参考Android
-
-
Install 安装
- Android
Jenkinsfile
steps { echo 'Installing ANDROID..' sh "$WORKSPACE/jenkins/android/install.sh" }install.sh
# 安装apk if [ "$ANDROID_DEBUG_FLAG" = true ]; then echo "==========开始安装Debug==========" $ANDROID_PROJ_DIR/gradlew --project-dir $ANDROID_PROJ_DIR installGpDebug echo "==========结束安装Debug==========" else echo "==========开始安装Release==========" $ANDROID_PROJ_DIR/gradlew --project-dir $ANDROID_PROJ_DIR installGpRelease echo "==========结束安装Release==========" fi
- Android
-
Deploy 发布
-
发布
-
Android
Jenkinsfile
steps { echo 'Deploying Android....' script { //查找产物文件名和路径 def artifact_name = "" def artifact_file_path = "" if (params.JENKIN_ANDROID_ARTIFACT_TYPE == 'APK') { //apk def files = findFiles(glob: "**/build/android/proj/build/app/outputs/apk/**/*.apk") if (files[0] != null) { artifact_name = files[0].name artifact_file_path = files[0].path } }else { //aab def files = findFiles(glob: "**/build/android/proj/build/app/outputs/bundle/**/*.aab") if (files[0] != null) { artifact_name = files[0].name artifact_file_path = files[0].path } } if(artifact_name && artifact_file_path){ def remote_dir_path = "/artifacts/blokus-king/android" //发布ftp sh "$WORKSPACE/jenkins/android/deploy.sh ${artifact_file_path} ${remote_dir_path}" //生成远程下载链接 ANDROID_ARTIFACT_REMOTE_URL="$(ftp_server_url)/${remote_dir_path}/${artifact_name}" echo "Remote Url : ${ANDROID_ARTIFACT_REMOTE_URL}" } } }deploy.sh
#第一个参数传入文件相对路径 RELATIVE_FILE=$1 #第二个参数传入上传文件夹 FTP_REMOTE_DIR=$2 # 上传Ftp echo "==========开始上传FTP==========" lftp -u "$USER_NAME","$PASSWORD" "$FTP_SERVER" <<EOF cd "$FTP_REMOTE_DIR" put "$PROJECT_DIR/$RELATIVE_FILE" bye EOF echo "==========结束上传FTP==========" -
IOS 参考
Jenkinsfile
steps { echo 'Deploying IOS....' sh "$WORKSPACE/jenkins/ios/deploy.sh" }deploy.sh
#验证ipa echo "==========开始验证ipa==========" xcrun altool \ --verbose \ --show-progress \ --validate-app \ -f $ALTOOL_PARAM_IPA_OUTPUT_FILE \ -t ios \ -u "$ALTOOL_PARAM_USER" \ -p "$ALTOOL_PARAM_PASSWORD" \ --output-format xml result=$(echo $?) echo "==========结束验证ipa 结果:$result==========" if [ $result -ne 0 ]; then echo "FAIL" echo '======DONE======' exit 1 fi #上传appstore echo "==========开始上传appstore==========" xcrun altool \ --verbose \ --show-progress \ --upload-package $ALTOOL_PARAM_IPA_OUTPUT_FILE \ -t ios \ --apple-id $ALTOOL_PARAM_APPLE_ID \ --team-id $ALTOOL_PARAM_TEAM_ID \ --bundle-id $ALTOOL_PARAM_BUNDLE_ID \ --bundle-short-version-string $IOS_BUNDLE_VERSION_SHORT \ --bundle-version $IOS_BUNDLE_VERSION \ -u "$ALTOOL_PARAM_USER" \ -p "$ALTOOL_PARAM_PASSWORD" \ --output-format xml result=$(echo $?) echo "==========结束上传appstore 结果:$result==========" if [ $result -ne 0 ]; then echo "FAIL" echo '======DONE======' exit 1 fi echo '======DONE======' exit 0 -
Facebook 参考
Jenkinsfile
略,参考IOSdeploy.sh
# 获取 access_token echo '======开始获取ACCESS_TOKEN======' resp=$(curl -s -X GET "https://graph.facebook.com/oauth/access_token?client_id=$APP_ID&client_secret=$APP_SECRET&grant_type=client_credentials") echo "======结束获取ACCESS_TOKEN 结果:$resp======" # 提取 access_token 的值(假设返回的格式是 access_token:your_token_here) access_token=$(echo $resp | jq -r '.access_token') echo $access_token # 开始上传 echo '======开始上传======' resp=$(curl -X POST https://graph-video.facebook.com/$APP_ID/assets \ -F 'access_token='$access_token \ -F 'type=BUNDLE' \ -F "asset=@$PROJECT_DIR/build/fb-instant-games/blokusking.zip" \ -F 'comment='$COMMENT) result=$(echo $resp | jq -r '.success') echo "======结束上传 结果:$result======" if [ "$result" = true ]; then echo '======DONE======' exit 0 else error=$(echo $resp | jq -r '.error') echo $error echo '======DONE======' exit 1 fi -
Web-mobile
略,参考Android
-
-
发送通知
-
飞书机器人 参考
Jenkinsfile
post { success { //用户决定是否发送 input message:'是否发送飞书通知', ok:'发送' echo "发送" script { def webhookUrl = "$(feishu_server_url)" //获取Git最新一次提交的信息 def gitLog = sh(script: 'git log -1 --pretty=oneline ${GIT_COMMIT}', returnStdout: true).trim() def title = "${env.BRANCH_NAME} ${params.JENKIN_BUILD_PLATFORM}发布" def downloadLink = "" if (params.JENKIN_BUILD_PLATFORM == 'ANDROID') { //Android才是生成下载链接 downloadLink = ANDROID_ARTIFACT_REMOTE_URL } def json = """ { "msg_type":"text", "content":{ "title":"${title}", "text":"${gitLog}", "archive_url":"${downloadLink}" } } """ sh "curl -X POST -H 'Content-Type: application/json' -d '${json}' ${webhookUrl}" } } }
-
-
-
完整的Jenkinsfile
Jenkinsfile.zip (2.5 KB)





