mirror of
https://github.com/quamotion/dotnet-packaging.git
synced 2026-10-05 13:00:27 +00:00
Draft dotnet-thirdparty, a tool which can be used to generate third party notice files for .NET software.
This commit is contained in:
@@ -15,6 +15,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-rpm", "dotnet-rpm\do
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-deb", "dotnet-deb\dotnet-deb.csproj", "{CB69DD41-E3DA-4418-8A32-419E1B5642DD}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-thirdparty", "dotnet-thirdparty\dotnet-thirdparty.csproj", "{3018EDCF-625E-4A1B-85BB-6C575EB74771}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -45,6 +47,10 @@ Global
|
||||
{CB69DD41-E3DA-4418-8A32-419E1B5642DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CB69DD41-E3DA-4418-8A32-419E1B5642DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CB69DD41-E3DA-4418-8A32-419E1B5642DD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3018EDCF-625E-4A1B-85BB-6C575EB74771}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3018EDCF-625E-4A1B-85BB-6C575EB74771}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3018EDCF-625E-4A1B-85BB-6C575EB74771}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3018EDCF-625E-4A1B-85BB-6C575EB74771}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
22
dotnet-thirdparty/GitHubLicense.cs
vendored
Normal file
22
dotnet-thirdparty/GitHubLicense.cs
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace DotNet.ThirdParty
|
||||
{
|
||||
class GitHubLicense
|
||||
{
|
||||
[JsonProperty("key")]
|
||||
public string Key { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("spdx_id")]
|
||||
public string Spdxid { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public string Url { get; set; }
|
||||
|
||||
[JsonProperty("node_id")]
|
||||
public string NodeId { get; set; }
|
||||
}
|
||||
}
|
||||
38
dotnet-thirdparty/GitHubProjectLicense.cs
vendored
Normal file
38
dotnet-thirdparty/GitHubProjectLicense.cs
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace DotNet.ThirdParty
|
||||
{
|
||||
class GitHubProjectLicense
|
||||
{
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("path")]
|
||||
public string Path { get; set; }
|
||||
|
||||
[JsonProperty("sha")]
|
||||
public string Sha { get; set; }
|
||||
|
||||
[JsonProperty("size")]
|
||||
public int Size { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public string Url { get; set; }
|
||||
|
||||
[JsonProperty("html_url")]
|
||||
public string HtmlUrl { get; set; }
|
||||
|
||||
[JsonProperty("git_url")]
|
||||
public string GitUrl { get; set; }
|
||||
|
||||
[JsonProperty("download_url")]
|
||||
public string DownloadUrl { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
// Content, encoding & links are ignored
|
||||
[JsonProperty("license")]
|
||||
public GitHubLicense License { get; set; }
|
||||
}
|
||||
}
|
||||
10
dotnet-thirdparty/ILicenseAnalysisService.cs
vendored
Normal file
10
dotnet-thirdparty/ILicenseAnalysisService.cs
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DotNet.ThirdParty
|
||||
{
|
||||
interface ILicenseAnalysisService
|
||||
{
|
||||
Task<List<ThirdPartyNotice>> AnalyzeProject(string projectAssetsPath, string nugetPackageRoot, string licenseCacheDirectory);
|
||||
}
|
||||
}
|
||||
11
dotnet-thirdparty/ILicenseResolver.cs
vendored
Normal file
11
dotnet-thirdparty/ILicenseResolver.cs
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DotNet.ThirdParty
|
||||
{
|
||||
interface ILicenseResolver
|
||||
{
|
||||
Task<License> GetSpdxLicense(string identifier, string licenseCacheFolder);
|
||||
Task<License> GetLicenseFromUrl(string url, string licenseCacheFolder);
|
||||
Task<License> GetLicenseFromGitHubUrl(string url, string licenseCacheFolder);
|
||||
}
|
||||
}
|
||||
11
dotnet-thirdparty/IProjectAssetsService.cs
vendored
Normal file
11
dotnet-thirdparty/IProjectAssetsService.cs
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace DotNet.ThirdParty
|
||||
{
|
||||
interface IProjectAssetsService
|
||||
{
|
||||
string ProjectAssetsPath { get; }
|
||||
string NuGetPackageRoot { get; }
|
||||
string IntermediateOutputPath { get; }
|
||||
|
||||
bool AnalyzeProjectAsset(string projectFilePath, bool restore);
|
||||
}
|
||||
}
|
||||
14
dotnet-thirdparty/License.cs
vendored
Normal file
14
dotnet-thirdparty/License.cs
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace DotNet.ThirdParty
|
||||
{
|
||||
class License
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Text { get; set; }
|
||||
public string LicenseId { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return this.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
118
dotnet-thirdparty/LicenseAnalysisService.cs
vendored
Normal file
118
dotnet-thirdparty/LicenseAnalysisService.cs
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
using NuGet.Common;
|
||||
using NuGet.Packaging.Core;
|
||||
using NuGet.Packaging.Licenses;
|
||||
using NuGet.ProjectModel;
|
||||
using NuGet.Protocol;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO.Abstractions;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DotNet.ThirdParty
|
||||
{
|
||||
internal class LicenseAnalysisService : ILicenseAnalysisService
|
||||
{
|
||||
private readonly IFileSystem fileSystem;
|
||||
private readonly ILicenseResolver licenseResolver;
|
||||
|
||||
public LicenseAnalysisService(IFileSystem fileSystem, ILicenseResolver licenseResolver)
|
||||
{
|
||||
this.fileSystem = fileSystem;
|
||||
this.licenseResolver = licenseResolver;
|
||||
}
|
||||
|
||||
public async Task<List<ThirdPartyNotice>> AnalyzeProject(string projectAssetsPath, string nugetPackageRoot, string licenseCacheDirectory)
|
||||
{
|
||||
List<ThirdPartyNotice> notices = new List<ThirdPartyNotice>();
|
||||
|
||||
// Load the lock file
|
||||
string lockFilePath = this.fileSystem.Path.Combine(projectAssetsPath);
|
||||
var lockFile = LockFileUtilities.GetLockFile(lockFilePath, NullLogger.Instance);
|
||||
|
||||
// TODO: Make this selection dynamic
|
||||
var target = lockFile.Targets[0];
|
||||
|
||||
foreach (var library in target.Libraries)
|
||||
{
|
||||
bool isDeployed =
|
||||
library.RuntimeAssemblies.Any()
|
||||
|| library.NativeLibraries.Any()
|
||||
|| library.ContentFiles.Any(c => c.CopyToOutput);
|
||||
|
||||
if (!isDeployed)
|
||||
{
|
||||
Debug.WriteLine($"Ignoring library {library.Name} because it has no runtime assemblies, native libraries or content files");
|
||||
continue;
|
||||
}
|
||||
|
||||
var packageIdentity = new PackageIdentity(library.Name, library.Version);
|
||||
var packageInfo = LocalFolderUtility.GetPackageV3(nugetPackageRoot, packageIdentity, NullLogger.Instance);
|
||||
|
||||
if (packageInfo == null)
|
||||
{
|
||||
Debug.WriteLine($"Ignoring library {library.Name} because its NuGet package could not be found");
|
||||
continue;
|
||||
}
|
||||
|
||||
var copyright = !string.IsNullOrWhiteSpace(packageInfo.Nuspec.GetCopyright()) ? packageInfo.Nuspec.GetCopyright() : $"Copyright (c) {packageInfo.Nuspec.GetAuthors()}";
|
||||
var licenseMetadata = packageInfo.Nuspec.GetLicenseMetadata();
|
||||
var licenseUrl = packageInfo.Nuspec.GetLicenseUrl();
|
||||
var projectUrl = packageInfo.Nuspec.GetProjectUrl();
|
||||
var packageName = !string.IsNullOrWhiteSpace(packageInfo.Nuspec.GetTitle()) ? packageInfo.Nuspec.GetTitle() : packageInfo.Nuspec.GetId();
|
||||
var packageUrl = packageInfo.Nuspec.GetProjectUrl();
|
||||
|
||||
if (copyright == null && licenseMetadata == null && licenseUrl == null)
|
||||
{
|
||||
Debug.WriteLine($"Ignoring library {library.Name} because no license information could be found");
|
||||
continue;
|
||||
}
|
||||
|
||||
License license = null;
|
||||
|
||||
if (license == null
|
||||
&& licenseMetadata != null
|
||||
&& licenseMetadata.LicenseExpression.Type == LicenseExpressionType.License)
|
||||
{
|
||||
var nugetLicense = (NuGetLicense)licenseMetadata.LicenseExpression;
|
||||
license = await this.licenseResolver.GetSpdxLicense(nugetLicense.Identifier, licenseCacheDirectory).ConfigureAwait(false);
|
||||
}
|
||||
else if (license == null
|
||||
&& licenseUrl != null
|
||||
&& licenseUrl.Contains("github.com/"))
|
||||
{
|
||||
license = await this.licenseResolver.GetLicenseFromGitHubUrl(licenseUrl, licenseCacheDirectory).ConfigureAwait(false);
|
||||
}
|
||||
else if (license == null
|
||||
&& projectUrl.Contains("github.com/"))
|
||||
{
|
||||
license = await this.licenseResolver.GetLicenseFromGitHubUrl(projectUrl, licenseCacheDirectory).ConfigureAwait(false);
|
||||
}
|
||||
else if (license == null
|
||||
&& licenseUrl != null)
|
||||
{
|
||||
license = await this.licenseResolver.GetLicenseFromUrl(licenseUrl, licenseCacheDirectory).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (license != null)
|
||||
{
|
||||
notices.Add(new ThirdPartyNotice()
|
||||
{
|
||||
Copyright = copyright,
|
||||
PackageName = packageName,
|
||||
PackageUrl = packageUrl,
|
||||
License = license
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.WriteLine($"Ignoring library {library.Name} because no license information could be found");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return notices;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
135
dotnet-thirdparty/LicenseResolver.cs
vendored
Normal file
135
dotnet-thirdparty/LicenseResolver.cs
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DotNet.ThirdParty
|
||||
{
|
||||
class LicenseResolver : ILicenseResolver
|
||||
{
|
||||
private readonly IFileSystem fileSystem;
|
||||
private readonly JsonSerializer serializer = new JsonSerializer();
|
||||
private readonly SHA256 sha = SHA256.Create();
|
||||
|
||||
public LicenseResolver(IFileSystem fileSystem)
|
||||
{
|
||||
this.fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
|
||||
}
|
||||
|
||||
public async Task<License> GetSpdxLicense(string identifier, string licenseCacheFolder)
|
||||
{
|
||||
var licensePath = await this.DownloadFile($"http://spdx.org/licenses/{identifier}.json", $"{identifier}.json", licenseCacheFolder).ConfigureAwait(false);
|
||||
|
||||
using (var stream = this.fileSystem.File.OpenRead(licensePath))
|
||||
using (var streamReader = new StreamReader(stream))
|
||||
using (var jsonTextReader = new JsonTextReader(streamReader))
|
||||
{
|
||||
var licenseEntry = serializer.Deserialize<SpdxLicenseEntry>(jsonTextReader);
|
||||
|
||||
return new License()
|
||||
{
|
||||
Name = licenseEntry.Name,
|
||||
LicenseId = licenseEntry.LicenseId,
|
||||
Text = licenseEntry.LicenseText
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<License> GetLicenseFromUrl(string url, string licenseCacheFolder)
|
||||
{
|
||||
var licensePath = await this.DownloadFile(url, licenseCacheFolder).ConfigureAwait(false);
|
||||
|
||||
// Something went wrong downloading the license file. Bail out.
|
||||
if (!this.fileSystem.File.Exists(licensePath))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new License()
|
||||
{
|
||||
Text = this.fileSystem.File.ReadAllText(licensePath)
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<License> GetLicenseFromGitHubUrl(string url, string licenseCacheFolder)
|
||||
{
|
||||
// Get the owner and repo
|
||||
string[] parts = url.Substring(8).Split(new char[] { '/' });
|
||||
var owner = parts[1];
|
||||
var repo = parts[2];
|
||||
|
||||
var licenseUrl = $"https://api.github.com/repos/{owner}/{repo}/license";
|
||||
var localName = $"github_{owner}_{repo}.json";
|
||||
|
||||
var licensePath = await this.DownloadFile(licenseUrl, localName, licenseCacheFolder).ConfigureAwait(false);
|
||||
|
||||
// Something went wrong downloading the license file. Bail out.
|
||||
if (!this.fileSystem.File.Exists(licensePath))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (var stream = this.fileSystem.File.OpenRead(licensePath))
|
||||
using (var streamReader = new StreamReader(stream))
|
||||
using (var jsonTextReader = new JsonTextReader(streamReader))
|
||||
{
|
||||
var licenseEntry = serializer.Deserialize<GitHubProjectLicense>(jsonTextReader);
|
||||
|
||||
if (licenseEntry.License.Spdxid != "NOASSERTION")
|
||||
{
|
||||
return await this.GetSpdxLicense(licenseEntry.License.Spdxid, licenseCacheFolder);
|
||||
}
|
||||
else
|
||||
{
|
||||
var licenseText = await this.DownloadFile(licenseEntry.DownloadUrl, licenseCacheFolder);
|
||||
|
||||
return new License()
|
||||
{
|
||||
Text = this.fileSystem.File.ReadAllText(licenseText)
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public Task<string> DownloadFile(string url, string licenseCacheFolder)
|
||||
{
|
||||
var hash = BitConverter.ToString(this.sha.ComputeHash(Encoding.UTF8.GetBytes(url))).Replace("-", string.Empty);
|
||||
return DownloadFile(url, hash, licenseCacheFolder);
|
||||
}
|
||||
|
||||
public async Task<string> DownloadFile(string url, string localName, string licenseCacheFolder)
|
||||
{
|
||||
var localPath = this.fileSystem.Path.Combine(licenseCacheFolder, localName);
|
||||
|
||||
if (!this.fileSystem.File.Exists(localPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("dotnet-thirdparty", "1.0.0"));
|
||||
|
||||
using (var response = await client.GetAsync(url).ConfigureAwait(false))
|
||||
using (var remoteStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
|
||||
using (var localStream = File.Open(localPath, FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
await remoteStream.CopyToAsync(localStream).ConfigureAwait(false);
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException)
|
||||
{
|
||||
this.fileSystem.File.Delete(localPath);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return localPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
132
dotnet-thirdparty/Program.cs
vendored
Normal file
132
dotnet-thirdparty/Program.cs
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
using DotNetOutdated.Exceptions;
|
||||
using DotNetOutdated.Services;
|
||||
using McMaster.Extensions.CommandLineUtils;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Newtonsoft.Json;
|
||||
using Nustache.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DotNet.ThirdParty
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static int Main(string[] args)
|
||||
{
|
||||
using (var services = new ServiceCollection()
|
||||
.AddSingleton<IFileSystem, FileSystem>()
|
||||
.AddSingleton<IProjectDiscoveryService, ProjectDiscoveryService>()
|
||||
.AddSingleton<ILicenseAnalysisService, LicenseAnalysisService>()
|
||||
.AddSingleton<IProjectAssetsService, ProjectAssetsService>()
|
||||
.AddSingleton<ILicenseResolver, LicenseResolver>()
|
||||
.BuildServiceProvider())
|
||||
{
|
||||
var app = new CommandLineApplication<Program>
|
||||
{
|
||||
ThrowOnUnexpectedArgument = true,
|
||||
FullName = $"dotnet thirdparty",
|
||||
LongVersionGetter = () => ThisAssembly.AssemblyInformationalVersion,
|
||||
ExtendedHelpText = $"{Environment.NewLine}See https://github.com/qmfrederik/dotnet-packaging for more information"
|
||||
};
|
||||
app.Conventions
|
||||
.UseDefaultConventions()
|
||||
.UseConstructorInjection(services);
|
||||
|
||||
return app.Execute(args);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IFileSystem fileSystem;
|
||||
private readonly IProjectDiscoveryService projectDiscoveryService;
|
||||
|
||||
[Argument(0, Description = "The path to a .csproj or .fsproj file, or to a directory containing a .NET Core project. " +
|
||||
"If none is specified, the current directory will be used.")]
|
||||
public string Path { get; set; }
|
||||
|
||||
[Argument(1, "--no-restore", Description = "Do not restore the project before analyzing the third party license notices.")]
|
||||
public bool SkipRestore { get; set; } = true;
|
||||
|
||||
private readonly ILicenseAnalysisService licenseAnalysisService;
|
||||
private readonly IProjectAssetsService projectAssetsService;
|
||||
|
||||
public Program(IFileSystem fileSystem, IProjectDiscoveryService projectDiscoveryService, IProjectAssetsService projectAssetsService, ILicenseAnalysisService licenseAnalysisService)
|
||||
{
|
||||
this.fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
|
||||
this.projectDiscoveryService = projectDiscoveryService ?? throw new ArgumentNullException(nameof(projectDiscoveryService));
|
||||
this.projectAssetsService = projectAssetsService ?? throw new ArgumentNullException(nameof(projectAssetsService));
|
||||
this.licenseAnalysisService = licenseAnalysisService ?? throw new ArgumentNullException(nameof(licenseAnalysisService));
|
||||
}
|
||||
|
||||
public async Task<int> OnExecute(CommandLineApplication app, IConsole console)
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine($"dotnet thirdparty ({ThisAssembly.AssemblyInformationalVersion})");
|
||||
|
||||
// If no path is set, use the current directory
|
||||
if (string.IsNullOrEmpty(Path))
|
||||
Path = this.fileSystem.Directory.GetCurrentDirectory();
|
||||
|
||||
// Get all the projects
|
||||
console.Write("Discovering project...");
|
||||
string projectFilePath = this.projectDiscoveryService.DiscoverProject(Path);
|
||||
console.WriteLine();
|
||||
|
||||
console.Write($"Analyzing '{projectFilePath}' dependencies");
|
||||
if (!this.projectAssetsService.AnalyzeProjectAsset(projectFilePath, restore: !this.SkipRestore))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
var licenseCachePath = this.fileSystem.Path.Combine(this.projectAssetsService.IntermediateOutputPath, "ThirdPartyNotices");
|
||||
if (!this.fileSystem.Directory.Exists(licenseCachePath))
|
||||
{
|
||||
this.fileSystem.Directory.CreateDirectory(licenseCachePath);
|
||||
}
|
||||
|
||||
var licenses = await this.licenseAnalysisService.AnalyzeProject(this.projectAssetsService.ProjectAssetsPath, this.projectAssetsService.NuGetPackageRoot, licenseCachePath).ConfigureAwait(false);
|
||||
|
||||
// Cache the license information in a JSON file
|
||||
using (var stream = this.fileSystem.File.Open(this.fileSystem.Path.Combine(this.projectAssetsService.IntermediateOutputPath, "ThirdPartyNotices", "licenses.json"), FileMode.Create, FileAccess.Write))
|
||||
using (var streamWriter = new StreamWriter(stream))
|
||||
using (var jsonTextWriter = new JsonTextWriter(streamWriter))
|
||||
{
|
||||
JsonSerializer serializer = new JsonSerializer();
|
||||
serializer.Serialize(jsonTextWriter, licenses);
|
||||
}
|
||||
|
||||
Helpers.Register(nameof(TitleLine), TitleLine);
|
||||
|
||||
// Inspiration: https://www.nexb.com/blog/oss_attribution_obligations.html
|
||||
this.fileSystem.File.WriteAllText(
|
||||
this.fileSystem.Path.Combine(this.projectAssetsService.IntermediateOutputPath, "ThirdPartyNotices", "THIRD-PARTY-NOTICES.TXT"),
|
||||
Render.FileToString("THIRD-PARTY-NOTICES.TXT.template", licenses));
|
||||
|
||||
console.WriteLine();
|
||||
return 0;
|
||||
}
|
||||
catch (CommandValidationException e)
|
||||
{
|
||||
console.Error.WriteLine(e.Message);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void TitleLine(RenderContext context, IList<object> arguments, IDictionary<string, object> options, RenderBlock fn, RenderBlock inverse)
|
||||
{
|
||||
if (arguments != null && arguments.Count > 0 && arguments[0] != null && arguments[0] is string)
|
||||
{
|
||||
context.Write(TitleLine(arguments[0] as string));
|
||||
}
|
||||
}
|
||||
|
||||
private static string TitleLine(string title)
|
||||
{
|
||||
return new string('-', title.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
35
dotnet-thirdparty/ProjectAssetsService.cs
vendored
Normal file
35
dotnet-thirdparty/ProjectAssetsService.cs
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
using Microsoft.Build.Locator;
|
||||
using MSBuildProject = Microsoft.Build.Evaluation.Project;
|
||||
|
||||
namespace DotNet.ThirdParty
|
||||
{
|
||||
class ProjectAssetsService : IProjectAssetsService
|
||||
{
|
||||
static ProjectAssetsService()
|
||||
{
|
||||
MSBuildLocator.RegisterDefaults();
|
||||
}
|
||||
|
||||
public string ProjectAssetsPath { get; private set; }
|
||||
public string NuGetPackageRoot { get; private set; }
|
||||
public string IntermediateOutputPath { get; private set; }
|
||||
|
||||
public bool AnalyzeProjectAsset(string projectFilePath, bool restore)
|
||||
{
|
||||
var project = new MSBuildProject(projectFilePath);
|
||||
|
||||
if (restore)
|
||||
{
|
||||
if (!project.Build("Restore"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
this.ProjectAssetsPath = project.GetPropertyValue("ProjectAssetsFile");
|
||||
this.NuGetPackageRoot = project.GetPropertyValue("NugetPackageRoot");
|
||||
this.IntermediateOutputPath = project.GetPropertyValue("IntermediateOutputPath");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
49
dotnet-thirdparty/ProjectDiscoveryService.cs
vendored
Normal file
49
dotnet-thirdparty/ProjectDiscoveryService.cs
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
using DotNetOutdated.Exceptions;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using System.Linq;
|
||||
|
||||
namespace DotNetOutdated.Services
|
||||
{
|
||||
internal class ProjectDiscoveryService : IProjectDiscoveryService
|
||||
{
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
public ProjectDiscoveryService(IFileSystem fileSystem)
|
||||
{
|
||||
_fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
public string DiscoverProject(string path)
|
||||
{
|
||||
if (!(_fileSystem.File.Exists(path) || _fileSystem.Directory.Exists(path)))
|
||||
throw new CommandValidationException(string.Format(Resources.ValidationErrorMessages.DirectoryOrFileDoesNotExist, path));
|
||||
|
||||
var fileAttributes = _fileSystem.File.GetAttributes(path);
|
||||
|
||||
// If a directory was passed in, search for a .csproj or .fsproj file
|
||||
if (fileAttributes.HasFlag(FileAttributes.Directory))
|
||||
{
|
||||
// Try and find individual projects
|
||||
var projectFiles = _fileSystem.Directory.GetFiles(path, "*.csproj").Concat(_fileSystem.Directory.GetFiles(path, "*.fsproj")).ToArray();
|
||||
if (projectFiles.Length == 1)
|
||||
return _fileSystem.Path.GetFullPath(projectFiles[0]);
|
||||
|
||||
if (projectFiles.Length > 1)
|
||||
throw new CommandValidationException(string.Format(Resources.ValidationErrorMessages.DirectoryContainsMultipleProjects, path));
|
||||
|
||||
// At this point the path contains no projects, so throw an exception
|
||||
throw new CommandValidationException(string.Format(Resources.ValidationErrorMessages.DirectoryDoesNotContainSolutionsOrProjects, path));
|
||||
}
|
||||
|
||||
// If a .csproj or .fsproj file was passed, just return that
|
||||
if ((string.Compare(_fileSystem.Path.GetExtension(path), ".csproj", StringComparison.OrdinalIgnoreCase) == 0) ||
|
||||
(string.Compare(_fileSystem.Path.GetExtension(path), ".fsproj", StringComparison.OrdinalIgnoreCase) == 0))
|
||||
return _fileSystem.Path.GetFullPath(path);
|
||||
|
||||
// At this point, we know the file passed in is not a valid project or solution
|
||||
throw new CommandValidationException(string.Format(Resources.ValidationErrorMessages.FileNotAValidSolutionOrProject, path));
|
||||
}
|
||||
}
|
||||
}
|
||||
26
dotnet-thirdparty/SpdxLicenseEntry.cs
vendored
Normal file
26
dotnet-thirdparty/SpdxLicenseEntry.cs
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace DotNet.ThirdParty
|
||||
{
|
||||
class SpdxLicenseEntry
|
||||
{
|
||||
[JsonProperty("isDeprecatedLicenseId")]
|
||||
public bool IsDeprecatedLicenseId { get; set; }
|
||||
|
||||
[JsonProperty("licenseText")]
|
||||
public string LicenseText { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("licenseId")]
|
||||
public string LicenseId { get; set; }
|
||||
|
||||
[JsonProperty("seeAlso")]
|
||||
public Collection<string> SeeAlso { get; set; }
|
||||
|
||||
[JsonProperty("isOsiApproved")]
|
||||
public bool IsOsiApproved { get; set; }
|
||||
}
|
||||
}
|
||||
22
dotnet-thirdparty/THIRD-PARTY-NOTICES.TXT.template
vendored
Normal file
22
dotnet-thirdparty/THIRD-PARTY-NOTICES.TXT.template
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
This software product uses third-party libraries or other resources that
|
||||
may be distributed under different licenses.
|
||||
|
||||
In the event that we accidentally failed to list a required notice, please
|
||||
bring it to our attention.
|
||||
|
||||
The attached notices are provided for information only.
|
||||
|
||||
{{#.}}
|
||||
{{PackageName}}
|
||||
{{TitleLine PackageName}}
|
||||
|
||||
{{Copyright}}
|
||||
|
||||
{{#License.Name}}
|
||||
Licensed under the terms of {{License.Name}}.
|
||||
{{/License.Name}}
|
||||
{{^License.Name}}
|
||||
{{License.Text}}
|
||||
{{/License.Name}}
|
||||
|
||||
{{/.}}
|
||||
15
dotnet-thirdparty/ThirdPartyNotice.cs
vendored
Normal file
15
dotnet-thirdparty/ThirdPartyNotice.cs
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace DotNet.ThirdParty
|
||||
{
|
||||
class ThirdPartyNotice
|
||||
{
|
||||
public string PackageName { get; set; }
|
||||
public string PackageUrl { get; set; }
|
||||
public string Copyright { get; set; }
|
||||
public License License { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{this.PackageName} - {this.License.Name}";
|
||||
}
|
||||
}
|
||||
}
|
||||
17
dotnet-thirdparty/dotnet-outdated/CommandValidationException.cs
vendored
Normal file
17
dotnet-thirdparty/dotnet-outdated/CommandValidationException.cs
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace DotNetOutdated.Exceptions
|
||||
{
|
||||
public class CommandValidationException : Exception
|
||||
{
|
||||
public CommandValidationException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public CommandValidationException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
7
dotnet-thirdparty/dotnet-outdated/IProjectDiscoveryService.cs
vendored
Normal file
7
dotnet-thirdparty/dotnet-outdated/IProjectDiscoveryService.cs
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace DotNetOutdated.Services
|
||||
{
|
||||
internal interface IProjectDiscoveryService
|
||||
{
|
||||
string DiscoverProject(string path);
|
||||
}
|
||||
}
|
||||
21
dotnet-thirdparty/dotnet-outdated/LICENSE
vendored
Normal file
21
dotnet-thirdparty/dotnet-outdated/LICENSE
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Jerrie Pelser
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
108
dotnet-thirdparty/dotnet-outdated/ValidationErrorMessages.Designer.cs
generated
vendored
Normal file
108
dotnet-thirdparty/dotnet-outdated/ValidationErrorMessages.Designer.cs
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace DotNetOutdated.Resources {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class ValidationErrorMessages {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal ValidationErrorMessages() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DotNet.ThirdParty.dotnet_outdated.ValidationErrorMessages", typeof(ValidationErrorMessages).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Specify which project file to use because the directory '{0}' contains more than one..
|
||||
/// </summary>
|
||||
internal static string DirectoryContainsMultipleProjects {
|
||||
get {
|
||||
return ResourceManager.GetString("DirectoryContainsMultipleProjects", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Specify which solution file to use because the directory '{0}' contains more than one..
|
||||
/// </summary>
|
||||
internal static string DirectoryContainsMultipleSolutions {
|
||||
get {
|
||||
return ResourceManager.GetString("DirectoryContainsMultipleSolutions", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The directory '{0}' does not contain any solutions or projects..
|
||||
/// </summary>
|
||||
internal static string DirectoryDoesNotContainSolutionsOrProjects {
|
||||
get {
|
||||
return ResourceManager.GetString("DirectoryDoesNotContainSolutionsOrProjects", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The directory or file '{0}' does not exist..
|
||||
/// </summary>
|
||||
internal static string DirectoryOrFileDoesNotExist {
|
||||
get {
|
||||
return ResourceManager.GetString("DirectoryOrFileDoesNotExist", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The file '{0}' is not a valid solution or project..
|
||||
/// </summary>
|
||||
internal static string FileNotAValidSolutionOrProject {
|
||||
get {
|
||||
return ResourceManager.GetString("FileNotAValidSolutionOrProject", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
135
dotnet-thirdparty/dotnet-outdated/ValidationErrorMessages.resx
vendored
Normal file
135
dotnet-thirdparty/dotnet-outdated/ValidationErrorMessages.resx
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="DirectoryContainsMultipleProjects" xml:space="preserve">
|
||||
<value>Specify which project file to use because the directory '{0}' contains more than one.</value>
|
||||
</data>
|
||||
<data name="DirectoryContainsMultipleSolutions" xml:space="preserve">
|
||||
<value>Specify which solution file to use because the directory '{0}' contains more than one.</value>
|
||||
</data>
|
||||
<data name="DirectoryDoesNotContainSolutionsOrProjects" xml:space="preserve">
|
||||
<value>The directory '{0}' does not contain any solutions or projects.</value>
|
||||
</data>
|
||||
<data name="DirectoryOrFileDoesNotExist" xml:space="preserve">
|
||||
<value>The directory or file '{0}' does not exist.</value>
|
||||
</data>
|
||||
<data name="FileNotAValidSolutionOrProject" xml:space="preserve">
|
||||
<value>The file '{0}' is not a valid solution or project.</value>
|
||||
</data>
|
||||
</root>
|
||||
28
dotnet-thirdparty/dotnet-thirdparty.csproj
vendored
Normal file
28
dotnet-thirdparty/dotnet-thirdparty.csproj
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
<RootNamespace>DotNet.ThirdParty</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="2.2.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.1" />
|
||||
<PackageReference Include="NerdBank.GitVersioning" Version="2.3.167" PrivateAssets="All" />
|
||||
<PackageReference Include="System.IO.Abstractions" Version="3.0.2" />
|
||||
<PackageReference Include="NuGet.Versioning" Version="5.1.0" />
|
||||
<PackageReference Include="NuGet.Configuration" Version="5.1.0" />
|
||||
<PackageReference Include="NuGet.ProjectModel" Version="5.1.0" />
|
||||
<PackageReference Include="Microsoft.Build" Version="15.8.166" ExcludeAssets="runtime" />
|
||||
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="15.8.166" ExcludeAssets="runtime" />
|
||||
<PackageReference Include="Microsoft.Build.Locator" Version="1.2.2" />
|
||||
<PackageReference Include="Nustache" Version="1.16.0.10" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="THIRD-PARTY-NOTICES.TXT.template">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Reference in New Issue
Block a user