在一个App从开发到测试的过程当中,我有很长一段时间都是这样作的:打包,上传到tower,在tower上编写本次更新说明,通知测试。通常状况下,打包及上传的过程大概也就2分钟。除此以外,因为项目代码有做混淆,而且使用了bugly,所以在发出每一个版本以后还须要将混淆的mapping.txt传到bugly上。当日复一日,而且有时还遇到网络较差的状况时,这种人工手动的工做方式就很影响工做效率及心情了。所以,自动化构建及发布就成了必须掌握的技能了。
本篇分享的是我在Android自动化构建的一些经验,涉及到的工具及网站以下:
- Gradle
- fir.im
- Gitlab
- gitlab-ci-multi-runnerlinux
所述内容包含:
- 使用Gradle自动构建并发布到fir
- 使用Gitlab-CI,在提交时自动化构建并发布到fir
- 在服务器配置docker版的gitlab-ci-multi-runner
- 多flavor时,在fir上同时发布的解决方案android
我接触fir.im的时间比较早,那时官方就已经提供了一个命令行打包并上传的工具fir-cli。可是有两个问题是我难以忍受的:
1. 它须要安装,而且因为使用ruby编写,因此还须要ruby环境
2. 它会构建全部flavor的版本,虽然最后只上传一个(该问题后来已经解决)c++
因而,在发现它有提供API以后,我查阅了下Gradle的文档,本身写了一个简单的fir发布插件——fir-publish。
这个插件很小很轻,没有使用额外依赖库,网络请求使用的也是Gradle自己就有的http-client的API。使用方式以下:
首先在根项目的build.gradle中加入如下依赖:git
buildscript { repositories { jcenter() }
dependencies { classpath 'com.githang:fir:0.1.6' }
}
而后在app里的build.gradle文件末尾加入如下配置:github
apply plugin: 'fir'
fir {
apiToken //fir.im上的API token
bundleId android.defaultConfig.applicationId
flavor "Test" (若是没有productFlavor,可不配置此项),仅在上传apk时须要
appName 你的应用名称,仅在上传apk时须要
icon 应用图标路径,仅在上传图标时须要
changeLog "更新日志" // 或者使用 file("日志文件路径")
}
其中的API token在你登陆fir以后,点击本身的帐号就能够获取。docker
该插件向你的project添加如下4个任务:
- firCert 获取上传凭证
- firIcon 上传图标,依赖于firCert
- firApk 上传APK,依赖于firCert及assembleRelease(或assembleFlavorRelease)
- firAll 上传图标及APK,依赖于firIcon及firApkshell
在上面的配置中,更新日志能够直接写在上面,也能够单首创建一个更新日志的文件(推荐),每次要发布时只须要修改这个文件上的更新日志,而后执行如下命令便可自动构建并发布到fir:ubuntu
./gradlew firAll
注:windows用户在前面不须要加./
这样,咱们只须要让测试人员关注fir上的更新动态便可,而没必要本身去等待构建完成再上传而后等待上传完成再去写更新日志。windows
关于自动上传mapping.txt到bugly的问题,其实bugly自己已有提供相关的Gradle插件bugly。可是它会在每次构建Release版本中都执行上传mapping.txt,而一般咱们只是在最终打包版本给测试的时候才须要,因此我修改了一下配置:api
def isPublish = hasProperty("publish")
bugly {
appId = '你的appId'
appKey = '你的appKey'
execute = isPublish
upload = isPublish
}
在这里新增长了一个变量 ,仅在有publish
属性时才执行上传的命令,这个属性在执行的时候带入,所以咱们打包并发布的命令将进一步演变为以下:
./gradlew firApk -Ppublish
在上面的过程当中,其实咱们解决的最大问题是把构建——发布——编写版本更新日志这三个步骤合成一步,少去了中间过程的等待,可是结果仍是咱们要在每次须要时去手动执行这一步。
CI类的服务可以让咱们把代码推送到服务器上时便可开始构建,使得咱们的整个构建过程达到真正的自动化,而不用人工参与。
因为公司使用的是Gitlab,因此这里只谈Gitlab-CI相关的内容。
新版的Gitlab CI中使用的是gitlab-ci-multi-runner,关于它的安装能够到参考其官方文档,这里再也不赘述。
须要注意一点的是,在安装以后进行注册时,若是你是想注册为共享runner(全部项目均可使用),那么第一个问题的地址应该是大家公司gitlab的地址,第二个问题的token在管理员界面的runner配置中能够看到。若是是想注册为私有的runner,则其url与token在项目的设置中能够看到。
接下来,只须要在咱们的项目的根目录中添加一个.gitlab-ci.yml
文件,并在其中进行CI配置,而后提交并推送到咱们的gitlab上便可。
仍是以我这里的公司项目为例。项目采用git-flow流程进行开发,在要发布时会建立release分支,所以须要发布到fir上给测试人员的是release分支及tag上的代码,其余分支的代码咱们只须要进行构建测试就能够了。在咱们公司的项目中,有开发环境 、测试环境及生产环境,分别对应三个productFlavor:Develop, Test,Official,它们之间只有API的地址不一样。所以,构建测试使用其中一个环境的就能够了。
因此脚本以下:
before_script:
- chmod +x ./gradlew
compileTest:
script: "./gradlew clean aDevelopDebug"
except:
- /^release.*$/ - tags
publishToFir:
script: "./gradlew clean firApk -Ppublish"
type: deploy
only:
- /^release.*$/ - tags
在这里,我定义了两个ci任务,分别是compileTest
以及publishToFir
。script
表示该任务所执行的命令。except
表示不对哪些分支进行构建。使用git-flow流程时,将发布的分支都是以release/xxx
来命名,因此这里用正则来表示。only
表示仅对哪些分支执行这个构建任务。type
表示任务的类型。
将配置提交,而后推送到Gitlab上,就可以触发CI去执行咱们所定义的构建任务了。若是你成功了配置了Gitlab上的邮箱发送服务,那么咱们就能够不用主动去关心这个结果,由于若是构建失败了,Gitlab将会向咱们发送邮件通知。
若是你不想使用docker来运行runner,可跳过下面这一节。
若是你也不须要同时在fir上发布不一样flavor的APK,那么后面的也不用看了。
上面虽然使用了gitlab-ci-multi-runner来完成自动化,可是它是在我本机上跑的。每次编译时占用的内存及CPU会对开发略有影响,而且还须要我在每次开机后开个终端运行一下这个runner。公司内部是有一台Ubuntu服务器专门用于代码及项目相目的服务的,若是把咱们的runner部署到这台服务器上那就更好了。
公司的这台服务器安装了Docker,其余的服务都是以docker形式运行的。既然这样,我也遵照规则用docker部署上runner吧。
向公司的技术大伽问来内部服务器的管理员帐号,又翻了一遍《Docker — 从入门到实践》的PDF,而后就开始写Dockerfile并在本身电脑上试验了。
在踩了ubuntu版本安装不了JDK八、挂载Android SDK目录、没有32位动态库致使Android SDK执行不了,以及中文乱码等坑以后,目前个人Dockerfile以下:
FROM ubuntu:15.04
MAINTAINER HuangHaohang <msdx.android@qq.com>
ENV ANDROID_HOME /android-sdk
RUN apt update && apt install -y openjdk-8-jdk curl
#若是遇到android-sdk里的命令没法执行,则须要安装32位的动态连接库。
RUN apt install -y libc6-i386 lib32stdc++6 lib32gcc1 lib32ncurses5 lib32z1
RUN curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-ci-multi-runner/script.deb.sh | bash
RUN apt-get install -y gitlab-ci-multi-runner
# Ensure UTF-8 locale
#COPY locale /etc/default/locale
RUN locale-gen zh_CN.UTF-8 && \
DEBIAN_FRONTEND=noninteractive dpkg-reconfigure locales
RUN locale-gen zh_CN.UTF-8
ENV LANG zh_CN.UTF-8
ENV LANGUAGE zh_CN:zh
ENV LC_ALL zh_CN.UTF-8
注:后续若是有变化,将在个人github项目上更新,地址为:https://github.com/msdx/dockerfile/blob/master/gitlab-ci-multi-runner/Dockerfile
而后执行docker build建立docker镜像以后,运行docker时挂载上android sdk以及可读写的gradle缓存目录就能够了,其余问题包括runner的注册等可参见我这里的项目说明:https://github.com/msdx/dockerfile/tree/master/gitlab-ci-multi-runner 。因为篇幅缘由,这里不做赘述。
我这里把android-sdk打包并经过ssh上传到了服务器,而后解压在了公司的用户目录的android-sdk下,并在该目录建立了一个文件夹GradleUserHome,用于放Gradle的缓存,最终启动这个docker的脚本以下:
#!/bin/bash
sudo docker run -d \
--name gitlab-ci-multi-runner \
--restart always \
-v /home/irain/android-sdk:/android-sdk:ro \
-v /home/irain/GradleUserHome:/root/.gradle:rw \
irain/gitlab-runner:registered \
gitlab-ci-multi-runner --debug run
前面提到咱们公司的项目API地址是有分多个环境的(开发环境、测试环境以及生产环境)。原本我只须要打包测试环境的给测试人员用,生产环境在最终要发布的时候再本身打包。可是在此次新版本的开发中,服务端的人员也但愿我可以打包开发环境的Apk给他,这样有时候他也能够自测一下。因为项目正在开发中,版本变化较快,因此我也想到经过自动构建发布到fir上,再由他本身去下载,这样就可保证他能够得到最新开发的版本。
然而,至关沮丧的一点是,fir上并不支持同一个应用多环境的发布,虽然这个需求在一年之前就有其余人提出。我问了客服,客服的建议是换一个bundleId(applicationId),固然这是不可能的,由于咱们的应用使用到了高德地图、微信分享及各类支付等许多和applicationId关联的SDK,不可能从新部署一套。最后查看其余人分享的一个实现技巧。
首先,你须要在fir上注册一个号(固然你也能够请你同事帮忙),而后把你的应用上传上去,再进入应用的权限控制,把你的大号邀请进来,这样你的大号上就有两个这样的应用了,而且能够对它上传新版原本更新。固然,若是你使用API来上传,则不须要邀请,只须要填不一样的API Token便可。因此最终,个人app的build.gradle
中关于fir发布的配置以下:
def envFlavor = hasProperty("flavor") ? getProperty("flavor") : "Test"
if (envFlavor == "Develop") {
fir {
apiToken "小号的api token"
bundleId android.defaultConfig.applicationId
flavor envFlavor
appName "XXX-开发版"
changeLog "git show -s --format=%B HEAD".execute().text
}
} else {
fir {
apiToken "大号的api token"
bundleId android.defaultConfig.applicationId
flavor envFlavor
appName "XXX-测试版"
changeLog file("./changeLog.txt")
}
}
其中,flavor是经过定义的envFlavor来设置,而envFlavor根据执行的时候传入的flavor属性的值来设置。对应的.gitlab-ci.yml
也修改以下:
before_script:
- chmod +x ./gradlew
compileTest:
script: "./gradlew clean firApk -Ppublish -Pflavor=Develop"
except:
- /^release.*$/ - tags
publishToFir:
script: "./gradlew clean firApk -Ppublish"
type: deploy
only:
- /^release.*$/ - tags