65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.95/5 (13投票s)

2015年8月19日

CPOL

6分钟阅读

viewsIcon

91159

使用 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 安装插件。导航到“管理插件”下的“可用”选项卡。使用搜索文本框安装以下插件。

  1. 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 生成的报告的快照。

单元测试用例执行报告

代码分析报告

代码覆盖率报告

参考文献

© . All rights reserved.