资料参考
介绍
什么是持续集成(Continuous Integration)
持续集成(CI)是一种实践,旨在缓和和稳固软件的构建过程。在整个软件开发生命周期内,它主要用于保证代码质量,确保尽早遇到问题(如代码缺陷),避免问题在软件开发周期晚期变复杂时才被发现。CI的作用主要有如下几个方面:
- 软件构建自动化。
- 构建可持续的自动化检查。
- 构建可持续的自动化测试。
- 生成后后续过程的自动化。
组件
实现CI的上述功能,需要三个组件:
- 用 Ant 或 Maven 等工具建立的自动构建过程。
- 一个代码存储库,比如 CVS 或 Subversion。
- 一个 CI 服务器,比如 Hudson。
hudson介绍
Hudson 是一种革命性的开放源码 CI 服务器,它从以前的 CI服务器吸取了许多经验教训。Hudson最吸引人的特性之一是它很容易配置:很难找到更容易设置的 CI 服务器,也很难找到开箱即用特性如此丰富的CI 服务器。Hudson 容易使用的第二个原因是它具有强大的插件框架 ,所以很容易添加特性。例如,一个 Hudson 插件可以随时间的推移跟踪FindBugs 和代码覆盖。它还可以报告测试结果的趋势(来自 JUnit 或
TestNG? )以及构建结果和对应的执行时间。有人调查过,Hudson是目前使用最多的CI服务器。
工作原理

构建过程
- 触发构建--手动、代码更新、定制时间。
- 更新代码--Svn、Git、CVS。
- 构建--执行 Shell、执行 Windows 批处理、使用Ant、使用Maven。
安装
- 安装sun-java6-jdk,设置环境变量JAVA_HOME
- 安装tomcat6
- 安装 ant (optional)
- 在hudson官网下载hudson.war,放到tomcat/webapps下。访问http://localhost:8080/hudson,进入hudson的启动页面。注:使用sudo apt-get install tomcat6时,将hudson.war放在/var/lib/tomcat6/webapps目录下。
- 默认在/usr/share/tomcat6/.hudson下建立hudson的主目录。注意权限问题。
通用配置
系统管理
用户配置
选中系统设置-启用安全 ,进行用户配置
- JNLP节点代理的TCP端口选项中选择“Disable”。
- 访问控制-安全域
- 选择“Hudson专有用户数据库”:使用Hudson自己的用户列表验证, 而不是外部系统代理. 这适用于没有用户数据库小范围的设定. 建立的用户配置信息保存在HUDSON_HOUME/users下。
- 允许用户注册:如果勾选上,则表示允许用户自己注册一个新账号,通过点击页面右上角的注册链接进行操作。取消这个选框表示禁止任意注册新账号。 当这个选框被取消,就必须使用系统管理员来创建账号。
- 授权策略-安全矩阵
- 需要先注册一个admin用户(与角色名称相同),赋予全部的权限以便检验,否则我们将无法管理hudson。
- 默认会有一个匿名用户角色,表示未登录用户的访问权限,你可以不授权或只授予read权限。
- 添加用户/组:输入一个名称,然后点击添加,这里实际上是先建立一个角色,因为这时用户并不存在。然后注册一个相同名字的用户。
- 授权策略-项目矩阵授权策略
- 这个授权模型扩展自"安全矩阵",允许把下面的ACL(访问控制列表)矩阵附加到每个项目定义中(在Job配置页面). 就是说,除了可以在这里进行全局授权外,还可以在JOB中进行配置。
- 任务设置-启用项目安全:这里只能对当前的JOb进行授权配置,同样的,这里也是创建角色,用户需要自己注册或管理员进行创建。如果“系统设置”与“JOB设置”中存在相同的角色,则权限取并集。
- 反设置
- 有时候会出现这样的情况,就是虽然创建了角色,但是没有勾选上“允许用户注册”,这时就无法控制hudson了,这时可以通过如下修改,开启“允许用户注册”。
- 在$HUDSON_HOME环境变量对应的文件夹下config.xml,修改true 元素为false。重启hudson,这时候的Hudson又回归到无认证模式,任何人都能拥有该系统的全部权限。
创建构建任务
配置JOB_WORKSPACE
- 构建时使用的目录,也就是从代码仓库中取得的代码所要保存的路径,默认路径为:HUDSON_HOME/jobs/$jobname/workspace
- job的配置中,所有涉及到路径的配置,都是基于JOB_WORKSPACE基础上的,配置时都是使用相对路径,也就是从这个路径开始计算。
- 可以在job设置中修改这个路径,找到Advanced Project Options,点击“Advanced”,勾选Use custom workspace,并设置一个绝对路径,比如/usr/local/hudson/jobname/workspace,保存。
ngux构建:svn+cppcheck+gtest
注:需要安装hudson的svn插件、cppcheck插件。
- 新建任务
- Source Code Management:设置项目的源码获取方式Subversion地址
svn+ssh://hjhei@192.168.1.4/home/projects/svn/ngux/trunk@HEAD
- Build Triggers:设置构建的触发器
- Poll SCM:指定一个定时作业表达式来定义Hudson每隔多久检查一下您源代码仓库的变化。如果发现变化,就执行一次构建。
- Build periodically:选项支持类似crontab格式的任务周期定制。* * * * *对应分钟,小时,月份相应的日期,月份,星期
- Build:添加指令以执行构建脚本,这里添加的是shell脚本
- 执行cppcheck的脚本,并在ngux的目录下生成cppcheck-result.xml测试报告。
cd ./ngux
cppcheck --enable=all -q --xml . 2>cppcheck-result.xml
-
- 编译执行gtest测试用例的脚本,在ngux/unittest/reports目录下生成每个类对应的xml测试报告。
cd ./ngux
make clean
make -j3
cd .unittest/
./run_gtest.sh
- run_gtest.sh文件内容
#!/bin/bash
TEST_ITEMS=(textviewtest imageviewtest gridviewtest pageviewtest pagenavigateviewtest panelviewtest boxlayoutviewtest pairlayoutviewtest buttonviewtest linelayoutviewtest scrollviewtest menuviewtest itemviewtest listviewtest listviewlooptest trackbarviewtest)
[ -e reports/ ] || mkdir reports
for item in ${TEST_ITEMS[*]}
do
./$item --gtest_output=xml:$item.xml
if [ -f $item.xml ]; then
mv $item.xml reports/
else
echo "====================build $item failed===================="
continue
fi
done
- Post-build Actions:构建完成后需要进行的处理
- Publish JUnit test result report:如果构建脚本执行了JUnit测试,此选项将指示Hudson处理XML测试文档并为每次连续构建产生一份可持续的报告,依据正在进行的测试汇总处理结果。其结果是当前工作主页的一份报告,作业中的单元测试会随着时间的推移按由老至新进行陈列。
ngux/unittest/reports/*.xml
- Publish Cppcheck results:发布cppcheck测试报告,同JUnit。
- E-mail Notification:当选择此选项,可以输入一个或多个电子邮件地址[多个可用空格分隔],当Hudson完成了执行作业后,将会给它们发送通知。事件触发时将产生一份 Email,包括构建失败、构建不稳定等。这儿有一个额外的选项,当由于用户的错误提交造成Hudson决定废弃此次构建,将会发送一份专门的邮件给这位 SCM提交者,以便让他检查源代码。注:如果集成了gtest,测试用例不通过,会构建失败。
Espier构建:svn+Ant+Findbugs
注:需要安装ant、Findbugs插件、Static Analysis Collector结果分析插件、Android Emulator模拟器插件、Xvnc插件。
- 新建任务
- Source Code Management:设置项目的源码获取方式Subversion地址
svn+ssh://hjhei@192.168.1.4/home/projects/svn/espier/branches/nowebcore/Espier@HEAD
- Build Triggers:设置构建的触发器
- Build Environment:
- 勾选Run Xvnc during build。
- 勾选Run an Android emulator during build,并设置模拟器的属性。
- Common emulator options:勾选显示模拟器窗口。
- Build:添加指令以执行构建脚本,这里使用的是Ant-build.xml。
-
- build.xml内容
<?xml version="1.0" encoding="UTF-8"?>
<project name="Espier" default="all" basedir=".">
<property environment="env"></property>
<property name="android_sdk_path" value="/opt/android-sdk-linux_x86"/>
<property name="android_api_level" value="10"/>
<property name="android_platform" value="${android_sdk_path}/platforms/android-${android_api_level}"/>
<property name="android_jar" value="${android_platform}/android.jar"/>
<property name="refjars" value="jars/common.jar:jars/framework.jar:jars/core.jar:jars/layout.jar:jars/fmsoft_guava.jar"/>
<property name="classpath" value="${refjars}:${android_jar}"/>
<property name="debug_keystore" value="/home/heihuijing/.android/debug.keystore"/>
<property name="espier_package" value="com.fmsoft.espier"/>
<target name="all" depends="Step7_jarsign"/>
<target name="Step0_updateVersion">
<echo message="Update Version in AndroidManifest.xml ..."/>
<exec executable="bash">
<arg line="build_version.sh"/>
</exec>
</target>
<target name="Step1_genR" depends="Step0_updateVersion">
<echo message="Generate R.java ..."/>
<mkdir dir="gen"/>
<exec executable="${android_sdk_path}/platform-tools/aapt">
<arg line="package -f --auto-add-overlay -m -J gen -S res -A assets -M AndroidManifest.xml -I ${android_jar}"/>
</exec>
</target>
<target name="Step2_aidl" depends="Step1_genR">
<echo message="Preprocess aidl ..."/>
</target>
<target name="Step3_compile_java" depends="Step2_aidl">
<echo message="Compile all java files ..."/>
<mkdir dir="bin"/>
<javac srcdir="gen:application:quicksearchbox" destdir="bin" classpath="${classpath}" debug="true" debuglevel="lines,vars,source"/>
</target>
<target name="Step4_dx" depends="Step3_compile_java">
<echo message="Package all classes to classes.dex ..."/>
<java jar="${android_sdk_path}/platform-tools/lib/dx.jar" args="--dex --output=bin/classes.dex bin" fork="true"/>
</target>
<target name="Step5_compile_res" depends="Step4_dx">
<echo message="Compile all resource files ..."/>
<exec executable="${android_sdk_path}/platform-tools/aapt">
<arg line="package -f -M AndroidManifest.xml -S res -A assets -F bin/ResClass -I ${android_jar}"/>
</exec>
</target>
<target name="Step6_apkbuild" depends="Step5_compile_res">
<echo message="Build apk ..."/>
<exec executable="${android_sdk_path}/tools/apkbuilder">
<arg line="bin/${ant.project.name}.apk -u -z bin/ResClass -f bin/classes.dex"/>
</exec>
</target>
<target name="Step7_jarsign" depends="Step6_apkbuild">
<echo message="Sign the apk ..."/>
<exec executable="jarsigner">
<arg line="-keystore ${debug_keystore}"/>
<arg line="-keypass android -storepass android"/>
<arg line="bin/${ant.project.name}.apk androiddebugkey"/>
</exec>
</target>
<!--clean target-->
<target name="clean">
<delete dir="bin"/>
<delete dir="gen"/>
</target>
<!--findbugs target-->
<taskdef name="findbugs" classname="edu.umd.cs.findbugs.anttask.FindBugsTask"/>
<property name="findbugs.home" value="/home/heihuijing/tool/findbugs-1.3.9" />
<path id="findbugs.path">
<fileset dir="/home/heihuijing/findbugs-1.3.9">
<include name="**/*.jar" />
</fileset>
</path>
<target name="findbugs">
<findbugs home="${findbugs.home}"
output="xml"
outputfile="findbugs.xml">
<!--auxClasspath path="${findbugs.home}/lib/Regex.jar" /-->
<sourcePath path="gen:application:quicksearchbox" />
<class location="${basedir}/bin" />
</findbugs>
</target>
</project>
- Post-build Actions:构建完成后需要进行的处理
- Publish FindBugs? analysis results :发布FindBugs测试报告,同Junit。
- Build other projects:任务完成后,可触发其他任务构建。如:espiertest。espier任务完成后,触发espiertest构建。
EspierTest? 构建:svn+Ant+JUnit
- 新建任务
- Source Code Management:设置项目的源码获取方式Subversion地址
svn+ssh://hjhei@192.168.1.4/home/projects/svn/espier/branches/nowebcore/EspierTest@HEAD
- Build Triggers:设置构建的触发器。如设置Build after other projects are built为espier,在espier构建完成后开始该任务的构建。
- Build Environment:
- 勾选Run Xvnc during build。
- 勾选Run an Android emulator during build,并设置模拟器的属性。
- Common emulator options:勾选显示模拟器窗口。
- Build:添加指令以执行构建脚本,这里使用的是Ant-build.xml。
- Invoke Ant-Targets
uninstall
reinstall
run-test
-
- build.xml内容
<?xml version="1.0" encoding="UTF-8"?>
<project name="EspierTest" default="Step7_jarsign" basedir=".">
<property environment="env"></property>
<property name="android_sdk_path" value="/opt/android-sdk-linux_x86"/>
<property name="android_api_level" value="10"/>
<property name="android_platform" value="${android_sdk_path}/platforms/android-${android_api_level}"/>
<property name="android_jar" value="${android_platform}/android.jar"/>
<property name="tested_project" value="/home/heihuijing/fm/android-espier/Espier"/>
<property name="targetPackage" value="mobi.espier.browser"/>
<!-- <property name="refjars" value="jars/common.jar:jars/framework.jar:jars/core.jar:jars/layout.jar:jars/fmsoft_guava.jar"/> -->
<!-- <property name="classpath" value="${refjars}:${android_jar}"/> -->
<property name="adb" value="${android_sdk_path}/platform-tools/adb"/>
<property name="classpath" value="${tested_project}/bin:${android_jar}"/>
<property name="debug_keystore" value="/home/heihuijing/.android/debug.keystore"/>
<property name="espiertest_package" value="mobi.espier.browser.test"/>
<target name="Step1_genR" >
<echo message="Generate R.java ..."/>
<exec executable="${android_sdk_path}/platform-tools/aapt">
<arg line="package -f --auto-add-overlay -m -J gen -S res -A assets -M AndroidManifest.xml -I ${android_jar}"/>
</exec>
</target>
<target name="Step2_aidl" depends="Step1_genR">
<echo message="Preprocess aidl ..."/>
</target>
<target name="Step3_compile_java" depends="Step2_aidl">
<echo message="Compile all java files ..."/>
<javac srcdir="gen:src" destdir="bin" classpath="${classpath}" debug="true" debuglevel="lines,vars,source" encoding="UTF-8" includeantruntime="false">
<classpath>
<fileset dir="${tested_project}/jars" includes="*.jar" />
<!-- <fileset dir="libs" includes="*.jar" /> -->
</classpath>
</javac>
</target>
<target name="Step4_dx" depends="Step3_compile_java">
<echo message="Package all classes to classes.dex ..."/>
<java jar="${android_sdk_path}/platform-tools/lib/dx.jar" fork="true">
<arg line="--dex --output=bin/classes.dex bin"/>
</java>
</target>
<target name="Step5_compile_res" depends="Step4_dx">
<echo message="Compile all resource files ..."/>
<exec executable="${android_sdk_path}/platform-tools/aapt">
<arg line="package -f -M AndroidManifest.xml -S res -A assets -F bin/ResClass -I ${android_jar}"/>
</exec>
</target>
<target name="Step6_apkbuild" depends="Step5_compile_res">
<echo message="Build apk ..."/>
<exec executable="${android_sdk_path}/tools/apkbuilder">
<arg line="bin/${ant.project.name}.apk -u -z bin/ResClass -f bin/classes.dex"/>
</exec>
</target>
<target name="Step7_jarsign" depends="Step6_apkbuild">
<echo message="Sign the apk ..."/>
<exec executable="jarsigner">
<arg line="-keystore ${debug_keystore}"/>
<arg line="-keypass android -storepass android"/>
<arg line="bin/${ant.project.name}.apk androiddebugkey"/>
</exec>
</target>
<!--clean testproject target-->
<target name="clean">
<delete includeEmptyDirs="true">
<fileset dir="bin"/>
<fileset dir="gen"/>
</delete>
</target>
<!--install-test-apk to emulator-->
<target name="install" depends="Step7_jarsign" description="Install android-test project.">
<exec executable="${adb}">
<arg line="install bin/${ant.project.name}.apk"/>
</exec>
</target>
<!--reinstall-test-apk to emulator-->
<target name="reinstall" depends="clean, Step7_jarsign" description="ReInstall android-test project.">
<exec executable="${adb}">
<arg line="install -r bin/${ant.project.name}.apk"/>
</exec>
</target>
<!--uninstall-test-apk from emulator-->
<target name="uninstall" description="Uninstall android-test project">
<exec executable="${adb}">
<arg line="uninstall ${espiertest_package}"/>
</exec>
</target>
<!--run-test-->
<target name="run-test" description="run test project">
<property name="reports.dir" value="reports"/>
<property name="files.dir" value="/data/data/${targetPackage}/files"/>
<!-- <property name="test.runner" value="com.zutubi.android.junitreport.JUnitReportTestRunner"/> -->
<property name="test.runner" value="mobi.espier.browser.test.XMLInstrumentationTestRunner"/>
<echo>Cleaning up previous test reports...</echo>
<exec executable="${adb}" failonerror="true">
<!-- <arg line="${adb.device.arg}" /> -->
<arg value="shell" />
<arg value="rm" />
<arg value="${files.dir}/*" />
</exec>
<echo>Running tests...</echo>
<exec executable="${adb}" failonerror="true">
<!-- <arg line="${adb.device.arg}"/> -->
<arg value="shell" />
<arg value="am" />
<arg value="instrument" />
<arg value="-w" />
<arg value="-e" />
<arg value="outfile" />
<arg value="test-results.xml" />
<!-- <arg value="-e" /> -->
<!-- <arg value="multiFile" /> -->
<!-- <arg value="true" /> -->
<arg value="${espiertest_package}/${test.runner}" />
</exec>
<echo>Downloading XML test reports...</echo>
<exec executable="${adb}" failonerror="true">
<!-- <arg line="${adb.device.arg}"/> -->
<arg value="pull" />
<arg value="${files.dir}" />
<arg value="${reports.dir}" />
</exec>
</target>
</project>
- Post-build Actions:构建完成后需要进行的处理
- Publish JUnit test result report:发布JUnit测试报告。