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 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 [опции]"); Console.WriteLine(); Console.WriteLine("Опции:"); Console.WriteLine(" --package-id ID пакета для скачивания (обязательный)"); Console.WriteLine(" --version Версия пакета (по умолчанию последняя стабильная)"); Console.WriteLine(" --framework Целевой framework (по умолчанию netstandard2.0)"); Console.WriteLine(" --output-dir Папка для сохранения пакетов (по умолчанию ./nuget-packages)"); Console.WriteLine(" --source Источник 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 [опции]"); Console.WriteLine(); Console.WriteLine("Опции:"); Console.WriteLine(" --package-id ID пакета для скачивания (обязательный)"); Console.WriteLine(" --version Версия пакета (по умолчанию последняя стабильная)"); Console.WriteLine(" --framework Целевой framework (по умолчанию netstandard2.0)"); Console.WriteLine(" --output-dir Папка для сохранения пакетов (по умолчанию ./nuget-packages)"); Console.WriteLine(" --source Источник 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(); // Определяем версию пакета 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(); var processedPackages = new HashSet(); 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(); 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 packagesToDownload, HashSet 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(); 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}"); } } }