持续集成之 Nuget 进阶
持续集成之 Nuget 进阶
Intro
之前介绍了一篇基于 Azure pipeline 的 nuget 包的持续集成配置,但是比较粗糙,这里介绍一下结合 Cake 实现更优雅的 nuget 包发布流程。
实现目标:
分支(除master/preview)有代码 push 或者 pr 时 自动 build
preview 分支有代码 push 的时候将 build 并将发布 preview 版的 nuget 包
master 分支有代码 push 的时候将 build 并将发布稳定版的 nuget 包
什么是Cake?为什么要使用 Cake?
Cake 是C# Make的缩写,是一个基于C# DSL的自动化构建系统。它可以用来编译代码,复制文件以及文件夹,运行单元测试,压缩文件以及构建Nuget包等等。
熟悉大名鼎鼎的Make的小伙伴,应该已经知道Cake大致是个什么样的工具了,Cake具有以下几个特点:
方便编写:使用基于C#的DSL,非常易于编写自动化的脚本。
跨平台: 基于Roslyn和Mono来编译我们写的自动化脚本,使得它可以运行在windows,linux,mac上。
可靠的:可以建立在自己的机器上,也可以建立在像AppVeyor,TeamCity,TFS,VSTS或Jenkins这样的CI系统上,都可以以相同的方式运行。
丰富的工具集:支持MSBuild,MSTest,xUnit,NUnit,Nuget,ILMerge,Wix和SignTool等等,以及支持丰富的插件(Cake Addins)。
开源:基于MIT开放源代码(Cake on Github),并且是.NET 基金会支持的一个项目(Cake on dotnet foundation)。
最初做自动化发布的时候自己尝试去写 powershell 和 bash shell 脚本,但是写的多了一点会发现,很多语法不太一致,往往写一个功能要写一个 powershell 脚本 再写一个 bash shell 脚本,徒然增加自己的工作量,而且有时候会发生一些奇怪的问题,在Windows上的路径和Linux的路径有时候会不同,使用了 Cake,我们就只需要专注于脚本要执行的过程,不需要关注 powershell 和 bashshell 的不同,不需要太多关注于 windows 和 linux 的差异。
Cake 文档
Cake api
使用 Cake
Cake 有 Visual Studio Code 插件,可以基于 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/**
示例项目
这里以我的一个个人开源项目 WeihanLi.Redis 为例
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 变量,这里需要转换一下,不然,直接从环境变量读取是读取不到的,详细参考:https://docs.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch&viewFallbackFrom=vsts#secret-variables
通过以上脚本可以本文开篇提到的目标:
分支(除master/preview)有代码 push 或者 pr 时 自动 build
preview 分支有代码 push 的时候将 build 并将发布 preview 版的 nuget 包
master 分支有代码 push 的时候将 build 并将发布稳定版的 nuget 包
preview 和 master 分支可以设置 branch policy,设置只能由 pull request 合并,不能直接 push 代码,如果必须要先发布 preview 再发布稳定版 nuget 包,可以添加自定以限制,限制 master 分支的代码只能从 preview 分支通过 pr 合并
Reference
https://www.cnblogs.com/linianhui/p/cake-overview.html
https://www.cakebuild.net/
https://github.com/cake-build/cake
https://github.com/WeihanLi/WeihanLi.Redis
https://docs.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch&viewFallbackFrom=vsts#secret-variables
持续集成之 Nuget 进阶相关推荐
- 持续集成配置之Nuget
Intro 本文是基于微软的 VSTS(Visual Studio Team Service) 做实现公众类库的自动打包及发布. 之前自己的项目有通过 Github 上的 Travis 和 Appve ...
- .NET Core 从 Github到 Nuget 持续集成、部署
一.前言 Nuget 作为一个.NET研发人员,我想你都不会陌生,他为我们提供非常方便的程序包管理,不管是版本,还是包的依赖都能轻松应对,可以说是我们的好助手.而 Nuget 除了官方nuget.or ...
- 不可错过的「持续集成」进阶指南
随着软件部署的越来越成熟,敏捷.DevOps.CI/CD.Docker等词语慢慢出现在工程师的视野中.对于持续集成,业界也没有一个通用的模式,每个团队可能习惯的方式和关注点都不一样.持续集成最关键的在 ...
- nuget 构建自己的包_适用于企业的NuGet:持续集成自动构建系统中的NuGet
nuget 构建自己的包 I had the pleasure of speaking at TechEd 2011 North America last week in Atlanta. You c ...
- 如何使用Jenkins持续集成C#网站项目
2019独角兽企业重金招聘Python工程师标准>>> 上两节分别讲了如何从vss迁移C#网站项目到gitlab和如何使用nuget管理C#网站项目,其实都是为了最后一节的内容做铺垫 ...
- Jenkins持续集成学习-Windows环境进行.Net开发4
目录 Jenkins持续集成学习-Windows环境进行.Net开发4 目录 前言 目标 Github持续集成 提交代码到Github 从Github更新代码 git上显示构建状态 自动触发构建 Gi ...
- 持续集成工具 Jetbrains TeamCity 简介
目录 安装 Windows下安装 Docker下安装 使用TeamCity 初始化 配置数据库 新建项目 设置构建步骤 构建项目 测试项目 自动构建 邮件通知 大名鼎鼎的Intellij IDEA大家 ...
- 以持续集成工具实现DevOps之禅
作为DevOps流程中的一个重要组成部分,持续集成(CI)的目标是对开发团队的代码进行集成,包括代码的构建.单元测试与集成测试的执行,以及生成执行结果的报表等等.CI使开发团队无需将时间浪费在处理代码 ...
- fir.im 持续集成技术实践
互联网时代,人人都在追求产品的快速响应.快速迭代和快速验证.不论是创业团队还是大中型企业,都在探索属于自己的敏捷开发.持续交付之道.fir.im 团队也在全面实施敏捷,并推出新持续集成服务 - flo ...
最新文章
- C#单例模式的懒汉与饿汉
- html用bmob做留言,bmob js-sdk 在vue中的使用教程
- MFC 缩放和显示IplImage
- lua 给userdata设置元表_lua学习之复习汇总篇
- git 查看远程仓库地址
- upload file to server
- nodejs实践录:开篇
- ajax 实时进度_【乐建工程宝】如何把控施工项目进度
- CSS大美集(关于细节)
- Swift - 动画效果的实现方法总结(附样例)
- 软件测试如何制作简历?
- Promise、then()、catch()详解
- 操作系统磁盘调度算法相关习题
- TMO (time-triggered message-triggered object)
- 如何在网页里插入3D模型并进行互动展示?
- python变量的声明和赋值
- VM安装的虚拟机如何在局域网内互相访问
- 如何判断两个QQ好友之间是否为好友关系
- 替代Xshell的良心国产SSH工具软件
- 夺命雷公狗—玩转SEO---44---外链群发原理
热门文章
- 多年经验的程序员迷失了自己,该怎么办?
- c语言 程序延时 校准,c语言实现系统时间校正工具代码分享
- vcenter 6.7 (vcsa)部署指南
- 算法:前K个最大的元素
- 219. 单页应用 会话管理(session、cookie、jwt)
- Linux文件系统之df
- Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析(2)...
- 基本线程同步(三)在同步的类里安排独立属性
- [Todo] 乐观悲观锁,自旋互斥锁等等
- SQL 结合CASE WHEN 实现二维统计