Foreword
Hello everyone, I recently automated packaging NuGet thinking about how to submit code to the time and then publish AzureDevOps
in Artifacts
, stepped on a lot of pits in the process, but also take a lot of detours, so this article is to explore the results of my and I some of the problems encountered in finishing to others.
My last article on CI/CD
the article "Using Gitlab CI / CD automate publishing site to IIS" is using scripts implemented in the form, then there is garden Friends in the following comments can be used Cake (C # Make) the tools to achieve which function, so this would not have a script. Cake will have time to use it to transform.
the whole idea:
Under first introduced
Cake
,AzureDevops Pipelines/Artifacts
how to useThen configure AzureDevops Pipelines
Creating AzureDevops Artifacts (NuGet server)
AzureDevops configuration PAT (Personal Access Tokens) and the desired Pipelines Variables (variable)
Cake packing increases, pushing NuGet package code.
View the final run results
Use the tools and version:
dotnet core 2.2
cake 0.33.0
PowerShell, NuGet, CredentialProvider
AzureDevops Pipelines 和 AzureDevops Artifacts
Introduction
Cake stands
C# Make
, it is an automated cross-platform build system, based on C # DSL, so you can use familiar C # language to replace in our previous use of scripts built. We can use it very easily compiled code, copy files and folders, and of course you can also run the unit tests in order to make sure that our code is no problem, we have this time to release NuGet Artifacts it accounts for a very important position.- AzureDevops, formerly known as VSTS, it provides Repos, Pipelines, Boards, Test Plans, Artifacts:
- Repos provide Git repository for source code control code, you can introduce your storage directly on GitHub.
- Pipelines provide build and publish services to support the application of continuous integration and delivery (CI / CD)
- Boards Agile provides a set of tools that support the use of Kanban and Scrum approach to planning and tracking, code defect management, etc., there is a similar tool Tencent TAPD , Ali cloud effect , Huawei cloud DevCloud and so on.
- Test Plans provides tools for a variety of test applications, including manual / exploratory testing and continuous testing
- Artifacts allow teams from public and private sources to share Maven, npm and NuGet package.
Collaboration tools, including team dashboards and customizable widget configurable to share information, progress and trends; wiki built for sharing information; configurable notification.
CredentialProvider is Credential Provider, when we NuGet Push requires authentication, simply put it down to NuGet program.
Cake installation and use
The case I have posted on the GitHub: https://github.com/WuMortal/CakePushNuGet.Example
Installation: Here I am using dotnet core presentation, cake also supports the .NET Framework, Mono. First we need to install the cake, dotnet tool by means of this command.
dotnet tool install --global cake.tool --version 0.33.0
Successful installation will appear the following prompt:
The cake is very simple to use, but also C # syntax believe it should be very easy to understand.
Here first we define a target variable, which saved it is the name of Task (task) we are going to perform. Then you can see in the definition of a number of Task code block, the "mission" there is a specific need to be performed, the first task is dependent reduction projects, in fact, the core code line DotNetCoreRestore(solution);
, the second task is to build the project, you need to Note that the third task is actually to integrate the first two tasks together. You can also the second mission .IsDependentOn ("Restore")
first task calls, of course, var target = Argument ("target", "Demo");
need to be changed var target = Argument ("target", "Build");
, and this depends on personal preference.
var rootPath = "../"; //根目录
var srcPath = rootPath + "src/";
var solution = srcPath + "Wigor.CakePushNuGet.Example.sln"; //解决方案文件
//需要执行的目标任务
var target = Argument ("target", "Demo");
Task ("Restore")
.Description ("还原项目依赖")
.Does (() => {
//Restore
Information ("开始执行还原项目依赖任务");
DotNetCoreRestore (solution);
});
Task ("Build")
.Description ("编译项目")
.Does (() => {
Information ("开始执行编译生成项目任务");
//Build
DotNetCoreBuild (solution, new DotNetCoreBuildSettings {
NoRestore = true, //不执行还原,上一步已经还原过了
Configuration = "Release"
});
});
// 执行的任务
Task ("Demo")
.IsDependentOn ("Restore") //1. 执行上面的 Restore 任务
.IsDependentOn ("Build") //2. 需要执行 上面的 Build 任务
.Does (() => {
Information ("所有任务完成");
});
//运行目标任务 Demo
RunTarget (target);
After writing a good cake we can try to run it, here is my cake path build / build.cake we can change depending on the circumstances, the command is as follows:
dotnet cake build/build.cake -verbosity=diagnostic
Here you can see the cake has run successfully, and the results and information which we will run each task is displayed on the console.
Here I believe we are doing a little bit of cake to understand it, about it with multi-use ways to access the official website: https://cakebuild.net/
AzureDevops Pipelines 使用
首先你需要一个 Microsoft 账号或者 GitHub 账号,登录地址为:https://dev.azure.com,登录之后你需要创建一个项目,这里我已经创建好一个项目了,首先我们点击 Pipelines 选择 Builds,之后会出现如下界面,点击 New Pipeline。然后跟着我下面图片的步骤一步一步来就行。
如果你的仓储就在 AzureDevops上那么直接选 Azure Repos Git 就行。
这里你的账号是 GitHub 授权登录的话会先跳转到授权界面可能会跳转多次,同意即可。
删除我选中的代码,因为我不打算用 AzureDevops Pipelines 的脚本来执行本次操作,它做的只是提供我们 cake 运行的环境。
更换为如下脚本,PowerShell.exe -file ./cake.ps1
是指使用 PowerShell 运行我们的 cake.ps1 文件,关于 cake.ps1 文件后面会介绍,这里我们先这样写,接着点击 Save and run
。
trigger:
- master
pool:
vmImage: 'windows-latest'
steps:
- script: PowerShell.exe -file ./cake.ps1
displayName: 'Push NuGet Package'
可以看到问们管道的运行出现了错误,那是因为我们上面在运行了 cake.ps1 这个脚本,但是我们现在还没有创建这个脚本。
回到我们的项目中,将 AzureDevops Pipelines 创建的 azure-pipelines.yml
文件 pull 到我们本地。
接着我们编写我们下面缺少的 cake.ps1 文件,它做的事情就是将我们之前手动在 cmd 中运行的命令放入了一个 PowerShell 脚本文件中,Linux 平台的话就编写一个 shell 脚本。
# Install cake.tool
dotnet tool install --global cake.tool --version 0.33.0
# 输出将要执行的命命令
Write-Host "dotnet cake build\build.cake -verbosity=diagnostic" -ForegroundColor GREEN
dotnet cake build\build.cake -verbosity=diagnostic
尝试项目根目录下运行这个脚本,在 cmd 中执行 powershell .\cake.ps1
,下面报了一个错。
我们只需要以管理员身份运行 PowerShell 然后执行 set-ExecutionPolicy RemoteSigned
即可
然后再次运行 powershell .\cake.ps1
或者命令,可以看到正确的输出了
OK,这次我们推送(git push)下代码,在到 AzureDevops Pipelines 看看我们执行结果。
点进去可以看到整个执行的过程,如果报错了也可以从这里看到出错的信息
如果是 powershell 报错 AzureDevops Pipelines 是不会显示执行失败的,如果没得到你想要的结果你就需要点开认真的分析你的脚本了。
AzureDevops Artifacts 使用
前面已经讲过了如果使用 cake 和 在 AzureDevops Pipelines 下执行 cake。下面我们需要创建一个 NuGet Repository,这里我使用 AzureDevops 提供的 Artifacts。
这里面会用的就是 package source URL
和下面命令中的 -ApiKey 中的 AzureDevOps
,还有这里我们需要将 NuGet + Credentials Provider
下载到我们的本地,如果你的运行环境是 Linux 或其他可以在 microsoft/artifacts-credprovider
的 GitHub 上获取对应平台的这两个包, 点击查看 GitHub 地址。
创建 PAT (Personal Access Tokens)
上面说过了我们推送 NuGet 包到 Artifacts 时候是需要为两个参数提供指的的 -UserName 和 -Password,这里的 UserName 我们可以随意填,但是 Password 填的的是我们创建的 PAT。
这是选择我们 PAT 所拥有的权限,需要点击 Show all scopes
找到 Packaging
勾选 Red,wirte,& manage
。
我们可以看到我们的 PAT ,需要注意的是这个 token 只会出现一次,你需要将它保存好,如果忘记了,那么可以点击 Regenerate
重新获取 token。
AzureDevops Pipelines 添加变量
在 上一篇文章 中我说过了为什么需要变量,这里就不重复了,有兴趣的可以看看。下面开始添加我们需要的变量。
我们需要添加的变量有四个,分别是 NUGET_REPOSITORY_API_URL
、NUGET_REPOSITORY_API_KEY
、USERNAME
、PASSWORD
。
NUGET_REPOSITORY_API_URL:就是我们在创建 AzureDevops Artifacts 后出现的
package source URL
。NUGET_REPOSITORY_API_KEY:就是那个 -ApiKey 参数的值
AzureDevOps
。USERNAME:这个上面说过了可以随便填。
PASSWORD:这个就是之前创建的 PAT。
点击保存(Save & queue)或者 Ctrl + s 保存。
添加 NuGet.Tool.cake 和 NuGet.exe、Credentials Provider
这里为已经封装过了的工具类包含了打包和推送方法,地址:NuGet.Tool.cake
using System;
using System.Collections.Generic;
using System.Linq;
using Cake.Common.Tools.DotNetCore;
using Cake.Common.Tools.DotNetCore.Pack;
using Cake.Common.Tools.NuGet;
using Cake.Common.Tools.NuGet.List;
using Cake.Core;
using NuGet.Packaging;
public class NuGetTool {
public ICakeContext CakeContext { get; }
public string RepositoryApiUrl { get; }
public string RepositoryApiKey { get; }
public string UserName { get; set; }
public string Password { get; set; }
private NuGetListSettings ListSettings => new NuGetListSettings {
AllVersions = true,
Source = new string[] { this.RepositoryApiUrl }
};
private DotNetCorePackSettings BuildPackSettings (string packOutputDirectory) => new DotNetCorePackSettings {
Configuration = "Release",
OutputDirectory = packOutputDirectory,
IncludeSource = true,
IncludeSymbols = true,
NoBuild = false
};
private NuGetTool (ICakeContext cakeContext) {
CakeContext = cakeContext;
RepositoryApiUrl = cakeContext.Environment.GetEnvironmentVariable ("NUGET_REPOSITORY_API_URL");
RepositoryApiKey = cakeContext.Environment.GetEnvironmentVariable ("NUGET_REPOSITORY_API_KEY");
UserName = cakeContext.Environment.GetEnvironmentVariable ("USERNAME");
Password = cakeContext.Environment.GetEnvironmentVariable ("PASSWORD");
CakeContext.Information ($"获取所需参数成功:{RepositoryApiUrl}");
}
public static NuGetTool FromCakeContext (ICakeContext cakeContext) {
return new NuGetTool (cakeContext);
}
public void Pack (List<string> projectFilePaths, string packOutputDirectory) {
projectFilePaths.ForEach (_ => CakeContext.DotNetCorePack (_, BuildPackSettings (packOutputDirectory)));
}
public void Push (List<string> packageFilePaths) {
foreach (var packageFilePath in packageFilePaths) {
CakeContext.NuGetAddSource (
"wigor",
this.RepositoryApiUrl,
new NuGetSourcesSettings {
UserName = this.UserName,
Password = this.Password
});
CakeContext.NuGetPush (packageFilePath, new NuGetPushSettings {
Source = "wigor",
ApiKey = this.RepositoryApiKey
});
}
}
}
在项目的 build/ 下创建 nuget.tool.cake
文件(build/nuget.tool.cake) 拷贝上面的代码。
这里参考了最开始提到的园友的项目,非常感谢它的贡献,GitHub 地址如下:cake.example
在创建 AzureDevops Artifacts
的时候那不是提供了 NuGet + Credentials Provider
的下载地址嘛,现在把它解压到我们项目的 build\tool\
下。再次说明这里我是 Windows 环境,如果你的运行环境是 Linux 或其他可以在 microsoft/artifacts-credprovider
的 GitHub 上获取对应平台的这两个包, 点击查看 GitHub 地址。
修改 cake.ps1 和 build.cake 文件
修改 cake.ps1,只是增加了 NuGet.exe 的环境变量,因为不加到时候 cake 会找不到 NuGet.exe,或许还有其他办法这里就先这么干,如果各位还有更方便的方法可以在下面留言,感谢!
# 执行的文件
[string]$SCRIPT = 'build/build.cake'
[string]$CAKE_VERSION = '0.33.0'
# 配置 NuGet 环境变量
$NUGET_EXE = "build/tool/NuGet.exe"
$NUGET_DIRECTORY = Get-ChildItem -Path $NUGET_EXE
$NUGET_DIRECTORY_NAME=$NUGET_DIRECTORY.DirectoryName
$ENV:Path += ";$NUGET_DIRECTORY_NAME"
# Install cake.tool
dotnet tool install --global cake.tool --version $CAKE_VERSION
# 参数:显需要执行cake 执行信息
[string]$CAKE_ARGS = "-verbosity=diagnostic"
# 输出将要执行的命命令
Write-Host "dotnet cake $SCRIPT $CAKE_ARGS $ARGS" -ForegroundColor GREEN
dotnet cake $SCRIPT $CAKE_ARGS $ARGS
修改 build.cake 文件,看着是多了很多东西其实就多了两个 Task (任务) 分别是: pack(打包)
和 push(推送包)
,这里需要大家需要修改的就是 solution
和 project
两个变量,将其修改为自己的解决方案名称和需要打包的项目名称。
#reference "NuGet.Packaging"
#load nuget.tool.cake
var target = Argument ("target", "PushPack");
var rootPath = "../";
var srcPath = rootPath + "src/";
var solution = srcPath + "Wigor.CakePushNuGet.Example.sln";
var project = GetFiles (srcPath + "Wigor.CakePushNuGet.HelloWorld/*.csproj");
var nugetPakcageDirectory = $"{srcPath}nugetPackage/";
var nugetTool = NuGetTool.FromCakeContext (Context);
Task ("Restore")
.Description ("还原项目依赖")
.Does (() => {
//Restore
Information ("开始执行还原项目依赖任务");
DotNetCoreRestore (solution);
});
Task ("Build")
.Description ("编译项目")
.Does (() => {
Information ("开始执行编译生成项目任务");
//Build
DotNetCoreBuild (solution, new DotNetCoreBuildSettings {
NoRestore = true,
Configuration = "Release"
});
});
Task ("UnitTest")
.Description ("单元测试")
.Does (() => {
Information ("开始执行单元测试任务");
DotNetCoreTest(solution);
});
Task ("Pack")
.Description ("Nuget 打包")
.Does (() => {
Information ("开始执行打包任务");
// 确保目录存在
EnsureDirectoryExists (nugetPakcageDirectory);
var packageFilePaths = project.Select (_ => _.FullPath).ToList ();
nugetTool.Pack (packageFilePaths, nugetPakcageDirectory);
});
Task ("Push")
.Description ("Nuget 发布")
.Does (() => {
Information ("开始执行 Nuget 包发布任务");
var packageFilePaths = GetFiles ($"{nugetPakcageDirectory}*.symbols.nupkg").Select (_ => _.FullPath).ToList ();
nugetTool.Push(packageFilePaths);
});
Task ("PushPack")
.Description ("发布 Nuget 包")
.IsDependentOn ("Restore")
.IsDependentOn ("Build")
.IsDependentOn ("Pack")
.IsDependentOn ("Push")
.Does (() => {
Information ("所有任务完成");
});
RunTarget (target);
最后我们推送修改后的代码,查看执行结果看看 NuGet 包是否发布到 AzureDevops Artifacts
上。
至此已经实现了 使用 Cake 推送 NuGet 包到 AzureDevops 的 Artifacts 上,你如果不熟悉 AzureDevops Pipelines
你也可以用其他的 CI/CD 工具来执行。
补充
在整个尝试过程中肯定会出现一些问题,不要着急认真分析,看看 AzureDevops Pipelines
上给出的提示,也可以现在本机跑一下看看是否正常。出现问题第一步查看错误信息,看看有没有错误信息(基本都有),然后根据错误信息去分析是我们的那个地方出错了,顺序是 cake.ps1 --> build.cake --> nuget.tool.cake,然后是所需的 PAT 的权限是否勾选,AzureDevops Pipelines
变量是否配置并且是 URL、Key 什么的都是正确,再然后就是 百度、Google。最后你可以在评论区留言(分享你碰到的问题以及解决方法)。
相关文献
在这里感谢各位的贡献!
《Pushing Packages From Azure Pipelines To Azure Artifacts Using Cake》
AzureDevops Pipelines variables related documents
"Meet Azure DevOps"
reference project: CakePushNuGet.Example