搭建Jenkins持续集成环境,并利用Jenkins搭建自动构建系统,继而利用自动构建系统发布测试版或进行自动测试等
目录
Jenkins
Jenkins是一个用Java编写的开源的持续集成工具。在与Oracle发生争执后,项目从Hudson项目复刻(派生/fork)。
Jenkins提供了软件开发的持续集成服务。它运行在Servlet容器中(例如Apache Tomcat)。它支持软件配置管理(SCM)工具(包括AccuRev SCM、CVS、Subversion、Git、Perforce、Clearcase和和RTC),可以执行基于Apache Ant和Apache Maven的项目,以及任意的Shell脚本和Windows批处理命令。Jenkins的主要开发者是川口耕介。Jenkins是在MIT许可证下发布的自由软件。
来自维基百科。
安装
安装基于2.32.2,下面的笔记针对1.647版本。
- 访问https://jenkins.io/,在下载按钮右侧的下拉框选择对应的平台。windows下得到的是一个zip文件。
- 安装。windows下解压得到
jenkins.msi
,直接安装。 - 安装完成。windows下安装完成会自动跳转到
localhost:8080
。 - 打开
Jenkins\secrets\initialAdminPassword
,复制密码到网页。 - 安装建议的插件。
- 创建管理员账号。
- 完成。
执行上面的步骤,就能看到下面的开始页。
之前使用的是1.647,相对来说简单点
登录
如果第一次打开Jenkins没有创建用户就退出了登录,那么需要使用用户名admin
和上面initialAdminPassword
里的密码来登录。如果创建了用户,则可以使用新用户登录。
全局配置
-
工作空间根目录 和 构建记录根目录
最好设置为Job所在的目录下,这样Job各自的工作空间就会在Job对应的目录下创建,而不会在Jenkins根目录下创建。
${ITEM_ROOTDIR}/workspace
${ITEM_ROOTDIR}/builds
-
Jenkins URL
可以设置为域名,也可以设置为IP,作为URL地址,用于别人访问Jenkins。 -
系统管理员邮件地址
Jenkins发送邮件时发件人信息。 -
Extended E-mail Notification
当添加了Email Extension插件时设置项,主要设置邮件服务器(SMTP server)、邮件后缀名(Default user E-mail suffix)以及邮件格式(Default Content Type)。 -
邮件通知
和邮件扩展插件一样,这里可以通过发送测试邮件检验配置是否有效。
插件管理
插件管理可以对Jenkins的插件进行增删改查操作。每一个插件都有对应的帮助页面,从上面可以了解到插件的用途,也可以获取插件提供的API、Demo、文档等等。有时候这些帮助页很有用,遇到的问题Google可能无法得到答案,而它就在插件的帮助页面上!使用某个插件遇到了问题,不妨先查看对应的帮助页面。
用户和权限管理
Jenkins安装完成后,默认不会启用用户管理,需要配置:
- 在Jenkins的
全局安全
里面,勾选启用安全
,选择Jenkins专有用户数据库
,并勾上允许用户注册
(一定要先勾上),然后保存。 - 保存后,Jenkins就会弹出注册页,这是第一个管理员,然后继续注册需要登录验证的用户。
- 使用某一个注册账号登录,取消
允许用户注册
后,就只有当初注册了的用户能够登录。然而,上面的操作后,普通用户一样具备管理权限。 - 此时,
授权策略
选择安全矩阵
,把匿名用户
的权限只保留Overall-Read,Job-Discover,Job-Read即可,这样未登录的用户只能查看。
如果需要按照角色分配权限,需要安装Role Strategy Plugin,这个插件的帮助页面有配置教程,可以实现按用户组分配权限。
环境变量
Jenkins的环境变量包括系统环境变量
、Jenkins内置环境变量
、Job构建时的环境变量
,以下是常用的环境变量:
BUILD_URL
当前构建信息URLJOB_URL
PROJECT_URL
项目工程目录URLBUILD_STATUS
构建结果SVN_REVISION
当前构建的SVN RevisionCHANGES
当前构建到上一次构建间的svn log合集(无论上一次构建成功与否,都会影响当前的这一次构建的CHANGES)
比如:
当前编译结果: ${BUILD_STATUS}
当前编译Revision: ${SVN_REVISION}
当前项目目录: ${PROJECT_URL}/ws
当前编译日志: ${BUILD_URL}/console
当前版本变化: ${CHANGES}
Job的配置
安装了不同的插件,Job会有不同的配置,但主要是这些配置。
Job的构建
Job的构建,分为构建参数设置、构建前、构建中、构建后,每个步骤都可以执行相应的脚本。
- 脚本可以支持:
windows命令行、Python、Groovy、Shell等等(可能需要安装对应的插件,或者安装Python然后在命令行里执行)。 - 构建结果:
脚本执行的返回值为0,则表示成功,非0表示失败(这点非常重要!Jenkins不能判断脚本内部如何执行,它只关心最后的返回值)。
参数化构建过程
参数化构建过程,顾名思义,指构建时传递到构建过程的参数,其中类型有:
- 字符串
- 文本
- bool值
- 列表
- List Subversion tags
这个参数可以列出版本库里所有的分支,在需要对版本库某个分支进行构建时非常有用(见下图)
每一个参数都有一个Name
字段,表示参数名,构建时参数名和对应的值将被设置为环境变量,Jenkins中可以通过${参数名}
来访问。
比如:Name为Version的参数,构建时值设置为1,则${Version}
的值为1(在Windows批处理下则是%Version%
的值为1)。
这个值可以在构建时的脚本中访问,可以在构建后的脚本中访问,也可以在构建后的邮件设置中访问。但需要注意,每个变量,都有它的生命周期,并非每个变量的生命周期都这样长,具体要看变量是如何定义的。
${xxx}为Shell访问变量的方式;%xxx%为Windows命令行访问变量的方式。
Rebuilder插件
当设置参数构建了一个版本后,希望参数能够自动保存,可以在下一次Build构建时使用,但是暂时还没有插件在Build构建时提供这个功能。Rebuilder插件能够提供类似功能,但是它不是Build操作,而是Rebuild操作。安装Rebuilder插件后,Rebuild时参数就是上一次构建时的参数,此时修改参数为新参数即可进行新一次构建。
Email Extension插件
顾名思义,叫邮件扩展插件,增加了设置邮件内容
、设置邮件触发器
等功能。在高级设置
里(很多插件的设置都在高级设置里),可以设置邮件触发器,一般处理失败和成功事件。邮件发送对象:
- Requestor 发起此次构建的用户邮件
- Developers 此次构建与上一次构建,进行了更改的开发者名称(版本管理器里的提交者)与邮件后缀组合得到的邮件地址
- (更多查看插件对应的帮助页面)
邮件附件: 附件帮助说使用Ant格式,结果试了很多次都失败!最后直接写*.dll
来测试,就成功了。附件可以使用常见的正则表达式,路径以workspace
为始目录,且附件必须在当前构建的目录下,否则会找不到附件。
Groovy Postbuild插件
此插件用于Build构建后Groovy脚本支持,可以在Build后做各种各样的事情,其中一样就是修改构建页显示的内容(见下图)
manager.addShortText( manager.getEnvVariable("VERSION") )
str = manager.getEnvVariable("SUBJECT")
manager.addShortText( str.substring(0, str.size() > 10 ? 10 : str.size()) ) //would crash while size < 10
manager.addShortText( 'V' + manager.getEnvVariable("VER") )
manager.addShortText( manager.getEnvVariable("SVN_REVISION") )
manager.getEnvVariable("REBUILD") == "true" ? "" : manager.addShortText("Build")
以上代码的效果是:
Multi-Branch Project Plugin插件
当一个仓库含有多个分支,需要为每个分支分别创建构建任务的时候,可以使用此插件。曾经试过使用上面的List Subversion tags
参数化构建过程实现,当需要构建某个分支时,下拉选择对应的分支然后进行构建。然而仓库更新会对整个仓库进行更新,所以只是构建某个分支时,更新仓库的操作就非常浪费时间;而在脚本里只更新某个仓库,却因为构建过程svn被锁定(lock)而无法操作。其实这个思路是行的通的,只是没有时间折腾,就选择了Multi-Branch
插件来实现。
邮件中使用脚本
Jenkins中邮件服务是比较重要的一个,构建过程成功与失败都可以通过邮件通知。邮件中也可以使用脚本,不过需要Email Extension插件。
邮件脚本支持jelly和groovy。插件帮助文档和Github有相关的demo。
获取构建时间
jelly版buildtime.jelly
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define">
本次构建用时: ${build.durationString}
</j:jelly>
groovy版buildtime.groovy
<%//ANSI中文%>
本次构建用时: ${build.durationString}
如上,脚本的编码需要注意。
获取项目变更集
通常Jenkins获取项目变更集直接使用${CHANGESET}
,但是构建失败后,再次构建CHANGESET会为空,所以需要使用脚本来做一些逻辑处理。
jelly版changeset.jelly
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define">
<j:set var="changeSet" value="${build.changeSet}" />
<j:if test="${changeSet!=null}">
<j:whitespace>变更汇总: </j:whitespace>
<j:set var="hadChanges" value="false"/>
<j:set var="index" value="1"/>
<j:forEach var="cs" items="${changeSet}" varStatus="loop">
<j:set var="hadChanges" value="true"/>
<j:set var="aUser" value="${cs.hudsonUser}"/>
<j:whitespace> ${index}. ${cs.msgAnnotated} - ${aUser!=null?aUser.displayName:cs.author.displayName} </j:whitespace>
<j:set var="index" value="${index+1}"/>
</j:forEach>
<j:if test="${!hadChanges}">
<j:whitespace>No Changes </j:whitespace>
</j:if>
</j:if>
</j:jelly>
groovy版changeset.groovy
<%//ANSI中文%>
变更汇总:
<%
/**
* 获取上次失败的构建列表(虽然失败,但是它们占用了changeSet)
* 此脚本运行于post-successful-build,所以lastSuccessfulBuild和lastBuild就是当前的构建
**/
def _getLastFailureBuilds()
{
def failList = []
def lastSuccessfulBuild
def allBuilds = project.getBuilds()
//you can't abort an "each" without throwing an exception.use a "find" or "any" closure instead
//默认迭代变量it
allBuilds.any()
{
if (it.getNumber() == build.getNumber()) //忽略自己
{
return false
}
else if (it.getResult() != Result.SUCCESS) //include ABORTED/FAILURE/UNSTABLE
{
failList << it
return false //手动return false,否则最后一句的返回值就是any的返回值,failList<<it会返回true而中断了遍历导致结果只有一条
}
else if (it.getResult() == Result.SUCCESS)
{
lastSuccessfulBuild = it
return true
}
}
return failList
}
//如果要函数内部能够访问,不能使用def或其他类型声明(比如 int i),声明是指代局部变量
hadChanges = false
index = 1
def _showChangeSet(changeSet)
{
if(changeSet != null)
{
changeSet.each()
{
cs-> //声明一个迭代变量
hadChanges = true
println " ${index}. ${cs.msgAnnotated} - ${cs.author}"
index += 1
}
}
}
/*Main*/
/*按照后提交先输出顺序*/
_showChangeSet(build.changeSet)
def lastFailureBuilds = _getLastFailureBuilds()
lastFailureBuilds.each()
{
_showChangeSet(it.getChangeSet()) //getChangeSet() 等价于 changeSet
}
if (! hadChanges)
{
println "No Changes"
}
%>
把上面四个文件,放到Jenkins\email-templates
下,没有该文件夹就手动创建。
然后在Email内容里调用:
此版本ChangeSet:
${SCRIPT, template="changeset.groovy"}
此版本构建用时:
${SCRIPT, template="buildtime.groovy"}
附上完整的邮件内容例子
++++++++++++++++++++++++++++++
+ $VERSION版本-$SUBJECT-测试版V$VER
++++++++++++++++++++++++++++++
此版本解决的问题:
${FIXBUG}
${SCRIPT, template="changeset.groovy"}
==============================
当前编译结果: ${BUILD_STATUS}
当前编译分支:
当前编译Revision: ${SVN_REVISION}
当前项目目录: ${PROJECT_URL}/ws
当前编译日志: ${BUILD_URL}/console
${SCRIPT, template="buildtime.groovy"}
From: gametaskcenter-autobuild
- this email is auto sent by Jenkins do not reply
邮件截图
参考文献
- 维基百科,Jenkins。
- Jenkins Doc. https://jenkins.io/doc/.
- Email-ext plugin help. https://wiki.jenkins-ci.org/display/JENKINS/Email-ext+plugin.
- Email-ext plugin github. https://github.com/jenkinsci/email-ext-plugin/tree/master/src/main/resources/hudson/plugins/emailext/templates.