240 lines
11 KiB
C#
240 lines
11 KiB
C#
using NuGet.Configuration;
|
||
using NuGet.Frameworks;
|
||
using NuGet.Packaging;
|
||
using NuGet.Packaging.Core;
|
||
using NuGet.Protocol;
|
||
using NuGet.Protocol.Core.Types;
|
||
using NuGet.Resolver;
|
||
using NuGet.Versioning;
|
||
using NuGet.Common;
|
||
|
||
class Program
|
||
{
|
||
static async Task<int> Main(string[] args)
|
||
{
|
||
if (args.Length == 0 || args.Contains("--help") || args.Contains("-h"))
|
||
{
|
||
Console.WriteLine("Утилита для скачивания NuGet пакетов с зависимостями");
|
||
Console.WriteLine();
|
||
Console.WriteLine("Использование:");
|
||
Console.WriteLine(" dotnet run -- --package-id <ID> [опции]");
|
||
Console.WriteLine();
|
||
Console.WriteLine("Опции:");
|
||
Console.WriteLine(" --package-id <ID> ID пакета для скачивания (обязательный)");
|
||
Console.WriteLine(" --version <VERSION> Версия пакета (по умолчанию последняя стабильная)");
|
||
Console.WriteLine(" --framework <FRAME> Целевой framework (по умолчанию netstandard2.0)");
|
||
Console.WriteLine(" --output-dir <DIR> Папка для сохранения пакетов (по умолчанию ./nuget-packages)");
|
||
Console.WriteLine(" --source <URL> Источник NuGet (по умолчанию https://api.nuget.org/v3/index.json)");
|
||
Console.WriteLine();
|
||
Console.WriteLine("Примеры:");
|
||
Console.WriteLine(" dotnet run -- --package-id Npgsql --framework netstandard2.0");
|
||
Console.WriteLine(" dotnet run -- --package-id Npgsql --version 7.0.6 --output-dir ./my-packages");
|
||
return 0;
|
||
}
|
||
|
||
string? packageId = null;
|
||
string? version = null;
|
||
string framework = "netstandard2.0";
|
||
string outputDir = "./nuget-packages";
|
||
string source = "https://api.nuget.org/v3/index.json";
|
||
|
||
for (int i = 0; i < args.Length; i++)
|
||
{
|
||
switch (args[i])
|
||
{
|
||
case "--package-id":
|
||
if (i + 1 < args.Length) packageId = args[++i];
|
||
break;
|
||
case "--version":
|
||
if (i + 1 < args.Length) version = args[++i];
|
||
break;
|
||
case "--framework":
|
||
if (i + 1 < args.Length) framework = args[++i];
|
||
break;
|
||
case "--output-dir":
|
||
if (i + 1 < args.Length) outputDir = args[++i];
|
||
break;
|
||
case "--source":
|
||
if (i + 1 < args.Length) source = args[++i];
|
||
break;
|
||
case "--help":
|
||
case "-h":
|
||
Console.WriteLine("Использование: dotnet run -- --package-id <ID> [опции]");
|
||
Console.WriteLine();
|
||
Console.WriteLine("Опции:");
|
||
Console.WriteLine(" --package-id <ID> ID пакета для скачивания (обязательный)");
|
||
Console.WriteLine(" --version <VERSION> Версия пакета (по умолчанию последняя стабильная)");
|
||
Console.WriteLine(" --framework <FRAME> Целевой framework (по умолчанию netstandard2.0)");
|
||
Console.WriteLine(" --output-dir <DIR> Папка для сохранения пакетов (по умолчанию ./nuget-packages)");
|
||
Console.WriteLine(" --source <URL> Источник NuGet (по умолчанию https://api.nuget.org/v3/index.json)");
|
||
Console.WriteLine();
|
||
Console.WriteLine("Примеры:");
|
||
Console.WriteLine(" dotnet run -- --package-id Npgsql --framework netstandard2.0");
|
||
Console.WriteLine(" dotnet run -- --package-id Npgsql --version 7.0.6 --output-dir ./my-packages");
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
if (string.IsNullOrEmpty(packageId))
|
||
{
|
||
Console.WriteLine("Ошибка: --package-id обязателен");
|
||
return 1;
|
||
}
|
||
|
||
await DownloadPackageWithDependencies(packageId, version, framework, outputDir, source);
|
||
return 0;
|
||
}
|
||
|
||
static async Task DownloadPackageWithDependencies(string packageId, string? version, string framework, string outputDir, string source)
|
||
{
|
||
try
|
||
{
|
||
Console.WriteLine($"Скачивание пакета {packageId} с зависимостями...");
|
||
Console.WriteLine($"Framework: {framework}");
|
||
Console.WriteLine($"Источник: {source}");
|
||
Console.WriteLine($"Папка назначения: {outputDir}");
|
||
|
||
// Создаем папку назначения
|
||
Directory.CreateDirectory(outputDir);
|
||
|
||
// Настраиваем источник пакетов
|
||
var settings = Settings.LoadDefaultSettings(null);
|
||
var sourceRepository = Repository.Factory.GetCoreV3(source);
|
||
var resource = await sourceRepository.GetResourceAsync<FindPackageByIdResource>();
|
||
|
||
// Определяем версию пакета
|
||
NuGetVersion packageVersion;
|
||
if (string.IsNullOrEmpty(version))
|
||
{
|
||
var versions = await resource.GetAllVersionsAsync(packageId, new SourceCacheContext(), NullLogger.Instance, CancellationToken.None);
|
||
packageVersion = versions.Where(v => !v.IsPrerelease).Max() ?? throw new InvalidOperationException($"Не найдена стабильная версия для пакета {packageId}");
|
||
Console.WriteLine($"Выбрана версия: {packageVersion}");
|
||
}
|
||
else
|
||
{
|
||
packageVersion = NuGetVersion.Parse(version);
|
||
}
|
||
|
||
// Создаем framework
|
||
var nugetFramework = NuGetFramework.Parse(framework);
|
||
|
||
// Собираем все зависимости
|
||
var packagesToDownload = new HashSet<PackageIdentity>();
|
||
var processedPackages = new HashSet<string>();
|
||
|
||
await CollectDependencies(packageId, packageVersion, nugetFramework, packagesToDownload, processedPackages, sourceRepository);
|
||
|
||
Console.WriteLine($"Найдено пакетов для скачивания: {packagesToDownload.Count}");
|
||
foreach (var package in packagesToDownload)
|
||
{
|
||
Console.WriteLine($" - {package.Id} {package.Version}");
|
||
}
|
||
|
||
// Скачиваем все пакеты
|
||
var cacheContext = new SourceCacheContext();
|
||
var downloadResource = await sourceRepository.GetResourceAsync<DownloadResource>();
|
||
|
||
foreach (var package in packagesToDownload)
|
||
{
|
||
await DownloadPackage(package, downloadResource, cacheContext, outputDir);
|
||
}
|
||
|
||
Console.WriteLine($"Все пакеты успешно скачаны в папку: {outputDir}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"Ошибка: {ex.Message}");
|
||
Environment.Exit(1);
|
||
}
|
||
}
|
||
|
||
static async Task CollectDependencies(string packageId, NuGetVersion version, NuGetFramework framework,
|
||
HashSet<PackageIdentity> packagesToDownload, HashSet<string> processedPackages, SourceRepository sourceRepository)
|
||
{
|
||
var packageIdentity = new PackageIdentity(packageId, version);
|
||
var packageKey = $"{packageId}.{version}";
|
||
|
||
if (processedPackages.Contains(packageKey))
|
||
return;
|
||
|
||
processedPackages.Add(packageKey);
|
||
packagesToDownload.Add(packageIdentity);
|
||
|
||
try
|
||
{
|
||
// Получаем зависимости через PackageReader
|
||
var downloadResource = await sourceRepository.GetResourceAsync<DownloadResource>();
|
||
var cacheContext = new SourceCacheContext();
|
||
var downloadResult = await downloadResource.GetDownloadResourceResultAsync(
|
||
packageIdentity,
|
||
new PackageDownloadContext(cacheContext),
|
||
Path.GetTempPath(),
|
||
NullLogger.Instance,
|
||
CancellationToken.None);
|
||
|
||
if (downloadResult.Status == DownloadResourceResultStatus.Available)
|
||
{
|
||
using var packageReader = new PackageArchiveReader(downloadResult.PackageStream);
|
||
var nuspecReader = packageReader.NuspecReader;
|
||
var dependencyGroups = nuspecReader.GetDependencyGroups();
|
||
|
||
// Находим подходящую группу зависимостей для нашего framework
|
||
var dependencyGroup = dependencyGroups
|
||
.FirstOrDefault(dg => dg.TargetFramework == null || dg.TargetFramework == framework);
|
||
|
||
if (dependencyGroup != null)
|
||
{
|
||
Console.WriteLine($" Найдено зависимостей для {packageId}: {dependencyGroup.Packages.Count()}");
|
||
foreach (var dependency in dependencyGroup.Packages)
|
||
{
|
||
// Получаем версию зависимости
|
||
var dependencyVersion = dependency.VersionRange?.MinVersion ?? dependency.VersionRange?.MaxVersion;
|
||
if (dependencyVersion != null)
|
||
{
|
||
Console.WriteLine($" Зависимость: {dependency.Id} {dependencyVersion}");
|
||
await CollectDependencies(dependency.Id, dependencyVersion, framework, packagesToDownload, processedPackages, sourceRepository);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Console.WriteLine($" Нет зависимостей для {packageId} в framework {framework}");
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"Предупреждение: Не удалось получить зависимости для {packageId} {version}: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
static async Task DownloadPackage(PackageIdentity packageIdentity, DownloadResource downloadResource,
|
||
SourceCacheContext cacheContext, string outputDir)
|
||
{
|
||
Console.WriteLine($"Скачивание пакета: {packageIdentity.Id} {packageIdentity.Version}");
|
||
|
||
var downloadResult = await downloadResource.GetDownloadResourceResultAsync(
|
||
packageIdentity,
|
||
new PackageDownloadContext(cacheContext),
|
||
Path.GetTempPath(),
|
||
NullLogger.Instance,
|
||
CancellationToken.None);
|
||
|
||
if (downloadResult.Status == DownloadResourceResultStatus.Available)
|
||
{
|
||
var packagePath = Path.Combine(outputDir, $"{packageIdentity.Id}.{packageIdentity.Version}.nupkg");
|
||
|
||
using (var packageStream = downloadResult.PackageStream)
|
||
using (var fileStream = File.Create(packagePath))
|
||
{
|
||
await packageStream.CopyToAsync(fileStream);
|
||
}
|
||
|
||
Console.WriteLine($" ✓ Скачан: {packagePath}");
|
||
}
|
||
else
|
||
{
|
||
Console.WriteLine($" ✗ Ошибка скачивания: {packageIdentity.Id} {packageIdentity.Version}");
|
||
}
|
||
}
|
||
} |