需求说明
公司目前有11个定制化版本,需要让测试人员基于jenkins自由选择打测试和正式版包。
我讲一下我这里的特色功能
grale编程实现apk自动乐固加固
apk自动上传svn
一般通过jenkins配置实现定制化打包网上一搜一大堆了这个步骤我就不说。我就说一下我这里特色功能。
1、我们的app使用的腾讯的乐固加固,我通过gradle编程 把加固的步骤挂载到gradle生命周期的task之后(就是apk生成的task之后)。
具体代码如下
gradle.taskGraph.whenReady { TaskExecutionGraph taskGraph ->
// println('taskGraph.afterTask')
taskGraph.getAllTasks().each { Task task ->
if (task.name.startsWith('assemble') && task.name.endsWith('Release')) {
task.doLast {
File apkFile = findNewestApk()
File leguFile = reinforce(apkFile)
File zipFile = zipalignApk(leguFile)
File signedApkFile = signApk(zipFile)
String oldFileName = leguFile.getPath()
apkFile.delete()
leguFile.delete()
zipFile.delete()
signedApkFile.renameTo(new File(oldFileName.replace("_zip_sgined", "")))
}
}
// println('>>>>==' + task.name)
}
}
这里就可以看到我整个的逻辑思维了。我就不卖关子了把我写的legu.gradle文件全部贴出来
legu.gradle
import groovy.io.FileType
ext {
APK_DIR = rootDir.getPath() + "/apk"
LEGU_DIR = rootDir.getPath() + "/config/legu/"
LEGU_JAR_PATH = LEGU_DIR + "ms-shield.jar"
SIGN_JAR_PATH = LEGU_DIR + "apksigner.jar"
ZIPALIGN_PATH = LEGU_DIR + "zipalign"
SECRET_ID = "AKIDg9ORFcN4HDsHPIKN"//这里是自己的乐固 secretid
SECRET_KEY = "RoVhibvA8v1qOgOvZ01lFV"//这里是乐固key
}
println APK_DIR
println LEGU_DIR
println LEGU_JAR_PATH
/**
* 查找最新的apk文件且是release
* @return apk路径
*/
File findNewestApk() {
File apkDir = new File(APK_DIR)
long lastTime = 0
File resultFile = null
apkDir.eachFileRecurse(FileType.FILES) { file ->
if (file.lastModified() > lastTime && file.name.endsWith('release.apk')) {
resultFile = file
lastTime = file.lastModified()
}
}
return resultFile
}
/**
* 乐固加固
* @param apk 加固的原始apk File
*/
def reinforce(File apkFile) {
boolean isJenkinsPack = true
String server_type = System.getProperty("SERVER_TYPE")
if (null == server_type || "" == server_type) {
isJenkinsPack = false
}
println "*************** 开始加固 ***************"
String newApkPath = apkFile.getParent() + "/" + apkFile.getName().replace(".apk", "") + "_legu.apk"
//jiagu.db中缓存了多渠道信息,如果不删除会合并到当前多渠道配置
def shield = new File(LEGU_JAR_PATH)
if (!shield.exists()) {
throw new RuntimeException("jar not exists!")
}
exec {
if (isWindows() && !isJenkinsPack) {
commandLine "powershell", "java -jar", LEGU_JAR_PATH, "-sid", SECRET_ID, "-skey", SECRET_KEY, "-uploadPath", apkFile.getPath(), "-downloadPath", apkFile.getParent()
} else {
commandLine "java", "-Dfile.encoding=utf-8", "-jar", shield.getAbsolutePath(), "-sid", SECRET_ID, "-skey", SECRET_KEY, "-uploadPath", apkFile.getPath(), "-downloadPath", apkFile.getParent()
}
}
println "*************** 加固完成 ***************"
return new File(newApkPath)
}
/**
* zipalign优化
*
* @param output
* @return
*/
def zipalignApk(File apkFile) {
boolean isJenkinsPack = true
String server_type = System.getProperty("SERVER_TYPE")
if (null == server_type || "" == server_type) {
isJenkinsPack = false
}
println "*************** zipalign优化开始 ***************"
String newApkPath = apkFile.getParent() + "/" + apkFile.getName().replace(".apk", "") + "_zip.apk"
exec {
if (isWindows() && !isJenkinsPack) {
commandLine "powershell", ZIPALIGN_PATH, "-v", "-p", "4", apkFile.getPath(), newApkPath
} else {
commandLine ZIPALIGN_PATH, "-v", "-p", "4", apkFile.getPath(), newApkPath
}
}
println "*************** zipalign优化完成 ***************"
return new File(newApkPath)
}
/**
* 对apk进行签名
*
* @param output
*/
File signApk(File apkFile) {
boolean isJenkinsPack = true
String server_type = System.getProperty("SERVER_TYPE")
if (null == server_type || "" == server_type) {
isJenkinsPack = false
}
println "*************** 开始签名 ***************"
String newApkPath = apkFile.getParent() + "/" + apkFile.getName().replace(".apk", "") + "_sgined.apk"
println "new apk path:" + newApkPath
String keyPath = ""
String alias = ""
String password = ""
//这里自己配置自己的jks文件和密码 alias
if (newApkPath.contains("segi")) {
keyPath = projectDir.getPath() + "/" + "uhomebk.jks"
alias = "xxx"
password = "xx.xx.xx"
}
println "keyPath:" + keyPath
println "alias:" + alias
println "password:" + password
exec {
if (isWindows() && !isJenkinsPack) {
commandLine "powershell", "java", "-jar", SIGN_JAR_PATH, "sign", "--ks", keyPath, "--ks-key-alias", alias, "--ks-pass", "pass:" + password, "--key-pass", "pass:" + password, "--out", newApkPath, apkFile.getPath()
} else {
commandLine "java", "-Dfile.encoding=utf-8", "-jar", SIGN_JAR_PATH, "sign", "--ks", keyPath, "--ks-key-alias", alias, "--ks-pass", "pass:" + password, "--key-pass", "pass:" + password, "--out", newApkPath, apkFile.getPath()
}
}
println "*************** 签名完成 ***************"
return new File(newApkPath)
}
/**
* 是否是window系统
* @return
*/
def isWindows() {
return org.gradle.internal.os.OperatingSystem.current().isWindows()
}
gradle.taskGraph.whenReady { TaskExecutionGraph taskGraph ->
// println('taskGraph.afterTask')
taskGraph.getAllTasks().each { Task task ->
if (task.name.startsWith('assemble') && task.name.endsWith('Release')) {
task.doLast {
File apkFile = findNewestApk()
File leguFile = reinforce(apkFile)
File zipFile = zipalignApk(leguFile)
File signedApkFile = signApk(zipFile)
String oldFileName = leguFile.getPath()
apkFile.delete()
leguFile.delete()
zipFile.delete()
signedApkFile.renameTo(new File(oldFileName.replace("_zip_sgined", "")))
}
}
// println('>>>>==' + task.name)
}
}
注意:大家需要根据自己的工程进行一些小的细节修改,我只是把代码贴出来给大家方便和思路。
SERVER_TYPE是gradle.propeties配置的SERVER_TYPE=gamma
这是我们工程特定的自己可以选择是否要添加或者修改。
然后在app的gradle文件中引用就好比如我的配置是
apply from: '../config/legu/legu.gradle'
还有里面的几个文件路径我也截图出来,因为放不了附件。里面的文件需要自己去乐固论坛上找。文件存放路径截图如下
给大家推荐一个github地址吧 这几个文件都可以下载
https://github.com/649519404/automaticpack。
好的到这里就可以通过gradle编程实现乐固自动加固了。其实其他360加固也可以这样弄的,大家可以自己去实现一下。
2、自动上传svn
这里是在jenkins配置windows的BAT脚本命令实现的截图看一下
里面的具体代码如下
@echo off
setlocal
REM ===================该bat命令的作用=======================
REM ===================== 通过生成的apk名称 获取到工程名和版本号=============
REM apk路径
set FolderName=uhomebk\apk\%COMPANY%
REM apk文件名称
set "apkName="
REM 项目名称_版本号
set "projectNameAndVersion="
REM 项目名称
set "projectName="
REM 版本号
set "version="
REM 获取指定目录下的apk名称
for /f "delims=\" %%v in ('dir /b /a-d /od "%FolderName%\*.apk"') do (
set "apkName=%%v"
)
REM 获取工程和版本号名称
for /f "tokens=1 delims=_" %%a in ("%apkName%") do (
set "projectNameAndVersion=%%a"
)
REM 获取工程名称 比如 常服务等
for /f "tokens=1 delims=v" %%b in ("%projectNameAndVersion%") do (
set "projectName=%%b"
)
echo 平台类型:%projectName%
REM 获取版本号 比如 3.9.6等
for /f "tokens=2 delims=v" %%b in ("%projectNameAndVersion%") do (
set "version=%%b"
)
echo 版本:%version%
echo SVN地址:http://192.168.1.6:9000/svn/test/test/test/app/%projectName%/%version%/Android/%apkName%
cd %FolderName%
del /q output.json
svn import http://192.168.1.6:9000/svn/test/test/test/app/%projectName%/%version%/Android --username=zhoudq --password=33337777 -m "%UPDATE_DESCRIPTION%"
pause
这里有个很巧妙的思想就是通过apk的名称获取工程名、版本号,然后上传到具体的svn路径。因为我们打出的apk名称是这样的比如常服务v3.9.8_10.8_11.21(gamma环境)_debug.apk。这样就能通过apk名称看出上面脚本的意图了。上传到svn的脚本就是svn import http://192.168.1.6:9000/svn/test/test/test/app/%projectName%/%version%/Android --username=zhoudq --password=33337777 -m "%UPDATE_DESCRIPTION%"
注意:我这里只在windows下配置了jenkins,如果是linux系统需要重新编写shell脚本。
总结
到此为止,就可以让测试开发完全释放双手,在jenkins中自由打包了,提高了生产效率。