Jenkins是一个开源的、提供友好操作界面的持续集成(CI)工具,起源于Hudson(Hudson是商用的),主要用于持续、自动的构建/测试软件项目、监控外部任务的运行(这个比较抽象,暂且写上,不做解释)。Jenkins用Java语言编写,可在Tomcat等流行的servlet容器中运行,也可独立运行。通常与版本管理工具(SCM)、构建工具结合使用。常用的版本控制工具有SVN、GIT,构建工具有Maven、Ant、Gradle。
1. 环境配置
进入jenkins主界面,系统管理->全局工具配置 进行配置,包括 JDK,Git,Gradle,Maven等,如下,关于jdk的配置的设置。
![[图片] 基于jenkins进行定制化开发_jenkins](https://s2.51cto.com/images/blog/202211/04112124_636485347ea8952868.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
(图1)
如上图,给定一个别名,通过下拉框选择相应的Jdk版本,也可以通过FTP工具上传已经下载到本地的Jdk包。
如下,git配置需要指定一个本地已经下载好的路径。这种需要通过Ftp或者脚本提前下载指定版本的git工具。
![[图片] 基于jenkins进行定制化开发_jar_02](https://s2.51cto.com/images/blog/202211/04112124_63648534a3e6b48634.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
(图2)
Gradle,Maven配置与jdk和git的配置雷同,不做介绍。
2. 如何创建一个构建任务
生成apk需要新建一个构建任务,这里构建一个自由风格的软件项目,输入任务名称后就会自动创建一个构建项。

(图 3)
构建项包括 General,gitBucket,JobNotifications,源码管理,构建触发器,构建环境,构建,构建后操作。
![[图片] 基于jenkins进行定制化开发_jar_04](https://s2.51cto.com/images/blog/202211/04112125_6364853525d0c8767.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
(图4)
General:可以设置一些项目介绍
gitBucket:未用到
JobNotifications:使用 UDP、TCP 或 HTTP 协议向定义的端点发送有关作业状态的通知,设置同时并发构建数量
源码管理:配置关联不同的源码仓库,git,svn,scms等
构建触发器:可以hook一些事件进行自动构建,比如定时构建,提交构建,轮询scm构建等
构建环境:允许在构建之前进行一些操作,比如更新lfs,清除上一次构建缓存等
构建:指定构建脚本
构建后操作:主要配置构建后可以触发的操作,如发布单元测试报告,发送邮件通知,发布工件到蒲公英,fir等
3. 生成apk
基于步骤1,步骤2,只需要进行简单的配置即可打包apk,如图5
![[图片] 基于jenkins进行定制化开发_jar_05](https://s2.51cto.com/images/blog/202211/04112125_6364853541bcd37838.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
(图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](https://s2.51cto.com/images/blog/202211/04112125_636485356fff648407.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
(图6)
如上图,在需要用到的地方使用${App_VERSION_CODE},既可以取出来2406值来使用
8.2选项参数
![[图片] 基于jenkins进行定制化开发_jar_07](https://s2.51cto.com/images/blog/202211/04112125_636485358bc0f75466.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
(图7)
8.3布尔参数
![[图片] 基于jenkins进行定制化开发_jenkins_08](https://s2.51cto.com/images/blog/202211/04112125_63648535a6e2d87024.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
(图8)
8.4动态选择参数
![[图片] 基于jenkins进行定制化开发_git_09](https://s2.51cto.com/images/blog/202211/04112125_63648535c0d7f91469.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
(图9)
8.5 最终构建任务样式

- 一键生成加固多渠道包脚本
脚本逻辑介绍:
对等待加固的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. 动态选择插件