如何解决如何下载没有依赖项的nuget包? 用法完整的 build.cake 示例nugetinstall.cakenugetmodel.cake
我使用下面的代码在 CI 过程中安装 nuget 包,
Cake.Common.Tools.NuGet.NuGetAliases.NuGetInstall(context,packageid,new NuGetInstallSettings { NoCache = true,OutputDirectory = "../Packages",});
问题:
如何下载没有依赖的nuget包?
解决方法
底层工具 NuGet.exe 目前不支持只下载包,有一个 GitHub 问题在跟踪这个 https://github.com/NuGet/Home/issues/5919
也就是说,您可以在 Cake 脚本中使用其他 Cake 别名或仅使用简单的 C# 来实现它。
一个定制的例子是 Cake 自己的网站,它直接从 NuGet.org 下载插件,只获取所需的 dll 和 xmldoc 文件,可以在以下位置找到: https://github.com/cake-build/website/blob/9a7bf2fbf8b485488517175376cf11baa3817098/nuget.cake#L33
如果您对定制的 NuGetInstall 等效项感兴趣,我可以用它更新此答案。
更新添加了“DownloadLatestPackage”示例
Cake NuGet 包下载示例
这是一个如何在 Cake 脚本中从 V3 NuGet 源下载 NuGet 包的示例。
用法
#load "nugetinstall.cake"
await context.DownloadLatestPackage(
"PackageId","../Packages"
);
完整的 build.cake 示例
#load "nugetinstall.cake"
Task("DownloadPackages")
.Does(
async context => {
foreach(var packageId in new [] { "Cake.Core","Cake.Common","Cake.Git" })
{
await context.DownloadLatestPackage(
packageId,"../Packages"
);
}
}
);
Task("Default")
.IsDependentOn("DownloadPackages");
RunTarget(Argument("target","Default"));
会输出类似的东西
========================================
DownloadPackages
========================================
Downloading package ../Packages/Cake.Core.1.0.0-rc0002...
Downloading package ../Packages/Cake.Common.1.0.0-rc0002...
Downloading package ../Packages/Cake.Git.0.22.0...
========================================
Default
========================================
Task Duration
--------------------------------------------------
DownloadPackages 00:00:03.3939241
--------------------------------------------------
Total: 00:00:03.3946443
导致包文件夹看起来像下面
Packages
+---Cake.Common.1.0.0-rc0002
|
+---Cake.Core.1.0.0-rc0002
|
\---Cake.Git.0.22.0
助手蛋糕脚本内容
nugetinstall.cake
#addin nuget:?package=System.Text.Json&version=4.6.0&loaddependencies=true
#load "nugetmodel.cake"
using System.Net.Http;
using System.Text.Json;
public static async Task<T> GetAsync<T>(this HttpClient client,string uri)
{
using (var stream = await client.GetStreamAsync(uri))
{
return await JsonSerializer.DeserializeAsync<T>(
stream,new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
);
}
}
public static async Task<bool> DownloadLatestPackage(this ICakeContext context,string packageId,DirectoryPath outputDirectory,string nuGetSource = "https://api.nuget.org/v3/index.json")
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (string.IsNullOrWhiteSpace(packageId))
{
throw new ArgumentNullException(nameof(packageId));
}
if (string.IsNullOrWhiteSpace(nuGetSource))
{
throw new ArgumentNullException(nameof(nuGetSource));
}
if (outputDirectory == null)
{
throw new ArgumentNullException(nameof(outputDirectory));
}
if (!context.DirectoryExists(outputDirectory))
{
throw new DirectoryNotFoundException($"{nameof(outputDirectory)} ({outputDirectory}) not found.");
}
using(var client = new HttpClient())
{
client.DefaultRequestHeaders.UserAgent.ParseAdd($"Cake NuGet Client/{context.Environment.Runtime.CakeVersion.ToString(3)}");
var nuGetIndex = await client.GetAsync<NuGetIndex>(nuGetSource);
var cakeBaseUrl = string.Concat(
nuGetIndex
?.Resources
?.Where(type => type.Type?.Length == 20
&& type.Type == "RegistrationsBaseUrl"
&& type.Id?.Length > 8 == true
&& type.Id.StartsWith("https://"))
.Select(url => url.Id)
.FirstOrDefault()
?? throw new Exception($"Failed to fetch RegistrationsBaseUrl from {nuGetSource}."),$"{packageId.ToLowerInvariant()}/index.json"
);
var cakeNuGetIndex = await client.GetAsync<NuGetContainer<NuGetContainer<NuGetPackageEntry>>>(cakeBaseUrl);
var packageEntry = (
from item in cakeNuGetIndex.Items
from version in item.Items
orderby SemVersion.TryParse(
version.CatalogEntry.Version,out var semVersion
)
? semVersion
: SemVersion.Zero
descending
select version.CatalogEntry
).FirstOrDefault();
if (string.IsNullOrWhiteSpace(packageEntry?.PackageContent))
{
throw new Exception($"Failed to found package uri for {packageId} on source {nuGetSource}");
}
var packageDirectory = outputDirectory.Combine($"{packageEntry.PackageId}.{packageEntry.Version}");
if(context.DirectoryExists(packageDirectory))
{
context.Information("Package {0} already downloaded.",packageDirectory);
return true;
}
context.Information("Downloading package {0}...",packageDirectory);
using (var stream = await client.GetStreamAsync(packageEntry?.PackageContent))
{
using (var zipStream = new System.IO.Compression.ZipArchive(stream))
{
foreach (var entry in zipStream.Entries)
{
var entryPath = packageDirectory.CombineWithFilePath(entry.FullName);
var directory = entryPath.GetDirectory();
context.EnsureDirectoryExists(directory);
using (System.IO.Stream source = entry.Open(),target = context.FileSystem.GetFile(entryPath).OpenWrite())
{
source.CopyTo(target);
}
}
}
}
return context.DirectoryExists(packageDirectory);
}
}
nugetmodel.cake
#addin nuget:?package=System.Text.Json&version=4.6.0&loaddependencies=true
using System;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
public class NuGetIndex
{
[JsonPropertyName("version")]
public string Version { get; set; }
[JsonPropertyName("resources")]
public NuGetResource[] Resources { get; set; }
}
public class NuGetResource
{
[JsonPropertyName("@id")]
public string Id { get; set; }
[JsonPropertyName("@type")]
public string Type { get; set; }
}
public class NuGetCommit
{
[JsonPropertyName("@id")]
public string Id { get; set; }
[JsonPropertyName("commitId")]
public Guid CommitId { get; set; }
[JsonPropertyName("commitTimeStamp")]
public DateTimeOffset CommitTimeStamp { get; set; }
}
public class NuGetContainer<T> : NuGetCommit
{
[JsonPropertyName("count")]
public int Count { get; set; }
[JsonPropertyName("items")]
public T[] Items { get; set; }
}
public class NuGetPackageEntry: NuGetCommit
{
[JsonPropertyName("catalogEntry")]
public NuGetCatalogEntry CatalogEntry { get; set; }
}
public class NuGetCatalogEntry: NuGetResource
{
[JsonPropertyName("version")]
public string Version { get; set; }
[JsonPropertyName("packageContent")]
public string PackageContent { get; set; }
[JsonPropertyName("id")]
public string PackageId { get; set; }
}
public struct SemVersion : IComparable,IComparable<SemVersion>,IEquatable<SemVersion>
{
public static SemVersion Zero { get; } = new SemVersion(0,null,"0.0.0");
static readonly Regex SemVerRegex =
new Regex (
@"(?<Major>0|(?:[1-9]\d*))(?:\.(?<Minor>0|(?:[1-9]\d*))(?:\.(?<Patch>0|(?:[1-9]\d*)))?(?:\-(?<PreRelease>[0-9A-Z\.-]+))?(?:\+(?<Meta>[0-9A-Z\.-]+))?)?",RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase
);
public int Major { get; }
public int Minor { get; }
public int Patch { get; }
public string PreRelease { get; }
public string Meta { get; }
public bool IsPreRelease { get; }
public bool HasMeta { get; }
public string VersionString { get; }
public SemVersion (int major,int minor,int patch,string preRelease = null,string meta = null) :
this (major,minor,patch,preRelease,meta,null)
{
}
private SemVersion (int major,string preRelease,string meta,string versionString)
{
Major = major;
Minor = minor;
Patch = patch;
IsPreRelease = !string.IsNullOrEmpty (preRelease);
HasMeta = !string.IsNullOrEmpty (meta);
PreRelease = IsPreRelease ? preRelease : null;
Meta = HasMeta ? meta : null;
if (!string.IsNullOrEmpty (versionString)) {
VersionString = versionString;
} else {
var sb = new StringBuilder ();
sb.AppendFormat (CultureInfo.InvariantCulture,"{0}.{1}.{2}",Major,Minor,Patch);
if (IsPreRelease) {
sb.AppendFormat (CultureInfo.InvariantCulture,"-{0}",PreRelease);
}
if (HasMeta) {
sb.AppendFormat (CultureInfo.InvariantCulture,"+{0}",Meta);
}
VersionString = sb.ToString ();
}
}
public static bool TryParse (string version,out SemVersion semVersion)
{
semVersion = Zero;
if (string.IsNullOrEmpty(version)) {
return false;
}
var match = SemVerRegex.Match (version);
if (!match.Success) {
return false;
}
if (!int.TryParse (
match.Groups["Major"].Value,NumberStyles.Integer,CultureInfo.InvariantCulture,out var major) ||
!int.TryParse (
match.Groups["Minor"].Value,out var minor) ||
!int.TryParse (
match.Groups["Patch"].Value,out var patch)) {
return false;
}
semVersion = new SemVersion (
major,match.Groups["PreRelease"]?.Value,match.Groups["Meta"]?.Value,version);
return true;
}
public bool Equals (SemVersion other)
{
return Major == other.Major
&& Minor == other.Minor
&& Patch == other.Patch
&& string.Equals(PreRelease,other.PreRelease,StringComparison.OrdinalIgnoreCase)
&& string.Equals(Meta,other.Meta,StringComparison.OrdinalIgnoreCase);
}
public int CompareTo (SemVersion other)
{
if (Equals(other))
{
return 0;
}
if (Major > other.Major) {
return 1;
}
if (Major < other.Major) {
return -1;
}
if (Minor > other.Minor) {
return 1;
}
if (Minor < other.Minor) {
return -1;
}
if (Patch > other.Patch) {
return 1;
}
if (Patch < other.Patch) {
return -1;
}
switch(StringComparer.InvariantCultureIgnoreCase.Compare(PreRelease,other.PreRelease)) {
case 1:
return 1;
case -1:
return -1;
default:
return StringComparer.InvariantCultureIgnoreCase.Compare (Meta,other.Meta);
}
}
public int CompareTo (object obj)
{
return (obj is SemVersion semVersion)
? CompareTo (semVersion)
: -1;
}
public override bool Equals (object obj)
{
return (obj is SemVersion semVersion)
&& Equals (semVersion);
}
public override int GetHashCode ()
{
unchecked {
var hashCode = Major;
hashCode = (hashCode * 397) ^ Minor;
hashCode = (hashCode * 397) ^ Patch;
hashCode = (hashCode * 397) ^ (PreRelease != null ? StringComparer.OrdinalIgnoreCase.GetHashCode (PreRelease) : 0);
hashCode = (hashCode * 397) ^ (Meta != null ? StringComparer.OrdinalIgnoreCase.GetHashCode (Meta) : 0);
return hashCode;
}
}
public override string ToString ()
=> VersionString;
// Define the is greater than operator.
public static bool operator > (SemVersion operand1,SemVersion operand2)
=> operand1.CompareTo (operand2) == 1;
// Define the is less than operator.
public static bool operator < (SemVersion operand1,SemVersion operand2)
=> operand1.CompareTo (operand2) == -1;
// Define the is greater than or equal to operator.
public static bool operator >= (SemVersion operand1,SemVersion operand2)
=> operand1.CompareTo (operand2) >= 0;
// Define the is less than or equal to operator.
public static bool operator <= (SemVersion operand1,SemVersion operand2)
=> operand1.CompareTo (operand2) <= 0;
}
要点
作为参考,完整的工作解决方案可以在下面的要点中找到 https://gist.github.com/devlead/ca566f58457f558dd33484a73f1352ed#file-build-cake
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。