APK 自動(dòng)編譯平臺(tái)搭建(gitlab+jenkins+svn+dingding)
搭建一個(gè)在 Jenkins 上的自動(dòng)編譯平臺(tái),將 apk 發(fā)布到 SVN,之后把編譯結(jié)果推送到釘釘。
安裝 Jenkins
這一步不贅述,網(wǎng)上的教程太多了,值的一提的是,你也可以通過 Docker 去安裝。Docker 可以理解為搭建了一個(gè)虛擬機(jī),在虛擬機(jī)里面運(yùn)行 Jenkins。
Jenkins 上編譯 APK
- 也有很多文檔可以參考,主要注意在Jenkins配置菜單里面增加 ANDROID_HOME 環(huán)境變量
- 注意 Gradle 配置。同時(shí)使用./sdkmanager --list 然后再安裝需要的一些 NDK 等
能夠成功編譯并在工作空間生成正確的APK就可以下一步了。
gitlab 觸發(fā) Jenkins 自動(dòng)編譯
需要打通這兩個(gè)平臺(tái),讓 gitlab 特定的分支收到 push 或者有 commit merge 后,主動(dòng)觸發(fā)編譯。
Jenkins 安裝 Gitlab Hook Plugin 和 GitLab Plugin 兩個(gè)插件。
-
進(jìn)入 Jenkins 任務(wù)的配置界面
Filter branches by name 填寫你關(guān)注的分支。
點(diǎn)擊 Generate 按鈕 生成 Secret token。
保存。
image
-
把??URL 和 Secret token 分別填寫到 gitlab 項(xiàng)目的集成選項(xiàng)里,Trigger只勾選 push events就行,merge后也會(huì)觸發(fā)的。勾選Enable SSL verification 后 Add webhook。添加后有 Test 按鈕,選擇 Push events。如果能正常觸發(fā) Jenkins 編譯就說明 OK 了。
image-20210903111908673
APK 發(fā)布到 SVN
因?yàn)闅v史原因,我們的待測(cè) APK 都是存放在 SVN 上的,SVN 提交 commit 需要把應(yīng)用的版本號(hào),gitSha,commit 帶上,方便追溯問題。
首先 commit 我們可以通過 Jenkins 自動(dòng)就拿到了,如下操作后會(huì)增加一個(gè)名為 “SCM_CHANGELOG”的環(huán)境變量。后續(xù)腳本讀取即可。

其次,版本號(hào)和 gitSha 可以編譯生成 app_1.0.2_sdjfhga.apk 這種 apk 格式然后解析獲得。具體編譯生成的方式如下:
//build.gradle (app)
//我這里的versionCode versionName都是隨著commit提交自動(dòng)遞增的,比如當(dāng)前的versionCode是100 versionName是1.1.100
//當(dāng)你提交一個(gè)commit后,生成的apk的versionCode自動(dòng)變?yōu)?01,versionName為1.1.101
//可以按需使用。
def getSelfDefinedVersion(type) {//編譯不過需要配置git環(huán)境變量
Process process = "git rev-list --count HEAD".execute()//git提交數(shù)
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"
}
}
}
}
提交內(nèi)容準(zhǔn)備好了,進(jìn)入服務(wù)器 jenkins相關(guān)目錄,建立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 說明一次提交過多。不認(rèn)為是正常的。
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 項(xiàng)目的配置界面,先增加兩個(gè)參數(shù),后面有用。再增加構(gòu)建后操作。兩個(gè)內(nèi)容分別是推送到SVN 和 把相關(guān)結(jié)果發(fā)到釘釘?shù)哪_本。

推送到 SVN?!癇UILD SUCCESSFUL” 是僅編譯成功才需要執(zhí)行下方腳本。

#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
結(jié)果發(fā)送到釘釘
到釘釘?shù)?“智能群助手” 中添加 “機(jī)器人”,選擇 “自定義”(通過Webhook接入自定義服務(wù))。安全設(shè)置選擇自定義關(guān)鍵詞,關(guān)鍵詞填寫 "點(diǎn)擊查看"。
添加機(jī)器人后,有個(gè)WebHook地址,復(fù)制下來。后續(xù)就可以通過給這個(gè)地址發(fā)消息到群里了。
-
回到 Jenkins 增加構(gòu)建后操作。 Log text 填寫 Build 因?yàn)闊o論編譯結(jié)果如何,都是要通知到發(fā)送到釘釘群里的。Script填寫如下:
cd $WORKSPACE/../script /bin/bash jenkins_to_dingding.sh "$JOB_NAME" "$BUILD_NUMBER" "$SCM_CHANGELOG" "$push_to_svn" -
到服務(wù)器 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服務(wù)器的地址,包括下方一些服務(wù)器地址,使用的話請(qǐng)自行修改。 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過長,請(qǐng)點(diǎn)擊連接,自定義commit并觸發(fā)編譯。</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 # 機(jī)器人 webhook 地址(上文添加釘釘機(jī)器人結(jié)束時(shí)復(fù)制的webhook地址) DINGTALK_WEBHOOK_URL='https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxxxxxxxxxxxxxxxxxxx' # 消息標(biāo)題 # 實(shí)際不起作用,但是不能少,否則發(fā)送失敗 DINGTALK_TITLE="Jenkins平臺(tái)有新的構(gòu)建" # 消息正文 # Jenkins Job構(gòu)建日志地址 DINGTALK_TEXT="### ${JOB_NAME} 有新的構(gòu)建\n\n>\ **【構(gòu)建ID】:${BUILD_NUMBER}**\n\n>\ **【編譯狀態(tài)】:${BUILD_STATUS}**\n\n>\ **【SVN狀態(tài)】:${SVN_BUILD_STATUS}**\n\n>\ **【commit】:** ${SCM_CHANGELOG}\n\n>\ **【Jenkins】:[點(diǎn)擊查看更多](${JENKINS_JOB_BUILD_LOG_URL})**\n " # # 發(fā)送釘釘消息通知函數(shù) ################################################################# 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\" } " } # 發(fā)送釘釘消息 ################################################################# SEND_MESSAGE_TO_DINGTALK "${DINGTALK_WEBHOOK_URL}" "${DINGTALK_TITLE}" "${DINGTALK_TEXT}" -
最后效果如下:
image-20210903143024939

總結(jié)
- 能用 git 就盡量別用 SVN 了。 一些插件都不好用,或者滿足不了需求。只能自己寫。
- 上面的腳本其實(shí)也不一定就能滿足你的需求,最好的辦法就是根據(jù)需求自己改,自己定制。包括我之所以寫這兩個(gè)腳本,也是因?yàn)?Jenkins 版本比較低,插件不能滿足需求,又擔(dān)心升級(jí)出問題,只能自己編寫,或者拿網(wǎng)上的模版改。
- 整個(gè)流程搞定后還是挺爽的,以前要先提交merge 合并完代碼后,再用 Android Studio 編譯生成 APK 后 手動(dòng)提交到 SVN。整個(gè)流程繁瑣,且容易不符合規(guī)定導(dǎo)致出錯(cuò),比如不合代碼,直接就先提交 SVN 了。搞定完之后,只需要驗(yàn)證完 提交merge就可以了。Jenkins代替你編譯并提交。另外提 merge 推薦Android Studio 的插件-- GitLab Quick Merge Request 非常好用,在 Android Studio里面就可以用快捷鍵提交了。詳細(xì)使用方法可以找我之前的文章。


