Jenkins自動化構建Android、RN混合apk打包+樂固加固+分渠道打包(需要開發配合用哪種分包方式)+推送打包通知到企業微信


要在Jenkins上做自動化打包,首先得把環境搞好。所需環境,包括JDK,Jenkins,gradle,AndroidSDK,nodejs,git,python。環境的安裝就不贅述了。做成三個工程組成上下游工程構建,打包一個工程,加固簽名一個工程,推送通知一個工程。下面粗糙的做了一個自動化打apk包的工程組合。直接上Jenkins自動化apk打包工程的配置:

(一)、打包工程

構建頁面如下:

 

 下面是配置:

首先設置參數--git 參數,由於是RN混合Android原生,所以gitlab倉庫的項目是rn項目加子模塊android的形式。所以設置兩個git參數:項目分支參數,子模塊的分支

 

 

 

 還有其他如環境參數Env,是否要推送到企業微信,是否要加固后簽名,是否要分渠道打包,如下圖:

 

 

 

 

 

 

 

 

 

 

 

 參數設置后,接下來設置源碼管理,目前所用的是git,因為是混合項目,有子模塊,所有選擇Multiple SCMs

 

 

 

 接下來配置構建環境:

 

 下面是構建步驟

一、由於我是在Linux環境下的Jenkins,所以打包所用的build-tools與windows不一樣,所以打包前需要修改build.gradle里面的build-tools版本,動態的修改Versioncode和Versionname,以及添加打包后檢查流程報錯的過濾。

修改項目下app里面的build.gradle的地方如下:

1、修改build-tools,Versioncode,Versionname

 

 2、修改輸出地址:

 

 3、增加三行代碼,避免執行lint的時候報錯

 4、修改配置文件Power.kt文件,按照測試環境或生產環境進行修改

 下面是做出修改的腳本

 

 

 

 

originoutput="C:\\\\\\\\Users\\\\\\\\dell\\\\\\\\Desktop\\\\\\\\"
newoutput="\/data\/jenkins\/workspace\/LuckyPanda"
powerpath=$WORKSPACE/android/app/src/main/java/com/wtsd/luckypanda/pack/config/Power.kt
gradlebuilepath=/data/jenkins/workspace/LuckyPanda/android/app/build.gradle
sed -i "s#$originoutput#$newoutput#g" $gradlebuilepath
sed -i "s/30.0.3/30.0.0-rc2/g" $gradlebuilepath
row=`sed -n  '/compileOptions/=' $gradlebuilepath`
sed -i "${row}i\        }" $gradlebuilepath
sed -i "${row}i\                abortOnError false" $gradlebuilepath
sed -i "${row}i\        lintOptions \{" $gradlebuilepath
Code=versionCode\ $versionCode
Name=versionName\ \'$versionName\'
#cp $JENKINS_HOME/workspace/build.gradle $gradlebuilepath
cp $JENKINS_HOME/workspace/local.properties $WORKSPACE/android/local.properties
if [ $Env == test ];then
echo --------------------Env為test,打測試包--------------------
:<<!
echo 復制測試環境配置文件Power.kt到utils目錄下並替換目錄下的Power.kt
cp $JENKINS_HOME/workspace/Power.kt ${powerpath}
!
echo 修改文件${powerpath}的配置,改為測試環境配置
sed -i 's/const val Log =.*$/const val Log = true \/\/發布APP 時 false/g' ${powerpath}
sed -i 's/const val Product =.*$/const val Product = false \/\/發布APP 時 true/g' ${powerpath}
sed -i 's/^.*const val Root = "https\:\/\/panda.wtsd.cn".*$/        \/\/const val Root = "https\:\/\/panda.wtsd.cn"/g' ${powerpath}
sed -i 's/^.*const val Root = "https\:\/\/test.szwtsd.com".*$/        const val Root = "https\:\/\/test.szwtsd.com"/g' ${powerpath}

elif [ $Env == product ];then
echo --------------------Env為product,打生產包---------------------
:<<!
echo 復制生產環境配置文件Power-pro.kt到utils目錄下重命名為Power.kt並替換目錄下原有的Power.kt
cp $JENKINS_HOME/workspace/Power-pro.kt ${powerpath}
!
echo 修改文件${powerpath}的配置,改為測試環境配置
sed -i 's/const val Log =.*$/const val Log = false \/\/發布APP 時 false/g' ${powerpath}
sed -i 's/const val Product =.*$/const val Product = true \/\/發布APP 時 true/g' ${powerpath}
sed -i 's/^.*const val Root = "https\:\/\/panda.wtsd.cn".*$/        const val Root = "https\:\/\/panda.wtsd.cn"/g' ${powerpath}
sed -i 's/^.*const val Root = "https\:\/\/test.szwtsd.com".*$/        \/\/const val Root = "https\:\/\/test.szwtsd.com"/g' ${powerpath}

fi
#sed -i 's#versionCode [[:digit:]]*#versionCode\ $versionCode#g' $gradlebuilepath
#sed -i 's/versionName \"[[:digit:]].*\"/versionName\ \"$versionName\"/g' $gradlebuilepath
echo ---------------------修改versionCode為$versionCode,修改versionName為$versionName--------------------
sed -i "/./{s/versionCode [[:digit:]]*/$Code/;s#versionName '[[:digit:]].*'#$Name#}" $gradlebuilepath

:<<!
crows=(`sed -n  '/versionCode/=' $gradlebuilepath`)
crow=${crows[0]}
cmdc="sed -i '${crow},${crow}s#[0-9]\+#${versionCode}#g' $gradlebuilepath"
eval $cmdc
vrows=(`sed -n  '/versionName/=' $gradlebuilepath`)
vrow=${vrows[0]}
cmd="sed -i '${vrow},${vrow}s#[0-9.]\+#${versionName}#g' $gradlebuilepath"
eval $cmd
!
View Code

 

 二、RN環境依賴下載,需要修改一個node_modules里面react-native-fs/android里面的build.gradle里面的版本配置,要不然會報錯“react-native-fs:verifyReleaseResources FAILED”,需要修改的地方如下圖

 

 

下面截圖是想以的shell腳本

 

#!/bin/bash
export NODE_HOME=/opt/nodejs/node-v14.15.3-linux-x64/bin
export PATH=${PATH}:${NODE_HOME}
yarn
if [ $Env == test ];then
yarn ${Env}
elif [ $Env == product ];then
yarn prod
fi
reactfs_buildegradle=$WORKSPACE/node_modules/react-native-fs/android/build.gradle
sed -i "s#compileSdkVersion', [[:digit:]]*#compileSdkVersion\'\,\ 28#g" $reactfs_buildegradle
sed -i "s#buildToolsVersion', '[[:digit:]].*'#buildToolsVersion\'\,\ \'28.0.3\'#g" $reactfs_buildegradle
sed -i "s#targetSdkVersion', [[:digit:]]*#targetSdkVersion\'\,\ 28#g" $reactfs_buildegradle
export GRADLE_HOME=/opt/gradle
export PATH=$PATH:$GRADLE_HOME/bin
cd android/
gradle clean build
View Code

 如果只單獨構建release包或者debug,替換命令行即可:

  • gradle assembleDebug 只打debug
  • gradle assembleRelease 只打release

 

構建后操作:

記錄生產版本的Versioncode

 

if(manager.build.buildVariables.get("Env").equals("product")) {manager.addShortText(manager.build.buildVariables.get("Env"))
manager.addShortText("versionCode:${manager.build.buildVariables.get("versionCode")}")}

 

 每次觸發后構建歷史會顯示如下:

 

傳遞參數到下游項目:

 

 

 (二)、加固簽名工程

構建頁面如下:

 

 配置如下:

參數出需要設置接收的傳遞參數:

 

 

 

 

 設置構建步驟:

1、使用騰訊雲樂固加固,加固的依賴jar包可以在騰訊雲社區下載https://cloud.tencent.com/developer/article/1193406?from=10680或依據以下步驟下載:

查看jar包更新歷史:

https://leguimg.qcloud.com/ms-client/java-tool/version.json

可以根據最近日期獲取到最新版本號

將上面獲取到的版本號替換下面的版本號進行下載:

如下下載最新版本1.0.3版本

https://leguimg.qcloud.com/ms-client/java-tool/1.0.3/ms-shield.jar

 

cp ${JENKINS_HOME}/workspace/${prj}/android/app/build/outputs/apk/release/*.apk ${JENKINS_HOME}/workspace/${prj}/android/LuckyPanda-$Env-$versionName.apk
cp ${JENKINS_HOME}/workspace/ms-shield.jar ${JENKINS_HOME}/workspace/${prj}/android/
leguapp=${JENKINS_HOME}/workspace/${prj}/result/LuckyPanda-${Env}-${versionName}_legu.apk
if [ $ShieldAndSigner == Y ];then
if [ ! -f "$leguapp" ];then
#樂固加固處理
cd ${JENKINS_HOME}/workspace/${prj}/android/
echo --------------------------------------------樂固加固處理--------------------------------------------------
java -Dfile.encoding=utf-8 -jar ms-shield.jar -sid AKIDLVP3RZR4SbU8etsb3FBsJUP2OUJq65pI -skey D9pFOFqBE0UieJgndeNp54nFRN2RaR1J -uploadPath ${JENKINS_HOME}/workspace/${prj}/android/LuckyPanda-${Env}-${versionName}.apk -downloadPath ${JENKINS_HOME}/workspace/${prj}/result/
else
echo 已做加固處理~
fi
else
echo 不做加固處理~
fi
View Code

 

 2、簽名並做分渠道打包處理,並且把渠道包到成zip包,(wall-cli-all.jar下載地址:https://github.com/Meituan-Dianping/walle/releases)

 

#重新簽名
leguapp=LuckyPanda-${Env}-${versionName}_legu.apk
buildToolsVersion=30.0.3 #編譯工具版本
keystore=${JENKINS_HOME}/workspace/${prj}/android/keystore/lucky.jks
storePwd="wtsd888888" # 默認的簽名文件密碼
keyPwd="wtsd666666" # 默認的簽名別名密碼
alias=luckypanda  #別名
signFile=LuckyPanda-${Env}-${versionName}_legu-signed.apk
signCmd="/opt/android-sdk-linux/build-tools/${buildToolsVersion}/apksigner sign \
--ks ${keystore} \
--v1-signing-enabled true \
--v2-signing-enabled true \
--ks-key-alias ${alias} \
--ks-pass pass:${storePwd} \
--key-pass pass:${keyPwd} \
--out ${JENKINS_HOME}/workspace/${prj}/result/${signFile} ${leguapp}"
if [ $ShieldAndSigner == Y ];then
cd ${JENKINS_HOME}/workspace/${prj}/result/
if [ ! -f "$signFile" ];then
echo '正在簽名'
${signCmd}
echo '簽名完成'
else
echo '已有簽名完成的apk包'
fi
else
echo '不做加固處理不需要簽名~'
fi
#多渠道打包
if [ $MultiChannel == Y ];then
#使用美團wall分渠道打包,把分渠道的依賴包復制到工作空間
cp $JENKINS_HOME/workspace/walle-cli-all.jar ${JENKINS_HOME}/workspace/${prj}/
#進入工作空間根目錄
cd ${JENKINS_HOME}/workspace/${prj}/
if [ ! -f "${JENKINS_HOME}/workspace/${prj}/result/LuckyPanda"*".zip" ];then
#多渠道分包
/opt/java/jdk1.8.0_181/bin/java -jar walle-cli-all.jar batch -f ${JENKINS_HOME}/workspace/${prj}/android/channel.txt ${JENKINS_HOME}/workspace/${prj}/result/$signFile
time=$(date "+%Y%m%d%H%M%S")
#分包后重新進入分包所在目錄,並把所得渠道包壓縮為一個gz包
cd ${JENKINS_HOME}/workspace/${prj}/result/
#tar --warning=no-file-changed -zcvf ${JOB_NAME}${time}.tar.gz *  --exclude=${leguapp} --exclude=${signFile} --exclude=${signFile}.idsig
zip ${prj}${time}.zip * -x "${leguapp}" "${signFile}" "${signFile}.idsig"
else
echo '已做好分渠道打包'
fi
else
echo '不做分渠道打包~'
fi
View Code

 

 構建后操作:

設置下游項目的傳遞參數

 

 

(三)推送企業微信

構建頁面如下:

 

 配置如下:

參數處設置接受上游項目傳遞過來的參數

 

 

 

 

 構建步驟

 

#!/bin/bash
cd /data/jenkins/workspace/
if [ $isPushToQYWX == Y ];then
echo isPushToQYWX參數為Y,把apk包推送到企業微信
if [ $ShieldAndSigner == Y ];then
if [ $MultiChannel == Y ];then
filepath=$JENKINS_HOME/workspace/${prj}/result/LuckyPanda*.zip
else
filepath=$JENKINS_HOME/workspace/${prj}/result/LuckyPanda-${Env}-${versionName}_legu-signed.apk
fi
else
filepath=$JENKINS_HOME/workspace/${prj}/android/LuckyPanda-${Env}-${versionName}.apk
fi
echo $filepath
python apkbuildresult.py $prj $filepath
else
echo 不推送到企業微信~
fi

推送的腳本apkbuildresult.py放在$JENKINS_HOME/workspace/下面,腳本如下:

#coding=utf-8
import requests
import json
import urllib
#import urllib.error
import time
import urllib2
import re
import os
import sys
jobname=sys.argv[1]
filepath=sys.argv[2]
# 獲取構建結果
a="--select--"
def getProName():
    fname = pathGitLab
    with open(fname, 'r') as f:
        first_line = str(f.readline())  # 讀第一行
        s = f.read()
        urls = re.findall(r'http://192.168.0.174/[a-zA-Z0-9._/\-:]*@[A-Z0-9]*', s)  # 用正則獲取svn路徑的url

        #print(first_line)
        builders=first_line.split(" ")
        #print(builders)
        while builders[0].isalpha():
            builders.remove(builders[0])
        builder=builders[0].strip("\n")
    builder=re.findall(ur'[^a-zA-Z0-9+\^:/\[\]]+',builder)
    #print(builder)
    builder=builder[-1]
    print(builder)
        f.closed
        if urls == []:
            urls = re.findall(r'http://192.168.0.174/[a-zA-Z0-9._/\-:]*', s)

            if urls == []:
                urls = re.findall(r'192.168.0.210[a-zA-Z0-9._/\-:]*.git', s)
                print urls
                branchs = re.findall(r'\(refs/remotes/origin/[a-zA-Z0-9._/\-:]*\)', s)
                print branchs
                if branchs==[]:
                    branchs=re.findall(r'\(detached\)', s)
                    if branchs==['(detached)']:
                        print branchs
                        branch="master"
                else:
                    B = branchs[0].split("/origin/")
                    print B
                    branchs0 = B[-1]
                    B1 = branchs0.split(")")
                    branch = B1[0]
                    print branch


                branch = "/branches/"+branch
                url=urls[0].replace(".git",branch)
                print url
            else:
                url = urls[0]  # 取第一個url
        else:
            url = urls[0]  # 取第一個url

        #print urls

        print url
        L = url.split("/branches/")#以branches作為分割點,得到分支
        print L
        a = L[0]
        branch=L[-1]
        print branch
        L1 = a.split('/')#以/分割得到項目名
        prj = L1[-1]
        if prj=="source":
            prj=L1[-2]
            if prj=="BMS" or prj=="MPS":
                prj = L1[-3]+L1[-2]
        elif prj=="BMS" or prj=="MPS":
            pb=L1[-2].split(':')
            prj = pb[-1]+L1[-1]
        print prj
        return prj,branch,builder
def getResult():
    fname = pathGitLab
    with open(fname, 'rb') as f:  # 打開文件

        first_line = f.readline()  # 讀第一行
        #print (first_line)
        off = -50  # 設置偏移量

        while True:

            f.seek(off, 2)  # seek(off, 2)表示文件指針:從文件末尾(2)開始向前50個字符(-50)

            lines = f.readlines()  # 讀取文件指針范圍內所有行
            # print (lines)

            if len(lines) >= 2:  # 判斷是否最后至少有兩行,這樣保證了最后一行是完整的

                last_line = lines[-1]  # 取最后一行
                print (last_line)

                break

            off *= 2

        if 'FAILURE' in last_line.decode():
            return 1
        elif 'SUCCESS' in last_line.decode():
            return 0

        else:

            return 2


# 獲取下一次構建的Number和當前構建的number

def getNextNumber(jobname):

    f = open(r'/data/jenkins/jobs/%s/nextBuildNumber'% jobname, 'r')

    currentNumber = int(f.read()) - 1

    return currentNumber


# 網絡模塊,用於企業微信發送信息

def jenkins(result,builder,proname,branch,jobname,jobnum):
    url = 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=5eac012b-20ea-43d0-a9bd-504b85c02eca'
    #url = 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=f088b2d7-6cd0-488d-a28d-2e4c9babb78b'
    # 企業微信機器人的webhook

    if result == 1:
        con = {"msgtype": "text",
               "text": {"content": "項目構建提醒\r\n構建者:%s\r\n項目名及分支:%s,%s\r\n構建結果:FAILURE\r\n打包失敗,請查看Jenkins構建具體日志!\r\nhttp://192.168.0.154:6689/job/%s/%s/console"% (builder,proname,branch,jobname,jobnum)}, }
    elif result == 0:
        con = {"msgtype": "text",
               "text": {"content": "項目構建提醒\r\n構建者:%s\r\n項目名及分支:%s,%s\r\n構建結果:SUCCESS\r\n打包成功!"% (builder,proname,branch)}, }
    else :
        con={"msgtype": "text","text": {"content":"項目構建提醒\r\n構建者:%s\r\n項目名及分支: %s,%s\r\n構建結果:UNSTABLE\r\n打包失敗,請查看Jenkins構建具體日志!\r\nhttp://192.168.0.154:6689/job/%s/%s/console"% (builder,proname,branch,jobname,jobnum)}, }

    jd = json.dumps(con).encode('utf-8')
    print jd
    req=urllib2.Request(url, jd)
    #req = urllib.request.Request(url, jd)

    req.add_header('Content-Type', 'application/json')
    response = urllib2.urlopen(req)
def apk_upload(file):
    name = os.path.basename(file)
    #id_url = 'https://qyapi.weixin.qq.com/cgi-bin/webhook/upload_media?key=5eac012b-20ea-43d0-a9bd-504b85c02eca&type=file'  # 上傳文件接口地址
    wx_url = 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=5eac012b-20ea-43d0-a9bd-504b85c02eca'  # 發送消息接口地址

    if result == 0:
        #data = {'file': open(file, 'rb')}  # post jason
        #response = requests.post(url=id_url, files=data)  # post 請求上傳文件
        #json_res = response.json()  # 返回轉為json
        #media_id = json_res['media_id']  # 提取返回ID
#
        #data = {"msgtype": "file", "file": {"media_id": media_id}}  # post json
        if ".zip" in name:
            data = {"msgtype": "text", "text": {
                "content": "因分渠道打包后壓縮包較大,請點擊下方鏈接下載\r\nhttp://192.168.0.154:6689/job/%s/ws/result/%s" % (jobname,name)}}
            # data = json.dumps(data).encode('utf-8')
            #r = requests.post(url=wx_url, json=data)  # post請求消息
        elif "signed" in name:
            data = {"msgtype": "text", "text": {
                "content": "因apk包較大,請點擊下方鏈接下載\r\nhttp://192.168.0.154:6689/job/%s/ws/result/%s" % (jobname,name)}}
        else:
            data = {"msgtype": "text", "text": {
                "content": "因apk包較大,請點擊下方鏈接下載\r\nhttp://192.168.0.154:6689/job/%s/ws/android/%s" % (jobname,name)}}
    else:
        return False
    r = requests.post(url=wx_url, json=data)  # post請求消息
    return r  # 返回請求狀態

if __name__ == '__main__':
    jobCurrentNumber = getNextNumber(jobname)  # 獲取當前構建number
    print (jobCurrentNumber)

    # 獲取當前構建的目錄比如D:\Jenkins\jobs\gk_check\builds\153,
    path = "/data/jenkins/jobs/%s/builds/" % jobname + str(jobCurrentNumber) + "/"
#
    pathGitLab = path + "log"  # 獲取構建日志的路徑
    #pathGitLab = "E:\\log"

    pathAuthor = path + "changelog.xml"  # 獲取遞交者信息的文件路徑

    tup=getProName()
    proname=tup[0]
    print proname
    branch=tup[1]
    print branch
    builder=tup[2]
    result = getResult()  # 讀取構建結果
    print (result)

    jenkins(result,builder,proname,branch,jobname,str(jobCurrentNumber))  # 最后執行函數
    apk_upload(filepath)
    getResult()
View Code

 

 配置結束。。。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM