使用 Jenkins 进行 .NET 应用程序持续集成 ( CI )






4.95/5 (13投票s)
使用 Jenkins & Gradle 脚本对 .NET 项目进行持续集成
引言
在本文中,我们将介绍如何将 .NET 应用程序配置到 Jenkins 服务器。我们将从零开始配置 Jenkins 服务器,以便您清楚地了解其设置和执行过程。此 Jenkins 服务器将首先连接到代码存储库服务器,下载最新代码,然后执行所有单元和集成测试。
我使用的 .NET 代码是使用 VS2013 开发的。用于脚本化 Jenkin 任务,我将使用 Gradle 脚本。我假设您对 .NET 代码有很好的理解。
我很乐意收到您对本文的反馈。请随时分享您的评论,您也可以发送电子邮件至 shettyashwin@outlook.com。如果您喜欢本文,请不要忘记评分。
背景
最近,我们计划在本地环境中设置 Jenkins。此活动背后的想法是建立一个持续监控代码质量并触发警报(如果未遵循编码标准)的门。此门还将检查已签入的脏代码,即某些错误的 कोड 已签入,或者开发人员忘记签入某些文件。
安装 Jenkins
从此处下载 Jenkins 安装程序的最新版本。Windows 版 Jenkins 安装程序以 .zip 文件形式提供。您需要将其解压到一个文件夹,然后双击 Jenkins.msi 进行安装。默认情况下,Jenkins 配置在您的 C:\ 驱动器上(或安装操作系统的主要驱动器)。确保您的 C: 驱动器上有足够的空间,因为一旦连接到代码存储库;最新代码将被获取到同一驱动器上。我建议选择另一个驱动器以避免将来的空间相关问题。在本文中,我使用的是一个不幸只有一个驱动器的虚拟机,所以我将不得不继续使用 C: 驱动器。
如果您已经在 C 驱动器上配置了 Jenkins,您可以在 Jenkins 主页/根目录 -> 管理 Jenkins -> 系统配置 中更改工作区位置,然后点击“主目录”下的“高级”按钮。您应该会看到“工作区目录”。我没有更改此路径并已验证。但如果您能够正确配置,请分享您的反馈。
安装插件
成功安装 Jenkins 应用程序后,您应该可以在 https://:8080 访问其 UI。Jenkins 服务器预装了一些插件,但我们需要一些额外的插件来设置 .NET 项目。要安装插件,请导航到 主页/根目录 -> 管理 Jenkins -> 管理插件。所有已安装的插件都应在“已安装”选项卡下。如果您的 Jenkins 服务器通过代理服务器访问互联网,您需要在 Jenkins UI 中进行一些额外的配置才能安装插件。或者,您可以下载插件并使用“高级”选项卡手动安装。我更倾向于采用以下配置,因为此选项简化了将来的更新。
要配置代理服务器,您需要更新 Jenkins 应用程序安装根文件夹中的 Jenkins.xml 文件。如果您将其安装在 C 驱动器上,它应该位于 C:\Program Files (x86)\Jenkins 下。打开此文件,在 service\argument 部分添加 -“Dhttp.proxyHost=[代理服务器 IP] -Dhttp.proxyPort=[代理服务器端口] -Dhttp.nonProxyHosts=[要排除的 IP]”。您需要重新启动 Jenkins 服务才能应用此设置。
重新启动服务器后,我们应该能够使用 Jenkins UI 安装插件。导航到“管理插件”下的“可用”选项卡。使用搜索文本框安装以下插件。
- MsBuild
设置 Jenkins
我们将使用 MSbuild 构建代码,使用 FXCop 进行静态代码分析,使用 Gradle 创建和执行任务。您需要在 Jenkins 服务器上下载并安装这些组件。相关配置如下。请注意您为每个设置指定的名称。这将在各个作业配置中使用。
另外,为了发送电子邮件通知,我们还需要配置 SMTP 详细信息。
设置新的 Jenkins 作业
让我们创建一个新的作业来配置 .NET 应用程序。导航回 Jenkins 主页。由于这是全新安装,您应该在欢迎消息下看到“创建新作业”选项。您也可以通过点击左侧菜单中的“新建项目”来创建新作业。点击“创建新作业”后,您会看到其他四个选项。这些选项下方有描述,因此我不会详细介绍。在“项目”中输入项目名称,然后选择“自由风格项目”。
点击“确定”继续。
现在,要配置将在该项目下构建和执行的源代码,请转到“源代码管理”部分。在“存储库 URL”文本框中,填写您的存储库路径。我正在为同一个项目使用 SVN。
如果您的存储库设置为安全模式,屏幕将提示您输入凭据。根据安全设置配置,选择屏幕上可用的相应选项。
对于代码审查,我们正在使用 FishEye。同样,它也可以用来通过 Jenkins 查看代码更改。您可以使用“存储库浏览器”下提供的许多其他选项。
这将配置 SVN 与 Jenkins。下一步将是在定期间隔或开发人员进行签入时触发构建。我个人偏好每次签入都构建。要配置此项,我们需要在“构建触发器”部分进行更改。选择“轮询 SCM”选项来设置时间间隔。您应该能在文本框下方看到下一个计划的构建时间。
好的,我们已经配置了定期触发构建。现在,我们需要定义构建触发后需要执行的所有设置。第一件也是最重要的事情是,如果您使用 Nugets 包来引用 DLL,您需要将其还原。要还原它,您需要使用 Nudget.exe。您可以从网上下载。成功安装 Nugets 后,点击“作业配置”页面上的“添加构建步骤”按钮。从菜单中选择“执行 Windows 批处理命令”。这将为构建添加一个新部分。使用以下详细信息配置文本框。
为了使用 MSBuild 构建我们的代码,您需要通过选择“使用 MSBuild 构建 Visual Studio 项目或解决方案”来添加一个新部分。这将添加一个新部分,请使用以下部分对其进行配置。确保在“命令行参数”中正确设置了构建参数。
Nugets 将从存储库还原所有 DLL,MSBuild 将重新构建您的代码。现在,我们将使用 Gradle 脚本来运行我们的单元、集成和静态代码分析。要添加这些步骤,我们需要再次点击“添加新步骤”,然后选择“调用 Gradle 脚本”。这将在页面上添加一个新部分。您需要选择在 Jenkins 配置页面进行 Gradle 配置时输入的 Gradle 脚本名称。不要忘记在“任务”列表中添加任务名称。任务将按照您在“任务”文本框中添加的顺序执行。
供您参考,我在此添加我的 Gradle 脚本代码。此 Gradle 脚本还包含使用 opencover 生成代码覆盖率报告的源代码。
task untiTestCaseWithCodeCoverage << {
String contents = ""
FileTree tree = fileTree(dir: 'Test',
includes: ['**/bin/Debug/**/[Project]*UnitTest.dll'], exclude:['**/bin/Debug/**/*[Project].dll'])
int i = 0
def reportDir = "${buildDir}\\report\\nunit"
def executedDll = []
file(reportDir).mkdirs()
tree.each { path ->
if(!executedDll.contains(path.name)){
def stdout = new ByteArrayOutputStream()
def s = "External\\Tools\\NUnit\\nunit-console.exe
\"$path\" /noshadow /xml:\"$reportDir\\${path.name}.xml\" /framework:net-4.0"
println s
contents = contents + s + "\r\n"
println "Output:\n$stdout"
}
else{
println "Excluded already executed dll $path.name"
}
}
new File( 'C:/Program Files (x86)/Jenkins/workspace/
[Project]/unitTestCase.bat' ).write( contents, 'UTF-8' )
exec {
commandLine 'cmd', '/c',
"C:\\JenkinAddOnFramework\\OpenCover\\OpenCover.Console.exe
-register:user -target:unitTestCase.bat -filter:+[Project*]*
-output:codeCoverageResult.xml"
}
exec {
commandLine 'cmd', '/c',
"C:\\JenkinAddOnFramework\\ReportGenerator\\ReportGenerator.exe
-reports:codeCoverageResult.xml -targetdir:coverage"
}
}
task runNUnitIntTest << {
FileTree tree = fileTree(dir: 'Test',
includes: ['**/bin/Debug/**/[ProjectName]*IntTest.dll',
'[Project]/bin/Debug/[Project].dll'], exclude:['**/bin/Debug/**/*Web.SecurityUnitTest.dll'])
int i = 0
def reportDir = "${buildDir}\\report\\nunit"
def executedDll = []
file(reportDir).mkdirs()
tree.each { path ->
if(!executedDll.contains(path.name)){
def stdout = new ByteArrayOutputStream()
def s = "External\\Tools\\NUnit\\nunit-console.exe
\"$path\" /noshadow /xml:\"$reportDir\\${path.name}.xml\" /framework:net-4.0"
println s
exec {
commandLine 'cmd', '/c', s
standardOutput = stdout
}
executedDll.add(path.name)
println "Output:\n$stdout"
}
else{
println "Excluded already executed dll $path.name"
}
}
}
task runNUnit << {
FileTree tree = fileTree(dir: 'Test',
includes: ['**/bin/Debug/**/[ProjectName]*UnitTest.dll',
'**/bin/Debug/**/[Project]*IntTest.dll',
'Framework/[Project]/bin/Debug/[Project].dll'], exclude:['**/bin/Debug/**/*[Project].dll'])
int i = 0
def reportDir = "${buildDir}\\report\\nunit"
def executedDll = []
file(reportDir).mkdirs()
tree.each { path ->
if(!executedDll.contains(path.name)){
def stdout = new ByteArrayOutputStream()
def s = "External\\Tools\\NUnit\\nunit-console.exe
\"$path\" /noshadow /xml:\"$reportDir\\${path.name}.xml\" /framework:net-4.0"
println s
exec {
commandLine 'cmd', '/c', s
standardOutput = stdout
}
executedDll.add(path.name)
println "Output:\n$stdout"
}
else{
println "Excluded already executed dll $path.name"
}
}
要发布报告并在发生故障时发送电子邮件,您需要设置以下配置。
点击“保存/应用”即可完成。下面是一些我们使用 Jenkins 生成的报告的快照。