Jenkins是一个开源的、提供友好操作界面的持续集成(CI)工具,起源于Hudson(Hudson是商用的),主要用于持续、自动的构建/测试软件项目、监控外部任务的运行(这个比较抽象,暂且写上,不做解释)。Jenkins用Java语言编写,可在Tomcat等流行的servlet容器中运行,也可独立运行。通常与版本管理工具(SCM)、构建工具结合使用。常用的版本控制工具有SVN、GIT,构建工具有Maven、Ant、Gradle。

1. 环境配置

进入jenkins主界面,系统管理->全局工具配置 进行配置,包括 JDK,Git,Gradle,Maven等,如下,关于jdk的配置的设置。

基于jenkins进行定制化开发_jenkins


(图1)

如上图,给定一个别名,通过下拉框选择相应的Jdk版本,也可以通过FTP工具上传已经下载到本地的Jdk包。

如下,git配置需要指定一个本地已经下载好的路径。这种需要通过Ftp或者脚本提前下载指定版本的git工具。

基于jenkins进行定制化开发_jar_02


(图2)

Gradle,Maven配置与jdk和git的配置雷同,不做介绍。

2. 如何创建一个构建任务

生成apk需要新建一个构建任务,这里构建一个自由风格的软件项目,输入任务名称后就会自动创建一个构建项。

基于jenkins进行定制化开发_jar_03

(图 3)

构建项包括 General,gitBucket,JobNotifications,源码管理,构建触发器,构建环境,构建,构建后操作。

基于jenkins进行定制化开发_jar_04

(图4)
General:可以设置一些项目介绍
gitBucket:未用到
JobNotifications:使用 UDP、TCP 或 HTTP 协议向定义的端点发送有关作业状态的通知,设置同时并发构建数量
源码管理:配置关联不同的源码仓库,git,svn,scms等
构建触发器:可以hook一些事件进行自动构建,比如定时构建,提交构建,轮询scm构建等
构建环境:允许在构建之前进行一些操作,比如更新lfs,清除上一次构建缓存等
构建:指定构建脚本
构建后操作:主要配置构建后可以触发的操作,如发布单元测试报告,发送邮件通知,发布工件到蒲公英,fir等

3. 生成apk

基于步骤1,步骤2,只需要进行简单的配置即可打包apk,如图5

基于jenkins进行定制化开发_jar_05

(图5)
配置tasks clean debugDebug

4. 生成二维码

为了方便apk的下载安装,将apk包的路径生成可直接下载的二维码图片。主要使用一个二维码生成的jar包。

主要实现步骤:
通过 ​​​jq​​工具,解析gradle生成apk时的output-metadata.json,获取apk名称,进行输出,找到输出的apk之后根据条件执行不同的逻辑,比如加固。使用cp 命令将apk副本拷贝到指定目录,然后通过QRcode.jar对目录中的apk路径生成二维码。在构建后操作 ​​build description​​ 插件指定二维码路径,就可以在指定位置显示二维码。(可以使用html调整二维码大小)

output-metadata.json
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.codeview.takumi",
"variantName": "debug",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 1,
"versionName": "1.0.0",
"outputFile": "app-debug.apk"
}
],
"elementType": "File"
}

使用方法需要用到 java -jar 命令

java -jar /data/service/tomcat/QRcode.jar url=http://apk.android.reworldgame.com/apk/$JOB_NAME.apk image=$JOB_NAME.jpg save=/data/www/image  # 使用二维码生成jar 生成二维码

格式: java -jar 二维码生成jar路径 url=apk路径 image=xx.jpg save=二维码地址

tips: 生成二维码的jar,在java 8平台编译,如果在java11 上不能运行。需要进行调整

5. 集成360加固

根据安全方面的考虑,对在国内应用市场上线的apk,上架app市场前,做加固处理。选择360加固,由于 360加固 助手提供命令行方式加固。

#加固apk脚本 [参数1:加固apk 参数2:加固apk输出路径 参数3: 海外国内来区分使用的签名文件]

if [ $jiagu = true ]; then
dos2unix /data/360/360jg.sh #忽略空格差异,造成无法执行脚本
bash -ex /data/360/360jg.sh ${WORKSPACE}/app/build/outputs/apk/${VARIABLE}/*.apk ${WORKSPACE}/app/build/outputs/apk/ china

完整shell脚本
#!/bin/bash
BASE=/data/360/jiagu/jiagu.jar
BASE2=/data/360/jiagu/
#360加固账号
NAME=360账户
PASSWORD=360密码
APK=$1
DEST=$2
flavor=$3
#chinaFlag="/apk/china/"
#根据输出路径判断是国内分支还是海外分支
#if[[$APK =~$chinaFlag]]
#then
#flavor="china"
#else
#flavor="overseas"
#fi
#签名文件根据输入flavor来区分
echo "flavor = "$flavor
if [ "$flavor" = "china" ];then
KEY_PATH= #密钥路径
KEY_PASSWORD= #密钥密码
ALIAS=key0 #别名
ALIAS_PASSWORD=#别名密码
elif [ "$flavor" = "SouthEastAsia" ];then
KEY_PATH=#密钥路径
KEY_PASSWORD= #密钥密码
ALIAS= #别名
ALIAS_PASSWORD=密码
#别名密码
else
KEY_PATH=/data/360/sign/reworldoversea.jks #密钥路径
KEY_PASSWORD=#密钥密码
ALIAS=#别名
ALIAS_PASSWORD= #别名密码
fi
echo "------ 打包签名详情! ------"
echo "使用的签名文件路径 ="$KEY_PATH
echo "Apk包路径 ="$APK
echo "加固包输出路径 ="$DESTi
echo "------ running! ------"
cd $BASE2
#java -jar ${BASE} -version
java -jar ${BASE} -login ${NAME} ${PASSWORD}
java -jar ${BASE} -importsign ${KEY_PATH} ${KEY_PASSWORD} ${ALIAS} ${ALIAS_PASSWORD}
java -jar ${BASE} -showsign
#java -jar ${BASE} -importmulpkg ${BASE2}/空渠道.txt #根据自身情况使用
#java -jar ${BASE} -importmulpkg ${BASE2}/空渠道.txt #根据自身情况使用
java -jar ${BASE} -showmulpkg
java -jar ${BASE} -showconfig
java -jar ${BASE} -jiagu ${APK} ${DEST} -autosign
#java -jar ${BASE} -pkgparam ${BASE2}/哈哈.txt
echo "--------finish------------"

tips:
${WORKSPACE} 占位符方式,可以获取jenkins默认本地变量和通过参数化构建设置的自定义变量

6. 集成蒲公英,fir平台插件

发布到蒲公英和fir内测平台,通过以上两个平台提供的快速发布插件,可快速集成,这里提供一种回显蒲公英平台生成二维码到jenkins的方案
发布到蒲公英平台之后,返回的json串格式,如下:

{
"code":0,
"message":"",
"data":{
"appKey":"c2c6568526071a91d8ce0dfb596b894f",
"userKey":"2b50d762a0957feaf481346938eba78e",
"appType":"2",
"appIsLastest":"1",
"appFileSize":"259772621",
"appName":"Reworld",
"appVersion":"1.0.0",
"appVersionNo":"1",
"appBuildVersion":"1",
"appIdentifier":"com.codereview.reworldsea",
"appIcon":"5ff42e3dc70e4deffb8c0f48a3703dc2",
"appDescription":"",
"appUpdateDescription":"",
"appScreenshots":"",
"appShortcutUrl":"DrpS",
"appCreated":"2021-11-25 14:29:00",
"appUpdated":"2021-11-25 14:29:00",
"appQRCodeURL":"https:\/\/www.pgyer.com\/app\/qrcodeHistory\/8273db84803bcb78f617c58c7d60f623ae20a25839d7efe9b90ea4788bba07f7"
}
}

shell脚本
在将apk发布到蒲公英之后,对返回的结果json串中appQRCodeURL的值进行解析。

# 脚本文件 pgyUpdate.sh
#!/bin/bash

#MAIN_MODULE AndroidStudio project directory under the main module
MAIN_MODULE="."
#The test address separately
#MAIN_MODULE="./app"
# pgy API account

PGYER_USER_KEY="蒲公英userkey"

PGYER_API_KEY="蒲公英api key"

#APK path
#APK_PATH="${MAIN_MODULE}/build/outputs/apk"
#Receive the external transfer apk path

APK_PATH=$1
echo "=============================================="
#echo "current path: `pwd`"
echo "APK_PATH=${APK_PATH}"
APK_LIST=`ls -t ${APK_PATH}`
echo "${APK_LIST}"
#ind the latest apk package
for APK_FILE in ${APK_LIST}; do
APK_NAME=${APK_FILE}
echo "apk file name= $APK_NAME File=$APK_FILE"
done

#-------------------------------------------------

if [ -n "$APK_NAME" ]; then # Determine whether is empty

echo "upload $APK_FILE pgy"
#upload pgy updateDescription(version descript)
RESULT=$(curl -F "file=@${APK_PATH}/${APK_NAME}" -F "uKey=${PGYER_USER_KEY}" -F "_api_key=${PGYER_API_KEY}" https://qiniu-storage.pgyer.com/apiv1/app/upload)

echo $RESULT > pgy.json
#-------------------send mail-------------------------
# echo "send mail to" test@qq.com

# echo "${RESULT}" | mail -s "${APK_NAME} already unload pgy" test@qq.com

# #http://www.pgyer.com/apiv1/app/upload
fi

构建块中添加执行发布到分发平台的shell脚本即可
#发布到蒲公英

if [ $pgy = true ]; then

cd /data/pgyupdate

./pgyUpdate.sh ${WORKSPACE}/app/build/outputs/apk/${VARIABLE}/

fi

fir类似,不赘述

7. 生成App Bundle

AAB格式为上架google play需要,与生成apk区别点在于构建方式不同。构建之后的处理与apk类似。

构建块中添加执行发布到分发平台的shell脚本即可

if [ "bundle" = "$packageType" ];
then
cd /data/www/aab/
# typeset -l lowerBuildChannel=${Build_channel}
# typeset -l lowerBuildType=${Build_type}
lowerBuildChannel=${Build_channel,}
lowerBuildType=${Build_type,,}
aabDir=${WORKSPACE}/app/build/outputs/bundle/${lowerBuildChannel}${Build_type}
aabName=${aabDir}/app-${lowerBuildChannel}-${lowerBuildType,}.aab
echo $aabName
cp ${aabDir}/*.aab /data/www/apk/$JOB_NAME.aab
java -jar /data/service/tomcat/QRcode.jar url=http://apk.android.reworldgame.com/apk/$JOB_NAME.aab image=$JOB_NAME.jpg save=/data/www/image
exit
fi

8. jenkins动态化参数介绍

参数构建是实现jenkins动态构建的基础。

8.1 字符参数

基于jenkins进行定制化开发_git_06

(图6)
如上图,在需要用到的地方使用${App_VERSION_CODE},既可以取出来2406值来使用

8.2选项参数

基于jenkins进行定制化开发_jar_07

(图7)

8.3布尔参数

基于jenkins进行定制化开发_jenkins_08

(图8)

8.4动态选择参数

基于jenkins进行定制化开发_git_09

(图9)

8.5 最终构建任务样式

基于jenkins进行定制化开发_java_10

  1. 一键生成加固多渠道包脚本
    脚本逻辑介绍:
    对等待加固的apk,使用360加固包提供的脚本jar文件进行加固,然后针对生成的加固包执行wallet.jar 多渠道脚本,基于加固包生成多个渠道的apk文件,提供给对应渠道进行上架。
@echo off


echo ========== push apk to here ============
::需要加固的apk路径
set/p APK= >nul
set APK_VERSION=1
set APK_FLAVOR=1


set remain=%APK%
set index=0
:loop
for /f "tokens=1* delims=_" %%a in ("%remain%") do (
if %index%==1 (
set APK_FLAVOR=%%a
)
if %index%==2 (
set APK_VERSION=%%a
)
set /a index=index+1
rem 将截取剩下的部分赋给变量remain,其实这里可以使用延迟变量开关
set remain=%%b
)
::如果还有剩余,则继续分割
if defined remain goto :loop


set APK_FLAVOR=%APK_FLAVOR:~0,7%
echo %APK%
echo APK_VERSION=%APK_VERSION% APK_FLAVOR=%APK_FLAVOR%


::加固配置
set SRT="C:\360\jiagu"
set NAME=360账户
set PASSWORD=360账户密码


cd %SRT%
echo current path ============== %cd%
if %APK_FLAVOR%==oversea (
echo =========== oversea =============
set BASE=C:\360\haiwai\jiagu\jiagu.jar
::密钥路径
set KEY_PATH=./jsk/reworldoversea.jks
::密钥
set KEY_PASSWORD=密码
::别名
set ALIAS=别名
::别名密码
set ALIAS_PASSWORD=别名密码
::输出加固包路径
set DEST=./海外/%APK_VERSION%
echo 本次使用印度签名
) else if %APK_FLAVOR%==southEa (
echo =========== SouthEastAsia =============
set BASE=C:\360\southEastAsia\jiagu\jiagu.jar
::密钥路径
set KEY_PATH=./jsk/xxx.jks
::密钥密码
set KEY_PASSWORD=密码
::别名
set ALIAS=别名
::别名密码
set ALIAS_PASSWORD=别名密码
::输出加固包路径
set DEST=./东南亚/%APK_VERSION%
echo 本次使用东南亚签名
) else (
echo =========== china =============
set BASE=./jiagu.jar
::密钥路径
set KEY_PATH=./jsk/test.jks
::密钥密码
set KEY_PASSWORD=密码
::别名
set ALIAS=别名
::别名密码
set ALIAS_PASSWORD=别名密码
::输出加固包路径
set DEST=./国内/%APK_VERSION%
echo 本次使用china签名
)
echo %BASE% jiagu apk output path ======: %DEST%
md "%DEST%"


echo "------ jaigu running! ------"


java -jar %BASE% -version
java -jar %BASE% -login %NAME% %PASSWORD%
java -jar %BASE% -importsign %KEY_PATH% %KEY_PASSWORD% %ALIAS% %ALIAS_PASSWORD%
java -jar %BASE% -showsign
java -jar %BASE% -showmulpkg
java -jar %BASE% -showconfig
java -jar %BASE% -jiagu %APK% %DEST% -autosign


echo "------ jaigu finished! ------"


echo "------ start rename ------"
cd %DEST%
echo current path ============== %cd%
for %%i in (./*.apk) do (
echo %%i| findstr "jiagu" >nul && (
echo find jiagu apk
echo %%i| findstr "sign" >nul && (
echo find signed apk
copy "%%i" "reworld.apk"
goto loop2
)
)
)
:loop2
echo "------ end rename ------"


echo "------ start walle ------"
cd %SRT%
echo current path ============== %cd%
set CHANNEL_PATH=%DEST%/%date:~0,4%%date:~5,2%%date:~8,2%%time:~0,2%%time:~3,2%%time:~6,2%
java -jar ./walle-cli-all.jar batch -f ./walle临时渠道.txt "%~f1" %DEST%/reworld.apk %CHANNEL_PATH%
::统计目录下文件个数
cd %CHANNEL_PATH%
echo current path ============== %cd%
set count=0
for %%i in (./*.apk) do set /a count+=1
echo ------ channel file count:%count% ------
echo "------ end walle ------"
Pause

10. 校验apk签名脚本

脚本介绍:
针对加固之后apk签名与正确签名进行比对,避免海外,国内市场apk签名错用。

@echo off


echo ========== push apk to here ============
::需要加固的apk路径
set/p APK= >nul
echo ========== apk path is %APK% ============
echo ========== push flavor to here ============
echo 1:china
echo 2:oversea
echo 3:southAsia
set/p APK_FLAVOR_TYPE= >nul
if %APK_FLAVOR_TYPE%==1 (
set APK_FLAVOR=china
set APK_DEFINE_SHA1=bd67d0933ec6a40fb
)else if %APK_FLAVOR_TYPE%==2 (
set APK_FLAVOR=oversea
set APK_DEFINE_SHA1=5e33c207b8621a5a8bc458eb
)else if %APK_FLAVOR_TYPE%==3 (
set APK_FLAVOR=southAsia
set APK_DEFINE_SHA1=478550704adff0a1a89
)else (
set APK_FLAVOR=unKnow
set APK_DEFINE_SHA1=unSet
)
echo ============ choice flavor is %APK_FLAVOR% ============
set SIGNER_INFO_PATH=temp_singer.txt
set LINE_SHA1_FLAG=SHA-1
java -jar .\apksigner.jar verify --print-certs %APK% > %SIGNER_INFO_PATH%
call :read_apk_signer_sha1 %SIGNER_INFO_PATH%
:read_apk_signer_sha1
for /f "tokens=1,2 delims=:" %%a IN (%1) do (
echo %%a%%b
echo %%a | findstr %LINE_SHA1_FLAG% >nul && set APK_SHA1=%%b
)
:del_left
if "%APK_SHA1:~0,1%"==" " set APK_SHA1=%APK_SHA1:~1%&&goto del_left
echo ============%APK_DEFINE_SHA1% ============
echo ============%APK_SHA1% ============
if %APK_SHA1% equ %APK_DEFINE_SHA1% (
echo ============ SHA1 is right ============
)else (
echo ============ SHA1 is wrong ============
)
pause

11. 插件

​1.jenkins 下载地址​​​​2. 动态选择插件​