博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
持续集成之 Nuget 进阶
阅读量:4563 次
发布时间:2019-06-08

本文共 5464 字,大约阅读时间需要 18 分钟。

持续集成之 Nuget 进阶

Intro

之前介绍了一篇基于 Azure pipeline 的 nuget 包的持续集成配置,但是比较粗糙,这里介绍一下结合 Cake 实现更优雅的 nuget 包发布流程。

实现目标:

  1. 分支(除master/preview)有代码 push 或者 pr 时 自动 build
  2. preview 分支有代码 push 的时候将 build 并将发布 preview 版的 nuget 包
  3. master 分支有代码 push 的时候将 build 并将发布稳定版的 nuget 包

什么是Cake?为什么要使用 Cake?

Cake 是的缩写,是一个基于C# DSL的自动化构建系统。它可以用来编译代码,复制文件以及文件夹,运行单元测试,压缩文件以及构建Nuget包等等。

熟悉大名鼎鼎的的小伙伴,应该已经知道Cake大致是个什么样的工具了,Cake具有以下几个特点:

  1. 方便编写:使用基于C#的DSL,非常易于编写自动化的脚本。
  2. 跨平台: 基于Roslyn和Mono来编译我们写的自动化脚本,使得它可以运行在windows,linux,mac上。
  3. 可靠的:可以建立在自己的机器上,也可以建立在像AppVeyor,TeamCity,TFS,VSTS或Jenkins这样的CI系统上,都可以以相同的方式运行。
  4. 丰富的工具集:支持MSBuild,MSTest,xUnit,NUnit,Nuget,ILMerge,Wix和SignTool等等,以及支持丰富的插件()。
  5. 开源:基于MIT开放源代码(),并且是.NET 基金会支持的一个项目()。

最初做自动化发布的时候自己尝试去写 powershell 和 bash shell 脚本,但是写的多了一点会发现,很多语法不太一致,往往写一个功能要写一个 powershell 脚本 再写一个 bash shell 脚本,徒然增加自己的工作量,而且有时候会发生一些奇怪的问题,在Windows上的路径和Linux的路径有时候会不同,使用了 Cake,我们就只需要专注于脚本要执行的过程,不需要关注 powershell 和 bashshell 的不同,不需要太多关注于 windows 和 linux 的差异。

使用 Cake

Cake 有 ,可以基于 VSCode 来编辑 cake 脚本

Cake 脚本示例

cake 主要文件:

  • build.ps1/build.sh 启动脚本,build.ps1 为 Windows 系统上要执行的 powershell 脚本,build.sh 为 *nix 上要执行的 shell 脚本
  • build.cake 实际执行的脚本,定义各种 build 需要的 task
  • tools/packages.config 启动脚本需要的 nuget 包

添加 cake 支持之后,你可能需要修改 .gitignore,官方推荐的 gitignore 是这样的

tools/**!tools/package.config

实际使用下来,即使没有 package.config 也是可以正常工作的,可以简化为一条

tools/**

示例项目

这里以我的一个个人开源项目 为例

cake 脚本

///// ARGUMENTS///var target = Argument("target", "Default");var configuration = Argument("configuration", "Release");var solutionPath = "./WeihanLi.Redis.sln";var srcProjects  = GetFiles("./src/**/*.csproj");var testProjects  = GetFiles("./test/**/*.csproj");var artifacts = "./artifacts/packages";var isWindowsAgent = (EnvironmentVariable("Agent_OS") ?? "Windows_NT") == "Windows_NT";var branchName = EnvironmentVariable("BUILD_SOURCEBRANCHNAME") ?? "local";///// SETUP / TEARDOWN///Setup(ctx =>{   // Executed BEFORE the first task.   Information("Running tasks...");   PrintBuildInfo();});Teardown(ctx =>{   // Executed AFTER the last task.   Information("Finished running tasks.");});///// TASKS///Task("clean")    .Description("Clean")    .Does(() =>    {       var deleteSetting = new DeleteDirectorySettings()       {          Force = true,          Recursive = true       };      if (DirectoryExists(artifacts))      {         DeleteDirectory(artifacts, deleteSetting);      }    });Task("restore")    .Description("Restore")    .Does(() =>     {      foreach(var project in srcProjects)      {         DotNetCoreRestore(project.FullPath);      }    });Task("build")        .Description("Build")    .IsDependentOn("clean")    .IsDependentOn("restore")    .Does(() =>    {      var buildSetting = new DotNetCoreBuildSettings{         NoRestore = true,         Configuration = configuration      };      foreach(var project in srcProjects)      {         DotNetCoreBuild(project.FullPath, buildSetting);      }    });Task("test")        .Description("Test")    .IsDependentOn("build")    .Does(() =>    {      var testSettings = new DotNetCoreTestSettings{         NoRestore = true,         Configuration = configuration      };      foreach(var project in testProjects)      {         DotNetCoreTest(project.FullPath, testSettings);      }    });Task("pack")    .Description("Pack package")    .IsDependentOn("test")    .Does(() =>    {      var settings = new DotNetCorePackSettings      {         Configuration = configuration,         OutputDirectory = artifacts,         VersionSuffix = "",         NoRestore = true,         NoBuild = true      };      if(branchName != "master"){         settings.VersionSuffix = $"preview-{DateTime.UtcNow:yyyyMMdd-HHmmss}";      }      foreach (var project in srcProjects)      {         DotNetCorePack(project.FullPath, settings);      }      PublishArtifacts();    });bool PublishArtifacts(){   if(!isWindowsAgent)   {      return false;   }   if(branchName == "master" || branchName == "preview")   {      var pushSetting =new DotNetCoreNuGetPushSettings      {         Source = EnvironmentVariable("Nuget__SourceUrl") ?? "https://api.nuget.org/v3/index.json",         ApiKey = EnvironmentVariable("Nuget__ApiKey")      };      var packages = GetFiles($"{artifacts}/*.nupkg");      foreach(var package in packages)      {         DotNetCoreNuGetPush(package.FullPath, pushSetting);      }      return true;   }   return false;}void PrintBuildInfo(){   Information($@"branch:{branchName}, agentOs={EnvironmentVariable("Agent_OS")}   BuildID:{EnvironmentVariable("BUILD_BUILDID")},BuildNumber:{EnvironmentVariable("BUILD_BUILDNUMBER")},BuildReason:{EnvironmentVariable("BUILD_REASON")}   ");}Task("Default")    .IsDependentOn("pack");RunTarget(target);

我这里使用 Azure pipeline 来实现持续集成,上面的里面有一些Azure pipeline 的变量,实际执行 build.ps1 脚本

Azure pipeline config

trigger:- '*'pool:  vmImage: 'vs2017-win2016'steps:- script: dotnet --info  displayName: 'dotnet info'- powershell: ./build.ps1  displayName: 'Powershell Script'  env:    Nuget__ApiKey: $(nugetApiKey)    Nuget__SourceUrl: $(nugetSourceUrl)

nugetApiKey 是比较敏感的信息,在 Azure Pipeline 里的 Variables 的 Secret 变量,这里需要转换一下,不然,直接从环境变量读取是读取不到的,详细参考:

通过以上脚本可以本文开篇提到的目标:

  1. 分支(除master/preview)有代码 push 或者 pr 时 自动 build
  2. preview 分支有代码 push 的时候将 build 并将发布 preview 版的 nuget 包
  3. master 分支有代码 push 的时候将 build 并将发布稳定版的 nuget 包

preview 和 master 分支可以设置 branch policy,设置只能由 pull request 合并,不能直接 push 代码,如果必须要先发布 preview 再发布稳定版 nuget 包,可以添加自定以限制,限制 master 分支的代码只能从 preview 分支通过 pr 合并

Reference

转载于:https://www.cnblogs.com/weihanli/p/advanced-nuget-ci.html

你可能感兴趣的文章
斗地主算法的设计与实现(四)--对牌进行排序
查看>>
How to get web browser history using cursor
查看>>
软键盘覆盖EditText解决方法
查看>>
Daily Scrumming* 2015.11.1(Day 13)
查看>>
css不定高图文垂直居中的三种方法
查看>>
剑指offer--1.二维数组中的查找
查看>>
第3次作业:团队介绍
查看>>
[html][javascript]父子窗体传值
查看>>
收房细则
查看>>
读《Android深度探索(卷1)HAL与驱动开发》的一些思考10
查看>>
二十三、uevnet机制和U盘自动挂载
查看>>
Kettle 提取mongodb最大编号
查看>>
Vue2.0-token权限处理
查看>>
Caffeine缓存
查看>>
JavaScript 回车键转成Tab键
查看>>
CentOS7配置MySQL5.7主备
查看>>
合并区间(LintCode)
查看>>
mysql索引的创建
查看>>
《 BCG 原创 :系列 三》 添加菜单栏
查看>>
Java中关于CyclicBarrier的使用
查看>>