Files
nuget-downloader/Program.cs
2025-07-29 11:09:34 +05:00

240 lines
11 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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}");
}
}
}