Building Android apps with Jenkins CI

在前东家,工程相关的都有专门的组来做,比如自动化的代码审查,编译,发布,测试,缺陷追踪等等系统。。。甚至当时就有人说就XXX的很多系统,即使司龄很大的员工都不一定都能使用的很熟练,因为实在是太多了。。。相反的是现在都没有,万事都需要自己动手,不过也还好,可以挑选些真的是非常有帮助的自动化系统来帮助我们提高工作效率(比如之前我一直都是不大记住写周报,其实我觉得那应该是小时报,不过还好,我当时的老大和部门经理对这个要求没有那么严格,我也不是偷懒的员工,所以基本是马马虎虎过了,但是我还是比较喜欢例如某些任务管理系统,用任务管理系统主要是防止自己忘记,一些大任务可以拆分成小任务,并且标记完成状况,或者是你在任务上撰写出你自己的意见/建议和老大进行沟通),今天就以自动化编译系统为起点来逐步搭建/定制出一个比较理想的持续集成系统。。。当然这是一个长期的任务,不是说它非常难,只是自己时间也不是特别多,会先挑些目前比较重要的东西来加入进来(building,lint,findbugs,monkey)。。。
今天先说编译系统,而且先是看基于Ant的,假设源码是放在GitHub上,比如https://github.com/guohai/and-expandable-listview/这个项目,我希望我的系统在每隔一定时间或者每次代码提交之后自己启动一次编译,第一这可以排错,防止开发人员不小心漏提交代码,或者merge代码的时候出错,尽早的发现这类比较低级的错误,编译系统编译失败会立刻邮件通知你,而不是你在本地编译测试了很多遍,信心满满的把代码提交(当然有人说会有人工代码审查,但是人工也不一定能发现所有错误,特别是编译错误),回家睡觉了,另外一个时区的同事刚开始新的一天,检出你的代码,发现根本编译不通过,那么他是该打你的电话叫醒你修改代码呢?还是等你第二天来修改呢?明显他两样都不想,所以最好是你保证自己提交的代码都是至少可以编译通过的(会有人说这么简单的事情会做不到吗?事实是当你很多人协同工作的时候,这个事情确实是有可能发生,很多次在XXX公司经历过一两天编译系统无法出一个可以通过的版本,当然这不是在黑前东家,只是事情确实有,而且公司也在逐步改用好一点的编译方式减少这种一个编译错误导致整个系统都无法编译出ROM的窘境);第二这个可以利用一些工具直接将编译的成果推送到自动化测试的服务里面完成基础的自动化测试,这样岂不是很好,减少些不必要的人工操作;第三有权限的人可以随时下载一个他需要的版本程序来安装运行,而不需要让你给他邮件发一个版本或者他自己安装编译环境,检出代码来完成编译。可能还有很多理由来需要这样一个系统,不过这里已经够了。。。
Jenkins的安装,基本使用就不说了,具体查看https://wiki.jenkins-ci.org/display/JENKINS/Use+Jenkins上面,我这里主要安装了一个GitHub插件,在Manage Jenkins->Manage Plugins当中可以很方便的完成,因为我本地都有Android SDK, Ant, Java等等一些基础环境,而且这些都已经在PATH当中,所以集成起来就很简单。然后就是创建任务, Jenkins当中称为job,然后就是配置该job,比如GitHub项目位置,仓库地址,keystore for release build,编译预处理,归档编译产物,以及触发编译条件,如下图:
Screen Shot 2014-01-27 at 12.00.47 AM
Screen Shot 2014-01-27 at 12.01.53 AM
Screen Shot 2014-01-27 at 12.14.36 AM
Screen Shot 2014-01-27 at 12.15.05 AM
Screen Shot 2014-01-27 at 12.34.11 AM
Screen Shot 2014-01-27 at 12.43.14 AM
这样就基本可以搭建出一个自动化的编译系统了,后面有机会来研究下基于Gradle的,毕竟这才是Google目前主导的方向。
当然你可以根据自己的需要调节更改配置来达到想要自己的目的,比如GitHub有提交就触发编译,只归档特定名字的文件等等,是否删除原有归档,如果新的版本有成功编译出来的话,等等可能还有更多的技巧。

洗完个澡,顺便试了下Gradle的,你可以在Jenkins当中下载Gradle插件,也可以不用专门下载,自己写个简单的脚本完成,比如我这里就是:
Screen Shot 2014-02-06 at 12.25.49 AM
而且也只有上述这一段不一样,其他都跟Ant的基本类似,不过生成需要归档的apk的路径可能需要改改,这里有个硬编码的ANDROID_HOME,目前没有发现比较好的解决方法,如果您知道如何解决,不吝赐教。在Ant版本中,我们sdk.dir是通过android update project生成的,但是在Gradle的项目当中似乎无法生成,没有SDK路径就会编译出错,比如:

* Where:
Build file '/Users/guohai/.jenkins/jobs/Gradle-Proj-Test/workspace/xxx/build.gradle' line: 9

* What went wrong:
A problem occurred evaluating project ':beckon'.
> SDK location not found. Define location with sdk.dir in the local.properties file or with an ANDROID_HOME environment variable.
于是就先硬编码这个ANDROID_HOME变量先跳过这一问题,有空再来研究看看。
1
export ANDROID_HOME=/Users/guohai/Dev/android-sdk-macosx
chmod +x ./gradlew 
./gradlew clean assembleDefaultFlavorDebug

当然这gradlew后面跟的task可以自己调整,比如debug版本或者release版本,或者编译子项目(./gradlew :sub-project:clean,sub-project就是实际子项目的名字)。

P.S.
通知邮件发送使用Jenkins Email Extension Plugin
不过在配置的时候出现
501 mail from address must be same as authorization user
参考http://bbs.csdn.net/topics/390353671设置System Admin e-mail address就可以了。

Updated at 2015.12.25
升级到最新版本之后出现即使代码没有改变,也不停的出 Started by SCM
https://issues.jenkins-ci.org/browse/JENKINS-17614
这是一个 bug,我这里导致的原因,是有多个 branch 的名字包含 develop,然后进行 branch 指定的时候,指定 develop 就分不清了。
删掉或者改名 branch 就可以正常工作了。

另外在 headless 的服务器上下载 Android SDK ,如果只想下载/更新指定的包,那么请参照这里。
http://tools.android.com/recent/updatingsdkfromcommand-line
简单来说先通过

$ android list sdk

查看有哪些更新
然后指定需要安装的包

android update sdk --no-ui --filter tools,3,8

不清楚的可以

android update sdk --help

看一下

比如强制使用 http 协议列出所有更新(包括已废弃的)

android list sdk --no-https --all

注意,调用更新的时候也要带 –all 参数,否则编号会错乱

小结二零一三

似乎已经过了年终小结的时间了,不过迟点就迟点吧,今年可以典型的分为上半年和下半年,上半年在为公司加班(已经是前东家了),下半年在为自己加班,还是有点忙,不过也没有什么好看的结果,人生很多时候是努力了不一定有明显的结果的。去年的愿望有实现了部分,乱七八糟的读了一些书,C/C++,还有部分多媒体知识有了些进步,有些东西看起来比较复杂和庞大,不过当你花了很多时间在上面,并且突然理解的时候,这是最开心的,比通常在公司里实现了一个又一个功能,解了一个又一个bug的感觉要好太多,当然我知道工作只是工作,如果你的工作不是你的兴趣所在,那你的路就是把它做好或者换一份工作,或者如果你无法换工作的时候就只是做好它然后自己做自己爱好的事情,老板给你的任务太重你需要表达出自己的状况,要不然很大的可能就是你被更多的工作包围着,这当然不是教你偷懒,万事都有一个度的问题。你若热爱它,怎么努力都不过分,若不热爱,就要努力去寻找到自己的胡萝卜。。。当然一些事情也没有能做的很好,比如多回去看看爸妈,比如多学习深入点的多媒体/信息论的知识,这也成为了2014的目标。具体点来说今年可能会更多的关注在语音这方面,因为刚刚加入了一家这方面的创业公司,估计还是一样的会比较忙碌,不过还是尽量不要让自己完全被工作给吞噬了。。。2014还是力争多读点书,语音方面理解AudioFlinger,至少熟悉一种CODEC。还有一个个人方面的愿望就是能有一个可以共度一生的人,在一个充满阳光的下午,两个人坐一起,一个人在写代码,一个人在看书,或者有时间两个人出去吃喝玩乐,平平淡淡,普普通通就好,不知道这是不是一种奢求!