C#持续集成与Azure DevOps流水线配置
2023-06-075.4k 阅读
C# 持续集成基础
在软件开发流程中,持续集成(Continuous Integration,简称 CI)是一种软件开发实践,团队开发成员经常集成他们的工作,通常每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译、发布、自动化测试)来验证,从而尽早地发现集成错误。对于 C# 项目而言,持续集成有着至关重要的意义。
C# 项目自动化构建工具
- MSBuild
- MSBuild 是微软为 .NET 项目提供的一个基于 XML 的构建引擎。它是 C# 项目构建的核心工具之一。在 C# 项目文件(通常是 .csproj 文件)中,定义了项目的各种构建相关信息,如引用的程序集、源文件位置等,MSBuild 会根据这些信息来编译项目。
- 例如,一个简单的 C# 控制台应用项目的 .csproj 文件内容如下:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
</Project>
- 当在命令行中执行
msbuild
命令时,MSBuild 会根据此项目文件的配置来编译项目。如果项目有多个源文件,它们会被正确编译并生成可执行文件。
- DotNet CLI
- DotNet CLI(.NET 命令行界面)是一个跨平台的工具,它提供了一组命令来管理 .NET 项目,包括创建、构建、测试等。它内部也依赖 MSBuild 进行实际的项目构建操作。
- 例如,要构建一个 C# 项目,可以在项目目录下执行
dotnet build
命令。DotNet CLI 会自动检测项目的 .csproj 文件,并调用 MSBuild 进行构建。 - 它还支持更多功能,如创建新项目:
dotnet new console -n MyConsoleApp
会创建一个名为MyConsoleApp
的 C# 控制台应用项目。
C# 项目自动化测试框架
- NUnit
- NUnit 是一个广泛使用的 C# 单元测试框架。它允许开发人员编写和运行单元测试,以验证代码的正确性。
- 首先,需要在项目中安装 NUnit 相关的 NuGet 包。在项目目录下执行
dotnet add package NUnit
和dotnet add package NUnit3TestAdapter
命令。 - 然后,可以编写如下的单元测试代码:
using NUnit.Framework;
namespace MyCSharpTest
{
public class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
}
[TestFixture]
public class CalculatorTests
{
[Test]
public void Add_ShouldReturnCorrectSum()
{
var calculator = new Calculator();
var result = calculator.Add(2, 3);
Assert.AreEqual(5, result);
}
}
}
- 执行
dotnet test
命令,NUnit 会运行这些测试,并报告测试结果。
- xUnit
- xUnit 也是一个流行的 C# 单元测试框架。它设计简洁,易于使用。
- 同样,先安装相关 NuGet 包:
dotnet add package xunit
和dotnet add package xunit.runner.visualstudio
。 - 以下是使用 xUnit 编写的测试代码示例:
using Xunit;
namespace MyCSharpTest
{
public class Calculator
{
public int Multiply(int a, int b)
{
return a * b;
}
}
public class CalculatorTests
{
[Fact]
public void Multiply_ShouldReturnCorrectProduct()
{
var calculator = new Calculator();
var result = calculator.Multiply(2, 3);
Assert.Equal(6, result);
}
}
}
- 执行
dotnet test
后,xUnit 会执行测试并给出结果。
Azure DevOps 简介
Azure DevOps 是微软提供的一套软件开发工具和服务的集合,涵盖了从项目规划、代码管理、构建、测试到发布的整个软件开发生命周期。它提供了多种功能,方便团队进行协作开发。
Azure DevOps 核心服务
- Azure Repos
- Azure Repos 是 Azure DevOps 提供的代码版本控制系统,支持 Git 和 Team Foundation Version Control(TFVC)。Git 是目前更常用的分布式版本控制系统,它允许开发人员在本地进行代码管理,然后将更改推送到远程仓库。
- 在 Azure Repos 中创建一个 Git 仓库非常简单。登录到 Azure DevOps 平台,创建一个新项目,在项目中可以直接创建一个新的 Git 仓库。开发人员可以通过
git clone
命令将远程仓库克隆到本地进行开发。
- Azure Pipelines
- Azure Pipelines 是实现持续集成和持续交付(CI/CD)的关键服务。它可以根据项目的需求,定义一系列的构建、测试和部署步骤。
- 例如,可以创建一个流水线来自动构建 C# 项目,运行测试,并将其部署到目标环境。Azure Pipelines 支持多种代理池,包括微软托管的代理和自托管的代理。微软托管的代理提供了预配置的环境,适合大多数常见的开发场景。
- Azure Test Plans
- Azure Test Plans 用于管理测试用例和测试执行。团队可以创建测试计划,定义测试套件,并将测试用例分配给不同的测试人员。它还支持手动测试和自动化测试的集成。
- 例如,在完成 C# 项目的自动化单元测试后,可以使用 Azure Test Plans 来管理更高级别的集成测试和系统测试。
Azure DevOps 与 C# 项目的关联
- 代码存储
- 可以将 C# 项目的代码存储在 Azure Repos 的 Git 仓库中。通过 Visual Studio 或命令行工具,可以方便地将本地的 C# 项目代码推送到 Azure Repos。
- 在 Visual Studio 中,打开 C# 项目,选择“团队资源管理器”,然后连接到 Azure DevOps 项目,并将项目推送到 Azure Repos 中的指定仓库。
- 构建与测试
- Azure Pipelines 可以配置为自动构建和测试 C# 项目。通过定义合适的流水线,可以调用 DotNet CLI 或 MSBuild 来构建项目,并运行 NUnit 或 xUnit 测试。
- 例如,在流水线中可以添加一个任务来执行
dotnet build
命令进行项目构建,再添加一个任务来执行dotnet test
命令运行测试。
C# 持续集成在 Azure DevOps 中的实现
创建 Azure DevOps 项目
- 登录 Azure DevOps
- 打开浏览器,访问 Azure DevOps 官网。使用微软账号登录,如果没有账号,需要先注册。
- 创建新项目
- 登录后,点击“新建项目”按钮。在弹出的对话框中,输入项目名称(例如“CSharpCIProject”),选择项目可见性(公开或私有),并选择项目模板(如“敏捷”)。然后点击“创建”按钮,即可创建一个新的 Azure DevOps 项目。
将 C# 项目代码上传到 Azure Repos
- 初始化本地 Git 仓库(如果未初始化)
- 打开命令行,导航到本地 C# 项目目录。执行
git init
命令,初始化一个本地 Git 仓库。
- 打开命令行,导航到本地 C# 项目目录。执行
- 添加和提交代码
- 执行
git add.
命令,将项目中的所有文件添加到暂存区。然后执行git commit -m "Initial commit"
命令,将暂存区的文件提交到本地仓库,提交信息为“Initial commit”。
- 执行
- 关联远程仓库
- 在 Azure DevOps 项目中,找到项目的 Git 仓库地址。在命令行中执行
git remote add origin <repository - url>
命令,将本地仓库与 Azure Repos 中的远程仓库关联。
- 在 Azure DevOps 项目中,找到项目的 Git 仓库地址。在命令行中执行
- 推送代码到远程仓库
- 执行
git push -u origin main
命令,将本地main
分支的代码推送到远程仓库的main
分支。这里-u
参数表示将本地分支与远程分支关联,以后推送和拉取可以简化命令。
- 执行
配置 Azure Pipelines 进行 C# 项目构建
- 创建新流水线
- 在 Azure DevOps 项目中,点击“Pipelines”,然后点击“新建流水线”。
- 选择代码源为“Azure Repos Git”,并选择之前上传 C# 项目代码的仓库。
- 选择模板
- 可以选择“Empty job”模板,然后手动配置流水线步骤;也可以选择与 .NET 相关的预定义模板,如“.NET Desktop”模板。这里以“Empty job”模板为例进行配置。
- 添加构建任务
- 在流水线编辑器中,点击“+”按钮添加任务。搜索并选择“DotNetCoreCLI”任务。
- 在任务配置中,选择“Command”为“build”,“Projects”字段填写项目的 .csproj 文件路径(如果项目结构简单,直接填写项目根目录下的 .csproj 文件名称即可,例如“MyConsoleApp.csproj”)。
- 可以根据需要设置其他参数,如“Arguments”字段可以添加构建相关的参数,如“/p:Configuration=Release”来指定构建为发布版本。
- 保存并运行流水线
- 点击“保存并运行”按钮,选择要运行流水线的分支(通常是
main
分支),然后点击“运行”。Azure Pipelines 会使用微软托管的代理来执行构建任务。如果构建成功,在流水线的运行结果中可以看到构建日志,显示项目已成功编译。
- 点击“保存并运行”按钮,选择要运行流水线的分支(通常是
配置 Azure Pipelines 进行 C# 项目测试
- 添加测试任务
- 在已经配置好构建任务的流水线中,继续点击“+”按钮添加任务。搜索并选择“DotNetCoreCLI”任务。
- 在任务配置中,选择“Command”为“test”,“Projects”字段填写测试项目的 .csproj 文件路径(如果使用 NUnit 或 xUnit 进行测试,需要有对应的测试项目,例如“MyConsoleApp.Tests.csproj”)。
- 如果测试项目需要特定的参数,如测试运行器的参数,可以在“Arguments”字段中填写。例如,对于 NUnit 测试,可以添加参数“--logger trx”来生成测试结果的 TRX 文件,方便在 Azure DevOps 中查看详细的测试报告。
- 查看测试结果
- 保存并再次运行流水线。当测试任务执行完成后,在流水线的运行结果中可以点击“测试结果”链接查看详细的测试报告。如果所有测试用例通过,会显示绿色的通过标记;如果有测试用例失败,会显示红色的失败标记,并详细列出失败的测试用例和错误信息。
高级 Azure DevOps 流水线配置
多阶段流水线
- 多阶段流水线概念
- 多阶段流水线允许将整个 CI/CD 过程划分为多个阶段,每个阶段可以包含多个任务。常见的阶段包括构建、测试、部署等。这种方式使得流水线的逻辑更加清晰,也便于对不同阶段进行独立的管理和控制。
- 配置多阶段流水线示例
- 以下是一个简单的多阶段流水线示例,包含构建、测试和部署三个阶段:
stages:
- stage: Build
displayName: Build Stage
jobs:
- job: BuildJob
displayName: Build Job
pool:
vmImage: 'windows - latest'
steps:
- task: DotNetCoreCLI@2
displayName: 'DotNet Core Build'
inputs:
command: 'build'
projects: 'MyConsoleApp.csproj'
- stage: Test
displayName: Test Stage
dependsOn: Build
jobs:
- job: TestJob
displayName: Test Job
pool:
vmImage: 'windows - latest'
steps:
- task: DotNetCoreCLI@2
displayName: 'DotNet Core Test'
inputs:
command: 'test'
projects: 'MyConsoleApp.Tests.csproj'
- stage: Deploy
displayName: Deploy Stage
dependsOn: Test
jobs:
- job: DeployJob
displayName: Deploy Job
pool:
vmImage: 'windows - latest'
steps:
- script: echo 'Deployment steps will be added here'
- 在这个示例中,
Build
阶段先执行项目构建任务,Test
阶段依赖于Build
阶段,只有Build
阶段成功后Test
阶段才会执行,Deploy
阶段同理依赖于Test
阶段。在实际的部署阶段,需要根据目标环境(如 Azure Web 应用、虚拟机等)添加具体的部署脚本或任务。
使用变量和条件
- 变量
- 在 Azure Pipelines 中,可以定义变量来存储一些常用的值,如版本号、目标环境地址等。变量可以在流水线的不同任务和阶段中使用。
- 例如,在流水线的 YAML 文件中定义一个变量
version
:
variables:
- name: version
value: 1.0.0
- 然后在构建任务中可以使用这个变量,如在
DotNetCoreCLI
任务的Arguments
字段中添加/p:Version=$(version)
,这样在构建项目时会将项目的版本号设置为定义的变量值。
- 条件
- 条件可以根据某些条件来决定是否执行某个任务或阶段。例如,只有在特定分支(如
main
分支)上才执行部署阶段:
- 条件可以根据某些条件来决定是否执行某个任务或阶段。例如,只有在特定分支(如
stages:
- stage: Build
displayName: Build Stage
jobs:
- job: BuildJob
displayName: Build Job
pool:
vmImage: 'windows - latest'
steps:
- task: DotNetCoreCLI@2
displayName: 'DotNet Core Build'
inputs:
command: 'build'
projects: 'MyConsoleApp.csproj'
- stage: Test
displayName: Test Stage
dependsOn: Build
jobs:
- job: TestJob
displayName: Test Job
pool:
vmImage: 'windows - latest'
steps:
- task: DotNetCoreCLI@2
displayName: 'DotNet Core Test'
inputs:
command: 'test'
projects: 'MyConsoleApp.Tests.csproj'
- stage: Deploy
displayName: Deploy Stage
dependsOn: Test
condition: eq(variables['Build.SourceBranchName'],'main')
jobs:
- job: DeployJob
displayName: Deploy Job
pool:
vmImage: 'windows - latest'
steps:
- script: echo 'Deployment steps will be added here'
- 在这个示例中,
Deploy
阶段的condition
设置为只有当Build.SourceBranchName
变量(表示当前构建的分支名称)等于main
时,才会执行Deploy
阶段。
流水线触发策略
- 分支触发
- 可以配置流水线在特定分支有代码变更时自动触发。在流水线的配置页面,选择“Triggers”选项卡。
- 勾选“Enable continuous integration”,然后在“Branch filters”中指定要监控的分支。例如,只监控
main
分支,当main
分支有新的代码提交时,流水线会自动启动运行。
- 时间触发
- 除了分支触发,还可以设置时间触发。在“Triggers”选项卡中,勾选“Enable scheduled triggers”。
- 可以设置流水线在每天的特定时间(如凌晨 2 点)运行,这样可以定期进行构建和测试,以确保项目的稳定性。例如,设置
cron
表达式为“0 2 * * *”,表示每天凌晨 2 点触发流水线。
处理常见问题与优化
构建失败问题排查
- 编译错误
- 如果构建任务失败,首先查看构建日志中的编译错误信息。常见的编译错误包括缺少引用、语法错误等。
- 例如,如果日志中提示“CS0246: The type or namespace name 'SomeNamespace' could not be found”,表示项目中缺少对
SomeNamespace
所在程序集的引用。需要检查项目的引用设置,通过 NuGet 安装相应的包或者手动添加引用。
- 依赖问题
- C# 项目可能依赖其他的 NuGet 包或本地程序集。如果依赖的包版本不兼容或者无法下载,也会导致构建失败。
- 可以在项目的 .csproj 文件中查看依赖的 NuGet 包,确保其版本是兼容的。如果是无法下载包的问题,检查网络连接以及 NuGet 源的配置。在流水线中,可以添加任务来清理 NuGet 缓存并重新安装包,例如:
steps:
- task: DotNetCoreCLI@2
displayName: 'DotNet Core Restore'
inputs:
command:'restore'
projects: 'MyConsoleApp.csproj'
测试失败问题排查
- 测试代码错误
- 当测试任务失败时,查看测试报告中的详细错误信息。可能是测试代码本身编写有误,例如断言条件不满足。
- 例如,在 NUnit 测试中,如果断言
Assert.AreEqual(5, calculator.Add(2, 3))
失败,而实际calculator.Add
方法的实现是正确的,那么可能是测试代码中其他地方影响了结果,需要仔细检查测试逻辑。
- 环境问题
- 测试可能依赖特定的环境配置,如数据库连接字符串、文件路径等。如果在测试运行环境中这些配置不正确,会导致测试失败。
- 可以在测试项目中通过配置文件(如
appsettings.json
)来管理环境相关的配置,在流水线中可以通过变量替换等方式来设置正确的配置值。例如,在测试项目的appsettings.json
中有如下配置:
{
"DatabaseConnection": "Server=localhost;Database=MyDb;User Id=myuser;Password=mypassword;"
}
- 在流水线中,可以通过设置环境变量来覆盖这个配置值,以适应不同的测试环境:
steps:
- task: PowerShell@2
displayName: 'Set Test Environment Variable'
inputs:
targetType: 'inline'
script: |
$env:DatabaseConnection = 'Server=testserver;Database=MyDb;User Id=testuser;Password=testpassword;'
流水线性能优化
- 缓存依赖项
- C# 项目的构建和测试通常依赖大量的 NuGet 包。在每次流水线运行时重新下载这些包会浪费时间。可以在流水线中配置缓存来存储已下载的包。
- 在 Azure Pipelines 的 YAML 文件中,可以使用
cache
关键字来配置缓存。例如:
steps:
- task: Cache@2
displayName: 'Cache NuGet packages'
inputs:
key: 'nuget | $(Agent.OS) | MyConsoleApp.csproj'
restoreKeys: 'nuget | $(Agent.OS)'
path: '$(Agent.TempDirectory)/nuget'
- task: DotNetCoreCLI@2
displayName: 'DotNet Core Restore'
inputs:
command:'restore'
projects: 'MyConsoleApp.csproj'
- 这样在后续的流水线运行中,如果缓存的包存在且项目的依赖没有变化,就可以直接从缓存中使用,加快构建速度。
- 优化任务并行执行
- 在多阶段流水线中,如果不同阶段或任务之间没有严格的依赖关系,可以配置它们并行执行,以提高整体的执行效率。
- 例如,在一个包含构建和测试的流水线中,如果测试可以在构建完成一部分后就开始执行,可以将测试任务配置为并行执行:
stages:
- stage: Build
displayName: Build Stage
jobs:
- job: BuildJob
displayName: Build Job
pool:
vmImage: 'windows - latest'
steps:
- task: DotNetCoreCLI@2
displayName: 'DotNet Core Build'
inputs:
command: 'build'
projects: 'MyConsoleApp.csproj'
- stage: Test
displayName: Test Stage
dependsOn: Build
jobs:
- job: TestJob
displayName: Test Job
pool:
vmImage: 'windows - latest'
strategy:
parallel: 2
steps:
- task: DotNetCoreCLI@2
displayName: 'DotNet Core Test'
inputs:
command: 'test'
projects: 'MyConsoleApp.Tests.csproj'
- 在这个示例中,
TestJob
的strategy
设置为parallel: 2
,表示可以并行执行两个测试任务,从而加快测试的整体执行时间。
通过以上对 C# 持续集成与 Azure DevOps 流水线配置的详细介绍,包括基础概念、实现步骤、高级配置以及常见问题处理和优化,开发团队可以有效地建立起一套高效的 C# 项目 CI/CD 流程,提高软件开发的质量和效率。