APK 自动编译平台搭建(gitlab+jenkins+svn+dingding)
搭建一个在 Jenkins 上的自动编译平台,将 apk 发布到 SVN,之后把编译结果推送到钉钉。
安装 Jenkins
这一步不赘述,网上的教程太多了,值的一提的是,你也可以通过 Docker 去安装。Docker 可以理解为搭建了一个虚拟机,在虚拟机里面运行 Jenkins。
Jenkins 上编译 APK
- 也有很多文档可以参考,主要注意在Jenkins配置菜单里面增加 ANDROID_HOME 环境变量
- 注意 Gradle 配置。同时使用./sdkmanager --list 然后再安装需要的一些 NDK 等
能够成功编译并在工作空间生成正确的APK就可以下一步了。
gitlab 触发 Jenkins 自动编译
需要打通这两个平台,让 gitlab 特定的分支收到 push 或者有 commit merge 后,主动触发编译。
-
Jenkins 安装 Gitlab Hook Plugin 和 GitLab Plugin 两个插件。
-
进入 Jenkins 任务的配置界面
Filter branches by name 填写你关注的分支。
点击 Generate 按钮 生成 Secret token。
保存。
image
-
把👆URL 和 Secret token 分别填写到 gitlab 项目的集成选项里,Trigger只勾选 push events就行,merge后也会触发的。勾选Enable SSL verification 后 Add webhook。添加后有 Test 按钮,选择 Push events。如果能正常触发 Jenkins 编译就说明 OK 了。
image-20210903111908673
APK 发布到 SVN
因为历史原因,我们的待测 APK 都是存放在 SVN 上的,SVN 提交 commit 需要把应用的版本号,gitSha,commit 带上,方便追溯问题。
首先 commit 我们可以通过 Jenkins 自动就拿到了,如下操作后会增加一个名为 “SCM_CHANGELOG”的环境变量。后续脚本读取即可。
image-20210903113059522其次,版本号和 gitSha 可以编译生成 app_1.0.2_sdjfhga.apk 这种 apk 格式然后解析获得。具体编译生成的方式如下:
//build.gradle (app)
//我这里的versionCode versionName都是随着commit提交自动递增的,比如当前的versionCode是100 versionName是1.1.100
//当你提交一个commit后,生成的apk的versionCode自动变为101,versionName为1.1.101
//可以按需使用。
def getSelfDefinedVersion(type) {//编译不过需要配置git环境变量
Process process = "git rev-list --count HEAD".execute()//git提交数
process.waitFor()
int commits = process.getText().toInteger()
if ("code" == type) {
commits
} else if ("name" == type) {
"1.1.$commits"
}
}
def gitSha = 'git rev-parse --short HEAD'.execute().text.trim()
android {
defaultConfig {
//xxx
}
compileOptions {
//xxx
}
//这里指定生成的APK文件的命名
applicationVariants.all {variant ->
variant.outputs.all {output ->
if (variant.buildType.name == "debug") {
output.outputFileName = "APP-debug.apk"
} else if (variant.buildType.name == "release") {
output.outputFileName = "APP"+gitSha+"_"+getSelfDefinedVersion('name')+".apk"
}
}
}
}
提交内容准备好了,进入服务器 jenkins相关目录,建立script目录,下面编写SVN推送的脚本svn_push_apk.sh 。
#!/bin/bash -ilex
#Jenkins 工作目录
WORKSPACE=$1
#编译生成的Apk的位置。比如app/build/outputs/apk/release/*.apk
RELEASE_APK_REG=$2
#SVN上APK的存放位置。比如app/JenkinsTest/App.apk
SVN_APK_PATH=$3
#git commit
SCM_CHANGELOG=$4
#存放SVN的目录
COMMONAPK_PATH=${WORKSPACE}/../CommonApk
RELEASE_APK_PATH=$(ls -1 ${WORKSPACE}/${RELEASE_APK_REG})
cd ${COMMONAPK_PATH}
fl=${RELEASE_APK_PATH##*/}
fileName=${fl%.*}
apkName=$(echo $fileName | cut -d _ -f 1)
gitsha=$(echo $fileName | cut -d _ -f 2)
versionName=$(echo $fileName | cut -d _ -f 3)
svn cleanup
svn up --username "autobuild" --password "xxxxxx"
#commit 超过300 说明一次提交过多。不认为是正常的。
if [ ${#SCM_CHANGELOG} -gt 300 ];then
echo "git commit too long"
#gitSha和上次一样,不做提交。
elif echo $(svn log ${SVN_APK_PATH} -l 1 --username "autobuild" --password "xxxxxx") |grep -q $gitsha; then
echo "gitsha same, Don't upload."
else
cp -rf ${RELEASE_APK_PATH} $SVN_APK_PATH
commitlog="${apkName} version:${versionName} gitsha:${gitsha} changeLog:${SCM_CHANGELOG}"
svn commit -m "${commitlog}" ${COMMONAPK_PATH}/${SVN_APK_PATH} --username "autobuild" --password "xxxxxx"
echo "svn successful"
fi
回到 Jenkins 项目的配置界面,先增加两个参数,后面有用。再增加构建后操作。两个内容分别是推送到SVN 和 把相关结果发到钉钉的脚本。
image-20210903114507062推送到 SVN。“BUILD SUCCESSFUL” 是仅编译成功才需要执行下方脚本。
image-20210903114210646#1-WORKSPACE 2-releaseApk位置 3-svn上apk位置 4-commitlog
if [ "$push_to_svn" = "true" ]; then
[ ! $custom_commit ] && SCM_CHANGELOG="$custom_commit"
echo "$SCM_CHANGELOG"
cd $WORKSPACE/../script
/bin/bash svn_push_apk.sh "$WORKSPACE" app/build/outputs/apk/release/*.apk app/JenkinsTest/App.apk "$SCM_CHANGELOG"
fi
结果发送到钉钉
-
到钉钉的 “智能群助手” 中添加 “机器人”,选择 “自定义”(通过Webhook接入自定义服务)。安全设置选择自定义关键词,关键词填写 "点击查看"。
-
添加机器人后,有个WebHook地址,复制下来。后续就可以通过给这个地址发消息到群里了。
-
回到 Jenkins 增加构建后操作。 Log text 填写 Build 因为无论编译结果如何,都是要通知到发送到钉钉群里的。Script填写如下:
cd $WORKSPACE/../script /bin/bash jenkins_to_dingding.sh "$JOB_NAME" "$BUILD_NUMBER" "$SCM_CHANGELOG" "$push_to_svn"
-
到服务器 Jenkins 之前建的 script 目录 编辑 jenkins_to_dingding.sh
#!/bin/bash -ilex ################################################################# # 钉钉消息变量定义 ################################################################# JOB_NAME=$1 BUILD_NUMBER=$2 SCM_CHANGELOG=$3 PUSH_TO_SVN=$4 BUILD_STATUS="<font color='#ff0000'>失败</font>" #这是我的Jenkins服务器的地址,包括下方一些服务器地址,使用的话请自行修改。 LAST_BUILD_BUILD_XML=`curl http://192.168.10.58:8090/job/${JOB_NAME}/lastBuild/api/xml` BUILD_RESULT=$(echo $LAST_BUILD_BUILD_XML | grep "<result>SUCCESS</result>") if [ "${BUILD_RESULT}" ];then BUILD_STATUS="<font color='#00ff00'>成功</font>" else BUILD_RESULT=$(echo $LAST_BUILD_BUILD_XML | grep "<result>FAILURE</result>") if [ "${BUILD_RESULT}" ];then BUILD_STATUS="<font color='#ff0000'>失败</font>" else BUILD_STATUS="<font color='#ff0000'>无法获取</font>" fi fi JENKINS_JOB_BUILD_LOG_URL="http://192.168.10.58:8090/job/${JOB_NAME}/${BUILD_NUMBER}/console" JENKINS_LAST_BUILD_CONSOLE=`curl http://192.168.10.58:8090/job/${JOB_NAME}/${BUILD_NUMBER}/consoleText` #echo $JENKINS_LAST_BUILD_CONSOLE if [ "$push_to_svn" = "true" ];then SVN_BUILD_STATUS="<font color='#ff0000'>失败</font>" SVN_BUILD_RESULT=$(echo $JENKINS_LAST_BUILD_CONSOLE | grep "Committed revision") if [ "${SVN_BUILD_RESULT}" ];then SVN_BUILD_STATUS="<font color='#00ff00'>成功</font>" else SVN_BUILD_RESULT=$(echo $JENKINS_LAST_BUILD_CONSOLE | grep "gitsha same") SVN_COMMIT_RESULT=$(echo $JENKINS_LAST_BUILD_CONSOLE | grep "git commit too long") if [ "${SVN_COMMIT_RESULT}" ];then SVN_BUILD_STATUS="<font color='#ff0000'>失败,git commit过长,请点击连接,自定义commit并触发编译。</font>" elif [ "${SVN_BUILD_RESULT}" ];then SVN_BUILD_STATUS="<font color='#ff0000'>失败,与上次提交gitsha相同</font>" else SVN_BUILD_STATUS="<font color='#ff0000'>失败</font>" fi fi else SVN_BUILD_STATUS="<font color='#a9a9a9'>跳过</font>" fi # 机器人 webhook 地址(上文添加钉钉机器人结束时复制的webhook地址) DINGTALK_WEBHOOK_URL='https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxxxxxxxxxxxxxxxxxxx' # 消息标题 # 实际不起作用,但是不能少,否则发送失败 DINGTALK_TITLE="Jenkins平台有新的构建" # 消息正文 # Jenkins Job构建日志地址 DINGTALK_TEXT="### ${JOB_NAME} 有新的构建\n\n>\ **【构建ID】:${BUILD_NUMBER}**\n\n>\ **【编译状态】:${BUILD_STATUS}**\n\n>\ **【SVN状态】:${SVN_BUILD_STATUS}**\n\n>\ **【commit】:** ${SCM_CHANGELOG}\n\n>\ **【Jenkins】:[点击查看更多](${JENKINS_JOB_BUILD_LOG_URL})**\n " # # 发送钉钉消息通知函数 ################################################################# function SEND_MESSAGE_TO_DINGTALK() { /usr/bin/curl "$1" -H 'Content-Type: application/json' -d " { \"markdown\": { \"title\": \"$2\", \"text\": \"$3\" }, \"at\": { \"atMobiles\": [], \"isAtAll\": false }, \"msgtype\": \"markdown\" } " } # 发送钉钉消息 ################################################################# SEND_MESSAGE_TO_DINGTALK "${DINGTALK_WEBHOOK_URL}" "${DINGTALK_TITLE}" "${DINGTALK_TEXT}"
-
最后效果如下:
image-20210903143024939
总结
- 能用 git 就尽量别用 SVN 了。 一些插件都不好用,或者满足不了需求。只能自己写。
- 上面的脚本其实也不一定就能满足你的需求,最好的办法就是根据需求自己改,自己定制。包括我之所以写这两个脚本,也是因为 Jenkins 版本比较低,插件不能满足需求,又担心升级出问题,只能自己编写,或者拿网上的模版改。
- 整个流程搞定后还是挺爽的,以前要先提交merge 合并完代码后,再用 Android Studio 编译生成 APK 后 手动提交到 SVN。整个流程繁琐,且容易不符合规定导致出错,比如不合代码,直接就先提交 SVN 了。搞定完之后,只需要验证完 提交merge就可以了。Jenkins代替你编译并提交。另外提 merge 推荐Android Studio 的插件-- GitLab Quick Merge Request 非常好用,在 Android Studio里面就可以用快捷键提交了。详细使用方法可以找我之前的文章。
网友评论