+86 13541016684Mon. - Fri. 10:00-22:00

基于Jenkins的Python代码集成整合

基于Jenkins的Python代码集成整合

Jenkins简介

曾经提及过 Jenkins。在本次实习中,Jenkins 是我每天都要使用的工具。在频繁的使用过程当中:通过实际工作感受了「持续集成」的概念(关于持续集成的概念,此处按下不表,待有时间的时候再详细总 结。);逐渐熟悉了 Jenkins 的使用,并且体会到其带来的方便。因此,希望总结一下 Jenkins 的使用。

然而 Jenkins 不通过具体的案例难以体会其方便之处,网上相关使用说明之类的文章又颇多,所以本文仅谈个人使用中的体会,并非学习 Jenkins 使用的教程。


Jenkins 是什么

Jenkins 是一个用 Java 编写的开源的 持续 Continuous 集成 Integration 工具。

Jenkins 是用 Java 开发的(界面和 Eclipse 一样,带着一股浓浓的 SWT 的味道,好在界面并不太影响使用。),对 Java 程序开发有天然的良好支持,如 JUnit/TestNG 测试,Ant 等 Java 开发中常用的工具都包含在 Jenkins 里。当然,Jenkins 也可以通过插件来实现其它语言的开发。

Jenkins 的特性

在使用的过程中,我体会比较深刻的特性有:

  • 项目易于配置

在 Jenkins 当中,我们可以新建 Job。在 Job 里,可以设置添加 构建脚本 Build Script。构建脚本支持 Bash、Ant、Makefile;Job 的参数、Meta 数据可以作为环境变量在脚本里直接使用,因此设置起来非常方便。

  • 种类繁多的插件 (这点也和 Eclipse 也颇为相似)

Jenkins 的开发者社区非常活跃, 第三方插件 很多,从而可以帮助我们实现很多常用的功能。 比如,Hipchat 插件可以在 Job 运行结束后把结果发送到 Hipchat 的聊天室里;Cobertura 插件可以显示测试覆盖率的数据。

Jenkins 的使用场景

在我们公司,Jenkins 主要被用来用于:

  • 构建 Build 测试 Test 部署 Deploy 代码

我们可以通过一个 Job 实现以下流程:

  1. 使用 Git 插件,从代码库下载任一版本或分支的源代码;
  2. 编译代码;
  3. 运行测试。

或者是:

  1. 启动若干个 EC2 实例;
  2. 将任一版本的代码部署到新建的实例上。

所有的这些流程在 Jenkins 里,都只需要设置几个简单的参数(如分支的名称,或者是实例的个数),再点击运行按钮就可以了。

  • 自动化一些复杂的流程,如数据库的迁移、备份,系统更新的安装等等

有一些常用,但是流程很复杂的过程,可以在 Jenkins 里通过 Job 来完成。

 

最近做了一个多月的项目是将代码测试覆盖率整合到公司持续整合(Continuous Integration)的流程当中。

This project uses Java and XML. How it could be good?

—— 组里的同事如此评价本项目

本文介绍该项目的大致流程,共分为两部分:

  1. 介绍 Automated python unit testing, code coverage and code quality analysis with Jenkinspart1, part2, part3)中使用 Jenkins 实现自动化测试、得到代码覆盖率和代码质量的方法。
  2. 简要介绍我们如何在这篇文章的基础上把代码覆盖率整合到公司的 Bitbucket 代码库当中。

基于 Jenkins 的 Python 自动化测试工具

使用到的 Python 模块:

  • coverage:用来生成代码覆盖率的数据;
  • nose: 用来运行单元测试;
  • pylint:用来得到 Python 代码质量的数据。

使用到的 Jenkins 插件:

安装需要的 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 脚本中的三个命令:

  1. nosetests 命令运行单元测试;
  2. 运行 coverage,将覆盖率数据输出为 xml 文件;
  3. 运行 pylint 得到代码的质量数据。

具体参数的含义可以参阅原文的第一、第二部分。

显示结果

上一步的 Build Script 有三个输出文件:

  1. nosetests.xml
  2. coverage.xml
  3. 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 的页面里。