基于Jenkins的Python代码集成整合
Jenkins简介
曾经提及过 Jenkins。在本次实习中,Jenkins 是我每天都要使用的工具。在频繁的使用过程当中:通过实际工作感受了「持续集成」的概念(关于持续集成的概念,此处按下不表,待有时间的时候再详细总 结。);逐渐熟悉了 Jenkins 的使用,并且体会到其带来的方便。因此,希望总结一下 Jenkins 的使用。
然而 Jenkins 不通过具体的案例难以体会其方便之处,网上相关使用说明之类的文章又颇多,所以本文仅谈个人使用中的体会,并非学习 Jenkins 使用的教程。
Jenkins 是什么
Jenkins 是一个用 Java 编写的开源的 持续 集成 工具。
Jenkins 是用 Java 开发的(界面和 Eclipse 一样,带着一股浓浓的 SWT 的味道,好在界面并不太影响使用。),对 Java 程序开发有天然的良好支持,如 JUnit/TestNG 测试,Ant 等 Java 开发中常用的工具都包含在 Jenkins 里。当然,Jenkins 也可以通过插件来实现其它语言的开发。
Jenkins 的特性
在使用的过程中,我体会比较深刻的特性有:
- 项目易于配置
在 Jenkins 当中,我们可以新建 Job。在 Job 里,可以设置添加 构建脚本 。构建脚本支持 Bash、Ant、Makefile;Job 的参数、 元 数据可以作为环境变量在脚本里直接使用,因此设置起来非常方便。
- 种类繁多的插件 (这点也和 Eclipse 也颇为相似)
Jenkins 的开发者社区非常活跃, 第三方插件 很多,从而可以帮助我们实现很多常用的功能。 比如,Hipchat 插件可以在 Job 运行结束后把结果发送到 Hipchat 的聊天室里;Cobertura 插件可以显示测试覆盖率的数据。
Jenkins 的使用场景
在我们公司,Jenkins 主要被用来用于:
- 构建 、 测试 、 部署 代码 ;
我们可以通过一个 Job 实现以下流程:
- 使用 Git 插件,从代码库下载任一版本或分支的源代码;
- 编译代码;
- 运行测试。
或者是:
- 启动若干个 EC2 实例;
- 将任一版本的代码部署到新建的实例上。
所有的这些流程在 Jenkins 里,都只需要设置几个简单的参数(如分支的名称,或者是实例的个数),再点击运行按钮就可以了。
- 自动化一些复杂的流程,如数据库的迁移、备份,系统更新的安装等等
有一些常用,但是流程很复杂的过程,可以在 Jenkins 里通过 Job 来完成。
最近做了一个多月的项目是将代码测试覆盖率整合到公司持续整合(Continuous Integration)的流程当中。
This project uses Java and XML. How it could be good?
—— 组里的同事如此评价本项目
本文介绍该项目的大致流程,共分为两部分:
- 介绍 Automated python unit testing, code coverage and code quality analysis with Jenkins(part1, part2, part3)中使用 Jenkins 实现自动化测试、得到代码覆盖率和代码质量的方法。
- 简要介绍我们如何在这篇文章的基础上把代码覆盖率整合到公司的 Bitbucket 代码库当中。
基于 Jenkins 的 Python 自动化测试工具
使用到的 Python 模块:
使用到的 Jenkins 插件:
- Cobertura plugin:用来显示代码覆盖率;
- GIT plugin:用来获取最新的代码;
- Violations plugin:用来显示 pylint 的结果。
安装需要的 Jenkins 插件之后,在 Jenkins 当中新建一个作业(Job)接下来进行设置。
从哪里得到代码
如下图所以,在 Jenkins 的 Source Code Management 当中可以添加 Git Repository。
Jenkins 同样支持 subversion 等 CVS 工具。
什么时候运行作业
在 Jenkins 中可以将 Build Triggers 设置为 Poll SCM 对代码库进行轮询。如下图,Schedule 设为 * * * * *
(含义和 Cron 一样)表示每分钟检查一次代码库,看是否有更新。如果代码库有更新的话则运行 Build。
当然,也可以使用 Git Hook,从而避免轮询消耗过多的资源。
运行什么
添加一段 Build Script:
PYTHONPATH=''
nosetests --with-xunit --all-modules --traverse-namespace --with-coverage --cover-package=project1 --cover-inclusive
python -m coverage xml --include=project1*
pylint -f parseable -d I0011,R0801 project1 | tee pylint.out
这段 Shell 脚本中的三个命令:
nosetests
命令运行单元测试;- 运行
coverage
,将覆盖率数据输出为 xml 文件; - 运行
pylint
得到代码的质量数据。
具体参数的含义可以参阅原文的第一、第二部分。
显示结果
上一步的 Build Script 有三个输出文件:
nosetests.xml
coverage.xml
pylint.out
接着,在 Jenkins 当中,在 Publish JUnit test result report 添加 nosetests.xml
显示单元测试的结果。在 Cobertura 插件 Publish Cobertura Coverage Report 里添加 coverage.xml
显示测试代码覆盖率。在 Report Violations 里添加 pylint.out
显示代码质量报告。
最终,运行一次作业之后,Jenkins 将可以得到下图显示的测试报告。
::__IHACKLOG_REMOTE_IMAGE_AUTODOWN_BLOCK__::0
持续整合!
我们持续整合的大致流程是这样的。在代码库中有一个 Master 分支,开发人员添加新功能,修复 Bug 都需要在新建的分支里进行。每新建一个合并到到 Master 的 Pull Request 时,Jenkins 可以自动运行测试。测试通过则在 Bitbucket 的 Pull Request 页面里添加一个的评论表示可以合并,否则会添加一个否决的评论。这个项目的目标就是再添加一个关于测试覆盖率的评论。
我们按照 Automated python unit testing, code coverage and code quality analysis with Jenkins 一文的思路实现了测试覆盖率的部分,区别是我们的代码库里包括 Java 和 Python 两种语言的代码,需要同时处理两份数据。经过一段时间的攻关之后,我们终于可以得到代码覆盖的数据。
相较于测试覆盖率的具体数值,我们更关心覆盖率的变化值。我们希望知道合并一个分支之后,测试覆盖率是增加了还是减少了。因此,现在我们需要得到测试覆盖率的变化值(Coverage diff)。
没想到 Python 连这种冷僻的使用场景都有第三方的库支持,还不只一个。我们使用的是 Pycobertura。
Pycobertura 可以直接比较两个 Cobertura 格式的 xml 文件,从而得到覆盖率的变化值。
from pycobertura import Cobertura
from pycobertura import TextReporterDelta
coverage1 = Cobertura('coverage1.xml')
coverage2 = Cobertura('coverage2.xml')
delta = TextReporterDelta(coverage1, coverage2)
delta.generate()
于是,我创建了一个 Fabric Task,调用 Pycobertura 分析测试生成的 xml 文件和 Master branch 的 xml 文件。在 Jenkins 里添加一段 Post build script 来运行 Fabric,这样 Build 完成之后就可以运行 Fabric 程序得到类似下面的输出结果:
Coverage Diff for Java code:
No coverage diff can be found.
Coverage Diff for Python code:
Name Stmts Miss Cover
------------ ------- ------ --------
dummy/dummy - -2 +50.00%
dummy/dummy2 +2 - +100.00%
TOTAL +2 -2 +50.00%
最后剩下的就是把之前一步的结果以评论的形式发布到 Bitbucket 里。这里,我们又添加了一个 Fabric 的 Task,通过调用 Bitbucket 的 API 把得到的结果发布到 Pull Request 的页面里。