Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions dotnet/src/Devolutions.Pinget.Cli/ImportCommandHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using System.Text.Json;
using Devolutions.Pinget.Core;

namespace Devolutions.Pinget.Cli;

internal sealed record ImportPackage(string Id, string? SourceName, string? Version, JsonElement Entry = default);

internal static class ImportCommandHandler
{
internal static int Run(
IEnumerable<ImportPackage> packages,
bool dryRun,
bool ignoreUnavailable,
bool noUpgrade,
Func<ImportPackage, bool> isInstalled,
Func<ImportPackage, InstallResult> install)
{
int total = 0;
int skipped = 0;
bool hasFailures = false;

foreach (var package in packages)
{
if (dryRun)
{
Console.WriteLine($"[dry-run] Would install: {package.Id}");
}
else
{
try
{
if (noUpgrade && isInstalled(package))
{
Console.WriteLine($"[no-upgrade] Skipping already installed package: {package.Id}");
skipped++;
}
else
{
Console.Write($"Installing {package.Id}...");
var result = install(package);
if (result.NoOp)
{
Console.WriteLine(" no-op");
PrintWarnings(result.Warnings);
skipped++;
}
else
{
PrintWarnings(result.Warnings);
Console.WriteLine(result.Success ? " done" : $" failed (exit {result.ExitCode})");
hasFailures |= !result.Success;
}
}
}
catch (Exception ex) when (ignoreUnavailable && CanIgnoreUnavailableImportFailure(ex))
{
Console.WriteLine(" unavailable");
Console.Error.WriteLine($"warning: Skipping unavailable package '{package.Id}': {ex.Message}");
skipped++;
}
catch (Exception ex)
{
Console.Error.WriteLine($" error: {ex.Message}");
hasFailures = true;
}
}
total++;
}

if (!dryRun && skipped > 0)
Console.WriteLine($"Skipped {skipped} package(s).");
Console.WriteLine($"{total} package(s) {(dryRun ? "would be installed" : "processed")}.");
return hasFailures ? 1 : 0;
}

private static bool CanIgnoreUnavailableImportFailure(Exception ex) =>
ex is InvalidOperationException &&
(ex.Message.Contains("No package matched the query", StringComparison.OrdinalIgnoreCase) ||
ex.Message.Contains("No package matched the supplied query", StringComparison.OrdinalIgnoreCase) ||
ex.Message.Contains("No applicable installer found", StringComparison.OrdinalIgnoreCase));

private static void PrintWarnings(IEnumerable<string> warnings)
{
foreach (var warning in warnings)
Console.Error.WriteLine($"warning: {warning}");
}
}
87 changes: 24 additions & 63 deletions dotnet/src/Devolutions.Pinget.Cli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1139,69 +1139,35 @@
var sources = doc.RootElement.GetProperty("Sources").EnumerateArray().ToList();

using var repo = Repository.Open();
int total = 0;
int skipped = 0;
foreach (var source in sources)
var importPackages = sources.SelectMany(source =>
{
var sourceName = source.TryGetProperty("SourceDetails", out var sourceDetails)
? GetJsonString(sourceDetails, "Name")
: null;
var packages = source.GetProperty("Packages").EnumerateArray().ToList();
foreach (var pkg in packages)
{
var pkgId = pkg.GetProperty("PackageIdentifier").GetString()!;
var pkgVersion = ignoreVersions ? null : GetJsonString(pkg, "Version");
if (dryRun)
{
Console.WriteLine($"[dry-run] Would install: {pkgId}");
}
else if (noUpgrade && IsInstalledPackagePresent(repo, pkgId, sourceName))
{
Console.WriteLine($"[no-upgrade] Skipping already installed package: {pkgId}");
skipped++;
}
else
return source.GetProperty("Packages").EnumerateArray().Select(pkg => new ImportPackage(
pkg.GetProperty("PackageIdentifier").GetString()!,
sourceName,
ignoreVersions ? null : GetJsonString(pkg, "Version"),
pkg));
}).ToList();

ctx.ExitCode = ImportCommandHandler.Run(
importPackages,
dryRun,
ignoreUnavailable,
noUpgrade,
pkg => IsInstalledPackagePresent(repo, pkg.Id, pkg.SourceName),
pkg => repo.Install(ImportInstallRequestFactory.Create(
new PackageQuery
{
try
{
Console.Write($"Installing {pkgId}...");
var result = repo.Install(ImportInstallRequestFactory.Create(
new PackageQuery
{
Id = pkgId,
Source = sourceName,
Exact = true,
Version = pkgVersion,
},
pkg,
acceptPackageAgreements,
noUpgrade));
if (result.NoOp)
{
Console.WriteLine(" no-op");
PrintWarnings(result.Warnings);
skipped++;
}
else
{
PrintWarnings(result.Warnings);
Console.WriteLine(result.Success ? " done" : $" failed (exit {result.ExitCode})");
}
}
catch (Exception ex) when (ignoreUnavailable && CanIgnoreUnavailableImportFailure(ex))
{
Console.WriteLine(" unavailable");
Console.Error.WriteLine($"warning: Skipping unavailable package '{pkgId}': {ex.Message}");
skipped++;
}
catch (Exception ex) { Console.Error.WriteLine($" error: {ex.Message}"); }
}
total++;
}
}
if (!dryRun && skipped > 0)
Console.WriteLine($"Skipped {skipped} package(s).");
Console.WriteLine($"{total} package(s) {(dryRun ? "would be installed" : "processed")}.");
Id = pkg.Id,
Source = pkg.SourceName,
Exact = true,
Version = pkg.Version,
},
pkg.Entry,
acceptPackageAgreements,
noUpgrade)));
});

// ── Add all commands to root ──
Expand Down Expand Up @@ -1770,11 +1736,6 @@ static bool IsInstalledPackagePresent(Repository repo, string packageId, string?
Count = 1,
}).Matches.Count > 0;

static bool CanIgnoreUnavailableImportFailure(Exception ex) =>
ex is InvalidOperationException &&
(ex.Message.Contains("No package matched the query", StringComparison.OrdinalIgnoreCase) ||
ex.Message.Contains("No applicable installer found", StringComparison.OrdinalIgnoreCase));

void WriteJsonNode(JsonNode value, OutputFormat output)
{
switch (output)
Expand Down
Loading
Loading