文章目录
- 前言
- 系列文章
- Android 持续集成实践(三)——编写 .gitlab-ci.yml 实现自动化
- 要实现的效果
- 定义 gitlab-ci 工作场景阶段
- 定义 build 任务
- 定义 reinforceAndChannel 任务
- 把 360加固保上传到 gitlab-runner 缓存目录
- 创建加固和多渠道的 shell 脚本
- 在 .gitlab-ci.yml 创建任务
- 定义 deploy 任务
- 测试版发布到 firim
- 正式版发布到各应用市场
- .gitlab-ci.yml 完整代码
- 效果展示
- FAQ
- 参考资料
前言
最近幸得空闲,就来自己实践一遍 Android 的持续集成,之前公司一直在使用同事搭建的 gitlab+ci+firim
,确实是方便了很多,所以就有了自己实现一遍的想法。
在实践的过程中,也是磕磕绊绊各种填坑。网上有很多实现持续集成的教程,但是自己实践的时候各种坑总是出其不意的出现,所以我想记录一下我的实践过程,尽可能的做到详细,希望实践过程中遇到的问题,在这里都能找到解决方案。
Android 持续集成实践(三)——编写 .gitlab-ci.yml 实现自动化
要实现的效果
在第二篇的时候,gitlab 已经可以根据 tag 自动打包正式版和测试版了,但是这还远不能满足日常发包场景。
一般的正常发版流程是:签名并打包正式版 -> 加固 -> 多渠道 -> 上传应用市场
接下来记录一下我使用 gitlab-ci 实践这一过程的自动化。
定义 gitlab-ci 工作场景阶段
实现着整个流程,需要划分不同的阶段:
- 编译阶段
build
在第二篇里边已经实现了编译任务,每次提交代码或者推送相应的 tag 标签的时候,就会执行编译任务。 - 加固和多渠道阶段
reinforceAndChannel
因为我们发正式版的时候才需要去加固和统计渠道信息,所以这个任务执行的一个前提条件是正式版编译成功。
当正式版编译完成后,就可以自动执行这个去进行加固和写入渠道信息。 - 部署阶段
deploy
这个任务就是把已经处理好的安装包,自动上传到对应的平台上,省去手动上传的成本。
对于正式版,目前国内应用市场众多,上传到各应用市场是否能实现还有待研究。
对于测试版,可以自动上传到 firim 这个 app 内测托管平台来完成测试版发版。
通过工作场景阶段的划分,正式版和测试版的自动化要经历的阶段就很清晰了:
正式版:通过 build -> reinforceAndChannel
这两个阶段输出加固过的渠道包
测试版:通过 build -> deploy
这两个阶段把编译的测试包发布到 firim
下面是 .gitlab-ci.yml
定义工作场景阶段的部分代码:
stages:
- build
- reinforceAndChannel
- deploy
阶段划分完成后,接下来要定义每个阶段的任务。
定义 build 任务
build 阶段有下面几种情况:
- 普通的一次提交代码到 gitlab
每次提交代码到远端,都要触发编译任务去编译一次,可以及时发现代码错误 - 发布测试版
利用特定的 Tag 格式来触发编译任务,之后阶段的任务触发也用相同的触发条件 - 发布正式版
利用特定的 Tag 格式来触发编译任务,之后阶段的任务触发也用相同的触发条件 - 打包测试用的正式包
因为发布正式版会加固和打渠道包,打包一次会很耗时。而如果我们只是想用于测试的话(比如测试微信登录和分享,需要正式环境测),只需要打包一个不需要加固的正式包就可以测了。所以还需要一个只打包一个正式包的任务。
下面是 .gitlab.yml
build 阶段的代码:
#region build 编译阶段
# 提交代码自动编译
build:
stage: build # 表示 build 阶段执行的任务
only:
- master # 只有在 master 分支有提交时才执行任务
script:
- ./gradlew assembleDebug # 由 runner 执行的 shell 脚本,这里是执行 gradle 脚本
tags:
- android # 指定脚本在有 android 标签的 runner 上运行
# 利用标签打测试包,格式:“x.x.x-beta.x”
beta:
stage: build
only:
- /^[\d]+\.[\d]+\.[\d]+-beta\.[\d]+$/
script:
- ./update-version-code # 这是自动更新版本号和版本名的脚本
- ./gradlew assembleDebug
artifacts:
paths:
- app/build/outputs/apk/debug/debug_com.tianxing.wln.aat_${CI_COMMIT_TAG}_${CI_PIPELINE_ID}.apk # 编译完成后输出debug安装包,用于 deploy 阶段上传 firim
tags:
- android
# 利用标签打测试包,格式:“x.x.x”
release:
stage: build
only:
- /^[\d]+\.[\d]+\.[\d]+$/
except:
- branches
script:
- ./update-version-code
- ./gradlew assembleRelease
artifacts:
paths:
- app/build/outputs/apk/release/
tags:
- android
# 测试用正式包编译任务
releaseOfficial:
stage: build
only:
- official # 新建 official 分支推送到远端,就可以自动打包测试用正式包了
script:
- export CI_COMMIT_TAG=$CI_PIPELINE_ID
- ./update-version-code
- ./gradlew assemble_360Release
artifacts:
paths:
- app/build/outputs/apk/release/
tags:
- android
#endregion
update-version-code
脚本:
#!/usr/bin/env bash
echo "Updating Android build information. New version code: ${CI_PIPELINE_ID}, New version name: ${CI_COMMIT_TAG}";
sed -i "s/versionCode .*$/versionCode ${CI_PIPELINE_ID}/;" app/build.gradle
sed -i "s/versionName .*$/versionName \"${CI_COMMIT_TAG}\"/;" app/build.gradle
定义 reinforceAndChannel 任务
加固和多渠道是用360加固保linux版实现的。360加固保提供了命令行进行操作的方式,写个脚本用在持续集成里边很方便。
把 360加固保上传到 gitlab-runner 缓存目录
- 下载 360加固保linux版 ,解压得到
jiagu
文件夹 - 把
jiagu
整个文件夹用FileZilla
上传到服务器上我们挂载的 runner 缓存目录里边,我配置的目录是/home/android-cache/
这时候 360加固的相关文件所在目录是/home/android-cache/jiagu
注意,我们所用的 360加固,必须是 linux 版的。虽然命令模式都是是用的 jar 包,但是 windows 版的 jar 包是不能用的,会加固失败,这里一定要格外注意。
创建加固和多渠道的 shell 脚本
360jiahu
脚本代码:
#!/usr/bin/env bash
# 如果你对这里的 cache 目录有疑问的话,请看第二篇
JAVACMD=/cache/jiagu/java/bin/java # 360加固自带的 java 运行环境
BASE=/cache/jiagu/jiagu.jar # 360加固的 jar 包
NAME=xxx # 这里写你的 360加固 的账号
PASSWORD=xxx # 360加固的密码
APK_NAME=xxxx.apk
APK_PATH=app/build/outputs/apk/release/${APK_NAME} #需要加固的apk路径
DEST=app/build/outputs/apk/release/ #输出加固包路径
# 打印一下参数看是否正确
echo "------ args info ------
JAVACMD = ${JAVACMD}
BASE = ${BASE}
NAME = ${NAME}
PASSWORD = ${PASSWORD}
APK_NAME = ${APK_NAME}
APK_PATH = ${APK_PATH}
DEST = ${DEST}
KEYSTORE_PATH = ${KEYSTORE_PATH}
KEY_PASSWORD = ${KEY_PASSWORD}
KEY_ALIAS = ${KEY_ALIAS}
STORE_PASSWORD = ${STORE_PASSWORD}"
echo "------ running! ------"
chmod +x ${JAVACMD}
${JAVACMD} -jar ${BASE} -version
${JAVACMD} -jar ${BASE} -login ${NAME} ${PASSWORD}
${JAVACMD} -jar ${BASE} -importsign ${KEYSTORE_PATH} ${KEY_PASSWORD} ${KEY_ALIAS} ${STORE_PASSWORD} # 配置签名信息
${JAVACMD} -jar ${BASE} -showsign
# 这里渠道信息的配置文件直接放在项目根目录,格式参考 360加固官方文档
${JAVACMD} -jar ${BASE} -importmulpkg ./channels.txt # 配置渠道信息
${JAVACMD} -jar ${BASE} -showmulpkg
${JAVACMD} -jar ${BASE} -showconfig
${JAVACMD} -jar ${BASE} -jiagu ${APK_PATH} ${DEST} -autosign -automulpkg
echo "------ finished! ------"
注意,这里的 java 运行环境,也必须用 360加固 自带的 java 运行环境,避免引发不必要的异常
在 .gitlab-ci.yml 创建任务
#region 加固和打渠道包阶段
reinforceAndChannel:
stage: reinforceAndChannel
only:
- /^[\d]+\.[\d]+\.[\d]+$/
script:
- ./360jiagu
artifacts:
paths:
- app/build/outputs/apk/release/
tags:
- android
#endregion
定义 deploy 任务
测试版发布到 firim
- 获取 api token
首先需要有一个 firim 的账号,没有的请先注册,由于我们用到它的 api,所以需要获取一下api_token
要想上传应用还必须完成实名认证
- 写上传 firim 的脚本
fir-publisher
#!/bin/bash
# Get API Token from http://fir.im/apps
API_TOKEN="xxxxxx" # 这里填你自己的 token
ORIGINAL_FILENAME="xxxxxx.apk" # gitlab 自动编译测试包生成的 apk 名字
PACKAGE_DIR="app/build/outputs/apk/debug/" # 安装包位置
# ios or android
TYPE="android"
# App 的 bundleId 或 包名
BUNDLE_ID="xxx.xxx.xxx"
package_path="${PACKAGE_DIR}${new_filename}"
# Get upload_url
credential=$(curl -X "POST" "http://api.fir.im/apps" \
-H "Content-Type: application/json" \
-d "{\"type\":\"${TYPE}\", \"bundle_id\":\"${BUNDLE_ID}\", \"api_token\":\"${API_TOKEN}\"}"
)
binary_response=$(echo ${credential} | grep -o "binary[^}]*")
KEY=$(echo ${binary_response} | awk -F '"' '{print $5}')
TOKEN=$(echo ${binary_response} | awk -F '"' '{print $9}')
UPLOAD_URL=$(echo ${binary_response} | awk -F '"' '{print $13}')
# Upload package
echo 'Uploading...'
echo '✈ -------------------------------------------- ✈'
response=$(curl -F "key=${KEY}" \
-F "token=${TOKEN}" \
-F "file=@${package_path}" \
-F "x:build=${CI_PIPELINE_ID}" \
${UPLOAD_URL}
)
echo $response;
- 在
.gitlab-ci.yml
中创建部署任务
uploadToFir:
stage: deploy
only:
- /^[\d]+\.[\d]+\.[\d]+-beta\.[\d]+$/
script:
- ./fir-publisher
tags:
- android
- 测试版 app 应用内更新
上传成功后,可以把 firim 检查更新的 api 用到 app 内,每次打开 app 检查新的测试版并提示测试用户去下载。需要注意的是,测试版和正式版包名不同,是两个 app,应用内更新的逻辑也是分两个,这里只针对测试版的应用内更新逻辑
正式版发布到各应用市场
这个目前没有好的想法,有待继续研究。。
.gitlab-ci.yml 完整代码
image: jangrewe/gitlab-ci-android
variables:
GRADLE_OPTS: "-Dorg.gradle.daemon=false"
before_script:
# 配置 gradle 的缓存目录
- export GRADLE_USER_HOME=/cache/.gradle
# 获取权限
- chmod +x ./update-version-code
- chmod +x ./360jiagu
- chmod +x ./gradlew
- chmod +x ./fir-publisher
stages:
- build
- reinforceAndChannel
- deploy
#region build 编译阶段
# 提交代码自动编译
build:
stage: build
only:
- master
script:
- ./gradlew assembleDebug
tags:
- android
# 利用标签打测试包,格式:“x.x.x-beta.x”
beta:
stage: build
only:
- /^[\d]+\.[\d]+\.[\d]+-beta\.[\d]+$/
script:
- ./update-version-code
- ./gradlew assembleDebug
artifacts:
paths:
- app/build/outputs/apk/debug/xxx.apk # xxx 写你编译生成的测试版 app 的名字
tags:
- android
# 利用标签打测试包,格式:“x.x.x”
release:
stage: build
only:
- /^[\d]+\.[\d]+\.[\d]+$/
except:
- branches
script:
- ./update-version-code
- ./gradlew assembleRelease
artifacts:
paths:
- app/build/outputs/apk/release/
tags:
- android
releaseOfficial:
stage: build
only:
- official
script:
- export CI_COMMIT_TAG=$CI_PIPELINE_ID
- ./update-version-code
- ./gradlew assemble_360Release
artifacts:
paths:
- app/build/outputs/apk/release/
tags:
- android
#endregion
#region 加固和打渠道包阶段
reinforceAndChannel:
stage: reinforceAndChannel
only:
- /^[\d]+\.[\d]+\.[\d]+$/
script:
- ./360jiagu
artifacts:
paths:
- app/build/outputs/apk/release/
tags:
- android
#endregion
uploadToFir:
stage: deploy
only:
- /^[\d]+\.[\d]+\.[\d]+-beta\.[\d]+$/
script:
- ./fir-publisher
tags:
- android
效果展示
- 发布测试版
- 发布正式版
FAQ
- curl: no URL specified!
没有上传地址,报这个错误基本上是 firim 获取上传凭证那个 api 没有成功,可以用 postman 模拟一下请求,看报了什么错。
我遇到的是因为刚注册的账号没有实名认证所以限制了不能上传应用 - Uploading artifacts to coordinator… too large archive should fail job
在打包的渠道包很多,输出渠道包的时候会报这个错误。这是因为输出的文件太大,超过了 gitlab 的Maximum artifacts size
的默认值100MB
,我们需要去Admin Area
->Settings
->CI/CD
->Continuous Integration and Deployment
把这个值设置大一点就可以了。
参考资料
Uploading artifacts to coordinator… too large archive should fail jobMaximum artifacts sizeGitLab CI/CD Pipeline Configuration Reference360命令行加固firim文档